DevOps
Terraform: продвинутый
В 2017 году инженер GitLab случайно выполнил `rm -rf` на production-БД, попутав терминалы. Через год HashiCorp заметила что 30% инцидентов крупных клиентов Terraform - похожие истории: 'не тот workspace', 'не тот state', 'не та environment variable'. Так родился Terraform Enterprise с Sentinel: контроль того, ЧТО можно деплоить, ОТКУДА и КЕМ - до того как apply нанесёт ущерб.
- **Coinbase**: 200+ инженеров, multi-region инфраструктура - Terragrunt + state splitting по командам, каждая команда владеет своим state-файлом
- **HashiCorp Sentinel в production**: GitHub Enterprise использует Sentinel для блокировки публичных S3-bucket и enforcement тегов на всех ресурсах
- **Lyft в 2019**: миграция с одного 8000-ресурсного state на 60+ изолированных state'ов - время apply упало с 45 минут до 3 минут, blast radius сократился на 99%
Workspaces: один код, несколько окружений
В 2021 году инженер GitLab случайно выкатил миграцию production-БД на staging-инстанс - он работал в неверном Terraform workspace. **Workspace** в Terraform - это именованный экземпляр state-файла для одной конфигурации. Тот же `main.tf` может управлять dev, staging и prod, если для каждого создать workspace: `terraform workspace new prod`. Переменная `${terraform.workspace}` доступна внутри конфига и позволяет менять имена ресурсов, инстанс-типы и тарифные планы по окружению.
Workspaces удобны для feature-веток (короткоживущие изолированные стенды), но НЕ рекомендуются для разделения долгоживущих окружений (dev/staging/prod). Причина: один backend, одна локация state - человеческий фактор `terraform workspace select` приводит к катастрофам. Best practice для prod - отдельные директории/репозитории с собственным backend (terragrunt или просто `envs/prod/main.tf`, `envs/dev/main.tf`).
Почему HashiCorp официально НЕ рекомендует использовать workspaces для разделения prod/staging/dev?
Remote State и блокировки
Локальный `terraform.tfstate` - смерть для команды. Двое инженеров запускают `terraform apply` одновременно - state переписывается, инфраструктура расходится. **Remote backend** хранит state в S3/GCS/Azure Blob и блокирует параллельные операции через DynamoDB/Cloud Storage. Каждый apply берёт lock, ставит свою операцию в очередь, и отпускает lock после завершения.
State содержит **секреты в открытом виде** - пароли БД, API-ключи, приватные IP. Поэтому: (1) шифрование в backend (S3 SSE-KMS), (2) IAM-политики с минимальным доступом, (3) версионирование bucket (S3 versioning) для отката после повреждения state, (4) **никогда** не коммитить tfstate в git. При утечке S3-bucket с state атакующий получает дамп всей инфраструктуры.
Команда из 10 инженеров мигрирует с локального state на S3 backend. Какой минимальный набор компонентов нужен?
Sentinel: Policy-as-Code
В 2019 году финансовая компания спалила $30k за ночь - инженер случайно создал 50 GPU-инстансов p3.16xlarge для эксперимента и забыл их потушить. **Sentinel** от HashiCorp (часть Terraform Enterprise/Cloud) - policy-as-code framework, проверяющий план Terraform до apply. Политики выражены на DSL, читаемом как обычный код, и могут быть `advisory` (предупреждение), `soft-mandatory` (требует ручного override) или `hard-mandatory` (блокирует apply).
Open-source альтернатива - **OPA (Open Policy Agent)** с Rego DSL. OPA не привязан к HashiCorp, работает с любыми JSON-структурами (Kubernetes admission control, API gateway, Terraform). Для Terraform используется через `conftest` (читает план как JSON) или плагин **terraform-compliance** (BDD-стиль `Given/When/Then`).
Какой enforcement level Sentinel-политики выбрать для правила 'все S3-bucket должны иметь шифрование'?
Blast radius и подходы к ограничению
**Blast radius** - объём ресурсов, которые `terraform destroy` или ошибочный apply может уничтожить за одно действие. Монолитный state с 5000 ресурсов = катастрофический blast radius (один неверный refactor может снести всё). Принцип борьбы - **state splitting**: разделение инфраструктуры на изолированные state-файлы по зоне ответственности (network, security, data, app), плюс composition через `terraform_remote_state` data source для shared outputs.
**Terragrunt** (обёртка над Terraform от Gruntwork) автоматизирует state splitting через `terragrunt.hcl` в каждой папке окружения. Каждая папка получает собственный backend и набор переменных, а DRY достигается через `include` и `dependency` блоки. Большие команды (Lyft, Coinbase) живут на Terragrunt именно из-за встроенного state splitting и dependency-graph между модулями.
Чем больше один tfstate, тем проще - всё в одном месте, все зависимости явные.
Большой state = большой blast radius + медленные plan/apply + риск конкурентных конфликтов. Правильно разделить state по границам зон ответственности (network, security, data, app).
Terraform делает refresh всех ресурсов в state на каждом plan (даже если меняется один). 5000 ресурсов = 20+ минут per plan, плюс любой `destroy` без -target становится экзистенциальной угрозой. State splitting - индустриальный стандарт со времён Terraform 0.12.
Команда из истории про EFF DES Cracker - сорри, история другая - команда поддерживает один монолитный tfstate с 3000 ресурсов, и каждый `terraform plan` занимает 20 минут. Что НЕ является правильным первым шагом для уменьшения blast radius?
Ключевые идеи
- **Workspaces** - один state на конфиг, удобны для feature-веток, НО НЕ для разделения prod/staging - человеческий фактор приводит к катастрофам
- **Remote state** - S3+DynamoDB (или эквивалент) даёт concurrent locking, версионирование, шифрование секретов; локальный tfstate в команде неприемлем
- **Sentinel/OPA** - policy-as-code на этапе plan: блокирует дорогие/небезопасные ресурсы до apply, enforcement levels от advisory до hard-mandatory
- **Blast radius** - state splitting по доменам + terraform_remote_state для composition; Terragrunt автоматизирует это с DRY-конфигом
- Большой монолитный state = медленные plan/apply + катастрофический blast radius - индустриальный стандарт это разделение
Связанные темы
Продвинутый Terraform встроен в более широкую DevOps-картину:
- GitOps и ArgoCD — Тот же подход 'state в git как source of truth' для Kubernetes - и те же проблемы blast radius
- Secrets management (Vault) — Vault хранит динамические секреты вне Terraform state, что снижает риск утечки
- Kubernetes operators — Альтернативная модель IaC - declarative state в etcd вместо tfstate, control loop вместо apply
Вопросы для размышления
- В вашей команде сейчас один монолитный state или разделённый? Если монолитный - какой реальный blast radius у одной ошибки `terraform apply`?
- Есть ли в CI проверка плана policy-as-code инструментом (Sentinel/OPA/checkov)? Если нет - какие 3 правила вы бы внедрили первыми?
- Если завтра S3-bucket с tfstate скомпрометируется, что атакующий узнает о вашей инфраструктуре? Какие данные стоит вынести в Vault?
Связанные уроки
- devops-12 — Основы Terraform - пресловутый state и resource граф
- devops-14 — Ansible управляет конфигурацией поверх Terraform инфраструктуры
- devops-11 — GitOps с ArgoCD - remote state как single source of truth
- ds-05-replication — Remote state lock - тот же mutual exclusion что в распределённых системах
- ds-12-service-discovery