Управление инфраструктурой с помощью terragrunt (terraform) и gitlab ci
В этом посте:
- Использование terraform модулей
- Организуем структуру каталогов с terraform модулями для terragrunt согласно вашей инфраструктуре
- Создание/Обновление/Удаление инфраструктуры одной terragrunt командой
- Настройка в gitlab ci для запуска и сохранения terraform lock и state в gitlab
- Бекап terraform state из gitlab
Terraform — это инструмент для управления инфраструктурой в облаке с использованием описания в виде кода.
Позволяет автоматизировать процесс развертывания и изменения инфраструктуры в облаке, предоставляя более надежное и прозрачное управление инфраструктурой.
Terragrunt — это обертка для Terraform, позволяющая решать проблемы, связанные с масштабированием и переиспользованием кода для настройки инфраструктуры.
Он позволяет повторно использовать конфигурационные параметры и поддерживает многоуровневые конфигурации и зависимости.
GitLab CI (Continuous Integration) — это система интеграции продолжения, которая позволяет пользователям автоматизировать процессы сборки, тестирования и деплоя для проектов, использующих репозиторий GitLab.
Он позволяет разработчикам начать проект быстро и просто, а также иметь возможность использовать настраиваемые пайплайны для автоматизации процессов сборки и деплоя.
Использование terraform модулей
Terraform модули — это предварительно настроенные компоненты Terraform, которые можно использовать для создания и управления различными инфраструктурными компонентами.
Они позволяют вам разбивать сложные конфигурации на повторно используемые компоненты, которые можно использовать для быстрого создания различных инфраструктурных решений.
Посты про terraform модули:
Будут использоваться модули:
- github.com/patsevanton/terraform-yandex-dns.git
- github.com/patsevanton/terraform-yandex-vpc-address.git
Организуем структуру каталогов с terraform модулями для terragrunt согласно вашей инфраструктуре
Можно использовать terraform модули с чистым terraform, но c terragrunt это будет удобнее:
- Код будет компактнее, а значит читабельнее
- Структура каталогов отражает то, как организована ваша инфраструктура.
- Можно создать/обновить/удалить инфраструктуру одной командой
- Можно периодически запускать plan и проверять изменилось что-нибудь
Посты про terragrunt:
Будет рассматриваться репозиторий https://gitlab.com/anton_patsev/gitlab_ci_terragrunt
Структура каталогов этого репозитория минимальная в целях объяснения принципов работы terragrunt:
.
├── dev
│ ├── env.hcl
│ └── group1
│ ├── dns
│ │ └── terragrunt.hcl
│ ├── group.hcl
│ └── vpc-address
│ └── terragrunt.hcl
├── README.md
└── terragrunt.hcl
Обычно инфраструктуру разделяют на какие-нибудь группы:
- dev/uat/prod
- vpc1/vpc2
- другие варианты
Пример структуры каталогов от самого terragrunt — https://github.com/gruntwork-io/terragrunt-infrastructure-live-example
Рассмотрим HCL конфиги
Рассмотрим корневой terragrunt.hcl.
Корневой terragrunt.hcl нужен для задания общих переменных, параметров.
# корневой terragrunt.hcl
locals { # переменные общие для всего проекта
env_vars = read_terragrunt_config(find_in_parent_folders("env.hcl")) # Функция read_terragrunt_config() используется для чтения конфига env.hcl (group.hcl),
group_vars = read_terragrunt_config(find_in_parent_folders("group.hcl")) # найденного с помощью функции find_in_parent_folders() в этой директории или вышележащих директориях до корневой директории.
project_id = "43542597" # project_id в gitlab
env = "dev" # Переменная, которая описывает директорию dev как dev окружение в этом проекте.
username = "anton_patsev" # ваш login в gitlab
string_path_relative_to_include = join(".", [replace("${path_relative_to_include()}", "/", "-"), "tfstate"]) # получение из пути dev/group1/dns получить имя tfstate: dev-group1-dns.tfstate
}
inputs = merge({
# merge в terragrunt используется для объединения входных параметров из нескольких источников.
# Это позволяет использовать общие параметры для нескольких конфигураций Terraform,
# а также позволяет переопределять параметры для конкретной конфигурации.
cloud_id = local.env_vars.locals.cloud_id
folder_id = local.env_vars.locals.folder_id
network_id = local.group_vars.locals.network_id
})
remote_state {
# В backend.tf определяется где сохраняется terraform state. В данном случае сохраняется в http сервисе.
backend = "http"
# Параметры для сохранения terraform state
config = {
address = "https://gitlab.com/api/v4/projects/${local.project_id}/terraform/state/${local.string_path_relative_to_include}"
lock_address = "https://gitlab.com/api/v4/projects/${local.project_id}/terraform/state/${local.string_path_relative_to_include}/lock"
unlock_address = "https://gitlab.com/api/v4/projects/${local.project_id}/terraform/state/${local.string_path_relative_to_include}/lock"
username = local.username # Login доступа к http сервису (в данном случае gitlab)
# Пароль доступа берется из переменной окружения
lock_method = "POST"
unlock_method = "DELETE"
retry_wait_min = 5
}
generate = {
path = "backend.tf"
# Создается файл backend.tf в каждом скачанном terraform модуле
if_exists = "overwrite_terragrunt"
}
}
Рассмотрим env.hcl.
В директории dev в конфиге env.hcl задаются параметры для dev окружения, например id folder или другие.
locals {
cloud_id = "b1gvct0b630bbm7i7v90" # cloud-patsevanton
folder_id = "b1g972v94kscfi3qmfmh" # default
}
Рассмотрим group.hcl.
В директории group1 в конфиге group.hcl задаются параметры для обособленной группы (group1), например network_id или другие.
locals {
network_id = "enprkje8ae9b74e0himb" # default
}
Рассмотрим terragrunt.hcl для вызова определенного terraform модуля. В качестве примера возьмем terragrunt.hcl из директории dns.
В директории group1 присутствуют 2 директории: dns и vpc-address. В каждой директории присутствует terragrunt.hcl.
terraform {
# Ссылка на terraform модуль и его тег или ветку
source = "github.com/patsevanton/terraform-yandex-dns.git//.?ref=main"
}
include {
# Этот блок кода используется для поиска и загрузки конфигурационных параметров из родительских папок.
# Это позволяет использовать один и тот же код для конфигурации нескольких подсистем.
path = find_in_parent_folders()
}
# Указывается зависимость текущего кода от vpc-address
dependency "vpc-address" {
config_path = "../vpc-address" # Указываем где искать vpc-address
# Mock_outputs_allowed_terraform_commands используется для того,
# чтобы позволить Terragrunt выполнять имитацию команд Terraform для проверки ваших конфигураций без изменения любого реального состояния.
# Это позволяет проверять и отлаживать ваши конфигурации до того, как вы будете применять их на самом деле.
mock_outputs_allowed_terraform_commands = ["init", "validate", "plan"]
# При выполнении terragrunt init/validate/plan в переменную external_ipv4_address будет подставлено фейковое значение
mock_outputs = {
external_ipv4_address = "fake_external_ipv4_address"
}
}
# Параметры, которые могут быть переданы в terraform модуль.
inputs = {
description = "grafana"
zone = "apatsev.org.ru."
name = "apatsev-org-ru"
public = true
recordset = [
{
name = "grafana1.apatsev.org.ru."
type = "A"
ttl = 600
data = [dependency.vpc-address.outputs.external_ipv4_address]
},
]
}
Запуск terragrunt в каждой директории
cd dev/group1/dns
terragrunt apply
cd ..
cd vpc-address
terragrunt apply
Создание/Обновление/Удаление инфраструктуры одной terragrunt командой
Можно создать/удалить инфраструктуру одной командой terragrunt run-all
:
cd dev
terragrunt run-all apply -auto-approve --terragrunt-non-interactive
Если вы хотите отладить и создать инфраструктуру вручную, необходимо закомментировать remote_state в корневом terragrunt.hcl
Настройка в gitlab ci для запуска и сохранения terraform lock и state в gitlab
Создайте Project access token с именем GITLAB_ACCESS_TOKEN согласно инструкции https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html
Чтобы передать Personal Access Token в переменную окружения GitLab откройте ваш проект, затем откройте Settings —> CI/CD и разверните Variables.
Создайте новую переменную окружения GITLAB_ACCESS_TOKEN и в качестве ее значения укажите содержимое Personal Access Token.
Для безопасности отметьте созданную переменную как protected.
Так же создайте новую переменную окружения YC_TOKEN и в качестве ее значения укажите содержимое OAuth-токена согласно инструкции https://cloud.yandex.ru/docs/iam/concepts/authorization/oauth-token
Для безопасности лучше использовать IAM-токен сервисного аккаунта.
Бекап terraform state из gitlab
curl --header "Content-Type: application/vnd.api+json" --header "Authorization: Bearer glpat-xxxx" \
https://gitlab.com/api/v4/projects/43542597/terraform/state/dev-group1-dns.tfstate
Где 43542597 — id проекта, dev-group1-dns.tfstate — название terraform state
Планы
- Рассмотреть альтернативу terragrunt — Terramate
- Рассмотреть использование Runatlantis