awesome-everything EN
↑ Обратно к восхождению

Деплой и инфра

Infrastructure as Code: план, файл состояния и drift

Суть IaC описывает желаемую инфраструктуру в коде под версионным контролем; инструмент сравнивает её с записанным файлом состояния и считает план. Этот файл состояния — одновременно источник истины и то, что заканчивает инцидент, когда два apply гонятся или кто-то кликает в консоли.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на junior-высоте — поверхность
◷ 16 min

Два инженера выкатывают хотфикс в пятницу. Оба запускают terraform apply со своих ноутбуков на один и тот же проект с разницей в девяносто секунд. Удалённого лока нет — состояние лежит в S3-бакете, но таблицу лока никто не подключил. Второй apply читает файл состояния, который первый ещё не дописал, считает план против полузаписанной реальности и пересоздаёт балансировщик, который уже существовал. Прод теряет соединения четыре минуты, а terraform.tfstate в бакете теперь расходится с тем, что реально есть в AWS. Авария была не из-за плохого конфига. Она была из-за двух писателей без лока.

Декларативно: ты описываешь пункт назначения, а не маршрут

Клики в облачной консоли — это императив: ты выполняешь шаги, а результат живёт только в провайдере и в твоей памяти. Infrastructure as Code переворачивает это: ты пишешь желаемое конечное состояние в файлах под версионным контролем (HCL для Terraform/OpenTofu, настоящий TypeScript/Go/Python для Pulumi), а инструмент сам выясняет шаги, чтобы туда прийти. Ты не говоришь «создай VPC, потом subnet, потом NAT gateway». Ты заявляешь, что эти ресурсы существуют с такими свойствами, а инструмент считает граф зависимостей и порядок.

В этом весь выигрыш. Поскольку желаемое состояние — это текст в git, твои окружения становятся воспроизводимыми (поднять идентичный staging из того же модуля), ревьюируемыми (изменения инфры идут через pull request как любой код) и аудируемыми (диф и история blame говорят, кто что и когда менял). Консоль не даёт ничего из этого — изменение там не оставляет ни ревью, ни дифа, ни истории, кроме тонкого audit log.

Цена декларативности — идемпотентность: запустить apply дважды должно сходиться к тому же результату, а не плодить дубликаты. Инструмент гарантирует это только потому, что помнит, что уже построил, — а для этого и нужен файл состояния.

Цикл plan/apply — это движок дифа

Ядро — две команды. terraform plan читает три вещи — твой конфиг (желаемое состояние), записанный файл состояния (что инструмент построил в прошлый раз) и реальность (он рефрешит её, опрашивая провайдер) — и печатает диф: что он создаст, изменит или удалит. Пока ничего не происходит. terraform apply исполняет этот диф и записывает новую реальность обратно в файл состояния.

Перечитай: plan и apply оба сначала делают refresh, согласовывая файл состояния с живым провайдером, прежде чем что-то считать. Именно на этом шаге refresh всплывает drift, и поэтому файл состояния — не опциональная бухгалтерия: это карта от символьных имён твоего конфига (aws_lb.web) к реальным id провайдера (arn:aws:elasticloadbalancing:...). Потеряй эту карту — и инструмент больше не знает, на какой реальный ресурс ссылается твой код.

Вход planЧто он представляетЕсли он неверен…
Конфиг (файлы .tf)Желаемое состояние — куда ты хочешь прийтиPlan предлагает не то изменение; ловится на ревью PR
Файл состояния (terraform.tfstate)Последняя известная реальность + карта id↔ресурсИнструмент теряет реальные ресурсы → пересоздаёт или сиротит их
Refresh (запрос к живому провайдеру)Фактическая реальность прямо сейчасDrift появляется в плане как неожиданные изменения

Файл состояния — это сердце и опасность

Всё хорошее в IaC проходит через файл состояния, и всё опасное — тоже. Три свойства заставляют сеньора обращаться с ним бережно.

Во-первых, он может хранить секреты в открытом виде. Terraform, OpenTofu и Pulumi сериализуют атрибуты ресурсов в состояние — поэтому если пароль базы, API-ключ или сгенерированный сертификат стал output или атрибутом, он лежит в файле незашифрованным по умолчанию. У любого с доступом на чтение этого бакета есть твои секреты. Митигация — никогда не гонять секреты через состояние как outputs: писать их сразу в secrets manager во время apply, а приложения пусть забирают их в рантайме. OpenTofu добавил встроенное шифрование состояния, чтобы это закалить; Pulumi шифрует секретные значения на каждый stack как first-class фичу.

Во-вторых, он должен жить в удалённом backend, а не на ноутбуке. Локальное состояние означает, что реальностью владеет один человек и команда не может работать совместно. Стандарт — удалённый backend (S3, GCS, облачный backend Terraform/OpenTofu) — с включённым versioning, чтобы повреждённое или обрезанное состояние можно было откатить к последней хорошей версии.

В-третьих, он должен быть залочен. Это то, что заканчивает инциденты.

Локинг: почему параллельные apply портят состояние

