Удалённое хранилище состояния: бэкенд на Yandex Object Storage

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

Удалённое хранилище состояния: бэкенд на Yandex Object Storage

Сообщение 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.tfstate - это бомба замедленного действия. Пока ты один на проекте, файл состояния спокойно лежит рядом с кодом и никого не трогает. Но стоит появиться второму человеку (или второй машине, или CI-раннеру) - и начинается. Один сделал terraform apply со своего ноута, второй со своего, и теперь у вас две разные версии реальности. Terraform думает, что ресурса нет, и создаёт его заново. Или удаляет то, что нужно. А ещё state часто содержит секреты в открытом виде, и хранить его в гите - это подарок любому, кто получит доступ к репозиторию.

Решение придумано давно: вынести terraform state в общее удалённое хранилище. В мире инфраструктуры как код (iac) это называется remote backend. Для Yandex Cloud самый удобный вариант - положить состояние в Object Storage, потому что он S3-совместимый, а у Terraform из коробки есть бэкенд "s3". В этом уроке разберём, как настроить terraform yandex cloud так, чтобы state жил в бакете, был защищён блокировкой и переживал любые катастрофы благодаря версионированию. Заодно глянем на новый механизм 2026 года - нативную блокировку без всякой DynamoDB.

Зачем вообще удалённый бэкенд

Удалённый бэкенд решает сразу три проблемы.
  • Общий источник правды. State лежит в одном месте, все берут его оттуда. Никаких "а у меня локально по-другому".
  • Блокировка (state locking). Пока один человек делает terraform plan apply, бэкенд ставит замок. Второй, кто попытается применить изменения параллельно, получит вежливый отказ и не испортит файл состояния гонкой.
  • Безопасность и бэкапы. Бакет можно зашифровать, ограничить по IAM, включить версионирование. State перестаёт валяться на чьём-то рабочем столе.
Важно понимать: бэкенд - это не то же самое, что провайдер. Провайдер yandex отвечает за создание ресурсов в облаке. Бэкенд отвечает только за то, где хранится state. Это два независимых механизма, которые просто часто настраивают рядом. То же самое, кстати, работает и в opentofu - синтаксис бэкенда там полностью совместим, так что всё ниже применимо и к нему.

Изображение

Что нужно подготовить в облаке

Object Storage не пускает по обычному IAM-токену для S3-протокола. Ему нужна пара статических ключей - access key и secret key, привязанных к сервисному аккаунту. Цепочка такая: создаём сервисный аккаунт, выдаём ему роль storage.editor на каталог, генерим статический ключ доступа. В Terraform это три ресурса, имена которых я взял прямо из документации провайдера, не выдумывая.

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

locals {
  folder_id = "b1gxxxxxxxxxxxxxxxxx"
}

# Сервисный аккаунт под управление состоянием
resource "yandex_iam_service_account" "tf_state" {
  folder_id = local.folder_id
  name      = "tf-state-sa"
}

# Роль на каталог: права писать в Object Storage
resource "yandex_resourcemanager_folder_iam_member" "tf_state_editor" {
  folder_id = local.folder_id
  role      = "storage.editor"
  member    = "serviceAccount:${yandex_iam_service_account.tf_state.id}"
}

# Статический ключ доступа сервисного аккаунта
resource "yandex_iam_service_account_static_access_key" "tf_state_key" {
  service_account_id = yandex_iam_service_account.tf_state.id
  description        = "static access key for terraform state bucket"
}

# Сам бакет под state, с версионированием
resource "yandex_storage_bucket" "tf_state" {
  access_key = yandex_iam_service_account_static_access_key.tf_state_key.access_key
  secret_key = yandex_iam_service_account_static_access_key.tf_state_key.secret_key
  bucket     = "cyberlake-tf-state"

  versioning {
    enabled = true
  }
}
Атрибуты access_key и secret_key у ресурса yandex_iam_service_account_static_access_key - реальные, secret_key помечен как Read-Only и доступен только сразу после создания. Блок versioning у бакета - тоже из доки; именно он даёт нам историю всех версий state-файла. Если кто-то затрёт состояние криво, ты откатишься на предыдущую версию объекта прямо в Object Storage. Это твой бесплатный страховочный трос.

Тонкий момент про курицу и яйцо. Бакет под state создаётся самим Terraform, но бэкенд должен существовать до первого apply. Поэтому схема обычно двухшаговая: сначала поднимаешь бакет с локальным state (или вообще руками через консоль), потом подключаешь бэкенд и мигрируешь состояние в этот бакет. Не пытайся в одном проекте одновременно создавать бакет и хранить в нём же его собственный state - получишь логический парадокс.

