Использование Terragrunt для расширения возможностей Terraform

В Transcend мы очень серьезно относимся к нашей инфраструктуре и ее безопасности . Наш продукт управляет личными данными пользователей других компаний; наша инфраструктура должна быть герметичной. Мы максимально автоматизируем, чтобы снизить риски наших выпусков и обеспечить легкость контроля и аудита наших стандартов безопасности. Для достижения этих целей мы используем Terragrunt в тандеме с Terraform.

Terraform (от HashiCorp ) позволяет людям использовать код для предоставления и управления любым облаком, инфраструктурой или услугой. Terragrunt (от Gruntwork , официального партнера HashiCorp) является оберткой для бинарного файла Terraform, предоставляя мощные улучшения, начиная от более первоклассных выражений и заканчивая зависимостями модулей и встроенным кэшированием.

Лучшая практика в Terragrunt поощряет повторное использование и расширяемость модулей по умолчанию: это заставляет нас принимать хорошие технические решения, поддерживающие наши принципы безопасности и разработки.

Terragrunt имеет значительные преимущества перед Terraform

Первоначальной целью Terragrunt было заполнить несколько пробелов в функциональности Terraform, и он постоянно расширялся за счет новых функций. Несмотря на то, что Terraform развился для поддержки более продвинутых наборов функций , у него все еще есть возможности для улучшения. Только Terragrunt предлагает следующий богатый набор функций:

  1. Явные зависимости: легко делитесь своим состоянием
  2. Автоматическая генерация конфигурации Atlantis: избавляет от тяжелого труда
  3. Поддержка переменных среды: не рекомендуется использовать жестко закодированные значения.
  4. Generateблоки: удалить повторяющиеся Terraform
  5. Автоматическая пометка ресурсов: универсальное применение метаданных
  6. Вывод произвольных команд из переменных: упрощает использование библиотеки
  7. read_terragrunt_configimports: устраните повторяющийся код Terragrunt.

Явные зависимости: легко делитесь своим состоянием

Чтобы модули Terraform можно было использовать повторно, их необходимо настраивать. Явные зависимости Terragrunt — самый эффективный способ добиться этого.

Хотя источник данных удаленного состояния Terraform позволяет передавать outputзначения между модулями, его использование может привести к сложному и многословному коду. Кроме того, это делает ваши модули менее пригодными для повторного использования в случаях, когда у вас разные конфигурации состояний, и дает мало указаний читателям вашего кода относительно того, откуда берутся значения.

Terragrunt предлагает альтернативу в виде явных dependencyблоков, которые намного мощнее. Вот пример использования зависимостей с Terragrunt для настройки базы данных на AWS внутри существующего виртуального частного облака (VPC) с существующей группой безопасности (виртуальным брандмауэром):

# Define the Terraform source module to use: an RDS database module in this example
2terraform {
3 source = “git::git@github.com:terraform-aws-modules/terraform-aws-rds?ref=v2.13.0”
4}
5
6# Define dependencies
7dependency “vpc” {
8 config_path = “../path/to/vpc_module”
9}
10
11dependency “sg” {
12 config_path = “../path/to/security_group_module”
13}
14
15dependency “some_other_dep” {
16 config_path = “../other/variables/for/making/an/RDS_database”
17}
18
19# Terraform modules have ‘variables’ that need to be populated before that
20# module can be run. In Terraform, these are provided by a static file.
21# In Terragrunt, this `input` block can be used to dynamically supply those variables!
22inputs = {
23 identifier = “backend”
24 db_subnet_group_name = dependency.vpc.outputs.database_subnet_group
25 vpc_security_group_ids = [dependency.sg.outputs.this_security_group_id]
26
27 other_necessary_variables = dependency.some_other_dep.outputs.any_output
28}

Здесь можно отметить много интересных вещей:

Зависимости Terragrunt определяют конфигурацию своего состояния только один раз.

В Terraform я должен определить зависимости, а затем в каждом отдельном модуле, который их требует, определить, как их принимать. Эти стандартные определения могут складываться.

Terragrunt знает, что нужно пойти и изучить конфигурацию этих модулей, чтобы узнать, как получить доступ к их состоянию, что чрезвычайно полезно для использования зависимостей между несколькими рабочими пространствами / учетными записями AWS.

Terragrunt применяет зависимости в их подразумеваемом порядке.