Запись в файл состояния не атомарна на уровне команды. Если два apply бегут одновременно, оба читают старое состояние, оба считают планы против него и оба пишут обратно — второй затирает первого, и теперь файл не описывает ни реальность. Фикс — лок: перед любой операцией записи backend берёт эксклюзивный лок (для S3 нативный lockfile через use_lockfile = true теперь дефолтный путь, а DynamoDB остаётся валидным легаси-механизмом), и любой второй apply ждёт или быстро падает с Error acquiring the state lock вместо гонки.

Сеньор знает острые края. terraform apply -lock=false отключает это и так ты воспроизводишь аварию из Hook нарочно. terraform force-unlock существует для устаревших локов от упавшего запуска — но запустить его, пока другой apply активно пишет, оставляет состояние полуобновлённым и повреждённым. Дисциплина в CI: concurrency group, чтобы джобы не пересекались, -lock-timeout (скажем 10m), чтобы легитимные запущенные apply ждали, а не падали, и plan после любого forced unlock, чтобы проверить консистентность состояния перед следующим apply.

Почему это работает

«Почему просто не дифить против живого облака каждый раз и не выбросить файл состояния?» Потому что refresh говорит только текущие атрибуты ресурсов, которые инструмент уже знает, — он никак не может узнать, что балансировщик с именем web в твоём аккаунте — это тот, которым управляет твой блок aws_lb.web, а не созданный руками или другой командой. Файл состояния — это карта идентичности. Без неё plan не отличит «измени этот ресурс» от «создай новый», и именно так отсутствующий файл состояния ведёт к дублирующейся инфраструктуре.

Drift: когда реальность уходит в сторону

Drift — это когда реальный мир расходится с файлом состояния, почти всегда потому, что кто-то поменял инфру out-of-band (клик в консоли, аварийный патч aws cli, другой инструмент). Следующий plan рефрешит, видит разницу и сообщает о ней. Теперь перед тобой решение сеньора: ручное изменение верное (тогда обнови конфиг, чтобы следующий apply его не откатил) или нежеланное (тогда дай apply восстановить заявленное состояние)?

Ловушка — тихий откат. Кто-то руками правит правило security group в инциденте в 2 ночи; никто не обновляет код; рутинный apply во вторник тихо удаляет правило, потому что его нет в желаемом состоянии. IaC всегда загоняет drift обратно к декларации — это и фича, и foot-gun. Детектируй его осознанно через terraform plan -refresh-only (безопаснее голого refresh, который перезаписывает состояние, не показывая тебе), в идеале по расписанию, чтобы drift ревьюили до того, как apply тихо его разрешит.

Более глубокое лекарство — immutable infrastructure: перестань править серверы руками и вместо этого заменяй их — запеки новый образ, выкати, уничтожь старый. Когда ничего не правится на месте, поверхности для drift гораздо меньше, а откат — это «выкати предыдущий образ», а не «вспомни каждую ручную правку».

Выбери лучший вариант

Коллега сделал ручной фикс в консоли во время инцидента. Следующий terraform plan теперь показывает изменение, откатывающее его. Что делает сеньор?

Викторина

Два инженера запускают terraform apply на один проект одновременно, без настроенного лока состояния. В чём ключевой риск?

Викторина

Почему файл состояния обычно должен жить в удалённом backend, а не на ноутбуке инженера?

Расставь шаги по порядку

Расставь, что происходит во время одного безопасного terraform apply:

  1. 1 Взять удалённый лок состояния, чтобы никакой другой apply не мог писать параллельно
  2. 2 Refresh: опросить живой провайдер, чтобы обновить файл состояния текущей реальностью
  3. 3 Посчитать диф между желаемым конфигом и обновлённым состоянием — план
  4. 4 Исполнить план на провайдере, создавая/меняя/уничтожая ресурсы
  5. 5 Записать новую реальность обратно в файл состояния и отпустить лок
Вспомните перед уходом
  1. 01
    Объясни коллеге, почему файл состояния — одновременно источник истины и крупнейшая опасность в Terraform-сетапе.
  2. 02
    Что такое drift, как детектировать его безопасно и почему рутинный apply может сделать его опасным?
Итог

Infrastructure as Code заменяет клики в консоли версионными декларациями желаемого состояния, делая окружения воспроизводимыми, ревьюируемыми и аудируемыми. Движок — это диф: plan рефрешит против живого провайдера, сравнивает твой конфиг с записанным файлом состояния и показывает, что он создаст, изменит или уничтожит; apply исполняет этот диф и пишет новую реальность обратно. Файл состояния — карта идентичности от твоего конфига к реальным id ресурсов, что делает его источником истины и в равной мере опасностью: он может хранить секреты в открытом виде, он может повредиться, а параллельные записи гонятся. Поэтому он живёт в версионном залоченном удалённом backend, никогда не несёт секреты, которые можно вместо этого отправить в secrets manager, и с ним обращаются как с продовой базой. Drift — реальность, уходящая в сторону после ручного изменения, — всплывает в следующем plan; детектируй его осознанно через refresh-only и решай намерение до apply, потому что IaC всегда согласует обратно к декларации и может тихо откатить аварийный фикс. Склоняйся к immutable infrastructure, чтобы дрейфить было нечему изначально.

Продолжить восхождение ↑Infrastructure as Code: тест с выбором
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.