Простая оболочка для Terraform
Terragrunt — Держите ваш код Terraform сухим
Расширьте возможности IAC
Проблемы с голым Terraform
Terraform — это инструмент, который позволяет вам писать код, определяющий вашу инфраструктуру, но есть несколько известных проблем с использованием terraform. Первый — это файл состояния терраформирования, второй, который мы постараемся решить сегодня, — это проблема с сохранением кода терраформирования СУХИМ (не повторяйтесь) — особенно если вы используете несколько окружений.
Решений немного — вроде рабочих пространств, оплаты за terraform cloud, скриптов с переменными. Мы сосредоточимся на гибком и элегантном решении, использующем обертку для terraform — Terragrunt.
Структуры папок Terragrunt
Terragrunt (вкратце) позволяет вам выбирать каталог кода терраформирования и заполнять переменные с помощью простого управления состоянием. Входные данные для переменных терраформирования определены в .hcl
файлах, которые могут ссылаться друг на друга и многое другое.
Это пример структуры папок, в которой используется terragrunt для стека AWS kubernetes:
. ├── common.hcl ├── dev │ └── terragrunt.hcl ├── prod │ └── terragrunt.hcl ├── staging │ └── terragrunt.hcl └── tf_files ├── envs │ ├── asg.tf │ ├── eks.tf │ ├── iam.tf │ ├── kubernetes.tf │ ├── lb.tf │ ├── main.tf │ ├── s3.tf │ ├── security_groups.tf │ ├── variables.tf │ └── vpc.tf
- common.hcl — входные данные и настройки, передаваемые в каждую среду.
- tf_files — код терраформирования.
- dev/prod/staging terragunt.hcl — входные данные используются только для указанной среды.
Теперь для развертывания среды используйте команду terragrunt apply
в одном из каталогов среды (dev/staging/prod), и происходит волшебство. Давайте обсудим, как .hcl
файлы позволяют быть более гибкими с входными данными terrafrom.
Структура конфигурационного файла Terragrunt
Конфиг Terragrunt содержит блоки и атрибуты. Начнем с файла common.hcl
. Он содержит определение вещей, которые будет использовать каждая среда:
# common.hcl # block locals locals { region = "us-east-1" bucket = "example" key = "terraform/${path_relative_to_include()}.tfstate" dynamodb_table = "terraform_locks" profile = "my-aws-profile" } # attributes local inputs = { } # block remote_state remote_state { backend = "s3" config = { bucket = local.bucket region = local.region key = local.key dynamodb_table = local.dynamodb_table profile = local.profile encrypt = true } }
Блок с местными жителями должен быть очевиден — вы можете легко сослаться на них позже в terragrunt.
Самая важная строка кода:
key = “terraform/${path_relative_to_include()}.tfstate”
Это гарантирует, что состояние будет сохранено по пути, который включает путь, в котором живет .hcl
. При этом вы можете быстро убедиться, что каждое состояние среды будет управляться в другом файле состояния.
Теперь, чтобы убедиться, что это будет упоминаться в других файлах terragrunt, вам нужно добавить это:
# dev/terragrunt.hcl include { path = find_in_parent_folders("common.hcl") } terraform { source = "${get_parent_terragrunt_dir()}//tf-files/envs/" } inputs { my_var = "var" }
Используйте блок include
, чтобы добавить файл common.hcl
. Затем используйте terraform
, чтобы указать каталог с кодом терраформирования, используя относительный путь, возвращаемый функцией get_parent_terragrunt_dir()
. Чтобы запустить разработку terragrunt.hcl
, перейдите в каталог и запустите terragrunt apply
. Состояние будет сохранено в ведре как:
“terraform/dev.tfstate”
Каждая конфигурация среды упакована в один файл .hcl
. Конечно, если вы хотите хранить состояния в разных сегментах, это также возможно. Просто измените имя корзины, чтобы использовать такую функцию, как ключ состояния.
Команды Terragrunt
Большинство команд terragrunt являются оболочками для команд terraform:
terragrunt plan -> terraform plan terragrunt apply -> terraform apply
Но некоторые из них продолжаются с дополнительными изменениями. terragrunt init
автоматически создает корзину или DynamoDB (при использовании AWS).
Генерация кода терраформирования с помощью Terragrunt
Вы можете сгенерировать код терраформирования, чтобы пропустить ручную работу. Пример пользовательского примера: вы хотите создать идентичный провайдер для каждой среды с возможностью изменения профиля и региона AWS.
Просто добавьте вам дополнительный блок common.hcl
:
generate "provider" { path = "provider.tf" if_exists = "overwrite" contents = <<EOF provider "aws" {
version = ">= 2.57.0" profile = var.aws_profile region = var.aws_region} EOF }
Объединение нескольких файлов Terragrunt
Иногда для создания нескольких ресурсов с подробной настройкой может потребоваться использование словарей с большим количеством ключей или вложенных блоков. Например:
# ETL glue_pythonshell_jobs = { match-trainingfile = { default_arguments = { "--extra-py-files" = "s3://extra-py-files/pyfile.py" "--job-bookmark-option" = "job-bookmark-disable" "--enable-metrics" = "true" } role_arn = "arn:aws:iam::<redacted>:role/<redacted>" script_location = "s3://scripts/match_trainingfile.py" name = "pythonshell" timeout = "2880" }, order-records = { default_arguments = { "--job-bookmark-option" = "job-bookmark-disable" "--job-language" = "python" "--TempDir" = "s3://pipeline/workspace/temporary/" "--enable-metrics" = "true" } role_arn = "arn:aws:iam::<redacted>:role/<redacted>" script_location = "s3://scripts/order_records.py" name = "pythonshell" timeout = "2880" }, ... # Kubernetes ...
Это словарь в атрибуте inputs
конфигурационного файла terragrunt. Если вы когда-нибудь захотите управлять еще и Kubernetes, или VPC, или чем-то еще, представьте, насколько большим может стать terragrunt с таким количеством входных данных. К счастью, terragrunt поставляется с решением для сортировки ваших входных данных в файлы и объединения их в основной файл terragrunt.
Вы можете переместить все входные данные для заданий Glue и ETL, которые я показал, в файл с именем etl.hcl
в том же каталоге и поместить их в атрибут inputs
:
inputs = { ## ## Glue ## .... }
А теперь обратитесь к этому файлу в нашем главном terragrunt.hcl
:
locals { etl_vars = read_terragrunt_config("${get_terragrunt_dir()}/etl.hcl", {inputs = {}}) } inputs = merge( local.etl_vars.inputs, { project_name = "test" project_env = "dev" aws_profile = "test-dev" aws_region = "us-east-1" vpc_cidr_block = "10.0.0.0/16" vpc_subnet_newbits = 8 # vpc_cidr_block mask bits + vpc_subnet_newbits = subnet size # Kubernetes ... } )
Это позволяет управлять кодом терраформирования, который требует подробных входных данных. Как видите, есть раздел # Kubernetes
— его тоже можно переместить в отдельный файл, если он разрастется до больших размеров и после этого объединится вот так:
locals { etl_vars = read_terragrunt_config("${get_terragrunt_dir()}/etl.hcl", {inputs = {}}) k8s_vars = read_terragrunt_config("${get_terragrunt_dir()}/k8s.hcl", {inputs = {}}) } inputs = merge( local.etl_vars.inputs, local.k8s_vars.inputs, { project_name = "test" project_env = "dev" aws_profile = "test-dev" aws_region = "us-east-1" vpc_cidr_block = "10.0.0.0/16" vpc_subnet_newbits = 8 # vpc_cidr_block mask bits + vpc_subnet_newbits = subnet size } )
Все остается организованным и легко читаемым.
Последние мысли
Terraform — отличный инструмент, но я не могу представить себе работу с ним без Terragrunt. Эта статья является лишь кратким введением и не углубляется в более скрытые функции. И да, Terragrunt поддерживает работу с модулями.
Разумеется, у Terraform есть официальное решение для управления несколькими средами — Terraform Cloud. Но если вас не устраивают корпоративные решения и затраты — вам подойдет Terragrunt.
Попробуй сам. Может изменить правила игры для вас.