Изоляция состояния: рабочие области против раскладки по папкам

Рейтинг: 57.8% · 13 голосов
Подробный курс по Terraform и OpenTofu на примерах Yandex Cloud: ресурсы и провайдеры, состояние и бэкенды, модули, циклы и условия HCL, секреты и Lockbox, тестирование, CI/CD и командная работа. С актуализацией на 2026 (Terraform 1.3-1.10, OpenTofu).
Ответить
Аватара пользователя
Vlad_DevOps
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Изоляция состояния: рабочие области против раскладки по папкам

Сообщение Vlad_DevOps »

Оглавление курса (47)
  1. Что такое инфраструктура как код и зачем она нужна
  2. Категории инструментов IaC: чем отличаются Ansible, Packer, Kubernetes и Terraform
  3. Чем Terraform отличается от Ansible, Pulumi и CloudFormation
  4. Как работает Terraform под капотом и кто такой OpenTofu
  5. Установка Terraform и OpenTofu, подготовка Yandex Cloud
  6. Первый ресурс: разворачиваем виртуальную машину в Yandex Cloud
  7. Веб-сервер на ВМ: метаданные, user-data и группы безопасности
  8. Переменные ввода и выходные значения в Terraform
  9. Кластер ВМ: группа экземпляров Yandex Compute Instance Group
  10. Балансировщик нагрузки: Application Load Balancer в Yandex Cloud
  11. Состояние Terraform: что такое tfstate и чем опасен локальный файл
  12. Удалённое хранилище состояния: бэкенд на Yandex Object Storage
  13. Изоляция состояния: рабочие области против раскладки по папкам (вы здесь)
  14. Источники данных и terraform_remote_state: связываем компоненты
  15. Модули Terraform: переиспользуем инфраструктуру
  16. Входные, локальные и выходные переменные модуля
  17. Версионирование модулей и подводные камни
  18. Циклы в Terraform: параметр count
  19. Циклы в Terraform: for_each по множествам и картам
  20. Выражения for и строковые директивы
  21. Условная логика: count-трюк, for_each и директива if
  22. Встроенные функции, типы и выражения HCL
  23. Развёртывание без простоя: create_before_destroy
  24. Подводные камни Terraform и рефакторинг с блоком moved
  25. Управление секретами: основы и типы
  26. Инструменты управления секретами: Yandex Lockbox, Vault, SOPS
  27. Аутентификация провайдера и секреты: сервисные аккаунты и OIDC
  28. Секреты в ресурсах и состоянии, ephemeral-значения
  29. Провайдеры Terraform: установка, версии, required_providers
  30. Несколько копий одного провайдера: alias и мультизона
  31. Несколько провайдеров и аккаунтов: configuration_aliases
  32. Terraform, Docker и Kubernetes: Managed Kubernetes в Yandex Cloud
  33. Код Terraform промышленного уровня: почему это долго
  34. Мелкие компонуемые модули вместо монолита
  35. Тестируемые модули и проверки: validation, precondition, postcondition
  36. Версионирование, публикация модулей и Terragrunt
  37. Ручное тестирование Terraform и очистка ресурсов
  38. Автоматические тесты на Terratest: модульные тесты
  39. Интеграционные и сквозные тесты, пирамида тестирования
  40. Нативный terraform test и статический анализ
  41. Внедрение Terraform в команде: процессы и культура
  42. Конвейер развёртывания прикладного кода
  43. Конвейер инфраструктурного кода и золотое правило Terraform
  44. Стиль, ревью и CI/CD-инструменты: Atlantis, TACOS, policy as code
  45. Что нового в Terraform 1.3-1.10: моды, импорт, провайдер-функции, Stacks
  46. OpenTofu в 2026: чем живёт форк и как мигрировать
  47. Сертификация HashiCorp Terraform Associate и карьера
Представь картину. Пятница, вечер, ты правишь конфиг и запускаешь terraform apply. Через минуту телефон разрывается от алертов - упал прод. А ты был уверен, что катишь изменения в dev. Знакомая боль? Корень почти всегда один: окружения dev, stage и prod делят одно состояние или путаются между собой. Один неосторожный apply - и ошибка в песочнице сносит боевую инфраструктуру.

В этом уроке разберём, как изолировать terraform state по окружениям, чтобы такого не случалось. Сравним два подхода - рабочие области (terraform workspaces) и раскладку по отдельным папкам - и я честно скажу, почему опытные команды в большинстве случаев выбирают второй. Спойлер: дело не в моде, а в том, что папки тяжелее прострелить себе ногой.

Почему состояние вообще надо изолировать

Напомню механику. Terraform хранит карту реального мира в файле состояния - terraform state. Это связь между тем, что написано в коде (инфраструктура как код, она же iac), и тем, что реально создано в облаке: id виртуалок, сетей, дисков. Когда ты делаешь terraform plan, инструмент сравнивает желаемое (код) с текущим (state) и показывает разницу. terraform apply эту разницу применяет.

