Alert fatigue в DevOps: как сократить шум в Prometheus и PagerDuty, чтобы дежурный реагировал на реальные проблемы

Коротко:

  • Усталость от уведомлений возникает не из-за количества метрик, а из-за плохо настроенной маршрутизации и отсутствия дедупликации.
  • Первый шаг - аудит: посмотреть, сколько сработавших правил за последние 30 дней не привели ни к одному действию дежурного.
  • Grouping и inhibition rules в Alertmanager убирают большую часть шума без изменения самих правил Prometheus.
  • В PagerDuty дедупликация работает через Event Rules и Service-level deduplication key - без этого каждый повтор создает новый инцидент.
  • Качество on-call мониторинга измеряется не количеством алертов, а долей тех, что потребовали реального действия.

Откуда берется шум

Представьте: дежурный получает 40 уведомлений за ночь. Из них 35 - это один и тот же упавший под, который Kubernetes перезапустил через 10 секунд. Три - предупреждения о высоком CPU, которые ушли сами. Одно - реальная проблема с базой данных. И одно - тест, который кто-то забыл отключить.

Через неделю такого режима дежурный начинает игнорировать уведомления. Не потому что ленится, а потому что мозг адаптируется к сигналам, которые не требуют действия. Это и есть alert fatigue - состояние, при котором важный инцидент тонет в потоке незначимых срабатываний.

Проблема почти никогда не в самих правилах Prometheus. Правила могут быть написаны корректно. Проблема в том, что между метрикой и звонком дежурному нет ни одного фильтра: нет группировки, нет подавления зависимых сигналов, нет дедупликации на уровне PagerDuty.

Аудит перед настройкой: что смотреть

Прежде чем что-то менять, нужно понять, где именно теряется качество. Хороший способ - выгрузить историю срабатываний за последние 30 дней и посмотреть на три вещи.

Actionability rate - доля уведомлений, после которых дежурный что-то сделал. Если она ниже 50%, система генерирует больше шума, чем сигнала. В здоровой системе этот показатель должен быть выше 80%.

Топ повторяющихся правил. Обычно 20% правил дают 80% срабатываний. Именно с ними нужно работать в первую очередь - либо поднять порог, либо добавить for-задержку, либо настроить inhibition.

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

Эти данные можно получить из Alertmanager API (/api/v2/alerts) или из истории инцидентов в PagerDuty через Analytics раздел.

Настройка алертов Prometheus: for, labels и severity

Большинство ложных срабатываний можно убрать еще на уровне правил, не трогая Alertmanager.

Параметр for задает минимальную продолжительность состояния до отправки уведомления. Без него правило срабатывает на любой кратковременный всплеск.

- alert: HighCPU
  expr: rate(container_cpu_usage_seconds_total[5m]) > 0.9
  for: 10m
  labels:
    severity: warning
  annotations:
    summary: "CPU выше 90% на {{ $labels.pod }}"

Десять минут - разумный порог для предупреждения о CPU. Если проблема ушла сама за 9 минут, дежурный не получит ничего. Для критических ситуаций - например, сервис полностью недоступен - for: 1m или даже for: 30s оправданы.

Метки severity и team - не просто документация. Они управляют маршрутизацией в Alertmanager. Если все правила имеют одинаковый severity или вообще не имеют его, routing tree работает вхолостую.

Хорошая практика - три уровня: critical (будит ночью), warning (приходит в Slack, не звонит), info (только в дашборд, без уведомлений). Большинство команд используют два уровня и отправляют все в PagerDuty - отсюда и шум.

Alertmanager routing: дерево маршрутов без хаоса

Alertmanager получает все сигналы от Prometheus и решает: кому отправить, когда, в каком виде и нужно ли вообще. Ключевые механизмы - это route, group_by, inhibit_rules и receivers.

Группировка

Без группировки каждое срабатывание - отдельное сообщение. С группировкой Alertmanager собирает похожие события в один пакет и отправляет их вместе.

route:
  group_by: ['alertname', 'cluster', 'service']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  receiver: 'slack-warnings'
  routes:
    - match:
        severity: critical
      receiver: 'pagerduty-critical'
      group_wait: 10s

group_wait - сколько ждать перед первой отправкой, чтобы собрать связанные события. group_interval - как часто отправлять обновления по уже открытой группе. repeat_interval - через сколько повторить, если проблема не решена.

Параметр repeat_interval: 4h для предупреждений - разумный минимум. Если его не выставить или оставить значение по умолчанию (1h), дежурный будет получать одно и то же каждый час.

Inhibition rules

Это один из самых недооцененных инструментов. Inhibition позволяет подавить одни уведомления, если уже активно другое - более высокоуровневое.

Классический пример: нода упала. Prometheus начинает генерировать десятки сигналов - недоступны поды, растет latency, падают health checks. Но все это - следствие одного события. Если настроить inhibition, дежурный получит только один сигнал: нода недоступна.

inhibit_rules:
  - source_match:
      severity: 'critical'
      alertname: 'NodeDown'
    target_match:
      severity: 'warning'
    equal: ['cluster', 'node']

