Автоматизация

Вся инфраструктура как код: 9 Ansible-ролей, которые пересоздают мой мониторинг за минуты

02.07.2026  ·  Горун Евгений Вадимович

В прошлой статье я рассказывал, как развернул две системы мониторинга одного парка — Zabbix с самописным дашбордом и Prometheus + Grafana. Обе работают, у обеих публичные демо. Но оставался неприятный вопрос: а что, если контейнер с мониторингом умрёт? Восстанавливать вручную — это часы работы и вспоминания «а как я тогда настраивал». Настоящий инженерный ответ один: инфраструктура должна описываться кодом.

За два дня я перевёл весь парк на Ansible. Не «написал пару плейбуков», а довёл до состояния, где любой сервис пересоздаётся из репозитория за минуты, изменения проверяются в CI, а на боевом ничего не ломается. Расскажу, как это устроено и на какие грабли наступил.

Что за парк

Гетерогенная среда на Proxmox: несколько LXC-контейнеров (мониторинг, CI-узел) и виртуальные машины, плюс облачный сервер. Управляет всем отдельный control-node — контейнер, на котором стоит Ansible (поставил через pipx, чтобы иметь свежую версию в изолированном окружении, а не то, что лежит в репозиториях дистрибутива).

Хосты описаны в inventory группами по назначению: стек Prometheus, гипервизоры, виртуалки, узлы с node_exporter, CI-узел. Ansible ходит к ним по SSH-ключу — где напрямую по локальной сети, где через VPN-туннель.

Девять ролей

Каждая роль отвечает за один компонент и воспроизводит его состояние точно:

Главное свойство — идемпотентность

Роль, которая при повторном запуске что-то меняет каждый раз, — плохая роль. Правильная роль на втором прогоне сообщает «изменений: 0». Это значит, что описанное в коде состояние уже достигнуто, и Ansible ничего не трогает зря — не перезапускает сервисы, не дёргает конфиги. Все девять ролей идемпотентны: второй прогон — ноль изменений на всех хостах. Проверял каждую.

Живое доказательство: снёс сервис — и восстановил за 8 секунд

Красивые слова про «инфраструктуру как код» ничего не стоят, пока не проверишь их на практике. Я провёл контролируемый эксперимент на наименее критичном хосте: удалил node_exporter полностью — бинарник, systemd-юнит, пользователя. В Prometheus сразу погас соответствующий таргет.

Потом запустил роль. Через ~8 секунд сервис вернулся: пользователь создан, бинарник скачан и установлен, юнит на месте, сервис запущен, таргет в Prometheus снова «up». Это и есть страховка — не теоретическая, а проверенная вживую.

Архитектура: как не устроить конфликт ролей

Тонкий момент, из-за которого IaC часто превращается в кашу: две роли лезут в один файл. У меня blackbox-мониторинг сайтов должен был добавить свою scrape-задачу в конфиг Prometheus — а этим конфигом уже управляет роль prometheus, байт в байт.

Решение — не костыль, а правильная развязка. Prometheus умеет подключать scrape-конфигурации из отдельных файлов (директива scrape_config_files). Я добавил в роль prometheus генерическую директиву: подхватывай всё из отдельного каталога. Теперь любой будущий экспортёр кладёт туда свой фрагмент, не трогая основной конфиг. Роль blackbox владеет своим фрагментом целиком, роль prometheus — своим конфигом. Один файл — один ответственный. Никаких конфликтов.

Тот же принцип с дашбордами Grafana: у blackbox свой provider дашбордов, отдельный от основного. Роли не наступают друг другу на ноги.

Работа на боевом без простоев

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

  1. Предпросмотр (—check —diff) — Ansible показывает, что именно изменится, ничего не применяя.
  2. Смотрю diff — особенно критичные места, например маскировку внутренних адресов в конфиге, чтобы она не пострадала.
  3. Применяю.
  4. Проверяю: сервисы живы, демо отдаёт 200, маскировка цела.
  5. Второй прогон — «изменений: 0».
  6. Наготове откат.

Ни одно демо за два дня не мигнуло.

Всё под контролем версий и CI

Репозиторий с ролями лежит в собственном Gitea. На каждый push срабатывает CI (Gitea Actions): линтер ansible-lint в режиме production — самом строгом. Красный линт — код не проходит. Это не даёт накопиться плохим практикам и держит роли в форме.

Грабли, на которые наступил

Версия Ansible сменила синтаксис

В свежем ansible-core убрали один параметр вывода — старый плейбук ругался. Поправил под актуальную версию. Принцип: проверять поведение конкретной версии, а не полагаться на память.

Неправильный пользователь в скрипте бэкапа

Скрипт обращался к сервису от имени одного пользователя, а реальный системный пользователь оказался другим. Поймал фактической проверкой до боевого прогона — иначе бэкап молча создавал бы пустышку. Урок: непроверенный бэкап — это не бэкап.

Артефакт «сухого» прогона

При предпросмотре роли, которая ставит пакет и сразу управляет сервисом, Ansible ругается: сервиса ещё нет (пакет в режиме проверки не ставится). Это не ошибка — в реальном прогоне пакет ставится первым, сервис появляется. Понял механику, не стал «чинить» несуществующую проблему.

Инженерная зрелость — знать, чего не делать

Отдельно скажу про то, что я сознательно не стал переводить в Ansible: боевой облачный сервер — единую точку входа во всю инфраструктуру. Риск ошибки там несоизмерим с выгодой: одна опечатка в конфигурации сети или SSH — и я теряю доступ ко всему разом. Его данные и так защищены отдельной системой бэкапа. Автоматизировать ради галочки — плохая инженерия. Зрелость в том, чтобы взвесить риск и выгоду, а не закодировать всё подряд.

Итог

Весь парк — установка, конфигурация, мониторинг, алертинг, защита SSH, бэкапы — теперь описан кодом. Девять идемпотентных ролей, проверяемых в CI и версионируемых в git. Любой компонент пересоздаётся из репозитория за минуты, а не за часы ручной работы. И это проверено вживую, а не только на бумаге.

Инфраструктура как код — это не про моду на инструмент. Это про то, чтобы спать спокойно, зная: если что-то отвалится, я подниму это одной командой.

Технологии: Ansible, ansible-lint, Gitea Actions (CI/CD), Prometheus, Grafana, Zabbix, blackbox_exporter, node_exporter, fail2ban, WireGuard, Proxmox.

← Два стека мониторинга с нуля: Zabbix + Vue-дашборд и Prometheus + GrafanaСвой мессенджер на Matrix: Synapse, федерация и видеозвонки →