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

Terraform для DevOps: лучшие практики

Дмитрий Бахтенков
Дмитрий Бахтенков
Технический писатель
27 сентября 2023 г.
687
13 минут чтения
Средний рейтинг статьи: 5

Terraform — это один из популярных инструментов Infrastructure as Code (IaC), который позволяет управлять инфраструктурой, используя различные облачные провайдеры. Он использует декларативный подход: вы просто описываете желаемую инфраструктуру, а не последовательность шагов для её создания — и она автоматически создаётся.

В этой статье будут описаны рекомендации для эффективной разработки с помощью Terraform.

Структура файлов и папок

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

Пример проекта:

-- PROJECT-DIRECTORY/
-- modules/
      -- <service1-name>/
         -- main.tf
         -- variables.tf
         -- outputs.tf
         -- provider.tf
         -- README
      -- <service2-name>/
         -- main.tf
         -- variables.tf
         -- outputs.tf
         -- provider.tf
         -- README
      -- ...other…
 -- environments/
      -- dev/
         -- backend.tf
         -- main.tf
         -- outputs.tf
         -- variables.tf
         -- terraform.tfvars
 -- qa/
         -- backend.tf
         -- main.tf
         -- outputs.tf
         -- variables.tf
         -- terraform.tfvars
-- stage/
         -- backend.tf
         -- main.tf
         -- outputs.tf
         -- variables.tf
         -- terraform.tfvars
-- prod/
         -- backend.tf
         -- main.tf
         -- outputs.tf
         -- variables.tf
         -- terraform.tfvars

Разделение файлов конфигураций

Оставлять весь код в одном файле main.tf — плохая идея. Лучше разделить всё по файлам с разным назначением, например:

  • main.tf — вызов модулей, источников данных для создания ресурсов
  • variables.tf — описание переменных, которые используются в main.tf
  • outputs.tf — описание выходных данных ресурсов, которые создаются в main.tf
  • versions.tf — требования к версиям Terraform и провайдеров
  • terraform.tfvars — значения переменных

Структура модулей

  • Стандартная структура модулей описана в документации.
  • Группируйте ресурсы по их назначению, например vps.tf, s3.tf, load_balancer.tf Избегайте получения каждого ресурса в отдельном файле.
  • Для каждого модуля создавайте файл README.md с понятным описанием модуля.

Разделение директорий для каждого приложения и окружения

  • Чтобы управлять инфраструктурой для разных приложений независимо друг от друга, помещайте ресурсы каждого приложения в отдельную папку.
  • В проектах могут использоваться общие ресурсы, например, сети. Файлы с общими ресурсами и сервисами нужно поместить в одну директорию.
  • Используйте отдельный каталог для каждого окружения (dev, qa, stage, production).
  • Используйте модули для общего кода всех окружений.
  • Каталог, в котором описывается окружение должен содержать следующие файлы:
    • backend.tf — описание Terraform Backend State.
    • main.tf — файл с описанием инфраструктуры.

Статические файлы и шаблоны

  • Статические файлы должны быть организованы в папку files/ 
  • Для файлов шаблонов используйте папку templates/

Общие рекомендации по структурированию кода

  • Включайте аргумент count / for_each внутри блока ресурса или источника данных как первый аргумент вверху и отделяйте новой строкой. Блоки tags, depends_on и lifecycle, если применимо, всегда должны быть перечислены как последние аргументы, всегда в одном и том же порядке. Все они должны быть разделены одной пустой строкой.
  • Модули ресурсов должны быть простые, не стоит их переусложнять.
  • Не задавайте жестко значения, которые могут быть переданы как переменные или обнаружены с помощью источников данных.

Naming, Code Style и формат файлов

Код Terraform должен быть написан так, чтобы другие люди могли легко его понять. Следование одинаковым и согласованным правилам именования объектов может упростить понимание кода

Основные правила именования

  • Используйте _ (нижнее подчёркивание) вместо -, чтобы разделить наименования из множества слов.
  • Используйте только символы в нижнем регистре и числа.
  • Используйте существительные в единственном числе для названий объектов.
  • Не повторяйте тип ресурса в его наименовании:
    • resource "aws_route_table" "public" {} — ОК
    • resource "aws_route_table" "public_route_table" {} — НЕ ОК
  • Чтобы различать ресурсы одного типа, используйте понятные наименования (например primary, secondary, public, private).

