# Backend в Terraform: где живёт state _Основы Terraform · TerraformLab Knowledge Base_ **TL;DR:** Backend, это место хранения state-файла. По умолчанию local, рядом с HCL. Remote backends (S3, GCS, Terraform Cloud, http) дают совместный доступ и блокировку. В этом курсе используется только local; remote, обзорно. ## Что такое backend Backend, это адрес, где terraform хранит state-файл и где берёт блокировку (lock) на время `apply`. По умолчанию backend, `local`: `terraform.tfstate` лежит в той же папке, что и HCL, никаких lock'ов кроме файлового флажка. Backend конфигурируется в блоке `terraform`: ```hcl terraform { backend "local" { path = "terraform.tfstate" } } ``` Локальный backend, дефолт, явно прописывать его не обязательно. ## Зачем уходить от local Local backend работает, пока: - проект один человек; - state не критичный; - нет CI/CD, который тоже хочет применить. Как только в проект приходит второй человек или CI, нужны: - **общее хранилище**, чтобы все видели один state, а не каждый свою локальную копию; - **блокировка**, чтобы два параллельных `apply` не покалечили state-файл (один пишет, второй пишет поверх, получается мусор); - **версионирование/бекап**, на случай, если кто-то случайно повредил state. Эти требования закрывают remote backends. ## Какие remote backends есть Курс не настраивает их (это интермедиат-материал), но знать про существование полезно. Все они объявляются в блоке `backend` с соответствующим именем. ### S3 (самый частый в AWS-проектах) ```hcl terraform { backend "s3" { bucket = "myorg-tfstate" key = "projects/web/terraform.tfstate" region = "us-east-1" encrypt = true dynamodb_table = "tf-locks" # для блокировок } } ``` Бакет хранит state, DynamoDB-таблица, lock. С Terraform 1.10+ блокировки можно держать прямо в S3 (через S3 conditional writes), тогда DynamoDB не нужна. ### Google Cloud Storage ```hcl terraform { backend "gcs" { bucket = "myorg-tfstate" prefix = "projects/web/" } } ``` Lock тоже в GCS (через generation-based если-conditions). ### HTTP (универсальный) ```hcl terraform { backend "http" { address = "https://example.com/state/web" lock_address = "https://example.com/state/web/lock" unlock_address = "https://example.com/state/web/lock" } } ``` Любой сервис, который реализует ожидаемый REST-контракт (GET/POST state, LOCK/UNLOCK). На этом построен Atlantis, GitLab managed state и большинство кастомных решений. ### Terraform Cloud / Enterprise Не совсем backend, отдельный блок `cloud {}` с workspaces и run history. Это управляющая платформа, в которой state, только одна из деталей. ## Как backend меняется Поменял `backend` в HCL, следующий `init` спросит: ``` Initial configuration of the requested backend "s3" Do you want to copy existing state to the new backend? Pre-existing state was found while migrating the previous "local" backend to the newly configured "s3" backend. No existing state was found in the newly configured "s3" backend. Do you want to copy this state to the new "s3" backend? Enter "yes" or "no": ``` - `yes`, terraform перенесёт state со старого места на новое. - `no`, оставит как есть; новый backend начнёт с пустого state. Чтобы пропустить вопрос, есть флаги: - `terraform init -migrate-state`, явно сказать «мигрируй». - `terraform init -reconfigure`, переинициализировать новый backend без переноса. Старый state остаётся как есть, новый, пустой. Без понимания, какой флаг ты дёргаешь, лучше не торопиться: можно затереть рабочий state. ## State locking, что это и зачем Lock, это «занят, идёт apply, никому не вмешиваться». Без него: - два инженера одновременно `apply`, два разных state. Кто последний сохранил, того и state. - apply упал на середине, без lock другой может стартовать поверх и добить. Local backend lock делает через файловый advisory lock (`.tfstate.lock.info`). Этого хватает для одного процесса, но не для команды. S3 backend. DynamoDB-таблица или S3 conditional writes. Terraform Cloud, встроенный механизм. HTTP, через `lock_address`. Если другой процесс держит lock и упал, есть `terraform force-unlock `. Этот рычаг страшный, использовать осторожно: можно сломать живущий apply, который просто медленный. ## Подводные камни - **`backend` нельзя параметризовать через переменные.** В блоке `backend "s3"` нельзя `bucket = var.bucket`. Это потому, что backend читается до того, как HCL загружен полностью. Решение, `-backend-config` флаги при init, либо отдельный `backend.tf` в разных директориях. - **State в local backend, открытый текст.** Любые `password`, `token`, `secret_key` лежат в нём как есть. На прод-окружениях это решается remote-backend'ом с шифрованием. - **Init без backend, ловушка.** Если убрал блок `backend` из HCL, terraform решит, что переехал на local, и спросит про миграцию. Не путай «нет backend в коде» (== local) с «есть пустой `backend "local" {}`». - **`force-unlock`, оружие массового поражения.** Если lock держит реально работающий apply, и ты снимешь его силой, второй apply запишет состояние поверх. State можно сломать на несколько часов. ## Команды ```bash terraform init ``` При изменениях в блоке backend: спросит, переносить ли state. Без флагов: интерактивно. ```bash terraform init -reconfigure ``` Переинициализировать backend без переноса state. Применять, когда новый backend начинается с нуля. ```bash terraform init -migrate-state ``` Явно сказать: перенеси state со старого backend на новый. Без флага terraform спросит интерактивно. ```bash terraform force-unlock ``` Снять lock руками. Только когда уверен, что процесс, который его держал, уже умер. ## См. также - [terraform init: первая команда в любом проекте](/terraform/kb/tf-init.md) - [terraform workspace: несколько state в одной директории](/terraform/kb/tf-workspace.md) - [Конфигурация Terraform CLI: terraformrc, env vars, TF_LOG](/terraform/kb/tf-cli-config.md) - [Version constraints в Terraform: required_version и провайдеры](/terraform/kb/tf-version-constraints.md)