Когда облачная консоль и IaC-код расходятся: автоматическое обнаружение дрейфа инфраструктуры

Вы уже несколько месяцев управляете инфраструктурой через Terraform. Всё описано в коде, проходит ревью через pull request и разворачивается через пайплайны. В один прекрасный день коллеге нужно быстро поправить конфигурацию. Вместо того чтобы гонять пайплайн, он заходит в облачную консоль, вручную меняет правило security group и идет дальше. Изменение работает. Никто больше о нем не вспоминает.

Через несколько недель вы запускаете новый деплой. Terraform планирует откатить то самое изменение в security group, потому что в коде всё еще старая версия. Если применить — целенаправленное изменение будет перезаписано. Если пропустить — код перестанет соответствовать реальности. Поздравляю, у вас дрейф инфраструктуры.

Дрейф — это расхождение между тем, что должно быть по коду инфраструктуры, и тем, что на самом деле существует в вашем облачном окружении. Это не теоретическая проблема. Она возникает в каждой команде, которая управляет инфраструктурой в масштабе. Вопрос не в том, случится ли дрейф, а в том, как быстро вы его обнаружите.

Почему ручное обнаружение дрейфа не работает

Технически вы можете обнаружить дрейф, открыв облачную консоль, проверив каждый ресурс и сравнив его с IaC-кодом. Это работает, когда у вас пять ресурсов. Перестает работать, когда их пятьдесят, пятьсот или пять тысяч.

У ручного обнаружения три проблемы. Первая — медлительность. Одно сравнение может занять минуты. Умножьте на сотни ресурсов — и вы тратите часы на то, что должно быть автоматизировано. Вторая — ненадежность. Человеческий глаз пропускает мелкие различия, особенно когда у ресурсов десятки полей конфигурации. Третья — непостоянство. Разные члены команды могут проверять разные вещи или вообще забыть проверить.

Главная проблема — время. Дрейф может возникнуть в любой момент. Ручная проверка раз в неделю означает, что вы можете жить с незамеченным изменением днями. Если это изменение создает уязвимость в безопасности или ломает зависимость, вы узнаете об этом жестким способом — во время следующего инцидента.

Как работает автоматическое обнаружение дрейфа

Автоматическое обнаружение дрейфа следует простому принципу: запускать сравнение между фактическим состоянием инфраструктуры и IaC-определениями по расписанию. Этот процесс называется сканированием дрейфа (drift scan). Сканирование дрейфа ничего не меняет. Оно только находит различия и сообщает о них.

Самый распространенный подход использует те же инструменты, которыми вы уже управляете инфраструктурой. Например, у Terraform есть команда plan, которая сравнивает ваш state-файл с реальными облачными ресурсами. State-файл — это запись, которую Terraform хранит локально или удаленно, содержащая информацию о созданных ресурсах и их текущей конфигурации. Когда вы запускаете terraform plan без изменения кода, инструмент проверяет, соответствует ли state-файл тому, что на самом деле существует в облаке. Если кто-то изменил ресурс напрямую в консоли, план покажет это как изменение, которое Terraform хочет внести.

Это основа обнаружения дрейфа: периодически запускать plan и проверять наличие неожиданных изменений.

Вот минимальный bash-скрипт, который запускает сканирование дрейфа и оповещает команду при его обнаружении:

#!/bin/bash
# drift-scan.sh - Запуск сканирования дрейфа Terraform и уведомление об изменениях

set -euo pipefail

cd /path/to/terraform/project

terraform init -input=false

# Обновляем state из реальных ресурсов, затем планируем с детальным кодом возврата
terraform plan -refresh-only -detailed-exitcode -out=drift.tfplan
PLAN_EXIT_CODE=$?

if [ $PLAN_EXIT_CODE -eq 2 ]; then
    # Код 2 означает, что есть изменения (обнаружен дрейф)
    echo "Drift detected in $(date)"
    
    # Генерируем читаемое описание
    terraform show drift.tfplan > drift-summary.txt
    
    # Отправляем уведомление в Slack (укажите свой webhook URL)
    curl -X POST -H 'Content-type: application/json' \
        --data "{\"text\":\"🚨 Drift detected in production!\n$(cat drift-summary.txt)\"}" \
        https://hooks.slack.com/services/YOUR/WEBHOOK/URL
elif [ $PLAN_EXIT_CODE -eq 1 ]; then
    echo "Error during plan execution"
    exit 1
else
    echo "No drift detected"
fi

Этот скрипт можно запускать через cron или по расписанию в CI/CD-пайплайне каждые несколько часов.

Следующая диаграмма показывает полный цикл обнаружения дрейфа:

flowchart TD A[IaC Code<br/>Desired State] -->|terraform apply| B[Cloud Resources<br/>Actual State] B -->|Manual change in console| C[Drifted Resource] C -->|Scheduled plan or refresh| D{Drift Detected?} D -->|Yes| E[Notification<br/>Slack or Email] D -->|No| B E --> F{Response Decision} F -->|Reconcile| G[terraform apply reverts change] F -->|Accept| H[Update IaC code to match actual state] G --> B H --> A