В Terraform, поскольку состояние доступно только после запуска модуля, порядок запуска модулей имеет значение (а Terraform не знает этого порядка). Оператор должен задокументировать порядок применения вещей.

Terragrunt создает дерево зависимостей и запускает все команды в правильном порядке, чтобы все необходимые зависимости были доступны во время выполнения.

Terragrunt поощряет использование хороших методов программирования.

Глядя на проверенные модули в реестре модулей Terraform , вырисовывается закономерность: почти ни один из них не использует remote_stateисточники данных. На самом деле организация Github terraform-aws-modules, на которой размещены десятки проверенных модулей, использует только одну remote_stateзависимость .

Это связано с тем, что использование remote_stateделает модули менее пригодными для повторного использования. В то время как переменные входные данные могут поступать откуда угодно, размещение remote_stateблоков в коде модуля ограничивает использование этого модуля, чтобы он работал только тогда, когда remote_state уже существует, и этот модуль имеет доступ к состоянию.

Бонус: Terragrunt упрощает тестирование.

Кроме того, зависимости Terragrunt имеют встроенную инъекцию зависимостей, что упрощает тестирование с помощью такого инструмента, как terratest .

Автоматическая генерация конфигурации Atlantis: избавляет от тяжелого труда

Чтобы поддерживать нашу инфраструктуру в актуальном состоянии, мы используем популярную систему CI/CD Atlantis . Иерархия зависимостей, созданная Terragrunt, позволяет нам автоматически генерировать нашу конфигурацию Atlantis, заменяя болезненный процесс ее ручной настройки. В Transcend мы с гордостью создали и поддерживаем инструмент с открытым исходным кодом terragrunt-atlantis-config : наш генератор конфигурации Atlantis.

Мы создали этот инструмент после борьбы с ручным и подверженным ошибкам процессом обновления нашего atlantis.yamlфайла. Эти файлы могут состоять из десятков тысяч строк, дерево зависимостей для каждого модуля должно быть определено в конфигурации, а неправильно определенные зависимости завершатся автоматически.

Избавьте себя от страданий: сгенерируйте свою конфигурацию.

Поддержка переменных среды: не рекомендуется использовать жестко закодированные значения.

Переменные среды позволяют, например, программно заполнять значения профиля AWS. Они могут обеспечить запасные варианты и препятствуют жесткому кодированию значений. Жизнь лучше с переменными окружения.

# Установите переменную `profile` так, чтобы она имела значение `AWS_PROFILE` env var
2# Значение по умолчанию “dev-profile”, если этот env var не установлен
3профиль = get_env ( “AWS_PROFILE” , “dev-profile” )

Terraform не планирует в ближайшее время поддерживать переменные окружения (как видно apparentlymartиз ответа пользователя (сопровождающего Terraform) на этот запрос на вытягивание ), и на то есть веские причины! Это может привести к очень трудным для отладки ошибкам Terraform, когда дочерние модули зависят от переменных среды, которые никогда не устанавливались явно. Философия Terraform заключается не в том, что переменные среды плохи, а в том, что они должны быть явно установлены и доступны только для модулей верхнего уровня. Поскольку Terragrunt — это оболочка, которая работает только с корневыми модулями, она может поддерживать и поддерживает переменные среды.

Generateблоки: удалить повторяющуюся конфигурацию Terraform

Если вы используете AWS, сколько раз вы объявляли переменную для tags? Как насчет переменной для региона AWS для развертывания? Как насчет роли IAM в провайдере? В скольких местах вы указали одни и те же ограничения версии поставщика Terraform?

Я так и думал. Вместо того, чтобы портировать этот общий код повсюду, с помощью Terragrunt можно использовать generateблоки для динамического добавления кода Terraform в модули до того, как они будут запланированы или применены.

1сгенерировать “провайдер” {
2 путь = “provider.tf”
3 if_exists = “перезаписать”
4 содержимое = файл ( $ { get_parent_terragrunt_dir ( ) } /vault/provider.block” )
5}

Написание провайдера со всеми необходимыми переменными позволяет вам добавить этот провайдер с помощью короткого generateблока везде, где это необходимо.

Автоматическая пометка ресурсов: универсальное применение метаданных

