MAATRIX / Блог / Антипаттерн: алерт на каждый скачок нагрузки

Антипаттерн: алерт на каждый скачок нагрузки

MAATRIX

Дежурный настраивает Zabbix или Prometheus Alertmanager и на радостях ставит правило «CPU выше 80% — уведомление», «load average выше числа ядер — уведомление». Логика понятная: чем больше отклонений ловим, тем быстрее узнаем о проблеме. На практике получается обратное — за первую же неделю в чат прилетает полсотни сообщений о ночном backup, batch-джобе или обычном дневном пике трафика, и уже через месяц дежурный сворачивает уведомления не глядя. Разбираем, почему алерт на голое отклонение метрики — это антипаттерн, и что поставить вместо него.

Как выглядит этот антипаттерн на практике

Механика всегда одна и та же. Кто-то поднимает мониторинг — Zabbix, Prometheus + Alertmanager, встроенные алерты хостинг-панели — и на первом шаге настраивает пороги по учебнику: CPU выше 80%, load average выше числа ядер, память выше 90%. Правило выглядит логично: метрика превысила разумный предел — значит, что-то не в порядке, надо сообщить.

# так антипаттерн выглядит в Prometheus rules — часто буквально в первой версии alerts.yml
groups:
  - name: node-basic
    rules:
      - alert: HighCPU
        expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[1m])) * 100) > 80
        labels:
          severity: warning
        annotations:
          summary: "CPU > 80% на {{ $labels.instance }}"

Ключевая деталь — здесь нет for: (условия на длительность), окно усреднения короткое ([1m]), а порог фиксированный и не привязан ни к типу нагрузки, ни к тому, что происходит с пользователями сервиса в этот момент. Правило реагирует на любую точку, где метрика хотя бы на секунду пересекла черту, — будь то реальная деградация или штатный всплеск. Через день такой алерт срабатывает по нескольку раз, потому что почти любой сервер время от времени легитимно упирается в CPU: ночной бэкап, пересборка индекса, batch-обработка очереди, крон, который раз в час что-то считает.

Почему нормальные скачки нагрузки всё равно попадают под алерт

Проблема не в том, что пороги стоят «неправильные числа» — проблема в том, что сама модель «метрика выше X = инцидент» не различает два принципиально разных случая:

  • кратковременный, ожидаемый скачок — batch-задача, ротация логов, резервное копирование, всплеск трафика от рекламной кампании или сезонного спроса;
  • устойчивое отклонение, которое действительно указывает на проблему — утечка памяти, зависший процесс, DDoS, деградация базы данных.

С точки зрения голой метрики оба случая выглядят одинаково: значение выше порога. Разница видна только во времени — легитимный скачок длится секунды-минуты и сам возвращается к норме, проблема держится и нарастает. Правило без учёта длительности («мгновенное значение выше X — алерт») физически не может отличить одно от другого, потому что смотрит на один момент времени, а не на паттерн поведения метрики.

Отдельно эту механику стоит проговорить для трафика и для CPU по отдельности, потому что источники «нормального шума» разные:

Источник ложного срабатыванияТипичная метрикаПочему это норма, а не инцидент
Ночной бэкап / cronCPU, disk I/OПлановая задача, фиксированное время, предсказуемая длительность
Пересборка индекса, batch-обработкаCPU, памятьЕдиноразовая тяжёлая операция, завершается сама
Всплеск трафика (акция, новость, сезон)CPU, load average, сетьРеальный спрос, сервис справляется, просто выше обычного baseline
Деплой / прогрев кэшаCPU, время ответаКратковременная просадка сразу после релиза, проходит за минуты
Автомасштабирование ещё не сработалоLoad averageСистема уже реагирует, алерт добавляет только шум поверх работающего процесса

Если правило не умеет отличать эти пять строк от «диск сыплется» или «утечка памяти растёт третий час подряд», оно будет слать одинаковое по тону сообщение и в первом, и во втором случае. У человека на приёмном конце нет способа понять серьёзность без того, чтобы открыть дашборд и разобраться руками — а если это происходит по десять раз в день, разбираться руками быстро перестают.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Как накапливается alert fatigue и что в итоге теряется

Первую неделю дежурный честно открывает каждое уведомление. Оказывается — ложная тревога, cron отработал, трафик сам спал. Мозг — экономная система: если действие (проверка) раз за разом не находит проблемы, приоритет стимула, который это действие запускает, снижается. Это называется привыканием (habituation), и это не вопрос дисциплины конкретного человека, а предсказуемая психологическая реакция на бесполезный шум — этот механизм подробно разобран в статье про усталость от алертов.

