ADG Оставить заявку
Блог DevOps 5 мин чтения

Git для инфраструктуры: ветвление по окружениям вместо папки «backup старый»

Перевели конфиги, playbook-и и скрипты в Git с ветками dev/stage/prod. Pull request на инфраструктуру - непривычно, но инциденты идут на убыль.

Контекст момента

Infrastructure as Code через Git становится практикой: конфиги, playbooks и скрипты хранятся в version control с полноценным workflow

В марте мы подключили Jenkins к Ansible-репозиторию - каждый push запускает синтаксис-чек и smoke-тест. С тех пор один вопрос не давал покоя: а что у нас вообще лежит в репозитории? Только Ansible-роли, или вся инфраструктурная конфигурация?

Оказалось - не вся. Nginx-конфиги на серверах клиентов правились вручную и «синхронизировались» копированием. Файрвольные правила жили в вики с пометкой «актуально на апрель». Crontab на продовом сервере отличался от стейджа тремя строками - никто не помнил почему. Классика.

Решили зайти системно: всё, что описывает инфраструктуру, должно лежать в Git. И раз уж в Git - с нормальным ветвлением под окружения.

Что именно переезжало

Список получился длиннее, чем казалось поначалу.

  • Ansible playbook-и и роли. Уже были в репозитории, но в одной ветке и без разделения по окружениям.
  • Конфиги сервисов. Nginx, PostgreSQL (postgresql.conf, pg_hba.conf), Zabbix-агент. Раньше хранились непосредственно на серверах и «бэкапировались» папкой _old.
  • Скрипты резервного копирования и мониторинга. Bash-скрипты, которые когда-то написали, положили на сервер и забыли где оригинал.
  • Firewall-правила. У нас iptables через конфиг-файл - вполне себе текст, место в Git.
  • Документация окружений. Не в смысле Word-файлов, а hosts-файлы инвентаря Ansible с описанием ролей хостов.

Что не переезжало: секреты (пароли, ключи) - они и раньше в репозиторий не попадали, Ansible Vault для этого.

Структура веток

Модель выбрали простую - три долгоживущие ветки, соответствующие окружениям.

main (prod)
  └── stage
        └── dev

Изменения идут снизу вверх: dev -> stage -> prod. Применяем через pull request - сначала между dev и stage, потом между stage и prod. Звучит тяжеловесно, на практике для большинства изменений это занимает 20 минут.

Логика такая: в dev свободно экспериментируем. В stage - только то, что проверили на тестовом стенде. В main - только то, что прошло через stage и не вызвало вопросов.

Jenkins смотрит на все три ветки. Push в dev - запускается синтаксис-чек. Push в stage - полный smoke-тест на тестовых VM. Push в main - smoke-тест плюс уведомление дежурному инженеру, что в prod идут изменения.

Pull request на изменение инфраструктуры

Вот это было самым непривычным. Инженеры привыкли что «поправить конфиг» - это зайти на сервер и поправить. Pull request для этого казался бюрократией.

Первые две недели было именно так: люди открывали PR, сами же его и мержили через минуту. Смысл нулевой.

Переломный момент - один PR, в котором кто-то менял worker_processes в nginx с auto на конкретное число. Коллега при ревью спросил: «а ты учёл что на стейдже 2 ядра, а на проде 8?» Нет, не учёл. Конфиг оказался не универсальным, а жёстко заточенным под конкретное железо.

После этого случая PR-ревью стало реальным. Не формальным «посмотрел, ок», а конкретным «на каком основании именно это значение?». Несколько раз это остановило изменения, которые были бы проблемой в prod.

Drift между окружениями: главный враг

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

Git с ветвлением решает это частично. Теперь разница между окружениями видна через git diff dev..stage - это не вся картина, но уже что-то. Если изменение применено в prod, но не зафиксировано в main - это видно при следующем деплое: Ansible покажет changed там, где не должно быть.

Стопроцентной защиты нет: никто не мешает зайти на сервер и поправить руками, минуя Git. Но это теперь явное нарушение договорённости, а не норма.

Что было неожиданно трудным

Первоначальный импорт. Взять конфиги с живых серверов и сделать из них «золотой» репозиторий оказалось нетривиально. Серверы расходились между собой - даже там где должны были быть одинаковыми. Пришлось разбираться: это намеренные различия или дрейф? На это ушло несколько дней.

Переменные вместо жёстких значений. Когда конфиг один на три окружения, всё специфичное для окружения должно быть в переменных. Это правильно, но требует рефакторинга конфигов, которые раньше писались под конкретный сервер. Nginx-конфиг с жёстко прописанными IP-адресами бэкендов - это не шаблон, это документ.

Объяснить зачем. «Зачем открывать PR чтобы поменять одну строку в cron?» - вопрос, который задавали не один раз. Ответ простой: чтобы через полгода был ответ на вопрос «кто и зачем это поменял». Git log как аудит-лог - это реально работает.

Текущее состояние

Репозиторий сопровождения работает три месяца по новой схеме. Ручных правок конфигов на серверах стало заметно меньше - не ноль, но меньше. Последний инцидент с «а почему на prod не так как на stage» случился в апреле - и именно он ускорил внедрение этой схемы.

Ansible при каждом деплое теперь показывает реальный drift: если что-то на сервере расходится с репозиторием, будет changed там где должен быть ok. Это неприятный сюрприз, но лучше узнать об этом при плановом деплое, чем при инциденте.

Главный вывод пока такой: pull request на инфраструктуру - это не про процесс ради процесса. Это про то, что второй человек смотрит на изменение до того как оно ушло в prod. Иногда второй человек видит то, что первый пропустил. Этого достаточно.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.