Говоря о tagsповсеместном использовании переменных, сколько мест вы определяете, какие теги передавать модулям? Они добавляют ценные метаданные, которые можно использовать для отслеживания всего, от самых дорогих ресурсов до расположения исходного кода ресурса. Если бы вы хотели добавить новый тег к каждому отдельному ресурсу в вашей учетной записи AWS с именем файла Terraform, который его создал, сколько работы это потребовало бы?

С Terragrunt это просто. Используйте generateблок, как описано выше, чтобы везде добавить tagsпеременную с другими общими переменными, которые вы хотите, затем в вашем родительском файле Terragrunt (у нас есть по одному для каждой среды) добавьте ввод:

1теги = {
2 Терраформ = правда
3 env = “некоторые_env”
4 TerraformPath = path_relative_to_include()
5}

И точно так же каждый отдельный ресурс сообщает нам, какой модуль им управляет. Если я просматриваю консоль AWS и вижу, что какая-то служба ECS дает сбой из-за отсутствия переменной среды, я могу просто проверить TerraformPathтег на этой службе, чтобы увидеть, какой именно файл в нашей кодовой базе мне нужно отредактировать.

Вывод произвольных команд из переменных: упрощает использование библиотеки

Функция Terragrunt run_cmdвыполняет произвольные команды в локальной оболочке. Это полезно в некоторых исключительных случаях, когда нет поставщика Terraform (библиотеки) для конкретной задачи.

Например, мы используем AWS IAM Authentication для подключения к нашим кластерам Hashicorp Vault. Внутри провайдера Vault мы можем пройти аутентификацию, используя метаданные экземпляра IAM… но для завершения аутентификации нам нужны заголовки запросов AWS IAM, а получение заголовков запросов AWS IAM — сложная задача. Путь наименьшего сопротивления — использование внешних библиотек aws4, таких как популярная библиотека NodeJS.

К счастью, поскольку я могу установить переменные для произвольного вывода команды, чтобы передать эту сложную переменную в Terraform, мне просто нужно написать скрипт, который генерирует заголовки, а затем указать переменную на этот вывод, как я делаю iam_request_headersв примере Terraform. код ниже:

1авторизация_логин {
2 путь = “авторизация/aws/логин/”
3
4 параметры = {
5 роль = var.vault_role
6 iam_http_request_method = “POST”
7 iam_request_url = base64encode( “https://sts.amazonaws.com/” )
8 iam_request_body = base64encode( “Action=GetCallerIdentity&Version=2011-06-15” )
9 iam_request_headers = var.request_headers
10 }
11}

Затем во входные данные Terragrunt просто добавьте:

1request_headers = run_cmd ( “–terragrunt-quiet” , “узел” , “путь/к/script.js” )

Престо! Terragrunt открывает доступ к множеству других скриптов!

read_terragrunt_configimports: Удалите повторяющийся код Terragrunt:

Terragrunt предлагает совершенно новую функцию read_terragrunt_config, позволяющую импортировать код Terragrunt из внешних файлов Terragrunt.

В Transcend у нас есть несколько определений контейнеров ECS, которые используют общее подмножество переменных среды. В общем файле мы создаем карту этих общих переменных окружения, а затем в наших модулях-контейнерах мы просто читаем эту общую конфигурацию и объединяем ее результаты с нашими входными данными.

Хотя сбор наших переменных среды имеет некоторую сложность (у него довольно много зависимостей и вызовов функций), он read_terragrunt_configпозволяет нам изолировать эту сложность в одном месте и использовать ее результаты в других местах.

Terragrunt продолжает развиваться и совершенствоваться

Как и в случае с любым программным обеспечением, со временем мы заметили некоторые недостающие функции, и нам пришлось работать над некоторыми шероховатостями. Давайте взглянем на некоторые исторические проблемы, с которыми мы столкнулись, и как мы с ними справились:

  • Управление задержкой.
  • Обеспечение согласованности использования переменных файлов
  • Уменьшение многословия вывода.

Управление задержкой

Как мы показали ранее, блоки конфигурации зависимостей Terragrunt невероятно мощны и полезны. Однако, поскольку модули часто имеют зависимости от десятков других модулей, каждый из которых может иметь свои собственные зависимости, дерево зависимостей может быстро раздуваться. Как только мы достигли около 300 модулей верхнего уровня, некоторые из которых имеют более 50 зависимостей, которые Terraform утверждает, Terragrunt должен искать, запуск terragrunt planкоманд может занять более десяти минут.

Чтобы справиться с этой задержкой, мы отправили запрос на извлечение (PR) в репозиторий Terragrunt , который обновил библиотеку для одновременного поиска всех зависимостей состояния с помощью горутин. Мы предложили PR в одну пятницу в 22:00, он был объединен менее чем за час, а на следующее утро он уже был выпущен на Homebrew.

После внесения этих изменений наш самый медленный модуль теперь занимает всего две минуты до planпервого запуска и менее минуты до planустановки кеша Terragrunt.

Обеспечение согласованности использования переменных файлов

Как в Terraform, так и в Terragrunt можно вносить изменения с предварительным созданием файла плана или без него. Либо:

1terraform plan -out planfile.out # Здесь представлены переменные Terraform
2terraform apply planfile.out # Terraform выдаст ошибку, если вы укажете здесь ошибки

Или просто,

1terraform apply # Без файла плана укажите здесь переменные Terraform

Поскольку Terragrunt так много автоматизирует, становится важным убедиться, что конфигурация приложения защищает от причуд Terraform: в противном случае легко непреднамеренно передать переменные в applyфайл плана, и все взорвется . (На самом деле это будет только ошибка. Это просто похоже на взрыв.)

Передовой опыт Terragrunt, заключающийся в том, что он всегда запускается terragrunt planраньше всех terraform apply, поможет решить эту проблему, но в Transcend мы решили пойти еще дальше, чтобы полностью устранить эту проблему.

Вместо использования файлов переменных Terraform мы загружаем все наши секретные значения непосредственно из кластеров Hashicorp Vault с помощью Hashicorp Vault Provider или через прямые запросы к нашему кластеру Vault из кода нашего приложения. Таким образом, мы никогда не делимся секретными файлами вручную (секреты в git не защищены), и мы можем быть очень уверены в безопасности нашего секретного хранилища.

Поскольку Terraform и Terragrunt хранят файлы состояния Terraform в виде открытого текста со всеми видимыми секретами , что является постоянной темой более шести лет обсуждения , перемещая как можно больше наших секретов в Vault, мы гарантируем, что они никогда не будут присутствовать в нашем состоянии.

Это не только делает наш инфраструктурный код компактным (что упростило перенос наших развертываний в систему CI с Atlantis), но и наши файлы состояния теперь намного более защищены в случае утечки: огромная победа в безопасности!

Уменьшение многословия вывода

Terragrunt — довольно многословный инструмент, настолько, что его самая популярная проблема за все время — это запрос на чистое ведение журнала. Мы можем относиться.

Некоторые из наших модулей создают тысячи журналов для stderrкаждого файла plan. Эти журналы могут быть полезны при возникновении ошибок, но имеют тенденцию быть избыточными, когда планы выполняются.

Чтобы справиться с этим, мы написали собственную небольшую оболочку Terragrunt , которая при успешных запусках отображает только выходные данные плана Terraform. Когда возникают проблемы с нашим кодом Terraform, наша оболочка отображает весь набор журналов Terragrunt, чтобы мы могли легко отладить, что пошло не так.

Не стесняйтесь использовать и разветвлять наше решение в этой сути!

С большим преимуществом и минимальным недостатком Terragrunt по-прежнему остается победителем.

Каждый инструмент в стеке системы усложняет работу, увеличивая как когнитивную нагрузку при работе в системе, так и входной барьер для новых разработчиков. В общем, мы пытаемся избегать сложности ради сложности и стремимся по возможности создавать компактную кодовую базу.

Мы в Transcend обнаружили, что Terragrunt не только сокращает нашу кодовую базу на несколько тысяч строк, но и значительно упрощает код нашей инфраструктуры. Нам больше не нужно повторять конфигурацию нашего провайдера, состояния или зависимостей, поэтому логика нашего кода теперь сосредоточена исключительно на том, на чем она должна быть сосредоточена: на ресурсах терраформирования, которые нужны нашим разработчикам.

В тех редких случаях, когда Terragrunt не оправдал наших ожиданий, сопровождающие были чрезвычайно открыты для отзывов, запросов функций и запросов на вытягивание и быстро устраняли наши болевые точки.

Благодаря всем замечательным функциям, которые Terragrunt привносит в Terraform, мы можем быть уверены, что каждое развертывание нашей инфраструктуры будет создавать безопасные, поддающиеся аудиту системы, обеспечивающие надежную защиту конфиденциальных данных наших клиентов.

Перейти к верхней панели