Критично то, что привыкание не различает конкретные алерты — оно обобщается на весь канал целиком. Через месяц дежурный не думает сознательно «этот алерт я пропущу» — он просто реагирует медленнее на все уведомления подряд, откладывает открытие чата, привыкает видеть счётчик непрочитанных и не проверять его. А дальше в тот же канал, тем же шрифтом и с той же историей ложных срабатываний за плечами приходит сообщение о том, что диск действительно заполнен на 95% или что реально начался DDoS. Разницы для получателя нет — оба сообщения выглядят как очередной шум из полусотни предыдущих.

Практический эффект — не абстрактный, а измеримый косвенно: растёт время до реакции (time to acknowledge). Пока алерты воспринимаются всерьёз, дежурный смотрит на уведомление почти сразу. После нескольких недель шума — открывает канал «между делом», раз в час, а не раз в минуту. Для инцидента, где счёт идёт на минуты (упавший чекаут, забитый диск, реальная атака), эта разница в реакции — это разница между «пользователи ничего не заметили» и «в поддержку уже пишут гневные тикеты».

Голая метрика — не то же самое, что ущерб для пользователя

Здесь и находится корень проблемы, а не только её симптом. Алерт «CPU > 80%» отвечает на вопрос «метрика высокая?», а не на вопрос «пользователю сейчас плохо?». Это два разных вопроса, и путать их — суть антипаттерна. Сервер вполне может держать CPU на 95% часами и при этом отвечать пользователям штатно — потому что запас производительности заложен именно на такие пики, а не потому, что что-то сломалось.

Обратная ситуация встречается не реже: метрика формально в норме, а сервис уже деградирует. Показательный разбор именно такого случая — когда загрузка пула соединений держалась в районе 70%, алерт был выставлен на 90% и не срабатывал, а p99-задержка API уже минут сорок ползла вверх — есть в статье про порог алерта на 90%, который не спас от деградации на 70%. Это зеркальная сторона той же проблемы: голое значение метрики плохо коррелирует с реальным влиянием на пользователя что в одну, что в другую сторону.

Отдельная ловушка — усреднение. Среднее время ответа может оставаться в норме, пока у части пользователей запросы уже отваливаются по таймауту: среднее «размазывает» проблему по всей выборке. Почему для алертинга важно смотреть на процентили (p95, p99), а не на среднее значение, разобрано в статье про то, почему среднее время ответа врёт про процентили. Тот же принцип применим и к CPU, и к load average: единственное число, усреднённое по времени или по всем инстансам, теряет именно ту информацию, которая нужна для решения «будить дежурного или нет».

Порог по длительности: устойчивое отклонение, а не пиковое значение

Первый практический шаг — перестать реагировать на мгновенное значение и начать требовать, чтобы отклонение продержалось. В Prometheus для этого есть параметр for: — правило переходит в состояние firing только если условие выполняется непрерывно заданное время:

groups:
  - name: node-improved
    rules:
      - alert: HighCPUSustained
        expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "CPU держится выше 85% дольше 10 минут на {{ $labels.instance }}"

Изменилось три вещи одновременно: окно усреднения выросло с одной минуты до пяти (сглаживает случайные всплески внутри самого расчёта), порог поднят до уровня, который на конкретном сервере действительно необычен (а не взят из мануала), и добавлено условие for: 10m — метрика должна оставаться выше порога десять минут подряд, а не один раз в момент опроса. Batch-задача, которая грузит CPU на пару минут и отпускает, под такое правило не попадёт вообще — она просто не успевает продержаться нужное время.

Конкретное значение for: — не универсальная константа, а вопрос к вашей нагрузке: для одного сервиса нормальный batch-джоб длится две минуты, для другого — двадцать. Смысл не в конкретном числе, а в том, что порог должен отвечать не на вопрос «метрика сейчас высокая?», а на вопрос «метрика высокая уже некоторое время, достаточное, чтобы это не было штатным пиком?». Методика подбора baseline и порогов под конкретный профиль нагрузки отдельно разобрана в статье про пороги алертов, которые не бесят — там же разбирается гистерезис (разные пороги на срабатывание и на сброс), который дополнительно убирает дребезг алерта на границе значения.

SLO-based алертинг: от значений метрик к влиянию на пользователя

Порог по длительности убирает часть шума, но не решает проблему целиком — потому что CPU, память и load average остаются метриками инфраструктуры, а не метриками пользовательского опыта. Более надёжный подход — алертить не по внутренним показателям сервера напрямую, а по SLO (Service Level Objective): доле успешных запросов, доле запросов уложившихся в целевую задержку, доступности сервиса за окно времени. CPU и load average при этом остаются полезными для диагностики «почему», но перестают быть тем, что напрямую будит дежурного ночью.

