devops
January 28, 2022

Кейс по алертам из Prometheus

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

Есть у нас некий сервис, живущий в kubernetes - в одном namespace сидит пачка pod-ов с разными его модулями (больше там ничего не живет). И отдают они все метрику, в том числе метрику health (очень-очень плохое имя метрики, но что поделать, разработчикам уже высказали, авось допилят, они раньше еще больший ад хотели сделать), которая может принимать значения от 0 до 3 включительно, где 3 - все ок, а остальное условно разные коды возможных неполадок (так делать в тоже не надо, кстати)

И вот надо сделать алерт, что если сервис не отдает 3 - то значит сообщить об этом. И вроде все просто:

health{namespace="bla-bla",cluster="prod"} != 3

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

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

absent_over_time(health{namespace="bla-bla",cluster="prod"}[1m]) == 1

И здесь все в среднем будет работать, кроме периодически странных кейсов, связанных с пересозданием пода. И вот здесь вторая проблема - метрика, даже появляясь, отдается уже другим подом, да и подняться может на другом узле, а значит один-два из лэйблов меняется и вот бац, это уже совсем другой сервис. То есть для prometheus это уже другой временной ряд и с тем никак не связан. Это может неплохо так сводить алертинг с ума. Поэтому мы сделаем еще следующее па:

absent_over_time(min without (pod,instance) (health{namespace="bla-bla",cluster="prod"})[1m]) == 1

Что же здесь? Во первых мы добавили оператор min, он здесь сам по себе не особо нужен, это минимальное значение нам не холодно, не жарко. Он нужен ради without, который уже позволяет отрубить один лейб и из всех даваемых метрик выбрать минимальную (pod-ы у нас в одном экземпляре и параллельно существовать и отдавать метрики не могут благо) - там мы отделываемся от разбиения временных рядов по признаку pod-а и сервера k8s, где конкретный вертится. Теперь для нас это просто единая метрика от сервиса и все. Ну а дальше мы алертим, если у него нет значений на каком-то временном участке (1 минута). Можно поставить и меньше, но особо смысла не вижу.

---

Теперь конечно можно эти запросы объединить через оператор or и создать один алерт, но так мы делать конечно не будем: это снизит понятность и читаемость алертов, в бонус так мы уже по получению алерта будем знать - не отдаются метрики вообще (pod мертв) или отдаются не те значения (проблема с сервисом или его зависимостями).