Блок backend "s3" для Yandex Cloud

Теперь самое главное - как сказать Terraform, чтобы он хранил state в нашем бакете. Бэкенд описывается внутри блока terraform. Object Storage прикидывается AWS S3, но это не настоящий AWS, поэтому половину "ауэсных" проверок надо отключить, иначе Terraform полезет в несуществующие STS и metadata API и упадёт.

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

terraform {
  backend "s3" {
    bucket = "cyberlake-tf-state"
    key    = "prod/infra.tfstate"
    region = "ru-central1"

    endpoints = {
      s3 = "https://storage.yandexcloud.net"
    }

    # Native state locking, Terraform 1.11+ / OpenTofu
    use_lockfile = true

    # Без этих флагов S3-бэкенд пытается ходить в реальный AWS
    skip_region_validation      = true
    skip_credentials_validation = true
    skip_requesting_account_id  = true
    skip_s3_checksum            = true
  }
}
Разберём по строчкам:
  • bucket - имя бакета, которое мы создали выше.
  • key - путь к файлу состояния внутри бакета. Удобно класть разные окружения по разным ключам: prod/infra.tfstate, stage/infra.tfstate.
  • region - подойдёт ru-central1. Object Storage не особо смотрит на регион, но поле обязательное, поэтому валидацию мы и выключаем.
  • endpoints.s3 - адрес Object Storage. Раньше писали плоский endpoint = "storage.yandexcloud.net", но начиная с Terraform 1.6 этот плоский атрибут объявлен устаревшим, и правильно использовать блок endpoints с ключом s3. Если у тебя старые гайды с одиночным endpoint - они ещё работают, но кидают deprecation warning.
  • skip_* флаги - отключают проверки, специфичные для настоящего AWS. Для любого S3-совместимого хранилища это стандартный набор.
А вот ключи доступа в сам блок backend лучше не зашивать - это секреты, и в гите им не место. Их передают через переменные окружения AWS_ACCESS_KEY_ID и AWS_SECRET_ACCESS_KEY (да, именно с префиксом AWS_, бэкенд-то s3-шный), либо через флаг -backend-config при инициализации.

Блокировка: нативный lockfile вместо DynamoDB

Историческая боль S3-бэкенда: сам по себе S3 не умеет атомарной блокировки, поэтому раньше для state locking приходилось поднимать отдельную таблицу - в AWS это DynamoDB, в Yandex Cloud её роль играла YDB в DynamoDB-совместимом режиме. Лишний ресурс, лишние права, лишняя точка отказа.

В 2026 году это уже архаизм. Начиная с Terraform 1.11 (экспериментально появилось в 1.10) бэкенд s3 умеет нативную блокировку через простой lock-файл прямо в бакете. Включается одной строкой: use_lockfile = true. Никакой таблицы не нужно. OpenTofu поддерживает то же самое. DynamoDB-локинг при этом объявлен устаревшим и в одной из будущих минорных версий уедет на пенсию, так что для новых проектов выбор очевиден - use_lockfile.

Как это работает: перед apply Terraform кладёт в бакет маленький объект рядом с твоим state (по ключу infra.tfstate.tflock), а после завершения - удаляет. Если объект уже есть, значит кто-то держит замок, и второй процесс ждёт или падает с понятной ошибкой. Если apply внезапно прервался и замок завис, его снимают вручную командой terraform force-unlock с ID из сообщения об ошибке - но без фанатизма, сначала убедись, что параллельного процесса реально нет.

Миграция состояния: init -migrate-state

Допустим, у тебя уже есть проект с локальным state, и ты только что дописал блок backend "s3". Просто так Terraform на новый бэкенд не переедет - надо инициализироваться и явно разрешить миграцию.

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

export AWS_ACCESS_KEY_ID="<access_key сервисного аккаунта>"
export AWS_SECRET_ACCESS_KEY="<secret_key сервисного аккаунта>"

terraform init -migrate-state
Terraform увидит, что бэкенд сменился, спросит "переложить существующий state из локального в s3?" - отвечаешь yes, и твой terraform.tfstate уезжает в бакет. После этого локальный файл больше не нужен (оставшийся terraform.tfstate станет пустой заглушкой). Если же бэкенд настраивается с нуля и мигрировать нечего, хватит обычного terraform init.

Команда terraform init -reconfigure - это другое: она не переносит state, а просто пересобирает настройки бэкенда, игнорируя сохранённые. Её используют, когда меняешь, например, ключ или бакет и не хочешь тащить старое состояние. Не путай -migrate-state и -reconfigure, это частый источник недоразумений.

