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

Alertmanager 0.15: многоуровневый routing и silence management - как мы победили alert fatigue

Настроили routing в Alertmanager 0.15: критические алерты - в PagerDuty, предупреждения - по командам в Slack, ночные - только SMS. Alert fatigue снизился заметно.

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

Prometheus Alertmanager 0.15 - улучшенный routing и silence management для крупных инсталляций

Когда мы переезжали на Prometheus 2.3 в июле, в том же посте упомянули Alertmanager 0.15 как «отдельный разговор». Разговор состоялся - и оказался длиннее, чем казалось.

Исходная ситуация была классическая: один Alertmanager на несколько проектов, один канал в Slack куда летело всё подряд, дежурные давно научились не читать уведомления. Это называется alert fatigue, и лечится оно не дополнительными алертами, а правильной маршрутизацией.

Что изменилось в 0.15

Alertmanager 0.15 вышел вместе с Prometheus 2.3. Принципиальных изменений два: переработанный движок routing и новый API для silence management.

Routing в предыдущих версиях был, но работал плоховато при сложных деревьях: матчинг по нескольким лейблам со вложенными маршрутами часто давал неожиданные результаты, и разобраться почему конкретный алерт ушёл не туда было нетривиально. В 0.15 ввели continue: true как нормальный флаг (раньше оно тоже было, но документация была мягко говоря скудной) и починили приоритет матчинга в поддеревьях.

Silence management теперь через API v2, который отдаёт JSON с нормальной схемой. Раньше силенсы создавались только через веб-интерфейс или curl с кривым форматом; теперь их можно автоматизировать: создать силенс на окно планового обслуживания скриптом из CI/CD - это реально работает без боли.

Как мы это раскатали

На managed-проектах у нас к этому моменту было три характерных класса алертов, которые вели себя совсем по-разному.

Первый - критические события: упал pod в production, кончилось место на диске, недоступен эндпойнт. Дежурный должен проснуться, даже если час ночи. Сюда - PagerDuty, звонок, SMS.

Второй - предупреждения: загрузка CPU выше 70% больше пяти минут, медленные запросы в базу, диск занят на 80%. Это информация для команды, которая работает прямо сейчас. Сюда - Slack в канал команды, в рабочее время.

Третий - информационный шум: успешные деплои, ротация сертификатов, плановые рестарты. Это нужно видеть, но не срочно. Сюда - отдельный Slack-канал, который читают по желанию.

Конфиг routing вырос примерно до такого дерева:

route:
  receiver: 'slack-ops-default'
  group_by: ['alertname', 'cluster', 'service']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h

  routes:
    - match:
        severity: critical
      receiver: 'pagerduty-oncall'
      continue: true

    - match:
        severity: critical
      receiver: 'slack-ops-critical'

    - match:
        severity: warning
      receiver: 'slack-team-routing'
      routes:
        - match:
            team: backend
          receiver: 'slack-backend'
        - match:
            team: infra
          receiver: 'slack-infra'

    - match:
        severity: info
      receiver: 'slack-ops-info'
      group_wait: 10m
      group_interval: 30m

Ключевой момент с continue: true на критических: алерт идёт в PagerDuty И в Slack одновременно. Без continue он останавливался бы на первом совпавшем маршруте, и команда бы ничего не видела в Slack - только дежурный получал звонок.

Лейблы - главная боль

Вся эта схема работает ровно настолько хорошо, насколько аккуратно расставлены лейблы в правилах алертов. Когда мы начали расставлять team: и severity: по всем правилам, обнаружилось что часть правил вообще без severity написана - просто alert: SomethingBad и всё. Это исторический хлам, накопившийся пока Alertmanager был простым.

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

Силенсы во время деплоев

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

Через API v2 мы добавили в деплой-скрипт создание силенса на время обновления:

curl -s -X POST \
  http://alertmanager:9093/api/v2/silences \
  -H 'Content-Type: application/json' \
  -d '{
    "matchers": [
      {"name": "service", "value": "'"$SERVICE"'", "isRegex": false}
    ],
    "startsAt": "'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'",
    "endsAt": "'"$(date -u -d '+30 minutes' +%Y-%m-%dT%H:%M:%SZ)"'",
    "createdBy": "deploy-script",
    "comment": "Planned deploy of '"$SERVICE"' '"$VERSION"'"
  }'

Тридцать минут с автоматическим истечением. Если деплой затянулся - деплой-скрипт может продлить силенс, если нет - он просто истечёт сам. Это не идеально (всё ещё нужна интеграция с CI/CD), но уже в разы лучше ручного режима.

Что по итогу

Субъективно - количество алертов, которые команды реально читают и реагируют на них, выросло. Количество жалоб в духе «опять Slack завалило» - упало. Точные цифры мы не мерили намеренно: слишком много переменных менялось одновременно, чтобы атрибутировать эффект именно Alertmanager.

Нерешённая проблема: routing-конфиг превращается в священный документ, который никто не трогает из страха сломать. Тест routing до деплоя - через amtool check-routes - помогает, но не полностью. Хочется что-то вроде юнит-тестов для маршрутизации, но пока это делается руками: берёшь пример алерта, прогоняешь через amtool config routes test, смотришь куда пошёл.

Следующий шаг - поставить amtool в CI и валидировать конфиг при каждом мерже. Это скорее организационная задача, чем техническая.

Контакт

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

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