Здесь: если активен NodeDown с severity: critical для конкретного узла, все предупреждения с тем же cluster и node подавляются. Дежурный видит одну проблему вместо двадцати.

Inhibition работает только для активных алертов - как только источник разрешится, подавленные снова станут видимы. Это важно: ничего не теряется, просто приоритизируется.

Маршрутизация по команде

Если несколько команд используют один Prometheus, имеет смысл разделить маршруты по метке team. Тогда backend-команда не получает уведомления о проблемах с фронтом, и наоборот.

routes:
  - match:
      team: backend
    receiver: 'pagerduty-backend'
  - match:
      team: frontend
    receiver: 'slack-frontend'
  - match:
      team: infra
    receiver: 'pagerduty-infra'

Это снижает шум для каждой конкретной команды и повышает релевантность: дежурный видит только то, за что отвечает.

PagerDuty дедупликация: как не плодить инциденты

Даже при хорошо настроенном Alertmanager PagerDuty может создавать дублирующие инциденты, если не настроена дедупликация на его стороне.

PagerDuty использует поле dedup_key (в Events API v2) для определения, является ли событие новым инцидентом или обновлением существующего. Если Alertmanager отправляет одно и то же событие несколько раз без явного dedup_key, каждый раз создается новый инцидент.

При интеграции через Alertmanager receiver для PagerDuty dedup_key формируется автоматически на основе группы алертов. Но если вы используете прямую интеграцию или вебхуки, нужно явно задавать ключ:

{
  "routing_key": "YOUR_INTEGRATION_KEY",
  "dedup_key": "prod-payment-service-high-error-rate",
  "event_action": "trigger",
  "payload": {
    "summary": "Error rate выше 5% на payment-service",
    "severity": "critical",
    "source": "prometheus"
  }
}

Хороший dedup_key - это детерминированная строка, которая одинакова для всех повторов одной и той же проблемы. Обычно это комбинация имени сервиса, типа проблемы и окружения: prod-payment-high-error-rate.

Event Rules в PagerDuty

Event Rules (в новом интерфейсе - Event Orchestration) позволяют фильтровать и трансформировать события до создания инцидента. Это второй рубеж фильтрации после Alertmanager.

Что можно сделать через Event Rules:

  • Подавить события с определенными метками в нерабочее время (например, предупреждения о медленных фоновых задачах).
  • Понизить severity для известных нестабильных сервисов во время планового обслуживания.
  • Направить события в нужный сервис на основе содержимого payload.
  • Автоматически добавить runbook-ссылку к инциденту.

Это особенно полезно, когда несколько источников (Prometheus, Datadog, CloudWatch) отправляют события в один PagerDuty аккаунт - правила позволяют нормализовать их без изменения каждого источника отдельно.

Silence и maintenance windows

Плановые работы - отдельная причина шума. Если команда деплоит в пятницу вечером и не выставляет silence, дежурный получает поток уведомлений о нормальных перезапусках подов.

В Alertmanager silence создается через UI или API:

amtool silence add \
  alertname="PodRestarting" \
  namespace="production" \
  --duration=2h \
  --comment="Плановый деплой payment-service"

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

Хорошая практика - добавить создание silence в CI/CD пайплайн. Перед деплоем пайплайн автоматически выставляет silence на 30 минут для затронутых сервисов. После успешного деплоя silence снимается.

Типичные ошибки при настройке

ОшибкаПоследствиеКак исправить
Нет параметра for в правилахСрабатывание на любой кратковременный всплескДобавить for: 5m или for: 10m для предупреждений
Все правила с severity: criticalДежурный не может приоритизироватьВвести три уровня и отправлять в PagerDuty только critical
Нет inhibition rulesОдна проблема генерирует десятки уведомленийНастроить подавление зависимых сигналов
repeat_interval не выставлен или слишком малОдно и то же уведомление каждый часУстановить repeat_interval: 4h для warning, 2h для critical
Нет dedup_key в PagerDutyКаждый повтор создает новый инцидентЗадать детерминированный dedup_key на основе сервиса и типа проблемы
Silence не используется при деплояхШум во время плановых работАвтоматизировать создание silence в CI/CD

Метрики качества on-call мониторинга

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

Actionability rate - главная метрика. Считается как отношение инцидентов, где дежурный выполнил хоть какое-то действие, к общему числу уведомлений. Цель - выше 80%.

Mean time to acknowledge (MTTA) - среднее время до подтверждения инцидента. Если оно растет, дежурный либо перегружен, либо перестал доверять системе.

Noise ratio - доля уведомлений, которые разрешились сами без вмешательства. Хорошее значение - ниже 10%. Если выше - нужно пересматривать пороги или добавлять задержки.

Pages per shift - количество ночных звонков за одно дежурство. Более трех звонков за ночь - это уже серьезная нагрузка, которая влияет на качество работы на следующий день.

Все эти метрики можно собрать из PagerDuty Analytics или построить дашборд в Grafana на основе данных Alertmanager.