Идея в двух частях. Во-первых, определить SLO — например, «99.5% запросов за скользящие 30 дней отвечают быстрее 300 мс» — и получить error budget: допустимую долю «плохих» минут, прежде чем цель нарушена. Во-вторых, алертить не на факт превышения порога метрики, а на скорость расходования этого бюджета (burn rate) — насколько быстро сервис «прожигает» запас. Это методология, известная как multiwindow multi-burn-rate alerting (описана в SRE-практиках Google): она сочетает короткое и длинное окно, чтобы отличить кратковременный всплеск ошибок от устойчивой деградации.

# иллюстративный пример: алерт по скорости расходования error budget,
# а не по значению CPU или load average напрямую
groups:
  - name: slo-burn-rate
    rules:
      - alert: ErrorBudgetBurnFast
        expr: |
          (
            sum(rate(http_requests_total{status=~"5.."}[5m]))
            /
            sum(rate(http_requests_total[5m]))
          ) > (14.4 * 0.005)
          and
          (
            sum(rate(http_requests_total{status=~"5.."}[1h]))
            /
            sum(rate(http_requests_total[1h]))
          ) > (14.4 * 0.005)
        labels:
          severity: critical
        annotations:
          summary: "Быстрое сжигание error budget по 5xx: короткое и длинное окно согласованы"

Конкретный множитель (в примере — 14.4) и целевой уровень ошибок (0.005 для SLO 99.5%) — параметры, которые считаются под ваш собственный SLO и период его действия, приводить их как универсальную константу было бы неверно: у вас они, скорее всего, будут другими. Смысл конструкции важнее конкретных чисел: алерт срабатывает, только когда оба окна — короткое (быстро реагирует) и длинное (отсекает случайный всплеск) — одновременно показывают повышенную долю ошибок. Это резко снижает число ложных срабатываний от кратковременных всплесков, потому что одна минута плохих ответов внутри пятиминутного окна почти никогда не «протащит» и часовое окно тоже.

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

Как внедрять постепенно, не переписывая всё сразу

Полный переход на SLO-based алертинг — не то, что делается за один вечер, особенно если правил уже десятки. Рабочая последовательность:

  1. Выпишите текущие алерты и оцените каждый по факту. За последний месяц — сколько раз сработал и сколько раз указал на реальную проблему. Правила с соотношением «10 срабатываний, 0 реальных инцидентов» — первые кандидаты на переделку или удаление.
  2. Добавьте for: туда, где его нет. Это самое дешёвое изменение с наибольшим эффектом — часто одно это уже убирает большую часть ложных срабатываний от batch-задач и кратковременных пиков.
  3. Соберите baseline по каждому серверу отдельно, а не переносите одинаковые пороги CPU/памяти между серверами с разным профилем нагрузки (веб-сервер и сервер с batch-обработкой ведут себя по-разному).
  4. Для критичных пользовательских сценариев (оплата, авторизация, API) определите SLO и постройте на них burn rate алерты — начните с одного-двух самых важных эндпоинтов, а не со всего сервиса сразу.
  5. Оставьте инфраструктурные метрики как diagnostic-уровень, не как критический алерт — они попадают на дашборд и в warning-канал, а не в личные уведомления дежурному.
  6. Раз в квартал пересматривайте пороги и статистику срабатываний — нагрузка меняется, сервис растёт, вчерашний baseline устаревает молча.

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

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Значит ли это, что алерты по CPU и load average вообще не нужны?

Нет, они остаются полезны для диагностики и для дашбордов — вопрос не «нужны или нет», а «должны ли они напрямую будить дежурного». Практический компромисс: инфраструктурные метрики — на warning-уровень и дашборд, SLO/burn rate — на критический алерт, который будит человека.

С чего начать, если алертов уже полсотни и все настроены по старой схеме?

Не переписывайте всё сразу. Начните с аудита: за последний месяц посмотрите, сколько раз каждое правило сработало и сколько раз это привело к реальному действию. Правила с нулевой полезностью — первые кандидаты на удаление или на добавление for:.

Сколько должен длиться for:, чтобы отсечь ложные срабатывания?

Единого числа нет — это зависит от того, сколько обычно длится ваш легитимный пик (batch-джоб, бэкап, всплеск трафика). Соберите статистику по своим batch-задачам за пару недель и возьмите длительность с запасом сверх типичной.

Обязательно ли использовать именно Prometheus и Alertmanager для SLO-алертинга?

Нет, методология (алерт по скорости расходования бюджета ошибок, а не по значению метрики) не привязана к конкретному инструменту — её можно реализовать и на Zabbix, и на Grafana с любым источником метрик, если он позволяет считать долю ошибок за скользящее окно.

Как понять, что у нас именно этот антипаттерн, а не что-то другое?

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

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →