Блог GrubLoader
/почему-last-n-зло-и-как-правильно-триггерить-по-n-значениям-в-zabbix

Почему last(#N) — зло, и как правильно триггерить по N значениям в Zabbix

Привет, дорогой читатель! Если ты когда‑нибудь писал триггеры в Zabbix и ловил себя на мысли «ну вроде работает, а вроде и хер поймёшь почему», — добро пожаловать.
Сегодня поговорим про одну из самых часто криво реализуемых логик: «N последних значений подряд должны быть плохими».

Спойлер: last(#2) — это почти всегда хрень. Сейчас объясню, почему.

Задача из реальной жизни

Есть некая метрика. Неважно какая: ответ API, здоровье сервиса, время реакции, температура утюга или задницы.
Ты хочешь:

триггерить только если проблема держится стабильно, а не из‑за одного скачка.

Не «один раз дернулось»
Не «что‑то было минуту назад»
А подряд N измерений — всё плохо


Как делают неправильно (и почему это боль)

Очень часто встречается такая логика:

last(/SomeTemplate/some.health.metric,#2) > {$THRESHOLD}

Что тут реально происходит:

  • #2 — это второе значение с конца
  • проверяется ОДНА точка
  • ни о какой стабильности речи нет

То есть ситуация:

[ OK ] [ ПЛОХО ] [ OK ]

И триггер может сработать.
Поздравляю, ты только что настроил рандом‑алерт‑генератор.


Канонический способ: min() + #N

Если тебе нужно:

N последних значений подряд ≥ порога

Правильная формула выглядит так:

min(/SomeTemplate/some.health.metric,#3) >= {$THRESHOLD}

Что это означает по‑взрослому:

  • берутся ровно 3 последних значения
  • считается минимум
  • если минимум ≥ порога → все три значения плохие

Логика тут железобетонная:

[ ПЛОХО ] [ ПЛОХО ] [ ПЛОХО ]  => триггер
[ ПЛОХО ] [ OK ] [ ПЛОХО ]  => нет триггера

Никакой магии. Только математика.


А почему не max() / avg() / last()?

Коротко:

  • last() — проверяет одну точку, нестабильно
  • avg() — сглаживает и прячет реальные пики
  • max() — реагирует на один выброс

min() — единственный вариант, который гарантирует:

«ВСЁ окно было плохим»


Окно по количеству (#N) или по времени (Nm)?

Есть два подхода:

1. По количеству значений
min(item,#5) >= threshold

Плюсы:

  • чётко: 5 подряд значений
  • не зависит от времени

Минусы:

  • если поменяется интервал сбора — логика ломается
2. По времени
min(item,5m) >= threshold

Плюсы:

  • логика «5 минут подряд плохо»
  • неважно, раз в 30с или раз в 1м метрика приходит

Минусы:

  • количество точек в окне не фиксировано

Выбор простой:

  • хочешь подряд N измерений#N
  • хочешь устойчивость по времениNm

Когда триггеров становится до хрена

А теперь реальность.

У тебя:

  • 20 сервисов
  • 20 айтемов
  • 20 одинаковых триггеров
  • везде #3

И вот однажды ты понимаешь, что надо не 3, а 5.
И начинается веселье: «открыть → поправить → сохранить → не забыть → не про%бать».

Логичный вопрос

«А можно вынести #3 в макрос?»

Интуитивно хочется так:

min(item,#{$POINTS}) >= {$THRESHOLD}
Ответ неприятный

Нельзя.
Zabbix не умеет подставлять пользовательские макросы в #N.

Парсер ожидает число, а не макрос.
Результат — ошибка и испорченное настроение.


Как делают нормальные люди

Используют временное окно + макрос.

Например:

{$HEALTH_WINDOW} = 3m

И триггер:

min(item,{$HEALTH_WINDOW}) >= {$THRESHOLD}

Теперь:

  • хочешь 3 минуты → 3m
  • хочешь 5 минут → 5m
  • хочешь 30 секунд → 30s

И все 20 триггеров меняются сразу.

Да, это не «ровно 3 значения».
Зато:

  • масштабируется
  • читаемо
  • поддерживаемо
  • не превращается в ад при росте проекта

А что в итоге?

Запомни раз и навсегда, дорогой мой читатель:

  • last(#2) — почти всегда ошибка
  • min(#N) — если нужен подряд N значений
  • min(Nm) + макрос — если триггеров много
  • макросы в #N — не работают
  • макросы во времени — работают идеально

Если триггер можно описать словами

«всё было плохо некоторое время»

— ты на правильном пути.

Если он описывается как

«вторая точка с конца была плохая»

— выкинь и перепиши.

Себя потом поблагодаришь.