Типичные грабли
  • Забыл skip-флаги. Самая популярная ошибка: пишешь endpoint, но не выключаешь валидацию - и init виснет или ругается на credentials, пытаясь достучаться до настоящего AWS. Лечится набором skip_*.
  • Ключи в коде. Зашил access_key/secret_key прямо в блок backend и закоммитил. Поздравляю, секрет утёк. Только переменные окружения или -backend-config.
  • Нет версионирования на бакете. State побился, а откатиться некуда. Включай versioning сразу, это бесплатная страховка.
  • Курица и яйцо. Пытаешься создать бакет и хранить в нём же его state в одном apply. Раздели на два шага.
  • Старый одиночный endpoint. Работает, но устарел. Используй блок endpoints = { s3 = ... }, чтобы не ловить warning и не переписывать конфиг через год.
  • Тянешь DynamoDB по привычке. В 2026 для блокировки достаточно use_lockfile. Таблица YDB/DynamoDB - лишняя сущность, если только тебе не нужна совместимость со старыми версиями Terraform.
Мини-лаба

Повтори руками, без этого в голове не уляжется:
  • Создай сервисный аккаунт, выдай роль storage.editor и сгенерь статический ключ (через Terraform с локальным state или через консоль).
  • Подними бакет yandex_storage_bucket с включённым versioning. Имя бакета глобально уникальное, придумай своё.
  • Допиши блок terraform { backend "s3" { ... } } с endpoints.s3, skip-флагами и use_lockfile = true.
  • Прогони terraform init -migrate-state и убедись, что state уехал: открой бакет в консоли и найди там свой .tfstate.
  • Запусти terraform plan из двух терминалов одновременно (или поставь долгий apply и параллельно дёрни plan) - поймай сообщение о блокировке. Это и есть state locking в действии.
  • Затри объект state в бакете и восстанови его из предыдущей версии - проверь, что версионирование реально спасает.
Контрольные вопросы
  • Почему Object Storage требует статический ключ доступа, а не обычный IAM-токен, для работы по S3-протоколу?
  • Чем отличается terraform init -migrate-state от terraform init -reconfigure?
  • Что делает use_lockfile = true и почему в 2026 году это лучше, чем отдельная таблица DynamoDB/YDB?
  • Зачем включать versioning на бакете со state и как это помогает при аварии?
Итог: что запомнить

Удалённый бэкенд - первое, что нужно настроить на любом командном проекте, иначе terraform state рано или поздно превратится в источник боли. В Yandex Cloud это бакет Object Storage плюс блок backend "s3" с адресом storage.yandexcloud.net в endpoints.s3 и обязательным набором skip-флагов. Доступ - через статический ключ сервисного аккаунта, который кладём в переменные окружения, а не в код. Блокировку в 2026 делаем нативным use_lockfile = true без всякой DynamoDB. Версионирование бакета держим включённым - это твой бэкап на случай, когда state всё-таки побьётся. И помни про два шага: сначала бакет, потом миграция через init -migrate-state.
👍6 ❤️1 🔥 😄 🤔1
Аватара пользователя
antonak
Сообщения: 1
Зарегистрирован: 11 май 2026, 03:14

Re: Удалённое хранилище состояния: бэкенд на Yandex Object Storage

Сообщение antonak »

Спасибо, наконец дошло зачем эти skip-флаги нужны. Я раньше копировал бэкенд из чужого гайда, оно падало с ошибкой про credentials, и я думал что у меня ключи кривые. А оно просто в настоящий AWS ломилось.
👍1 ❤️ 🔥 😄 🤔
Аватара пользователя
powergli
Сообщения: 1
Зарегистрирован: 11 май 2026, 17:58

Re: Удалённое хранилище состояния: бэкенд на Yandex Object Storage

Сообщение powergli »

А вопрос по lock-файлу: если apply упал на середине и замок завис, force-unlock безопасно дёргать? Боюсь снять блокировку пока реально кто-то другой не доделал, и испортить state.
👍 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Состояние Terraform: что такое tfstate и чем опасен локальный файл
Следующая глава →
Изоляция состояния: рабочие области против раскладки по папкам

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

Поделиться темой: ✈ Telegram VK
Похожие запросы: удаленный backend terraform на object storage yandex cloudуправление секретами в terraform yandex lockboxstatefulset в kubernetes для баз данных и хранилищаkubernetes pvc и storageclass как подключить хранилище

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

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

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