Теперь главное. Если dev и prod пишут в один и тот же state, у Terraform в голове каша. Он видит ресурсы обоих окружений в одной карте. Меняешь параметр для теста - а план затрагивает боевые ресурсы, потому что они лежат рядом. Плюс блокировки: пока кто-то катит dev, прод-деплой ждёт лок. И самое опасное - terraform destroy, нацеленный не туда, выносит всё разом.

Вывод простой: у каждого окружения должно быть своё, отдельное состояние. Физически отдельный файл (или ключ в бэкенде), который ничего не знает про соседей. Вопрос только в том, как это организовать. Вот тут и расходятся два пути.

Изображение

Способ 1: рабочие области (terraform workspaces)

Workspaces - встроенный механизм. Идея: один набор кода, один бэкенд, но несколько именованных состояний внутри него. По умолчанию ты всегда в области default. Команды простые:

Код: Выделить всё

terraform workspace new dev
terraform workspace new prod
terraform workspace list
terraform workspace select prod
Под капотом при использовании удалённого бэкенда (например, S3-совместимого Object Storage в Yandex Cloud) Terraform раскладывает состояния по разным ключам автоматически - в подпапку env: внутри бакета. То есть бэкенд один, а файлов состояния столько, сколько областей.

В коде к текущей области обращаются через terraform.workspace. Типичный приём - подмешивать имя области в параметры:

Код: Выделить всё

locals {
  env       = terraform.workspace
  vm_count  = terraform.workspace == "prod" ? 3 : 1
  disk_size = terraform.workspace == "prod" ? 50 : 20
}

resource "yandex_compute_instance" "app" {
  count = local.vm_count
  name  = "app-${local.env}-${count.index}"
  zone  = "ru-central1-a"

  resources {
    cores  = 2
    memory = 2
  }

  boot_disk {
    initialize_params {
      image_id = "fd8vmcue7aajpmeo39kk"
      size     = local.disk_size
    }
  }

  network_interface {
    subnet_id = yandex_vpc_subnet.this.id
    nat       = true
  }
}
Выглядит компактно, и в этом ловушка. Главная беда workspaces в том, что текущая область - это невидимое состояние твоего терминала, а не часть кода. Открыл код - и не понимаешь, для какого окружения сейчас всё применится. Узнать можно только командой terraform workspace show. А значит, рано или поздно ты забудешь переключиться и закатишь dev-эксперимент в prod. Это не теоретический риск, на этом обжигались тысячи людей.

Что ещё плохо. Все окружения делят один бэкенд и один набор прав - изолировать доступ к боевому состоянию на уровне IAM или отдельного бакета уже не выйдет. Конфигурация различается через тернарники в коде, и когда условий становится много, читать это больно. И провайдеры тоже общие - нельзя, например, держать dev и prod в разных каталогах Yandex Cloud аккуратно и явно.

Где workspaces реально уместны? Когда нужно быстро поднять несколько почти одинаковых временных копий: ветка фичи, краткоживущий стенд для ревью, эфемерное окружение в CI. Вот там это удобно. А для постоянных dev/stage/prod - нет.

Способ 2: раскладка по папкам (рекомендуемый)

Подход, который советуют почти все книги и зрелые команды: для каждого окружения - своя директория со своим backend-ключом. Никакой магии скрытого состояния. Какую папку открыл - в том окружении и работаешь. Это видно глазами, это видно в git diff, это сложно перепутать.

Структура проекта обычно такая:

Код: Выделить всё

.
├── modules/
│   ├── network/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   └── app/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
└── environments/
    ├── dev/
    │   ├── main.tf
    │   ├── backend.tf
    │   └── terraform.tfvars
    ├── stage/
    │   ├── main.tf
    │   ├── backend.tf
    │   └── terraform.tfvars
    └── prod/
        ├── main.tf
        ├── backend.tf
        └── terraform.tfvars
Общая логика выносится в модули (модули terraform), а в каждой папке окружения лежит тонкая обёртка: подключает модуль и передаёт свои значения. Ключевое - у каждого окружения свой backend с уникальным ключом состояния. Вот backend.tf для prod на Object Storage в Yandex Cloud:

Код: Выделить всё

terraform {
  backend "s3" {
    endpoints = {
      s3 = "https://storage.yandexcloud.net"
    }
    bucket = "tfstate-cyberlake"
    key    = "prod/terraform.tfstate"
    region = "ru-central1"

    skip_region_validation      = true
    skip_credentials_validation = true
    skip_requesting_account_id  = true
    skip_s3_checksum            = true
  }
}
Для dev файл такой же, но key = "dev/terraform.tfstate". Состояния физически лежат под разными ключами в бакете и не пересекаются никогда. А вот main.tf окружения, который дёргает общий модуль:

Код: Выделить всё

module "app" {
  source = "../../modules/app"

  env       = "prod"
  vm_count  = 3
  disk_size = 50
  subnet_id = module.network.subnet_id
}

module "network" {
  source = "../../modules/network"
  env    = "prod"
}
Заметь разницу с первым способом: окружение задано явной строкой "prod" прямо в коде, а не вычисляется из невидимой области. Чтобы что-то применить в проде, ты буквально заходишь в каталог environments/prod и делаешь там terraform plan и terraform apply. Перепутать сложно. Бонусом - можно дать на бакет prod-состояния отдельные права в IAM, чтобы джуниоры физически не могли туда писать.

И да, всё сказанное один в один работает с OpenTofu - это форк Terraform, команды и формат бэкенда те же, так что выбор подхода от инструмента не зависит.

Типичные грабли
  • Забыл terraform workspace select. Классика жанра при первом способе. Сидишь в default, думаешь, что в dev, катишь в прод. Лечится только дисциплиной или баннером в promt - что ненадёжно.
  • Копипаста папок целиком. При раскладке по папкам люди копируют всю логику из dev в prod вместо выноса в модуль. В итоге dev и prod расходятся, фиксы доезжают не везде. Общее - в modules, в папках только параметры.
  • Одинаковый backend key в двух папках. Скопировал backend.tf и забыл поменять key. Два окружения начинают топтать одно состояние - тот самый ад, от которого ты бежал. Проверяй key после каждого копирования.
  • tfvars с секретами в git. terraform.tfvars удобно класть рядом, но пароли и токены туда совать нельзя - утекут в историю репозитория. Секреты - через переменные окружения или Lockbox.
Мини-лаба

Повтори руками, это закрепляет лучше любого текста:
  • Создай структуру: папку modules/app с простейшим yandex_compute_instance и папки environments/dev и environments/prod.
  • В каждое окружение положи backend.tf с разными key (dev/terraform.tfstate и prod/terraform.tfstate), указывающими на один бакет в Object Storage.
  • Зайди в environments/dev, выполни terraform init и terraform plan. Затем то же в prod. Убедись, что планы независимы.
  • Теперь для сравнения создай terraform workspace new test в одной из папок и посмотри через terraform workspace show, как состояние стало невидимым в коде. Прочувствуй разницу.
Контрольные вопросы
  • Что физически произойдёт, если dev и prod пишут в один файл состояния, и чем это грозит при terraform destroy?
  • Почему имя текущей рабочей области называют "невидимым состоянием" и в чём тут опасность?
  • Что должно лежать в папке окружения, а что выносится в модуль при раскладке по папкам?
  • Какой ровно один атрибут в backend-конфигурации отвечает за изоляцию состояний разных окружений в одном бакете?
Итог

Запомни главное. Каждое окружение - отдельный terraform state, это не обсуждается. Workspaces дают изоляцию дёшево, но прячут окружение в невидимое состояние терминала, и на этом легко обжечься - оставь их для эфемерных стендов. Раскладка по папкам с отдельным backend-ключом на окружение многословнее, зато окружение видно в коде, права изолируются на уровне бакета, а прострелить ногу заметно труднее. Для постоянных dev/stage/prod бери папки. Это и есть зрелый terraform yandex cloud в проде.
👍3 ❤️5 🔥1 😄 🤔3
Аватара пользователя
LinuxNinja
Сообщения: 1
Зарегистрирован: 12 май 2026, 13:18

Re: Изоляция состояния: рабочие области против раскладки по папкам

Сообщение LinuxNinja »

Спасибо, наконец дошло почему все вокруг кричат про папки. Я как раз сидел на workspaces и пару раз ловил себя на том что не помню в каком окружении нахожусь. Пойду переделывать.
👍 ❤️ 🔥1 😄 🤔
Аватара пользователя
pogore1
Сообщения: 1
Зарегистрирован: 23 май 2026, 09:35

Re: Изоляция состояния: рабочие области против раскладки по папкам

Сообщение pogore1 »

Вопрос по бэкенду: а если бакет один на все окружения, не опасно ли что джун случайно снесёт чужой state по соседнему ключу? Или права в IAM реально настраиваются на префикс ключа отдельно?
👍1 ❤️1 🔥1 😄 🤔
Ответить
← Предыдущая глава
Удалённое хранилище состояния: бэкенд на Yandex Object Storage
Следующая глава →
Источники данных и terraform_remote_state: связываем компоненты

Все главы курса «Terraform: инфраструктура как код на Yandex Cloud»

Поделиться темой: ✈ Telegram VK

Вернуться в «Terraform: инфраструктура как код на Yandex Cloud»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 2 гостя