Правила работы с переменными

  • Объявляйте все переменные в файле variables.tf.
  • Давайте переменным описательные имена, соответствующие их использованию или назначению.
  • Предоставляйте содержательное описание для всех переменных, даже если вы считаете, что всё очевидно. Описание автоматически включается в автоматически сгенерированную документацию модуля и добавляют дополнительный контекст для новых разработчиков.
  • Упорядочивайте ключи в блоке переменных следующим образом: description, type, default, validation.
  • По возможности предоставляйте значения по умолчанию.
    • Для переменных, которые имеют независимые от среды значения (например, размер диска), предоставляйте значения по умолчанию.
    • Для переменных, которые имеют специфичные для среды значения, не предоставляйте значений по умолчанию.
  • Используйте множественное число в имени переменной, когда тип — list(...) или map(...).
  • Отдавайте предпочтение простым типам (number, string, list(...), map(...), any) перед специфическим типом, например, object(), если вам не нужно иметь строгие ограничения на каждый ключ.
  • Для упрощения условной логики давайте булевым переменным положительные имена (например, enable_external_access).
  • Входные данные, локальные переменные и выходные данные, представляющие числовые значения — такие как размер диска или оперативной памяти — должны иметь имена с единицами измерения (например, ram_size_gb).
    • Для единиц хранения используйте двоичные префиксы единиц (кило, мега, гига).

Правила работы с выходными значениями

  • Все выходные организуются в файле outputs.tf.
  • Выводите все полезные значения, которые могут понадобиться связанным модулям.
  • Предоставьте содержательное описание для всех выходных значений.
  • Название выходного значения должно описывать свойство, которое он содержит. Хорошая структура для названия вывода выглядит так: {name}_{type}_{attribute}.
  • Документируйте эти значения в файле README.md. Автоматически генерируйте описание при коммите с помощью инструментов, таких как terraform-docs.

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

  • Команда terraform fmt используется для перезаписи файлов конфигурации Terraform в канонический формат и стиль. 
  • Все файлы Terraform должны соответствовать стандартам terraform fmt.

Лучшие практики безопасности 

  • Terraform использует данные данные для доступа к вашей облачной инфраструктуре, например ключи API. 
  • Никогда не храните файл состояния на вашем локальном компьютере или в системе контроля версий — используйте Terraform Remote State, например, s3. Этот файл может содержать чувствительные значения в виде обычного текста, представляющие угрозу безопасности; любой, у кого есть доступ к вашему компьютеру или этому файлу, потенциально может его просмотреть. . 
  • Используйте gitignore для файлов состояния Terraform. 
  • Шифруйте состояние: даже если в файле состояния не должно быть секретов, всегда шифруйте состояние как дополнительную меру защиты. 
  • Резервируйте свои файлы состояния. 
  • Используйте одно состояние на окружение.

Настройте блокировку состояния бэкэнда

State: Locking | Terraform | HashiCorp Developer

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

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

Не все бэкэнды поддерживают блокировку. например, Azure Blob storage поддерживает блокировку изначально, в то время как Amazon S3 поддерживает использование DynamoDB в AWS.

Не храните секреты в файле состояния

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

Для хранения секретов можно использовать сервисы (например AWS Secret Manager, HashiCorp Vault), и ссылаться на них, используя источники данных.

Минимизируйте радиус поражения

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

Проводите непрерывные аудиты

После выполнения команды terraform apply запустите автоматические проверки безопасности. Эти проверки могут помочь гарантировать, что инфраструктура не перейдет в небезопасное состояние.

Инструменты InSpec и Serverspec являются подходящими вариантами для этого типа проверок.

Используйте переменные с флагом sensitive

Конфигурация Terraform часто включает чувствительные входные данные, такие как пароли, токены API и др.

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

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

variable "db_password" {

description = "Пароль администратора базы данных."
  type        = string
  sensitive   = true
}

