Terraform: инфраструктура на уровне кода — страница 64 из 65

инфраструктуры. Например, взгляните на репозиторий на рис. 8.4.

Рис. 8.4. Структура каталогов при активном копировании сред и модулей внутри каждой среды

Репозиторий текущей инфраструктуры содержит большое количество регионов с множеством модулей в каждом из них. И большинство этих модулей были скопированы. Конечно, у каждого из них имеется файл main.tf, который ссылается на модуль в вашем репозитории modules, поэтому ситуация с дублированием могла быть и хуже. Но, даже если вы просто создаете экземпляр одного модуля, вам все равно приходится использовать большой объем шаблонного кода, одинакового для всех окружений:

• конфигурацию provider;

• конфигурацию backend;

• задание всех входных переменных для модуля;

• задание всех выходных переменных, которые возвращает модуль.

В результате в каждом вашем модуле могут накопиться десятки и сотни строчек в основном идентичного кода, который копируется из одной среды в другую. Чтобы минимизировать дублирование по принципу DRY и облегчить продвижение кода Terraform по разным средам, вы можете использовать открытый инструмент под названием Terragrunt (https://github.com/gruntwork-io/terragrunt), о котором я уже упоминал ранее. Terragrunt представляет собой тонкую обертку вокруг Terraform. После его установки (инструкции по установке — в файле README по адресу https://github.com/gruntwork-io/terragrunt#install-terragrunt) вы сможете выполнять стандартные команды terraform, применяя исполняемый файл terragrunt:

$ terragrunt plan

$ terragrunt apply

$ terragrunt output

Terragrunt запускает Terraform с заданной вами командой, но с некоторым дополнительным поведением на основе конфигурации, указанной в файле terra­grunt.hcl. Основная идея в том, что весь код Terraform находится в единственном экземпляре в репозитории modules, тогда как репозиторий текущей инфраструктуры содержит файлы terragrunt.hcl, которые позволяют конфигурировать и развертывать каждый модуль в любой среде без дублирования. Это проиллюстрировано на рис. 8.5.

Рис. 8.5. Структура файлов и каталогов при использовании Terragrunt

Для начала добавьте конфигурацию provider в файлы modules/data-stores/mysql/main.tf и modules/services/hello-world-app/main.tf:

provider "aws" {

  region = "us-east-2"

  # Разрешаем любую версию провайдера AWS вида 2.x

  version = "~> 2.0"

}

Затем добавьте конфигурацию backend в файлы modules/data-stores/mysql/­main.tf и modules/services/hello-world-app/main.tf, но оставьте блок config пустым (вскоре вы увидите, как наполнить его с помощью Terragrunt):

terraform {

  # Подойдет любая версия Terraform

  # вида 0.12.x

  required_version = ">= 0.12, < 0.13"

  # Частичная конфигурация. Остальное

  # добавит Terragrunt.

  backend "s3" {}

}

Зафиксируйте эти изменения и выпустите новую версию своего репозитория modules:

$ git add modules/data-stores/mysql/main.tf

$ git add modules/services/hello-world-app/main.tf

$ git commit -m "Update mysql and hello-world-app for Terragrunt"

$ git tag -a "v0.0.7" -m "Update Hello, World text"

$ git push --follow-tags

Теперь перейдите в репозиторий текущей инфраструктуры и удалите все файлы .tf. Этот код Terraform, который вы скопировали ранее, будет заменен файлом terragrunt.hcl для каждого модуля. Например, так вы­глядит terragrunt.hcl для live/stage/data-stores/mysql/terra­grunt.hcl:

terraform {

  source = "github.com//

  modules//data-stores/

  mysql?ref=v0.0.7"

}

inputs = {

  db_name     = "example_stage"

  db_username = "admin"

  # Установите пароль с помощью переменной среды TF_VAR_db_password

}

Как видите, файлы terragrunt.hcl используют тот же синтаксис HCL (Hashi­Corp Configuration Language), что и Terraform. Когда вы запустите команду terragruntapply, она найдет в файле terragrunt.hcl параметр source и сделает следующее.

1. Загрузит во временную папку код, находящийся по заданному URL-адресу. Здесь поддерживается тот же синтаксис URL-адресов, что и в параметре source модулей Terraform. Поэтому вы можете использовать локальные пути, обычные и версионированные адреса Git (с помощью параметра ref, как показано в предыдущем примере) и т. д.

2. Выполнит во временной папке команду terraformapply, передав ей входные переменные, которые вы указали в блоке inputs={…}.

Преимущество такого подхода в том, что весь код каждого модуля в репозитории текущей инфраструктуры сводится к единственному файлу terragrunt.hcl, который содержит указатель на используемый модуль (с указанием определенной версии) и входные переменные, установленные для определенной среды. Таким образом, мы следуем принципу DRY настолько строго, насколько это возможно.

Terragrunt также позволяет избавиться от дублирования конфигурации backend. Чтобы не указывать bucket, key, dynamodb_table и другие параметры в каждом модуле, вы можете разместить их в файле terragrunt.hcl для той или иной среды. Например, создайте в файле live/stage/terragrunt.hcl следующую конфигурацию:

remote_state {

  backend = "s3"

  config = {

    bucket         = ""

    key            = "${path_relative_to_include()}/terraform.tfstate"

    region         = "us-east-2"

    encrypt        = true

    dynamodb_table = ""

  }

}

Конфигурация backend размещается в блоке remote_state как обычно, только для значения key используется встроенная в Terragrunt функция под названием path_relative_to_include(). Эта функция возвращает относительный путь между этим корневым файлом terragrunt.hcl и любым дочерним модулем, который его подключает. Например, чтобы подключить этот корневой файл в live/stage/data-stores/mysql/terragrunt.hcl, просто добавьте блок include:

terraform {

  source = "github.com//modules//data-stores/mysql?ref=v0.0.7"

}

include {

  path = find_in_parent_folders()

}

inputs = {

  db_name     = "example_stage"

  db_username = "admin"

  # Установите пароль с помощью переменной среды TF_VAR_db_password

}

Блок include находит корневой файл terragrunt.hcl с помощью функции find_in_parent_folders(), встроенной в Terragrunt, и автоматически наследует все его параметры, включая конфигурацию remote_state. В результате этот модуль mysql будет использовать те же настройки backend, что и корневой файл, а полю key будет автоматически присвоено значение data-stores/mysql/terraform.tfstate. Это означает, что для хранения состояния Terraform будет применяться та же структура каталогов, что и для репозитория текущей инфраструктуры, благодаря чему вам будет проще разобраться в том, какие файлы состояния сгенерировал тот или иной модуль.

Чтобы развернуть этот модуль, выполните terragruntapply:

$ terragrunt apply

[terragrunt] Reading Terragrunt config file at terragrunt.hcl

[terragrunt] Downloading Terraform configurations from

             github.com//modules//data-stores/mysql?ref=v0.0.7

[terragrunt] Running command: terraform init -backend-config=(...)

(...)

[terragrunt] Running command: terraform apply

(...)

Apply complete! Resources: 5 added, 0 changed, 0 destroyed.

В этом выводе видно, как Terragrunt считывает ваш файл terragrunt.hcl, загружает заданный вами модуль, запускает terraforminit, чтобы сконфигурировать ваш блок backend (он даже создаст для вас бакет S3 и таблицу DynamoDB, если их еще не существует), и затем разворачивает все это с помощью команды terraformapply.

Теперь вы можете развернуть модуль hello-world-app в среде Staging, добавив файл live/stage/services/hello-world-app/terragrunt.hcl и запустив terragruntapply:

terraform {

  source = "github.com//modules//services/hello-world-app?ref=v0.0.7"

}

include {

  path = find_in_parent_folders()

}

inputs = {

  environment = "stage"

  min_size = 2

  max_size = 2

  enable_autoscaling = false

  db_remote_state_bucket = ""

  db_remote_state_key    = ""

}

Этот модуль также использует блок include, чтобы взять все параметры из корневого файла terragrunt.hcl. Таким образом, он наследует все то же содержимое backend, кроме поля key, которому, как можно было бы ожидать, автоматически присваивается services/hello-world-app/terraform.tfstate. Если в среде Staging все работает хорошо, вы можете создать аналогичные файлы terragrunt.hcl в папке live/prod и продвинуть ту же версию артефакта v0.0.7 в промышленную среду, выполнив в каждом модуле команду terragruntapply.


Собираем все вместе

Вы познакомились с тем, как прикладной и инфраструктурный код проходят свой путь от стадии разработки до промышленной среды. В табл. 8.1 приводится сравнение этих двух рабочих процессов.


Таблица 8.1. Рабочие процессы для доставки прикладного и инфраструктурного кода

Процесс

Прикладной код

Инфраструктурный код

Использование системы управления версиями

git clone

По одному репозиторию на приложение.

Используйте ветки

git clone

Репозитории live и modules.

Не используйте ветки

Локальное выполнение кода

Выполняйте на локальном компьютере.

ruby web-server.rb

ruby web-server-test.rb

Выполняйте в изолированной среде.

terraform apply

go test

Внесение изменений в код

Отредактируйте код.

ruby web-server.rb

ruby web-server-test.rb

Отредактируйте код.

terraform apply

go test

Используйте стадии тестирования

Подача изменений на рассмотрение

Подайте запрос на сохранение внесенных изменений.

Убедитесь, что соблюдаются рекомендации по написанию кода