Сжатый справочник архитектурных шаблонов, проверяемых на экзамене 004. Читайте сверху вниз или переходите к нужному разделу.
Понимание концепций Infrastructure as Code (IaC)
Команда вручную нажимает кнопки в облачных консолях; никто не знает, как должен выглядеть production.
Внедрите IaC. Конфигурации хранятся в версионированных файлах - эти файлы являются авторитетным источником истины, а не облачная консоль.
Почему: Аудит, peer review, откаты и репликация - все это вытекает из системы контроля версий. Ручное provisioning не имеет ни одного из этих преимуществ.
Выбор между описанием желаемого конечного состояния и написанием скрипта шагов для его достижения.
Terraform декларативен. Вы описываете цель; Terraform сам определяет, какие API-вызовы выполнить. Ansible, Bash-скрипты, кастомные SDK - императивны.
Почему: Декларативные конфигурации идемпотентны и приводят к одному и тому же состоянию независимо от начального. Императивные скрипты должны учитывать каждый переход.
Разграничение первоначального provisioning и текущих операций.
Day 0 = проектирование и планирование. Day 1 = первоначальный provisioning (первый apply). Day 2 = текущие операции: исправление, масштабирование, ротация сертификатов, устранение drift.
Почему: Большая часть production-работы приходится на Day 2. Инструменты IaC должны поддерживать итерации, а не только первое развертывание.
Снижение риска drift в консоли; требование, чтобы все изменения инфраструктуры проходили через review.
GitOps с IaC: Git является источником истины, PRs запускают plan, merges запускают apply. Блокируйте прямой доступ к консоли через SCP/IAM.
Понимание назначения Terraform
Нагрузка распределена между AWS и GCP; команда хочет использовать один инструмент, один рабочий процесс.
Terraform с несколькими провайдерами в одной конфигурации: `hashicorp/aws` + `hashicorp/google`. Выходы одного провайдера являются входами для другого.
Почему: Ядро, не зависящее от провайдера; SDK для каждого облака находятся в подключаемых провайдерах. CloudFormation/ARM ориентированы на одно облако.
Почему Terraform нужен state-файл вместо запроса к облаку при каждом запуске?
State сопоставляет адреса конфигурации (`aws_instance.web`) с реальными ID ресурсов, отслеживает зависимости и кэширует метаданные. Без него Terraform не может знать, какие облачные ресурсы он управляет, а какие были созданы в другом месте.
Выбор между CLI с открытым исходным кодом и HCP Terraform.
Solo / небольшая команда / нет принудительного применения политик → OSS CLI с remote backend. Несколько команд / политики Sentinel/OPA / VCS-driven runs / динамические учетные данные → HCP Terraform.
Почему: Оба запускают один и тот же бинарник Terraform; HCP добавляет возможности для совместной работы, управления и инфраструктуру remote-runner.
Terraform создает облачные ресурсы (VM, сети, IAM). Ansible/Chef/Puppet настраивают ПО внутри VM. Они дополняют друг друга, а не конкурируют.
Компания, использующая только AWS, выбирает между CloudFormation и Terraform.
CloudFormation имеет более тесную интеграцию с AWS, нативный rollback и нулевое управление state-файлами. Terraform выигрывает в multi-cloud, имеет более крупную экосистему модулей и лучшую эргономику HCL. Выберите CFN, если только AWS и rollback наиболее важен; в противном случае - Terraform.
Понимание основ Terraform
Фиксирование источника и версии провайдера для воспроизводимых установок.
Объявите в `terraform { required_providers { aws = { source = "hashicorp/aws", version = "~> 5.0" } } }`.
Почему: Без явного `required_providers` Terraform предполагает устаревшие пространства имен registry и может выбрать несовместимые версии.
Конфигурация имеет `required_providers` для AWS, но не имеет блока `provider "aws"`.
Terraform создает пустую конфигурацию провайдера по умолчанию. Провайдер AWS затем считывает `AWS_ACCESS_KEY_ID`/`AWS_PROFILE`/IMDS в своем стандартном порядке приоритета.
Почему: Явный блок `provider` является необязательным; он нужен только для переопределения значений по умолчанию или для создания псевдонимов нескольких экземпляров.
Скачивает провайдеры согласно `required_providers`, инициализирует backend, скачивает исходники модулей и записывает `.terraform/` плюс `.terraform.lock.hcl`. Обязательно перед `plan` или `apply`.
Файл `.terraform.lock.hcl` появился после `init`. Коммитить его или добавить в `.gitignore`?
Коммитить его. Файл блокировки записывает точные версии провайдеров + криптографические контрольные суммы. Гарантирует, что каждый участник и CI-раннер установят идентичные провайдеры.
Почему: Без него `terraform init` может выбрать более новую совместимую версию на другой машине, что приведет к неожиданным различиям.
Обнаружение синтаксических ошибок и несоответствий типов без обращения к облачным API.
`terraform validate` проверяет синтаксис HCL, согласованность типов, обязательные аргументы и внутренние ссылки. Только локально, аутентификация провайдера не требуется.
Почему: Не обнаруживает проблем на стороне провайдера (аутентификация, отсутствующие ресурсы) - они проявляются только на этапе `plan`.
CI многократно запускает `terraform init` во многих проектах, каждый раз скачивая одни и те же провайдеры с нуля.
Установите `TF_PLUGIN_CACHE_DIR` (или `plugin_cache_dir` в CLI config). Провайдеры скачиваются один раз и символически связываются с каталогами `.terraform/` каждого проекта.
Адреса источников провайдеров следуют какому формату?
`<hostname>/<namespace>/<type>` - например, `registry.terraform.io/hashicorp/aws`. Если hostname опущен, по умолчанию используется публичный Terraform Registry. Namespace = vendor. Type = provider name.
Предотвратить запуск несовместимой версии Terraform CLI для участников команды.
`terraform { required_version = ">= 1.5, < 2.0" }`. CLI отказывается работать, если запущенная версия не соответствует.
Выбрать аутентификацию провайдера для CI/CD-раннера.
Лучше всего: динамические краткосрочные учетные данные (OIDC trust к AWS/Azure/GCP, динамические учетные данные провайдера HCP Terraform). Приемлемо: переменные окружения (`AWS_ACCESS_KEY_ID`). Избегать: жестко закодированные учетные данные в блоке `provider`.
Почему: Статические долгосрочные секреты в конфигурации являются основной причиной инцидентов с утечкой учетных данных.
Где находятся настройки верхнего уровня?
Блок `terraform { ... }` содержит `required_version`, `required_providers`, `backend`, `cloud` и experiments. Несколько блоков `terraform` в разных файлах объединяются.
Использование Terraform вне основного рабочего процесса
Ресурс существует в state, но больше не должен управляться Terraform; облачный ресурс должен продолжать работать.
`terraform state rm <addr>`. Удаляет из state без уничтожения облачного ресурса.
Применение crashed в середине выполнения; блокировка DynamoDB застряла, и другие инженеры заблокированы.
`terraform force-unlock <LOCK_ID>` (ID блокировки находится в сообщении об ошибке). Проверьте, что никто другой не работает, прежде чем разблокировать.
Почему: Блокировки не освобождаются автоматически при сбое, потому что фаза очистки не была выполнена.
Существующий вручную созданный S3 bucket необходимо перевести под управление Terraform без повторного создания.
Напишите блок ресурса, затем `terraform import aws_s3_bucket.legacy legacy-bucket-name`. State теперь записывает существующий bucket; последующие планы показывают только drift.
Необходимо импортировать много ресурсов воспроизводимо через CI, а не с помощью одноразовых CLI-команд.
Используйте блок `import` (Terraform 1.5+): `import { to = aws_s3_bucket.legacy, id = "legacy-bucket-name" }`. Импорт происходит во время `apply`, версионируется и работает в output плана.
Протестировать выражение, такое как `merge()` или `cidrsubnet()`, относительно текущего состояния без применения изменений.
`terraform console` открывает интерактивный REPL с текущей конфигурацией + state в области видимости. Полезно для прототипирования `locals` и `outputs`.
`terraform output -json <name>` выводит JSON, безопасный для парсинга `jq`. Обычный `terraform output` форматируется для человека и небезопасен для скриптов.
Проверить, что в настоящее время отслеживается в state.
`terraform state list` показывает все адреса ресурсов. `terraform state show <addr>` показывает атрибуты для одного ресурса. Для индексированных ресурсов используйте кавычки: `terraform state show 'aws_instance.web[0]'`.
Повторное использование одного и того же модуля в dev/staging/prod.
Две схемы: (1) Workspaces с одной корневой конфигурацией + `*.tfvars` для каждого workspace. (2) Корневые конфигурации для каждой среды (`envs/dev/main.tf`, `envs/prod/main.tf`), каждая из которых вызывает общие модули. Схема 2 чаще используется для изоляции нескольких команд.
Использование основного рабочего процесса Terraform
Убедитесь, что CI применяет именно то, что было просмотрено в plan, без drift между шагами.
`terraform plan -out=tfplan` сохраняет план. Затем `terraform apply tfplan` применяет именно этот план. Отказ от повторного планирования исключает риск time-of-check/time-of-use.
Расшифровка символов вывода plan: `+`, `-`, `~`, `-/+`, `<=`.
`+` создать. `-` уничтожить. `~` обновить на месте. `-/+` уничтожить, затем создать (заменить). `<=` прочитать (data source). Символ замены означает, что изменился атрибут `forces replacement`.
Быстро исправить один сломанный ресурс, не затрагивая остальную конфигурацию.
`terraform apply -target=aws_instance.web`. Используйте экономно - обходит отслеживание зависимостей и может привести к несогласованности state. Документируйте, почему использовался каждый `-target`.
Почему: HashiCorp явно заявляет, что `-target` предназначен для исключительных случаев устранения неполадок, а не для обычного рабочего процесса.
Ресурс A должен существовать до ресурса B, но B не ссылается на атрибуты A.
Добавьте `depends_on = [resource_a.name]` к B. Используйте только тогда, когда неявные ссылки на атрибуты не могут выразить зависимость (например, политика IAM должна распространиться до того, как EC2 ее использует).
CI/CD pipeline применяет изменения после успешного job plan; нет человека за клавиатурой.
`terraform apply -auto-approve` пропускает интерактивное подтверждение. Объедините с сохраненным файлом плана (`terraform apply tfplan`), чтобы сделать CI детерминированным.
Облачный провайдер rate-limits Terraform; многие создания ресурсов intermittently завершаются сбоем.
`terraform apply -parallelism=5` (default 10) ограничивает количество параллельных операций с ресурсами.
Рефакторинг имени/пути ресурса в HCL без destroy/recreate.
`moved { from = aws_instance.old_web; to = aws_instance.new_web }` (Terraform 1.1+). Проверяется в плане PR; заменяет одноразовую команду `terraform state mv`.
Почему: Находится в HCL, версионируется, идемпотентно для всех участников. `state mv` - это одноразовый побочный эффект CLI.
Сопоставление адреса ресурса с ID облачного ресурса, снимки атрибутов, метаданные графа зависимостей и ссылки на модули/провайдеры. Используется для планирования различий и обнаружения drift.
Один инженер прототипирует локально - допустим ли локальный state?
Да, для одноразовой работы в одиночку. Terraform записывает `terraform.tfstate` рядом с конфигурацией. Переключитесь на remote backend, как только появится второй человек, CI-раннер или production-среда.
Команде нужен общий state для инженеров, с блокировкой + версионированием, размещенный на AWS.
S3 backend. Настройте `bucket`, `key`, `region`. Добавьте `dynamodb_table` для блокировки state. Включите версионирование bucket и SSE-KMS серверное шифрование.
Почему: S3 + DynamoDB - это канонический remote backend, размещенный на AWS. Версионирование позволяет восстановиться после поврежденного или случайно перезаписанного state.
Azure-hosted общий state без статических учетных данных в CI.
Тип backend `azurerm`. Установите `use_msi = true` (Managed Identity) или `use_oidc = true`, чтобы раннер аутентифицировался по своей identity. Нативная blob lease обрабатывает блокировку.
Два инженера одновременно запускают apply для одного и того же state, хранящегося на S3.
DynamoDB lock table предотвращает одновременные записи. Первый приобретает блокировку, второй видит ошибку `state lock failed` и должен подождать или `force-unlock`, если она устарела.
Почему: Без DynamoDB параллельные apply могут повредить S3 state-файл.
Конфигурация backend изменилась (например, новый bucket), но вы не хотите мигрировать существующий state.
`terraform init -reconfigure`. Переинициализирует backend и игнорирует существующий локальный кэш. Используйте, когда вы намеренно начинаете с чистого листа.
Управление dev / staging / prod с помощью одной корневой конфигурации.
Workspaces - `terraform workspace new staging`, `terraform workspace select prod`. Каждый имеет свой собственный state-файл. Ссылка через `terraform.workspace` в HCL.
Почему: Легковесный вариант. Для реальной изоляции (разные IAM, отдельные аккаунты) предпочтительны корневые конфигурации для каждой среды.
Команда безопасности спрашивает, как Terraform обрабатывает пароли RDS в state.
Sensitive-значения хранятся **plaintext** в state. Меры по снижению рисков: зашифровать backend в состоянии покоя (S3 SSE-KMS, HCP native), ограничить IAM-доступ к state bucket и по возможности избегать помещения секретов в Terraform.
Почему: Флаг `sensitive = true` только скрывает значения из вывода CLI, но не из state.
Требования соответствия требуют шифрования state в состоянии покоя.
S3: включить SSE-KMS на bucket. Azure: шифрование учетной записи хранения (по умолчанию включено). GCS: ключи шифрования, управляемые клиентом. HCP Terraform: нативное шифрование в состоянии покоя.
Инженер случайно запустил `terraform destroy` на production. State теперь пуст.
Восстановите предыдущую версию state-файла из S3 versioning, затем запустите `terraform plan`, чтобы увидеть, что, по мнению Terraform, теперь нужно создать/импортировать. Объедините с восстановлением на стороне облака (снимки, `aws backup`) для уничтоженных ресурсов.
State-файл выглядит неправильно; возникает соблазн отредактировать его напрямую.
Просмотреть raw remote state JSON или push восстановленный state-файл.
`terraform state pull` выводит текущий remote state в stdout. `terraform state push <file>` перезаписывает remote state указанным файлом. Push является деструктивным - сначала сделайте резервную копию.
Передать временный API-токен через Terraform, чтобы он не попал в state.
Пометьте переменную `ephemeral = true` (Terraform 1.10+). Ephemeral-значения никогда не сохраняются в state или plan-файлах. Чтобы экспортировать через output модуля, также пометьте output `ephemeral = true`.
Почему: `sensitive` скрыт в CLI, но хранится в state в plaintext. `ephemeral` действительно никогда не хранится.
Разница между `sensitive`-аргументом и write-only-аргументом (например, `password_wo`).
`sensitive` хранится в state в plaintext, скрывается только в CLI. Write-only **никогда** не хранится в state. Для обнаружения изменений в write-only-атрибутах Terraform использует сопутствующее поле версии (например, `password_wo_version`).
Заключите адрес в кавычки: `terraform state show 'aws_instance.web[0]'`. Без кавычек shell может интерпретировать скобки.
Разные команды должны работать независимо, не мешая друг другу state.
Один state-файл на команду (или на среду на команду). Отдельные backends/buckets. Используйте data source `terraform_remote_state` для чтения другого state в read-only режиме.
Конфигурация написана для Terraform 0.13; хотим использовать 1.x.
State-файлы forward-compatible: 1.x читает state 0.13. HashiCorp рекомендует инкрементные обновления (0.13 → 0.14 → … → 1.x), запуская каждую версию против state.
Кто-то изменил security group через консоль; хотим, чтобы Terraform либо подтвердил, либо принял новое состояние.
Повторное подтверждение: `terraform apply` - Terraform возвращается к конфигурации. Принять: обновите HCL, чтобы он соответствовал реальности, затем примените (без diff). Сначала обнаружьте с помощью `terraform plan -refresh-only`.
Чтение, генерация и изменение конфигурации
Строго типизировать переменную.
Примитивы: `string`, `number`, `bool`. Коллекции: `list(<type>)`, `set(<type>)`, `map(<type>)`. Структурные: `object({...})`, `tuple([...])`. Используйте `any` только когда действительно полиморфно.
Вывести пароль базы данных так, чтобы он не отображался в `terraform output`.
`output "db_password" { value = ...; sensitive = true }`. CLI показывает `(sensitive value)`. Все еще читается через `terraform output db_password` или `-json` (намеренно - для скриптов).
Создать N похожих ресурсов - выбрать `count` или `for_each`.
`for_each` (с map или set), когда элементы имеют стабильную идентичность (имена регионов, ключи окружения). `count` для "мне нужно N копий, порядок не имеет значения, идентичность - это просто индекс". Добавление/удаление в середине `count` вызывает destroy/recreate; `for_each` сохраняет идентичность.
Создать один ресурс для каждого элемента в списке строк.
`for_each = toset(["a", "b", "c"])`. `each.key` и `each.value` оба дают строку. Для maps: `for_each = var.users` - `each.key` = ключ map, `each.value` = значение map.
Получить список атрибутов для всех экземпляров ресурса `count`.
`aws_instance.web[*].id` возвращает список ID. Работает с `count` и `for_each` (но `for_each` производит неупорядоченную map, поэтому `values(aws_instance.web)[*].id`).
Прочитать значение map с default-значением, если ключ отсутствует.
`lookup(var.config, "region", "us-east-1")`. Возвращает default, если ключ отсутствует. Для глубоко опциональных структур предпочтительнее использовать `try()`.
Отобразить templated user-data скрипт со значениями из переменных.
`templatefile("init.sh.tpl", { region = var.region, env = var.env })`. Шаблон использует синтаксис `${region}`. Более новая альтернатива для статической отрисовки: `file()` + `format()`.
Один и тот же провайдер в двух регионах (например, AWS us-east-1 + us-west-2).
Два блока `provider "aws" { alias = "..." }`. Ресурсы подключаются через `provider = aws.us_west`. Модули принимают alias через `configuration_aliases`.
Запустить shell-команду на только что созданной VM.
`remote-exec` provisioner внутри ресурса. Инструмент последней инстанции - предпочтительнее использовать cloud-init, user data или системы управления конфигурацией. Provisioners не отслеживаются в state и не перезапускаются при drift.
Непрерывная проверка runtime-инварианта (например, health endpoint возвращает 200) без блокировки apply.
`check "endpoint" { data "http" "h" { url = "..." }; assert { condition = data.http.h.status_code == 200; error_message = "..." } }`. Запускается при plan/apply; сбой является предупреждением, а не жесткой ошибкой.
Почему: `check` позволяет использовать scoped data sources, доступные только внутри проверки. `precondition`/`postcondition` являются жесткими ошибками на этапе plan/apply.
Блок `variables { ... }` на уровне файла применяется ко всем запускам. Блок `variables { ... }` для каждого запуска переопределяет только для этого запуска.
Обеспечить, чтобы все S3 bucket были зашифрованы, блокируя apply в случае нарушения.
Политика Sentinel с жестким обязательным применением. Проверяет plan; сбой блокирует apply. Soft-mandatory позволяет администратору переопределить. Advisory только логирует, но никогда не блокирует.
Интегрировать сторонний сканер безопасности в run pipeline HCP Terraform.
Run task на этапе post-plan. HCP POSTs plan на ваш endpoint; endpoint отвечает pass/fail. Обязательное применение блокирует apply при сбое; advisory только предупреждает.
Speculative plans запускаются на PRs (или ветках), комментируют результаты обратно в PR и никогда не применяются. Запускаются автоматически при включении интеграции VCS.
Избежать хранения AWS access keys как статических секретов в переменных HCP Terraform.
Динамические учетные данные провайдера. HCP Terraform запрашивает краткосрочные учетные данные через OIDC trust к AWS/Azure/GCP. Нет статических ключей; per-run identities; удобно для аудита.
Почему: Статические ключи утекают. OIDC trust привязывает учетные данные к workspace + run, истекает в течение часа.
Использовать одни и те же учетные данные провайдера во многих workspaces.
Variable sets. Определите один раз на уровне организации/проекта, прикрепите ко многим workspaces. Обновления распространяются без редактирования каждого workspace.
Встроенный проект, который существует в каждой организации и не может быть удален (переименование разрешено). Все workspaces принадлежат ему, если явно не назначены другому проекту.
Применить downstream workspace всякий раз, когда upstream workspace успешно завершает apply.
Run triggers. Настройте source workspace(s) на зависимом workspace; HCP ставит в очередь run на зависимом после каждого успешного upstream apply.