Коротко. Желаемое состояние в репозитории, изменения только через запрос на слияние, применяет агент или конвейер. На виртуальных машинах дрейф ловится ночным --check --diff, в кластере это делает агент. Секреты шифруются поштучно, у аварийных правок есть процедура, и о приостановленной синхронизации напоминает мониторинг.

Служба ИБ спрашивает простую вещь: кто в прошлый четверг поменял настройки PostgreSQL на продуктивном контуре RX, кто это согласовал и как вернуть как было. Если инфраструктура меняется запуском Ansible с ноутбука администратора или правкой файлов на сервере, честный ответ звучит как «сейчас поищем». GitOps делает ответ на этот вопрос ссылкой на запрос на слияние в репозитории.

Симптом

  • Конфигурация на двух серверах, которые должны быть одинаковыми, отличается, и никто не помнит почему.
  • Повторный запуск плейбука «откатывает» исправление, которое кто-то сделал руками на сервере.
  • У разных администраторов разные версии Ansible и ролей, и один и тот же плейбук ведёт себя по-разному.
  • Пароли и ключи лежат в репозитории открытым текстом или на ноутбуках в файлах переменных.
  • На вопрос «что сейчас раскатано на продуктивном» отвечают по памяти.

Почему так

Привычная схема выглядит так: репозиторий с ролями есть, но запускает их человек со своей машины, когда сочтёт нужным. Репозиторий при этом описывает, как должно быть, но не то, что стоит на серверах. Между ними три разрыва:

  1. Что запущено. Администратор мог запустить плейбук из ветки, с незакоммиченными правками или с другой версией роли.
  2. Кто разрешил. Запуск с ноутбука не проходит ревью. В лучшем случае остаётся строка в чате.
  3. Что изменилось после. Ручная правка на сервере нигде не записана и живёт до следующего запуска плейбука.

GitOps закрывает все три разрыва одним правилом: желаемое состояние описано в репозитории, менять его можно только через запрос на слияние с ревью, применяет изменения автоматика.

Как это устроено

«Ansible с ноутбука» GitOps
Кто применяет администратор со своей машины агент в кластере или сборочный конвейер
Что применяется что лежит у администратора локально ровно то, что в защищённой ветке
Ревью по желанию обязательно, ветка защищена
Доступ к серверам у каждого администратора у агента или конвейера
Ручная правка живёт до следующего запуска агент возвращает состояние из репозитория или сообщает о расхождении
Откат вспомнить, что было, и запустить отменить коммит

В Kubernetes

Агент внутри кластера (Flux или Argo CD) регулярно читает репозиторий и приводит кластер к описанному в нём состоянию: чарты, значения, манифесты. Агенту нужен только доступ на чтение репозитория, учётные данные администратора кластера в конвейере не хранятся.

Основные команды Flux для эксплуатации:

flux get kustomizations -A          # что применено, на каком коммите, есть ли ошибки
flux get helmreleases -A            # то же для Helm-релизов
flux diff kustomization rx --path ./clusters/prod/rx   # что изменится, до слияния
flux reconcile kustomization rx --with-source          # применить сейчас, не дожидаясь интервала
flux events -n flux-system          # что агент делал и на чём споткнулся

В выводе первой команды видно, какой коммит сейчас раскатан. Это и есть ответ на вопрос «что на продуктивном».

На виртуальных машинах

Большинство инсталляций RX работают на виртуальных машинах без Kubernetes, и там агента нет. Принцип тот же, исполнитель другой: Ansible запускает сборочный конвейер.

  1. Роли и коллекции закреплены по версиям в файле зависимостей с конкретными коммитами или тегами.
  2. Изменение приходит запросом на слияние. Конвейер прогоняет проверки (ansible-lint, синтаксис) и пробный запуск с --check --diff против целевого контура. Вывод пробного запуска прикладывается к запросу, и ревьюер видит, какие файлы на каких серверах поменяются.
  3. После слияния конвейер запускает плейбук. Учётные данные для серверов есть только у конвейера.
  4. Плейбук записывает на сервер, какой коммит применён, например в файл в /etc. Тогда ответ на вопрос «что здесь раскатано» лежит на самом сервере.
  5. По расписанию, например каждую ночь, конвейер запускает тот же плейбук с --check --diff. Любое изменение в выводе означает ручную правку или дрейф, и об этом приходит уведомление.
ansible-playbook -i inventories/prod site.yml --check --diff

Последний пункт заменяет агента: изменения применяются не автоматически, но расхождение видно на следующее утро, а не через полгода.

Секреты

Пароли в репозитории открытым текстом недопустимы, а GitOps без секретов в репозитории не работает. Обычно выбирают одно из двух.

  • Шифрование в репозитории. SOPS с ключами age или Ansible Vault. Файл лежит в репозитории зашифрованным, расшифровывает его агент или конвейер своим ключом. Удобно для небольшой команды.
  • Внешнее хранилище секретов. HashiCorp Vault, OpenBao или аналог. В репозитории только ссылка на секрет, значение подставляется при применении. Удобно, когда секретов много и их нужно менять по расписанию.

Шифровать стоит только секретные значения, а не файлы целиком. Иначе ревьюер не видит, что поменялось в остальной конфигурации, и ревью теряет смысл.

Аварийные правки

Во время ночной аварии ревью никто ждать не будет, и для этого заранее описывают процедуру.

  1. Правка делается руками или ускоренным слиянием без ревью. Если в кластере работает агент, сначала приостанавливается его синхронизация для нужного объекта (flux suspend), иначе он вернёт состояние из репозитория через несколько минут.
  2. В течение рабочего дня правка оформляется запросом на слияние, задним числом проходит ревью.
  3. Синхронизация возобновляется (flux resume). Приостановленный и забытый объект хуже, чем отсутствие GitOps: репозиторий и кластер расходятся, а все думают, что совпадают. Мониторинг должен сообщать о приостановленных объектах дольше суток.

Что это даёт при проверке ИБ

Вопрос проверяющего Где ответ
Кто внёс изменение автор коммита и запроса на слияние
Кто согласовал одобрение в запросе на слияние, ветка защищена от прямых коммитов
Когда применено журнал конвейера или статус агента
Что было до изменения история репозитория
Как откатить отмена коммита и повторное применение
У кого есть доступ к серверам у конвейера или агента; у людей только аварийный доступ, и его использование видно

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

Ревью роли идёт быстрее, когда в редакторе видно, где модуль, где его параметры, а где ключи задачи. Как включить такую подсветку в VS Code, разобрано в статье про подсветку Ansible по смыслу.

Чего не делать

  • Не оставлять администраторам постоянный доступ к серверам «на всякий случай». Тогда ручные правки продолжатся, и GitOps останется на бумаге.
  • Не держать окружения в разных ветках с ручным переносом коммитов. Ветки расходятся незаметно. Удобнее одна ветка и отдельный каталог на каждое окружение.
  • Не шифровать файлы конфигурации целиком. Ревью зашифрованного файла ничего не показывает.
  • Не забывать возобновить синхронизацию после аварии.
  • Не считать GitOps заменой резервного копирования. Репозиторий восстанавливает конфигурацию, но не базу и не документы.