Как реализовать сложные триггеры для мониторинга, например отправку SMS при загрузке CPU и низком потреблении памяти
Что проверяют
Понимаете ли вы, что алертинг это не цикл с psutil и if, а отдельная система с длительностью условия, дедупликацией и маршрутизацией. Следом спрашивают, как избежать шторма SMS, когда метрика прыгает вокруг порога, и кто узнает о проблеме, если упал сам мониторинг.
Как отвечать
Сначала уточните условие: «низкое потребление памяти» при высокой загрузке CPU звучит странно, чаще имеют в виду мало свободной памяти. Затем каркас:
- сбор метрик: node_exporter на хостах, Prometheus их опрашивает. свои метрики Python-сервиса отдаются через
prometheus_client; - составное правило: в PromQL два условия соединяются оператором
and, причём сon(instance), чтобы сравнивались метрики одного хоста. Для памяти беритеnode_memory_MemAvailable_bytes, а неMemFree: Linux занимает свободную память под кеш, иMemFreeпочти всегда низкий; - длительность:
for: 5mв правиле. Условие должно держаться пять минут подряд, короткий пик не будит дежурного; - маршрутизация: Alertmanager группирует похожие алерты, подавляет следствия при наличии причины (inhibition) и повторяет уведомление не чаще
repeat_interval. SMS отправляется через webhook-получатель: небольшой сервис на FastAPI принимает JSON от Alertmanager и зовёт API SMS-провайдера; - контроль самого мониторинга: постоянно горящий алерт Watchdog, пропажу которого замечает внешний сервис.
Та же логика есть в Zabbix (выражения триггеров с and, гистерезис через recovery expression) и в Grafana Alerting. Скрипт на psutil уместен как прототип или для одной машины, и тогда в нём нужны те же элементы: скользящее окно, порог восстановления ниже порога срабатывания, лимит на отправку.
Где спотыкаются
Проверяют мгновенное значение раз в минуту. Загрузка скачет вокруг 80%, и дежурный получает двадцать SMS за час, после чего перестаёт их читать.
Запускают проверку на том же сервере, который мониторят. Машина зависает вместе со скриптом, и тревога не уходит.
Не знают, что psutil.cpu_percent() без interval при первом вызове возвращает бессмысленный 0.0, а с interval=1 блокирует поток на секунду. В асинхронном сервисе такой вызов останавливает весь event loop.