Больше, чем просто plan: получение точных результатов

Здесь есть тонкий, но важный момент. Обычный terraform plan сравнивает state-файл с реальными ресурсами. Но что, если сам state-файл устарел? Если кто-то сделал изменение в консоли, state-файл может этого не отражать. План не обнаружит дрейф, потому что сравнивает две сущности, которые обе рассинхронизированы.

Некоторые инструменты справляются с этим лучше. Terraform Cloud и OpenTofu предлагают обнаружение дрейфа, которое идет глубже. Вместо простого сравнения state с ресурсами они сначала обновляют state, загружая актуальную конфигурацию из облака, а затем сравнивают обновленный state с вашим кодом. Это дает сравнение от кода к реальным ресурсам, а не от state к ресурсам. Результат точнее, потому что он ловит изменения, произошедшие вообще вне state-файла.

Это различие важно. Если вы полагаетесь только на базовый вывод plan, вы можете пропустить дрейф, который появился до последнего обновления state-файла. Правильное сканирование дрейфа всегда должно начинаться с обновления state из реального окружения.

Расписание и уведомления

Сканирование дрейфа должно запускаться по расписанию. Как часто — зависит от того, как часто изменения происходят вне вашего пайплайна. Команды, управляющие критичной продакшн-инфраструктурой, часто запускают сканирование каждые несколько часов. Команды с меньшей активностью — раз в день. Ключевое — постоянство: сканирование должно запускаться автоматически, без участия человека.

Вы можете настроить расписание через cron на вашем CI/CD-сервере, триггер пайплайна по расписанию или встроенный планировщик вашего IaC-инструмента. Важно, чтобы расписание было надежным и не зависело от того, что кто-то вспомнит его запустить.

Когда дрейф обнаружен, следующий шаг — уведомление. Автоматические оповещения должны приходить в канал связи вашей команды: Slack, email или тикет-систему. Хорошее уведомление содержит три вещи: какой ресурс дрейфует, что именно изменилось и когда это было обнаружено. Если ваш облачный провайдер логирует, кто сделал изменение, добавьте и эту информацию. Это значительно ускоряет расследование.

Что делать после обнаружения

Найти дрейф — только первый шаг. Как только вы знаете о нем, у вас есть два варианта.

Первый вариант — реконсилиация: вернуть ресурс в состояние, определенное в коде. Это стандартный ответ для большинства команд. Вы запускаете terraform apply или его аналог, и инструмент откатывает ручное изменение. Это хорошо работает, когда дрейф был случайным или несанкционированным.

Второй вариант — принятие: ручное изменение было намеренным и должно остаться. В этом случае вы обновляете IaC-код, чтобы он соответствовал фактическому состоянию. Это правильный выбор, когда изменение было легитимным исправлением, сделанным быстро в консоли, и коду нужно догнать реальность. Опасность в том, что если делать так слишком часто, ваш код перестает быть источником истины и становится просто исторической записью.

Выбор между реконсилиацией и принятием зависит от политики вашей команды. Некоторые команды всегда делают реконсилиацию, если нет документально оформленного исключения. Другие разрешают принятие, но требуют последующий pull request в течение определенного времени. Важно, чтобы решение было осознанным, а не случайным.

Практический чек-лист для автоматического обнаружения дрейфа

Если вы настраиваете обнаружение дрейфа впервые, вот краткий чек-лист для начала:

  • Выберите инструмент обнаружения: используйте встроенное обнаружение дрейфа вашего IaC-инструмента или выделенный инструмент вроде Terraform Cloud, OpenTofu или собственный скрипт, который периодически запускает plan.
  • Настройте расписание сканирования: начните с одного раза в день для некритичных окружений и каждые несколько часов для продакшна. Корректируйте в зависимости от того, как часто происходят ручные изменения.
  • Настройте уведомления: отправляйте оповещения в командный чат или тикет-систему. Включайте имя ресурса, конкретное изменение и временную метку.
  • Определите политику реагирования: решите, будете ли вы делать реконсилиацию или принимать дрейф. Документируйте процесс, чтобы вся команда следовала одному подходу.
  • Протестируйте процесс: внесите намеренное ручное изменение в стейджинг-окружении, дайте сканированию его обнаружить и проверьте, что уведомление и реакция работают как ожидается.

Конкретный вывод

Дрейф инфраструктуры — это не признак плохой команды. Это признак того, что ваша команда движется быстро и иногда срезает углы. Проблема не в самом сокращении пути. Проблема в том, что вы не знаете о нем, пока он не вызовет инцидент.

Автоматическое обнаружение дрейфа превращает невидимую проблему в видимую. Оно не мешает людям вносить изменения в консоли. Оно гарантирует, что когда они это делают, остальная команда узнает об этом быстро и сможет решить, что делать дальше. Эта видимость — то, что поддерживает надежность вашей инфраструктуры, даже когда код и облако расходятся.