Перейти к содержанию

Локальное состояние и раздельные окружения

Выделение модуля не разделило состояние: оба сайта «Контура», сеть и образ по-прежнему принадлежат одному корневому запуску. Теперь сделаем место хранения состояния явным и обсудим независимые окружения. Это урок об идентичности и владении, а не о создании дополнительных серверов. В архив не включены реальные state-файлы или резервные копии.

Backend Terraform определяет, где хранится состояние и как инструмент взаимодействует с ним. Провайдер отвечает за инфраструктурные объекты; backend отвечает за запись связи между адресами и объектами. Эти обязанности часто путают. Выбор Docker-провайдера не требует облачного хранения состояния, а выбор удалённого backend сам по себе не переносит контейнеры на удалённый хост.

Явный локальный backend

В versions.tf появился блок backend "local" с путём kontur.tfstate. Он относится к текущему корневому модулю. Относительный путь указывает файл рабочей среды, а не файл внутри каждого дочернего модуля. У modules/site отдельного backend нет: его ресурсы остаются в общем состоянии. Для лаборатории это делает границу управления простой и наблюдаемой.

Полный файл versions.tf для этого состояния:

terraform {
  backend "local" { path = "kontur.tfstate" }
  required_version = "= 1.12.2"
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "= 3.6.2"
    }
  }
}
provider "docker" {}

Команды для отдельной самостоятельной работы читателя; при написании не выполнялись:

terraform init -migrate-state

Путь backend нельзя вычислять обычной переменной var.prefix: инициализация backend происходит до привычного вычисления входов модуля. Поэтому желаемое название окружения не надо напрямую вставлять в этот блок через переменные. Частичная настройка backend и отдельные параметры инициализации существуют, но для нашего локального примера достаточно разных рабочих каталогов. Они дают физически разные state-файлы без усложнения первого знакомства.

Если раньше читатель применял конфигурацию с подразумеваемым локальным backend, состояние находилось под стандартным именем. Изменение пути требует осознанной миграции, а не простого копирования исходников в новую папку. Команда init -migrate-state предназначена для переноса по правилам Terraform. Перед такой операцией сохраняют настоящую резервную копию и проверяют, какой исходный backend выбран. В этой редакционной работе никаких state-файлов не было и перенос не выполнялся.

Два независимых окружения

Для dev и preview используем отдельные рабочие копии исходников. В первой префикс kontur-dev, порты 8088 и 8089. Во второй префикс kontur-preview, порты 8098 и 8099. Оба окружения могут содержать одинаковые Terraform-адреса module.site["home"], поскольку принадлежат разным состояниям. Настоящие Docker-имена и опубликованные порты при этом должны отличаться, если daemon общий.

Копируется исходный код, но не .terraform, не state и не сохранённый plan. Последствия каждого исключения разные. Служебный каталог связывает локальную установку и backend; state связывает конкретные реальные объекты; план фиксирует конкретное предложенное изменение. Их перенос в независимое окружение создаёт ложное продолжение старой истории. Отдельное окружение должно начинаться со своей идентичности и собственных параметров.

Если просто скопировать state из dev в preview, появятся два писателя, считающих одни контейнеры своими. Даже разные Docker-префиксы в новых входах не исправят ситуацию автоматически: один из планов может предложить заменить объекты, которыми пользуется другое окружение. При независимом создании копируется только намерение. При переносе существующего управления, напротив, история состояния сохраняется осознанно, а прежний писатель прекращает работу.

Что сохранять и кому разрешать доступ

State может содержать значения ресурсов и входов. Пометка sensitive скрывает некоторые значения в обычном выводе, но не превращает state в зашифрованный безопасный контейнер. Нельзя считать любой файл с таким флагом подходящим для публичного репозитория. Для нашей лаборатории чувствительные данные не нужны, однако правила исключения состояния из Git заложены заранее. Настоящее хранение требует ограничений доступа и резервирования.

Резервная копия полезна только вместе с понятным моментом создания и владельцем. Случайные файлы old-final-state.json не объясняют, какие ресурсы после этого менялись. Хорошая запись связывает копию с рабочим каталогом, выбранным endpoint и операцией, перед которой она сохранена. При восстановлении надо учитывать реальные объекты, изменившиеся после копии. Возврат старого JSON не возвращает сам мир к старому состоянию.

Механизм блокировки защищает одновременную запись в состояние в рамках конкретного backend, но не делает два разных файла одним состоянием. Если две рабочие копии используют независимые пути и управляют одним контейнером, файловая блокировка не обнаружит это как единый конфликт. Поэтому владение ресурса определяется организационной границей и конфигурацией, а не надеждой, что lock найдёт любую ошибку оператора. Для общей командной работы нужен специально выбранный backend и порядок доступа.

Снимок исходников и продолжение работы

Каталоги lesson-01—lesson-12 в архиве являются полными учебными состояниями кода. Они не являются готовыми живыми окружениями. Читатель выбирает один снимок для нового опыта или переносит изменения в уже управляемую рабочую копию. Вторая стратегия требует помнить историю реального состояния. Создать новый state рядом с прежними работающими контейнерами и сразу применить его — способ получить конфликт имен, а не аккуратное обновление урока.

Отдельная папка adoption в следующем уроке будет примером самостоятельной границы управления. Она получит собственное состояние и сможет принять только один заранее определённый контейнер. Основной «Контур» не начнёт автоматически владеть этим объектом. Такая демонстрация помогает увидеть разницу между повторным использованием одинакового провайдера и совместным использованием одного состояния.

К концу урока место хранения описано явно, а окружения разделены и по state, и по реальным именам с портами. Важный вопрос перед следующей командой теперь звучит конкретно: какой рабочий каталог управляет каким набором объектов? Если ответ неясен, сначала выясняют границы и сохраняют данные, а не запускают новую инициализацию в случайной папке.

Файл блокировки и файл состояния

Оба файла создаются инструментом, но решают разные задачи. Lockfile фиксирует выбранный пакет и сведения для проверки его получения; state фиксирует ресурсы конкретного окружения. Первый обычно полезен всем рабочим копиям одного проекта. Второй переносится только как осознанная операция с существующей инфраструктурой. Из общего происхождения файлов не следует одинаковая политика хранения.

Если коллега меняет провайдер, он должен показать обновлённый профиль и независимо полученный lockfile. Если коллега меняет состояние, нужно знать, какую операцию над реальными объектами это сопровождает. В архиве курса нет ни выдуманного lockfile, ни демонстрационного state с фальшивыми идентификаторами. Читатель получает переносимое намерение и обязан создать свою рабочую историю в выбранной локальной среде.

Оглавление серии, общая подборка и полный архив исходников. Для этого урока выберите lesson-08.

Первичные источники: Версии Terraform, Учебник Terraform и Docker, Схема Docker container 3.6.2, Схема Docker image 3.6.2, Адреса и moved, Локальный backend, Import, Refresh-only plan.

Редакционный статус: локальный черновик. Учебные команды, приложение и проверки не запускались; описанные результаты ожидаемые. Профиль версий закреплён, разрешённый lockfile не создавался.