Чеклист: что проверить перед следующим дежурством

  1. Все правила Prometheus имеют параметр for с разумным значением.
  2. Используются три уровня severity, в PagerDuty попадает только critical.
  3. Настроены inhibition rules для зависимых сигналов (нода, кластер, база данных).
  4. Выставлены group_by, group_wait и repeat_interval в Alertmanager.
  5. В PagerDuty настроен dedup_key для всех интеграций.
  6. Есть процедура создания silence при плановых деплоях.
  7. Настроены Event Rules или Event Orchestration в PagerDuty для фильтрации второго уровня.
  8. Команда регулярно (раз в месяц) проводит ревью правил и удаляет неактивные или неинформативные.
  9. Actionability rate отслеживается и видна в дашборде.
  10. Каждый новый алерт проходит ревью: что именно он должен сообщить дежурному и что тот должен сделать.

Как проверить правило до того, как оно попадет в продакшен

Одна из причин накопления шума - правила добавляются без проверки реального поведения. Инженер пишет выражение, смотрит, что оно синтаксически корректно, и отправляет в репозиторий. Через неделю выясняется, что правило срабатывает каждые 15 минут на нормальный трафик.

Перед тем как добавить новое правило, стоит пройти три шага.

Шаг 1: проверить выражение в Prometheus UI. Вкладка Graph показывает, как менялось значение метрики за последние часы или дни. Если выражение уже сейчас возвращает ненулевое значение на стабильной системе, порог выбран неверно.

Шаг 2: оценить частоту исторических пересечений порога. Если за последние 7 дней условие выполнялось 50 раз, а инцидентов было 2, правило будет генерировать шум. Нужно либо поднять порог, либо увеличить for.

Шаг 3: сформулировать runbook до публикации. Если нельзя ответить на вопрос «что именно должен сделать дежурный, получив это уведомление», правило не готово. Уведомление без понятного действия - это шум по определению.

Этот процесс можно формализовать через шаблон pull request для изменений в правилах: поле «ожидаемая частота срабатываний» и поле «действие дежурного» делают ревью быстрее и снижают риск добавления проблемных правил.

Разница между симптомным и причинным подходом к правилам

Большинство команд пишут правила на уровне причин: CPU выше порога, память заканчивается, диск заполнен. Это удобно, потому что метрики очевидны. Но такой подход дает много шума: причины могут меняться без влияния на пользователей.

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

Тип правилаПримерЧастота ложных срабатыванийКогда использовать
ПричинноеCPU выше 90%ВысокаяДля capacity planning, не для on-call
ПричинноеДиск заполнен на 85%СредняяПредупреждение заранее, не критичный звонок
СимптомноеHTTP 5xx выше 1% за 5 минутНизкаяОсновной сигнал для дежурного
Симптомноеp99 latency выше 2 секундНизкаяОсновной сигнал для дежурного
СимптомноеСервис недоступен более 30 секундОчень низкаяКритичный звонок ночью

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

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

Работа с унаследованными правилами: как разобрать накопленный долг

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

Разбирать такой долг проще поэтапно, а не за один раз.

Первый этап: инвентаризация. Выгрузить все активные правила и для каждого проверить: когда последний раз срабатывало, есть ли у него annotations с описанием и runbook, есть ли owner - метка команды или сервиса.

Второй этап: категоризация. Разделить правила на три группы: активно используемые и понятные, активные но без понятного действия, давно не срабатывавшие. Третья группа - первые кандидаты на удаление или перевод в silent режим.

Третий этап: постепенная чистка. Не удалять сразу - сначала перевести подозрительные правила в severity: info и наблюдать две недели. Если никто не заметил отсутствия уведомлений, правило можно убирать.

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

часто задаваемые вопросы

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

Silence - это ручное или автоматическое отключение уведомлений на заданный период. Inhibition - это динамическое подавление: одно активное событие автоматически блокирует другие, связанные с ним. Silence нужен для плановых работ, inhibition - для устранения каскадного шума при инцидентах.

Главный признак - дежурный реагирует на большинство уведомлений реальным действием. Если actionability rate выше 80%, noise ratio ниже 10% и количество ночных звонков не превышает двух-трех за смену, система настроена адекватно.

Да, нужна. Alertmanager группирует события внутри себя, но если несколько источников отправляют данные напрямую в PagerDuty (например, CloudWatch и Prometheus одновременно), дедупликация на уровне PagerDuty - единственный способ избежать дублирующих инцидентов.

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

Начать с Alertmanager, не трогая сами правила Prometheus. Добавить группировку, inhibition и правильные repeat_interval - это уже даст заметный эффект. Параллельно вести статистику: через месяц цифры сами убедят команду пересмотреть пороги.

Итог

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

Три уровня severity, inhibition rules, правильная группировка в Alertmanager и dedup_key в PagerDuty - это не сложные изменения. Большинство из них занимают несколько часов на внедрение, но кардинально меняют качество дежурств. Дежурный, который доверяет своей системе мониторинга, реагирует быстрее и принимает лучшие решения под давлением.