Terraform в команде: разделение state, радиус поражения и проверенные планы
Как разделить state в Terraform, запускать планы в CI и применять только проверенное, чтобы изменения инфраструктуры стали рутиной, а не риском.
Примеры обобщённые и иллюстративные. Они не взяты из среды какого-либо клиента: подстройте пороги и параметры под свою систему.
Файл state — настоящая граница
Каждый terraform apply может изменить что угодно в том state, с которым работает. Поэтому единицей риска является state, а не модуль. При одном state на всё окружение опечатка в DNS-записи и изменение продакшен-базы проходят через один и тот же план — а он считается минутами.
Делите по жизненному циклу и радиусу поражения
Группируйте ресурсы, которые меняются и отказывают вместе. Распространённая схема разделяет в каждом окружении:
- Фундамент: аккаунты, сети, DNS-зоны — меняются редко.
- Данные: базы, бакеты, очереди — меняются осторожно и защищены от удаления.
- Вычисления и сервисы: кластеры, функции, балансировщики — меняются часто.
Каждый слой читает выходные значения нижнего и никогда не управляет его ресурсами. Окружения используют одни и те же модули, но с отдельными state и учётными данными, чтобы apply в staging не мог затронуть продакшен.
Удалённый state, блокировки и версии
- Храните state в удалённом бэкенде с включёнными блокировками и версионированием — никогда в Git и никогда на ноутбуке.
- Коммитьте lock-файл зависимостей (.terraform.lock.hcl) и фиксируйте версии модулей, чтобы план сегодня и план через месяц выполняли один и тот же код.
- Защищайте ресурсы с состоянием через prevent_destroy и встроенную защиту от удаления у провайдера.
План на каждый pull request, apply того, что проверено
Запускайте terraform plan в CI для каждого изменения и публикуйте результат в pull request. После одобрения применяйте сохранённый план, а не планируйте заново, чтобы продакшен получил ровно то, что прочитали ревьюеры; если state за это время изменился, Terraform отклонит устаревший план. Пайплайн должен аутентифицироваться краткоживущими учётными данными через OIDC-федерацию, а не долгоживущими ключами в секретах.
terraform init -input=false
terraform plan -input=false -lock-timeout=5m -out=tfplan
terraform show -no-color tfplan > plan.txt # attach to the pull request
# after approval, applying the same saved plan:
terraform apply -input=false tfplan
Регулярно ищите дрейф
Изменения через консоль всё равно случаются — обычно во время инцидентов. Плановая задача с terraform plan -detailed-exitcode их находит: код выхода 0 означает отсутствие изменений, 2 — реальная инфраструктура отличается от кода, 1 — ошибку. Превращайте 2 в тикет, а затем либо внесите изменение в код, либо откатите его.
Политики до apply
Правила вроде «никаких публичных бакетов» или «у каждого ресурса есть тег владельца» машина проверяет лучше, чем ревьюер в конце долгого дня. Инструменты policy as code, например Open Policy Agent через Conftest, могут проверять JSON-представление плана (terraform show -json) в CI и блокировать merge.