Используйте файлы определений переменных (.tfvars)

  • Чтобы задать много переменных, удобнее указывать их значения в файле определений переменных (с именем файла, заканчивающимся на .tfvars или .tfvars.json).
  • Укажите этот файл в командной строке с помощью -var-file: terraform apply -var-file="testing.tfvars"
  • Terraform также автоматически загружает ряд файлов определений переменных, если они присутствуют.
  • Всегда рекомендуется передавать переменные для пароля, секретного ключа и т. д. локально через -var-file, а не сохранять их внутри конфигураций terraform или на удаленном месте системы контроля версий.

Использование модулей

Модули предназначены для повторного использования кода.

Используйте общие модули

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

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

Выпускайте версии с тегами

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

Не используйте провайдеры и Remote State

Общие модули не должны объявлять провайдеры или remote state. Вместо этого объявляйте такие параметры в корневых модулях.

Выдавайте outputs для всех ресурсов

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

Для каждого ресурса, определенного в общем модуле, включите хотя бы одно output-значение, которое ссылается на ресурс.

Используйте подмодули для сложной логики

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

  • Размещайте модули в modules/$modulename.
  • Рассматривайте модули как приватные, не предназначенные для использования внешними модулями, если документация общего модуля не указывает иное.

Минимизируйте количество ресурсов в каждом корневом модуле

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

С меньшим количеством ресурсов в проекте проще и быстрее работать.

Дополнительные рекомендации

Контроль версий

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

Тестирование

  • Статический анализ: для проверки содержимого конфигурации, а также тестирования синтаксиса и структуры вашей конфигурации без развертывания каких-либо ресурсов, используя инструменты, такие как компиляторы, линтеры и dry-run. Используйте terraform validate и инструменты, такие как tflint, config-lint, Checkov, Terrascan, tfsec, Deepsource.
  • Интеграционное тестирование: для обеспечения правильной работы модулей тестируйте отдельные модули изолированно. Используйте инструменты и фреймворки, такие как Terratest, Kitchen-Terraform, InSpec.
  • Проверяйте вывод terraform validate и plan перед тем, как разрешать применять изменения terraform к окружению.

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

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

Запустите команду terraform -v, чтобы проверить наличие нового обновления.

Защищайте ресурсы с состоянием

Убедитесь, что включена защита от удаления для stateful-ресурсов, таких как например БД.

Используйте переменную self

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

Например, вы хотите использовать IP-адрес экземпляра, который будет развернут только после команды terraform apply, поэтому вы не знаете IP-адреса, пока он не будет запущен и работает.

Используйте Terraform Workspaces

Воркспейсы позволяют вам управлять несколькими экземплярами одной и той же конфигурации Terraform, и у каждого экземпляра будет своё состояние. Это полезно, когда вам нужно управлять несколькими средами, такими как dev, staging и production, с одной и той же кодовой базой Terraform.

Для создания рабочей области используйте команду terraform workspace new, а для переключения между рабочими областями — команду terraform workspace select. Например, вот как вы могли бы использовать рабочие области для управления средами dev и prod:

terraform workspace new dev
terraform apply
terraform workspace new prod
terraform apply

Используйте Provisioners

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

В Terraform есть два типа провайдеров: local-exec и remote-exec. Local-exec запускает команды локально на машине, на которой запущен Terraform, а remote-exec запускает команды на самом ресурсе. Вот пример конфигурации, которая использует провайдер local-exec для запуска скрипта после создания конфигурации:


resource "twc_server" "example-server" {
name = "Example server"
os_id = data.twc_os.example-os.id

configuration {
configurator_id = data.twc_configurator.example-configurator.id
    disk = 1024 * 10
    cpu = 1
    ram = 1024
  }
  provisioner “local-exec” { command = “echo ‘Привет, Мир!’ } 
}

После запуска terraform apply выполнится команда:

Image1

Зарегистрируйтесь и начните пользоваться
сервисами Timeweb Cloud прямо сейчас

15 лет опыта
Сосредоточьтесь на своей работе: об остальном позаботимся мы
165 000 клиентов
Нам доверяют частные лица и компании, от небольших фирм до корпораций
Поддержка 24/7
100+ специалистов поддержки, готовых помочь в чате, тикете и по телефону