Лаг упал в ноль, а ничего не починилось: зависший Kafka-консьюмер, спрятанный переименованием group.id
Лаг консьюмера рос пять дней, потом упал в ноль и там остался. Выглядело как «само рассосалось». На деле консьюмер завис, метрика исчезла, а алерт зарезолвился сам.
На одной продуктовой платформе в Gambchamp график лага консьюмера пять дней рос примерно до 80 000 сообщений — а потом упал в ноль и лёг ровной линией. Казалось, проблема ушла сама. Одновременно панель «суммарный лаг» показывала отрицательное число, а «активные группы» — одну, хотя в кластере десятки живых групп.
Три вещи, каждая из которых выглядела нормально, были сломаны одновременно.
Слой первый: консьюмер жив и ничего не делает
После пересоздания брокеров committed offset группы застыл и не двигался больше восьми часов, пока продюсеры продолжали писать. Консьюмер оставался членом группы, так что для Kafka он был жив — просто не продвигался. Клиентская библиотека не переподключилась как следует после рестарта брокеров.
Правду дала сама Kafka, в обход экспортера:
kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group <group>
CURRENT-OFFSET, который не меняется при растущем LOG-END-OFFSET, — это зависший консьюмер, что бы ни говорило членство в группе.
Слой второй: ноль — не выздоровление
Падение в ноль совпало с деплоем, который поменял схему именования group.id. Старые группы осиротели, новая начала читать. Но новое имя не попало под whitelist --group.filter экспортера, и метрика по ней просто исчезла.
Алерт по лагу увидел, что серия пропала, и зарезолвился. Ещё три дня правило ни за чем не следило, а график выглядел здоровым, потому что на нём ничего не было.
Слой третий: отрицательный лаг
kafka-exporter отдаёт lag = -1 для партиций без committed offset — там, где сама Kafka показывает LAG = "-". Панель, суммирующая лаг, уходила в минус, а порог алерта тихо сдвигался на число таких партиций.
В довесок метрики консьюмеров самого приложения существовали, но не собирались: чарт объявлял порт метрик 9001, а приложение слушало 9006.
Исправления
- Скрейпить реальный порт. Relabel в vmagent оставляет объявленный порт для дедупликации, но переписывает адрес на тот порт, который приложение реально слушает.
- Blacklist вместо whitelist.
--group.filter=.*плюс исключение эфемерных групп, привязанных к поду. Whitelist по имени ломается при первом же ренейме — ровно это нас и ослепило. clamp_min(..., 0)в панелях лага. Убирает-1, сохраняя серию; фильтр> 0выкинул бы серию целиком.- Алертить на клиентскую метрику.
highwater - positionот самого консьюмера не зависит ни от именования групп, ни от фильтра экспортера.
Выводы
- Каждому пороговому алерту нужен парный
absent(). Иначе исчезновение метрики читается как выздоровление — ровно это и произошло. - Whitelist в экспортере метрик — мина, которая ждёт следующего переименования.
LAG = "-"— это не ноль, а-1от экспортера — не число, которое можно суммировать.- Проверяйте, что порт, объявленный в чарте, — это тот порт, который слушает приложение.
- Один канал для всех алертов — это отсутствие алертинга: пятидневный firing утонул среди тысяч уведомлений. Владелец и SLA для warning важнее ещё одного правила.