Terraform: план, провайдеры, версии
Поднял стенд и прогнал всё руками. Команда просмотра плана сказала «будет создано 3 ресурса» - и на диске не появилось ни одного файла. Команда применения создала три. Повторный просмотр плана ответил «No changes. Your infrastructure matches the configuration». Три команды - и видно всё устройство инструмента: он сравнивает описанное с существующим и показывает разницу до того, как что-то тронуть.
Стержень: инструмент не выполняет шаги, а приводит реальность к описанию; план - это предъявленная разница, которую читают ДО применения.
// Формулировки: «что делает plan?», «зачем закреплять версии провайдеров?», «чем декларативный подход отличается от скрипта?»
// Всё в этой теме проверено на OpenTofu - открытом форке Terraform с тем же языком и теми же командами.
Описание вместо шагов
Ресурс тут - единица описания: сервер, сеть, база, правило доступа, файл. Скрипт выполняет шаги над ними: создай, положи, запусти. Запусти его дважды - и получишь либо ошибку «уже существует», либо второй экземпляр. Описательный подход устроен иначе: вы записываете, что должно быть, а инструмент сам решает, что для этого сделать - создать, изменить или не трогать.
Отсюда и главное свойство, которое я проверил: повторное применение ничего не делает. Первый прогон создал три файла, второй ответил, что расхождений нет. Это позволяет запускать применение сколько угодно раз, в том числе после обрыва посередине.
// План - отдельная команда и отдельная привычка. Он показывает разницу и не трогает ничего: после просмотра плана на диске было ноль файлов. Читать план перед применением - базовая дисциплина: именно там видно строку «будет уничтожено 2 ресурса», которую никто не заказывал.
- описательный подход
- записываем нужное состояние, а не шаги до него
- план
- предъявленная разница между описанием и реальностью; ничего не меняет
Провайдеры и закрепление версий
Сам инструмент ничего не умеет: работу делают провайдеры - модули, которые знают, как разговаривать с конкретной системой. Провайдер облака, провайдер базы, провайдер локальных файлов. Они скачиваются при подготовке рабочего каталога и записываются в файл замков.
Версии закрепляют дважды. Версию самого инструмента - потому что формат его записной книжки о созданных ресурсах между крупными выпусками меняется, и коллега со свежей версией может обновить состояние так, что вы к нему больше не подойдёте. Версию провайдера - потому что новый провайдер иногда меняет умолчания, и внезапно план предлагает пересоздать половину инфраструктуры.
// Отсюда практика: файл замков коммитят в репозиторий, версии указывают точно, а обновление провайдеров делают отдельным изменением, с планом и глазами. Обновлять инструмент вместе с релизом продукта - плохая идея: если что-то пойдёт не так, вы будете разбирать сразу две проблемы.
- провайдер
- модуль, который умеет создавать и менять ресурсы конкретной системы
- файл замков
- запись точных версий провайдеров; коммитится в репозиторий
Что читать в плане
План говорит на своём языке, и его стоит понимать буквально. Строка с плюсом - создание. С минусом - уничтожение. С тильдой - изменение на месте. А вот сочетание минуса и плюса означает замену: ресурс будет уничтожен и создан заново, и если это база данных, то вместе с данными.
Итоговая строка вида «Plan: 1 to add, 0 to change, 2 to destroy» - главное, что смотрят перед применением. Я специально ловил такой случай: удаление одного элемента из списка дало план с двумя уничтожениями и одним созданием, хотя в описании изменилась одна строчка.
// Поэтому в конвейере - наборе шагов, через который проходит каждое изменение, - план публикуют прямо в изменение комментарием, а применение делают отдельным шагом с ручным подтверждением там, где ресурсы дорогие. И отдельно ставят проверку: если план содержит уничтожение, требуется вторая пара глаз.
- замена ресурса
- уничтожить и создать заново; для базы означает потерю данных
- итог плана
- строка «сколько добавить, изменить, уничтожить»; читается всегда
Как отвечать: «Что делает команда plan и зачем она нужна, если есть apply?»
План сравнивает описание с текущим состоянием и показывает разницу, ничего при этом не меняя. Я проверял буквально: после просмотра плана на диске не появилось ни одного файла, всё создалось только на применении. Нужен он как последняя проверка перед изменением прода, и читать его надо целиком. Плюс - создание, минус - уничтожение, тильда - изменение на месте, а минус вместе с плюсом означает замену: ресурс уничтожат и создадут заново, и если это база, то вместе с данными. Я специально ловил такой случай: удаление одного элемента из списка дало план с двумя уничтожениями при одной изменённой строке в описании. Поэтому в конвейере план публикуется в изменение как комментарий, применение идёт отдельным шагом, а на планы с уничтожением ставится обязательное второе одобрение.
Ответ объясняет назначение, расшифровывает язык плана и заканчивается процессом. Пример с неожиданным уничтожением показывает, зачем это всё нужно на самом деле.
На чём валятся
- −Применяют изменения не глядя в план и обнаруживают уничтожение постфактум.
- −Не различают изменение на месте и замену ресурса.
- −Не закрепляют версии провайдеров: обновление меняет умолчания и предлагает пересоздать половину.
- −Не коммитят файл замков, и у каждого своя версия провайдера.
- −Обновляют версию инструмента вместе с релизом продукта и разбирают две проблемы разом.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 8, остальные разбираются в тренажёре.
- В российском контуре terraform init падает на скачивании провайдера из публичного реестра. Как это решают?A)Складывают бинарь провайдера в репозиторий проектаB)Снимают пины версий и берут, что скачалосьC)Собирают провайдер из исходников при каждом initD)Поднимают зеркало реестра или локальный каталог провайдеров
показать ответ и разбор
+D)Поднимают зеркало реестра или локальный каталог провайдеров// разбор: Публичные реестры к 2026 ограничивают доступ из России, а провайдеров отечественных облаков там может не быть вовсе. Штатный механизм — зеркала: сетевое зеркало во внутреннем артефактории или каталог провайдеров на диске, прописанный в конфигурации CLI. Тогда init берёт бинари изнутри периметра, сборки перестают зависеть от внешней доступности, а версии остаются зафиксированными.
- Коллега создал сервер руками в консоли облака. Terraform его «не видит» и при apply хочет создать свой. Почему?A)Terraform игнорирует ресурсы, созданные в веб-консолиB)Провайдер не успел просканировать облако на новые ресурсыC)Terraform управляет лишь тем, что есть в его stateD)Ручной ресурс создан в другом регионе, поэтому не виден
показать ответ и разбор
+C)Terraform управляет лишь тем, что есть в его state// разбор: Terraform знает только ресурсы, записанные в его state, и сверяет с ними конфигурацию. Ресурс, созданный вручную (или другим инструментом), в state отсутствует — для terraform его как будто нет, поэтому по конфигу он захочет создать свой. Чтобы «усыновить» существующий, его импортируют в state (terraform import / import-блок), написав под него конфигурацию.
- Есть боевой ресурс, созданный руками. Нужно взять его под управление Terraform, не пересоздавая. Как?A)terraform import: привязать ресурс к stateB)Просто описать его в коде и сделать applyC)Удалить ресурс и дать terraform создать его зановоD)Скопировать чужой state-файл с этим ресурсом к себе
показать ответ и разбор
+A)terraform import: привязать ресурс к state// разбор: terraform import привязывает существующий облачный ресурс к адресу в state, не трогая сам ресурс. Порядок: описать ресурс в конфиге (или сгенерировать), затем terraform import <адрес> <id> (или import-блок с плановой генерацией конфига в новых версиях). После этого plan должен показывать «без изменений» — значит конфиг совпал с реальностью. Пересоздавать боевой ресурс не нужно.
- Тот же код время от времени даёт другой план, хотя правок не было. Как сделать планы предсказуемыми?A)Запускать terraform init -upgrade перед каждым планомB)Держать провайдеры последних версий, чтобы не отставатьC)Отключить проверку версий провайдеров совсемD)Закрепить версии провайдеров и коммитить lock-файл
показать ответ и разбор
+D)Закрепить версии провайдеров и коммитить lock-файл// разбор: «Плавающий» план обычно от того, что подтянулась новая версия провайдера/модуля с изменённым поведением или дефолтами. Лечится закреплением: required_providers с оператором версий и коммит .terraform.lock.hcl в репозиторий — тогда все и CI берут одни и те же версии, и план воспроизводим. Обновление версий делают осознанно (init -upgrade) и ревьюят получившийся diff.
- Что означает подход «инфраструктура как код»?A)Серверы и сети описывают файлами в репозитории и разворачивают по нимB)Инфраструктуру настраивают вручную, но записывают шаги в документациюC)Приложение и инфраструктуру пишут на одном языке программированияD)Код приложения хранят прямо на серверах, минуя сборку
показать ответ и разбор
+A)Серверы и сети описывают файлами в репозитории и разворачивают по ним// разбор: Описание живёт в git наравне с кодом: его ревьюят, версионируют и катят инструментом. Отсюда и главная выгода — окружение воспроизводится из файла, а не из памяти инженера, который его когда-то настраивал.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.