Event-Driven Ansible в production: три rulebook-паттерна для автоматического реагирования на инциденты
Несколько месяцев с EDA в боевом периметре - разбираем три паттерна, которые реально прижились: авто-рестарт, эскалация в ITSM и карантин хоста при EDR-алерте.
Event-Driven Ansible в production: паттерны автоматического реагирования на инциденты
Когда мы в феврале писали про AAP 2.5 и планировали пилот EDA у одного из клиентов, ещё не было понятно, что из этого получится в реальной эксплуатации. Прошло несколько месяцев - пилот перешёл в production, подключили ещё один периметр. Пора поделиться тем, какие паттерны оказались рабочими, а какие выглядели хорошо на стенде, но в бою потребовали переделки.
Сразу оговорка: у нас не задача автоматизировать всё. Задача - убрать дежурного из петли там, где решение однозначное и обратимое. Где неоднозначно или необратимо - человек остаётся.
Паттерн 1: авто-рестарт сервиса с дедупликацией
Самый очевидный сценарий. Zabbix поймал, что сервис упал, EDA среагировал, playbook перезапустил. Звучит просто - и первая версия rulebook была буквально такой: одно условие, один action.
Проблема вылезла через неделю. Zabbix может генерировать несколько алертов за короткое время на один и тот же триггер, если хост нестабилен. EDA без дедупликации пытался рестартовать сервис три раза подряд - и при этом мог столкнуться с предыдущим job, который ещё не завершился. На некоторых сервисах это приводило к двойному запуску.
Решение - добавить throttle в rulebook и проверять, нет ли уже активного job для того же хоста:
rules:
- name: Restart failed service
condition: event.payload.trigger_name == "Service is down"
throttle:
once_within: 5 minutes
group_by_attributes:
- event.payload.host
- event.payload.service
action:
run_job_template:
name: "Restart Service"
organization: "Default"
job_args:
extra_vars:
target_host: "{{ event.payload.host }}"
service_name: "{{ event.payload.service }}"
once_within с группировкой по хосту и сервису - ключевой момент. Без этого EDA честно выполняет всё, что приходит. Ещё добавили в playbook проверку: если сервис уже запущен к моменту выполнения - просто логируем и выходим без ошибки. Маленькая деталь, но она экономит нервы при разборе джобов.
Паттерн 2: эскалация в ITSM без создания дублей
Второй паттерн - не автоматическое устранение, а автоматическое создание тикета в ITSM при инцидентах, которые EDA не должен чинить сам. У клиента - отечественная ITSM-система с REST API.
Здесь главная засада оказалась не в EDA, а в идемпотентности. ITSM не всегда умеет самостоятельно дедуплицировать входящие заявки по source + alert_id. Если алерт пришёл дважды (повторная активация, рестарт EDA-воркера) - создавались два тикета на один инцидент. Диспетчеры ругались справедливо.
Пришлось добавить промежуточный шаг в playbook: перед созданием тикета делать поиск по полям source и external_id, и создавать только если совпадений нет. Это чуть усложнило playbook, но убрало дубли полностью.
Ещё одна вещь: при эскалации в ITSM важно передавать контекст, а не просто «сработал алерт». В extra_vars мы прокидываем:
- alert_id и trigger_name - для трассировки обратно в Zabbix
- severity - чтобы ITSM сразу ставил нужный SLA
- last_value - что именно зафиксировал мониторинг в момент алерта
- host_group - к какому кластеру или сервису относится хост
Без этого тикет получается «что-то упало», что для диспетчера почти бесполезно.
Паттерн 3: карантин хоста при EDR-алерте
Этот паттерн появился позже и потребовал больше всего осторожности. Один из клиентов использует отечественное EDR-решение с возможностью webhook при срабатывании. Запрос был такой: при определённых EDR-алертах (подозрительный процесс, нетипичные сетевые соединения) автоматически изолировать хост - убрать его из load balancer, заблокировать исходящий трафик через firewall-правило, создать тикет с пометкой P1.
Это первый наш сценарий с необратимым (точнее, требующим ручной отмены) действием. Поэтому здесь мы добавили дополнительный слой: before-playbook, который проверяет тип алерта по whitelist критичности и запрашивает подтверждение через отдельный механизм - в данном случае API-вызов во внутренний чат-бот с таймаутом. Если за 2 минуты никто не ответил «отмена» - карантин применяется автоматически.
flowchart LR
EDR[EDR Alert] -->|webhook| EDA[EDA Rulebook]
EDA -->|severity check| GATE{Критичный?}
GATE -->|нет| ITSM[Создать тикет]
GATE -->|да| WAIT[Уведомить + таймаут 2 мин]
WAIT -->|отмена не пришла| Q[Карантин хоста]
WAIT -->|ручная отмена| ITSM
Q --> ITSM
Схема выглядит сложнее, чем первые два паттерна - и это правильно. Изоляция хоста в production должна иметь хотя бы один человеческий шаг вмешательства, даже если он пассивный (молчание = согласие).
Что оказалось важным на практике
Три наблюдения, которые не очевидны до эксплуатации в реальном потоке:
Логирование активаций - критично. EDA-воркер по умолчанию логирует не так подробно, как хотелось бы. Мы настроили отдельный handler, который пишет в Loki каждое срабатывание с контекстом: какое событие, какое условие совпало, какой job запустился. Без этого разбор «почему EDA что-то сделал в 3 ночи» превращается в детективную историю.
Сеть событий при вспышке инцидентов. При массовом сбое (упал свитч, несколько хостов одновременно) EDA может получить десятки алертов за секунды. Тут throttle спасает, но нужно также учитывать, что Automation Controller имеет свои лимиты на параллельные job-ы. Без capacity planning это превращается в очередь.
Версионирование rulebook. Это кажется очевидным, но первое время мы редактировали rulebook прямо в UI EDA Controller без контроля версий. После одной непреднамеренной правки, которая изменила поведение в production, перенесли все rulebook в Git с review-процессом как у обычного кода.
Где сейчас
EDA у нас работает в двух клиентских периметрах в рамках managed-сопровождения инфраструктуры. Первые два паттерна - рестарт и ITSM-эскалация - закрыты и стабильны. Паттерн с карантином по EDR-алерту пока в пилотном статусе у одного клиента: хотим накопить статистику ложных срабатываний прежде чем применять шире.
Главный вывод за эти месяцы: EDA хорошо работает там, где у вас уже есть описанные runbook-и. Он не придумывает логику - он только вызывает её автоматически. Если runbook размыт или зависит от контекста, который EDA не может получить из события, автоматизация будет хрупкой.