Внешние изменения и обновление состояния
После импорта становится особенно заметно, что инфраструктура живёт вне файлов Terraform. Контейнер может быть остановлен, удалён или изменён вручную. Такие внешние изменения называют drift — расхождением между прежним наблюдением и текущей реальностью. Теперь разберём, какие решения доступны владельцу «Контура» и почему обновление state не равно обновлению исходников.
Для этого урока исходная модель прежняя: два сайта в модулях, общая сеть, образ и локальное состояние. Конфигурация задаёт намерение. Провайдер читает настоящие объекты. Terraform использует эти сведения при расчёте плана. Если ничего внешнего не произошло, сравнение может не предложить действий. Если объект исчез, прежняя запись состояния не делает его настоящим существующим сервером.
Наблюдение и намерение
Нормальный план обычно включает обновление сведений о ресурсах, затем сравнение с декларативной конфигурацией. Он может предложить восстановить то, что изменено снаружи. Допустим, контейнер основного сайта удалён вручную. При корректном чтении Docker инструмент обнаружит отсутствие объекта и предложит создать его снова. Это не «дубликат из-за кеша», а следствие сохранённого намерения: сайт всё ещё объявлен.
Режим -refresh-only предназначен для другой цели: привести запись состояния и выходные значения к наблюдаемой реальности без изменения инфраструктурного намерения. В таком режиме можно сначала получить отдельный план и посмотреть, какие сведения будут приняты. Если читатель потом применяет refresh-only план, это не означает, что Terraform исправил HTML или создал пропавший контейнер. Обновлён учет, а не декларативная цель.
Команды для отдельной самостоятельной работы читателя; при написании не выполнялись:
terraform plan -out=normal.tfplan
terraform plan -refresh-only -out=observed.tfplan
terraform show observed.tfplan
Эту разницу удобно выразить словами. Обычный план спрашивает, какие действия нужны для согласования реальности с конфигурацией. Refresh-only план показывает, как согласовать запись наблюдений с реальностью. Ни один режим не редактирует автоматически строки .tf, объясняя, чего теперь хочет владелец системы. Если внешнее изменение было правильным, намерение надо изменить в исходниках отдельно. Иначе следующий обычный план снова увидит расхождение.
Принять или восстановить
Представим, что внешний оператор изменил политику перезапуска контейнера. Сначала выясните причину: это аварийное исправление, ошибочный ручной шаг или новая согласованная настройка? Если согласованная, её отражают в конфигурации и документируют. Если ошибочная, восстановление прежнего намерения может быть правильным решением. Выбор не должен зависеть от того, какой способ проще скрывает строку в diff. Drift является фактом, а действие зависит от смысла этого факта.
Другой вариант — исчезновение сети. Сеть и контейнеры связаны зависимостями, поэтому восстановление может затронуть несколько объектов. Запись конкретного идентификатора в state не удерживает сеть от удаления через Docker. План надо читать на уровне всего затронутого графа, а не одной строки. Особенно внимательно рассматривают замену контейнеров, которые имели записываемые данные. Для нашей статической лаборатории содержимое строится из исходников; для базы данных это уже другое последствие.
Можно отдельно сравнить обычный и refresh-only планы. Их результаты описывают разные намерения операции, поэтому различие ожидаемо. Если оба выглядят неожиданно, сначала уточняют источник состояния и выбранный endpoint. Похожее имя Docker-контейнера на другом daemon не является тем же объектом. Потеря доступа или изменение подключения способны выглядеть как отсутствие ресурсов. Слепое применение восстановительного плана в такой ситуации создаёт новые объекты в неверном месте.
Почему нельзя просто игнорировать всё
Аргумент ignore_changes позволяет задать особое распределение ответственности за конкретные свойства ресурса. Он уместен, когда другая система действительно управляет отдельным полем и это описано как контракт. Но широкое игнорирование ради чистого плана убирает сигнал о важных расхождениях. Если игнорировать порт, можно сохранить прежний красивый output, который больше не соответствует настоящему доступу. Отсутствие diff тогда станет следствием ослеплённой модели.
В «Контуре» мы не вводим blanket-игнорирование. Внешнее изменение образа или сети должно стать заметным и получить решение. Даже точечное исключение требует вопроса: кто теперь владеет этим свойством, как он меняет его и как потребитель узнаёт актуальное значение? Если ответ отсутствует, правило не оформляет разделение труда, а только скрывает неопределённость. Для маленькой лаборатории проще сохранить полное декларативное владение.
Ручная правка JSON состояния ещё опаснее как способ убрать неудобный факт. Состояние содержит связи и служебные сведения, формат которых не является произвольной записной книжкой. Ошибка способна оставить существующий объект без управления или направить изменения на чужой идентификатор. Для операций со state предусмотрены инструменты Terraform, однако их тоже используют только после анализа и резервирования. В этом уроке никаких правок или команд состояния не выполнялось.
Повторяемое наблюдение
При разборе drift полезно фиксировать время и причину внешнего изменения. Одна фотография текущего состояния не показывает последовательность. Если контейнер регулярно исчезает, возможно, другой процесс чистит лабораторные ресурсы по именам. Простое восстановление каждый раз не устраняет конкурирующее управление. Нужно найти второй источник действий и согласовать границы. Terraform способен восстановить намерение, но не может заменить ясное владение системой.
Не обещайте автоматическое обнаружение всех внешних изменений. Провайдер читает доступные ему свойства и работает в своей схеме. Внутренние данные приложения, содержимое ответа HTTP или состояние стороннего сервиса могут не входить в ресурсную модель. Изменение текста в живом контейнере не всегда будет отражено так, как ожидает автор статьи. Поэтому будущая диагностика сочетает конфигурационный план с отдельно выбранными наблюдениями приложения.
Теперь читатель различает восстановление намерения, обновление записи наблюдений и изменение самого намерения в исходниках. Это три связанные, но самостоятельные действия. Дальнейший урок добавит конкретные эксплуатационные ограничения контейнеров и правила обновления образа. С ними drift станет обсуждаться через явные обещания системы: сколько памяти разрешено, когда процесс должен перезапускаться и какие байты приложения допустимы.
Сохранение внешнего исправления
Если оператор верно изменил параметр вручную во время аварии, полезно сохранить причину вместе с будущим изменением исходников. Тогда план после обновления конфигурации может стать спокойным по понятной причине. Одного refresh-only недостаточно: он обновляет наблюдения, но прежняя декларация продолжает требовать старую настройку. Именно это различие объясняет повторное появление diff после казалось бы принятого внешнего исправления.
Когда причина внешнего изменения неизвестна, не называйте его автоматически ошибкой. Сначала найдите владельца действия и назначение объекта. Возможно, это плановая операция другой команды, которой нужно передать ресурс полностью. В таком случае задача состоит в изменении границы управления, а не в бесконечном восстановлении старых параметров. Drift полезен как сигнал о несогласованном намерении между людьми и системами.
Оглавление серии, общая подборка и полный архив исходников. Для этого урока выберите lesson-10.
Первичные источники: Версии Terraform, Учебник Terraform и Docker, Схема Docker container 3.6.2, Схема Docker image 3.6.2, Адреса и moved, Локальный backend, Import, Refresh-only plan.
Редакционный статус: локальный черновик. Учебные команды, приложение и проверки не запускались; описанные результаты ожидаемые. Профиль версий закреплён, разрешённый lockfile не создавался.