Prometheus без магии: метрики, дашборды и алерты, которые реально срабатывают
Разберём, какие метрики выбирать для сервисов, как строить meaningful дашборды и как формировать алерты, чтобы не утонуть в ложных срабатываниях.
Содержание
Prometheus без магии: метрики, дашборды и алерты, которые реально срабатывают
Prometheus любят за простоту модели: HTTP-эндпоинт /metrics, тайм-серии, запросы PromQL и дальше — визуализация и алертинг. Но на практике почти всегда упираешься в три вопроса:
- Какие метрики выбрать, чтобы по ним можно было принимать решения, а не просто рисовать графики.
- Как строить дашборды, которые помогают отвечать на конкретные эксплуатационные вопросы.
- Как формировать алерты, чтобы они не превращались в шум, который со временем перестают слушать.
Разберём это системно: от выбора метрик до правил алертинга, с примерами кода и типичными ошибками.
Архитектура метрик: что именно вы измеряете
Прежде чем выбирать конкретные метрики, полезно помнить: Prometheus — это система измерений, а не система мониторинга “в целом”. Мониторинг появляется только тогда, когда вы:
- измеряете входные/выходные свойства системы (latency, errors, throughput),
- фиксируете ресурсные ограничения (CPU, memory, saturation),
- отслеживаете жизненный цикл (доступность, перезапуски, лаги),
- добавляете контекст (labels, разрезы по сервисам, экземплярам, путям),
- и наконец — формируете операционные правила (SLO/SLI, алертинг, runbook).
Метрики — это не только “что-то из коробки”
Обычно стартуют с базовых export-метрик (CPU/RAM, диск, сеть) и с эндпоинтных метрик приложения. Но “работающие” метрики почти всегда включают зависимости: базы данных, очереди, внешние API, кэши, DNS. Если зависимости не измерять, вы быстро получите ситуацию “алерт есть, но причина непонятна”.
Типовая проблема: измеряем всё, но не то
Слишком часто собирают набор:
- общие счетчики запросов,
- несколько гистограмм,
- “производные” графики без единой логики,
- алерты по CPU/memory без понимания, что считается нормой для вашего профиля.
В результате дашборды выглядят богато, а эксплуатационная ценность — низкая. Prometheus в этом смысле “без магии”: он показывает данные, но ответственность за смысл лежит на вас.
Какие метрики выбирать для сервисов: практичный набор
Ниже — рекомендованный каркас метрик для большинства микросервисных систем. Он не универсален на 100%, но хорошо покрывает 80% реальных инцидентов.
1) Доступность и ошибки: что пользователю “не нравится”
HTTP/gRPC ошибки
Измеряйте на уровне приложения:
- доля ошибочных ответов (4xx/5xx),
- абсолютное число ошибок (для сигнализации при малом трафике),
- код ответа (label
statusили более агрегированный признакerror_type).
Пример типовой метрики:
http_requests_total{service, handler, method, status}grpc_server_handling_seconds_count{grpc_method, grpc_code}
Важно: если у вас слишком много вариаций (например, handler на каждый уникальный путь), вы получите взрыв кардинальности. Часто рациональнее нормализовать путь (например, /users/{id} вместо /users/123).
Латентность (latency) — но с “семантикой”
Латентность — основа для пользовательских SLA. Для алертов лучше иметь гистограммы и смотреть percentiles, но аккуратно:
- гистограмма даёт распределение,
- percentiles зависят от того, как вы выбрали buckets.
Метрики:
http_request_duration_seconds_bucket{le, route, method, status}(через client library)http_request_duration_seconds_sum,_count
Рекомендация: выбирайте buckets под ожидаемые диапазоны (p50/p95/p99), иначе либо перетратите точность, либо потеряете различимость.
2) Загрузка и насыщение: когда “всё работает”, но уже плохо
Одна из самых полезных категорий — saturation: сколько ресурсов “тратится” и насколько система приближается к пределу.
Метрики:
- размер очереди (например,
queue_length,consumer_lag), - глубина буферов,
- количество активных воркеров / in-flight запросов,
- время ожидания в очередях (если вы отделяете обработку и ожидание).
Для JVM/Ruby/Node/Go есть свои нюансы, но суть одинаковая: когда вы видите рост очереди и одновременно рост latency — это почти всегда сигнал перегрузки или “узкого горлыка”.
3) Ресурсы: CPU/Memory — как индикатор деградации
CPU и memory сами по себе редко “объясняют” проблему, но помогают быстро отличить тип инцидента:
- CPU near limit + latency вверх → вероятна проблема с вычислениями/профилированием/алгоритмами
- memory рост + GC pauses/oom → утечки/нагрузка на кэш
- дисковая нагрузка/IO wait → bottleneck по хранилищу
Метрики:
process_cpu_seconds_totalprocess_resident_memory_bytesgo_memstats_alloc_bytes/ JVM GC и т.п.container_cpu_usage_seconds_totalcontainer_memory_working_set_bytes
Ключевой момент: используйте проценты или производные, а не сырые значения, если у вас меняется размер инстансов/лимиты. Например, для CPU удобнее смотреть rate и нормировать на лимит (или хотя бы на время).
4) Метрики зависимостей: база данных, очередь, внешний мир
Реальные инциденты почти всегда начинаются с “снаружи” или “в зависимости”.
Для каждой зависимости измеряйте:
- latency,
- ошибки,
- трафик (throughput),
- если возможно — размер пула/конкуренцию (connection pool saturation).
Примеры:
- DB:
db_query_duration_seconds,db_query_errors_total - Redis:
redis_command_duration_seconds,redis_errors_total - Queue:
queue_consume_duration_seconds,queue_depth,consumer_lag_seconds
А ещё полезно иметь метрику таймаутов как отдельный сигнал: “timeout” часто даёт более точную диагностику, чем “5xx” на верхнем уровне.
5) Бизнес-метрики поверх технарских (опционально)
Если у вас есть поток событий или критичные операции, имеет смысл добавить хотя бы несколько SLI-показателей, ближе к домену:
- процент успешных платежей,
- обработанные сообщения за период,
- скорость воронки (например, “создание заказа → оплата” по стадиям).
Это не заменяет технарские метрики, но помогает связать техсбой с пользовательским эффектом.
PromQL для meaningful мониторинга: как думать о запросах
Дашборды и алерты строятся на PromQL. Чтобы не утонуть, полезно иметь в голове три шаблона.
1) Считать скорости и доли
Почти любые счетчики с префиксом *_total надо обрабатывать через rate или irate.
Пример: доля 5xx из общего числа запросов:
sum by (service) (
rate(http_requests_total{status=~"5.."}[5m])
)
/
sum by (service) (
rate(http_requests_total[5m])
)
Подводный камень: при низком трафике доля может быть нестабильной. Тогда полезнее либо увеличить окно ([15m], [30m]), либо добавить условие “достаточная выборка” (например, минимум RPS).
2) Выделять p95/p99 из гистограмм
Для алертов лучше заранее понимать “какой SLI вы хотите”.
Например, p95 latency для конкретного обработчика:
histogram_quantile(
0.95,
sum by (le, route, method) (
rate(http_request_duration_seconds_bucket{route="/api/payments", method="POST"}[5m])
)
)
Важный нюанс: histogram_quantile не всегда применяется одинаково в разных схемах запросов; чаще используют стандартный подход с histogram_quantile или функцией quantile_over_time по *_bucket. Для практики главное — тестировать запросы на ваших данных и смотреть, что вы реально получаете.
Если у вас нет гистограмм — не пытайтесь выдумывать percentiles по суммам и счетчикам: это сломается в момент, когда распределение поменяется.
3) Сегментировать по labels — но контролировать кардинальность
Фильтрация по instance, pod, route, status удобна для диагностики. Но если вы строите алерт “на каждый уникальный route”, вы получите сотни алертов.
Правило: для алертов выбирайте ограниченный набор label’ов, а для дашбордов можно позволить больше детализации (но тоже без крайностей).
Дашборды, которые отвечают на вопросы, а не “всё обо всём”
Meaningful dashboard — это не коллекция красивых графиков. Это интерфейс для оператора и инженера на SRE-подходе:
- “Что происходит?”
- “Насколько критично?”
- “С какой причиной это связано?”
- “Что делать дальше?”
Структура дашборда: от общей картины к деталям
Рекомендуемый подход:
- Обзор SLO/SLI (ошибки, latency, availability) — 1 экран.
- Сегменты по сервисам/эндпоинтам — 1 экран.
- Зависимости — 1 экран.
- Ресурсы/пулы — 1 экран.
- Инцидентные маркеры (deploy, масштабирование, backlog) — один слой контекста.
Если дашборд сразу “разворачивает всё”, его трудно читать и сложно поддерживать.
Какие графики должны быть всегда
Error budget и/или доли ошибок
График доли 5xx и 4xx по сервису и/или по маршруту.
Латентность с percentiles
p50/p95/p99 — минимум. Даже если вы не делаете алерты по всем, оператору нужен масштаб.
Трафик
Параллельно latency и errors показывайте RPS. Иначе вы рискуете принять “рост доли ошибок” за “рост качества”: при падении трафика метрики становятся иллюзорными.
Например, полезная связка:
- RPS по сервису
- доля 5xx
- p95 latency
Saturation
Очереди, consumer lag, in-flight, длина пулов. Это часто “объясняет” почему latency растёт.
График без контекста — плохой график
Типичная ошибка: один график latency без errors и без RPS. Тогда при росте latency вы не поймёте: это деградация работы или просто изменения в трафике/в нагрузке.
Более практичный вариант: в одной строке дашборда — latency и RPS, в следующей — ошибки, далее — зависимости и saturation.
Как формировать алерты, чтобы они реально срабатывали
Алертинг — самая чувствительная часть. Можно собрать идеальные дашборды, но если алерты либо молчат, либо срабатывают на всё подряд, система мониторинга перестаёт восприниматься.
В основе — три принципа:
- Алерт должен соответствовать действию (что вы будете делать при срабатывании).
- Алерт должен быть устойчив к шуму (stability).
- Алерт должен иметь достаточную “доказательность” (enough signal) — не реагировать на микроскопические случайности.
Принцип 1: алерт по SLI, а не по индикатору “в среднем”
CPU > 90% — это индикатор. Но что вы будете делать? Масштабировать? Разбираться с профилированием? Отключать фичи? Без связи с user impact алерт получается “техническим”.
Лучше делать алерт по user-affecting показателю:
- доля ошибок ↑,
- p95 latency ↑,
- availability ↓,
- consumer lag ↑ (в терминах доставки).
Ресурсы — как дополнительный алерт/поддерживающий контекст, но не как главный.
Принцип 2: минимум трафика перед тем как реагировать на доли
Если RPS низкий, доли ошибок могут прыгать. Добавляйте условие “достаточно запросов”.
Пример: доля 5xx и минимум 100 запросов в 5 минут:
(
sum by (service) (rate(http_requests_total{status=~"5.."}[5m]))
)
/
(
sum by (service) (rate(http_requests_total[5m]))
)
> 0.05
and
sum by (service) (rate(http_requests_total[5m])) > 20
Здесь “20” — это порог RPS в рамках окна (100/5m = 20/мин, в rate будет “в секунду”, поэтому при необходимости пересчитайте). Смысл — не алертить по процентам при почти нулевом объёме.
Принцип 3: сравнивайте с базой и используйте длительность
Алерты лучше строить на:
- минутных/пятиминутных окнах,
for:задержке в Alertmanager,- иногда — на сравнениях с историческими значениями (через recording rules), если у вас есть зрелость в аналитике.
Пример алерта на рост p95 latency:
histogram_quantile(
0.95,
sum by (le, service) (
rate(http_request_duration_seconds_bucket{route=~"/api/.*"}[5m])
)
) > 0.7
Дальше в Alertmanager (условно):
for: 10m
Так вы отсеете “спайки”, которые могут происходить при редких событиях (GC, кратковременные сетевые микропроблемы).
Принцип 4: разделяйте “деградацию” и “катастрофу”
Один алерт на всё — плохая идея. Лучше две ступени:
- Warning: начинается деградация (например, p95 вырос выше порога, но ещё без критической доли ошибок)
- Critical: явный user impact (например, p99/5xx или availability)
Пример:
- Warning: p95 > 0.5s
for 10m - Critical: 5xx доля > 1% и RPS > порог
for 5m
Это снижает стресс и ускоряет реакцию.
Принцип 5: алерт должен включать разрез для диагностики
Алерт должен содержать labels/annotations так, чтобы по нему можно было понять:
- какой сервис,
- какой endpoint/метод (если уместно),
- какой инстанс (если проблема локальная),
- какой параметр “перешёл порог”.
Например, аннотация:
- “p95 latency для route=/api/payments выросла до 0.82s”
- “5xx ratio поднялась до 3.1% при RPS=120”
В Prometheus это делается через шаблоны Alertmanager и переменные из PromQL ({{$labels.service}}, и т.д.).
Подводные камни алертинга
1) “Алерт по пустоте”
Если метрика перестала приходить, это тоже сигнал, но требует аккуратной настройки.
Частый анти-паттерн: алерт, который срабатывает каждый раз после рестарта, потому что метрика на секунду пропала. Лучше использовать:
- “staleness” через
absent(), - либо правила, учитывающие жизненный цикл (например, не алертить, если pod в Pending).
2) Неправильное окно rate / irate
Если окно слишком короткое, rate шумный. Если слишком длинное — алерт запаздывает.
Обычно для HTTP ошибок/латентности стартуют с 5m или 10m, затем настраивают под вашу динамику.
3) Слишком много алертов на один корень
Одна проблема может триггерить 5 разных алертов (latency, errors, CPU, queue). Без корреляции вы тонете.
Практика зрелых команд:
- делайте алерт “симптомов” и один “корневой” (или хотя бы наиболее близкий к реальному impact),
- используйте inhibition/grouping в Alertmanager,
- объединяйте похожие алерты в один “incident” по labels.
Рекомендованная схема: recording rules и “продукты” для дашбордов/алертов
Чтобы запросы были не только корректными, но и поддерживаемыми, используйте recording rules. Это снижает нагрузку на Prometheus и делает логику централизованной.
Пример: записать rate запросов и p95 latency в отдельные временные ряды.
groups:
- name: app_rules
rules:
- record: service:http_requests_rate:sum
expr: sum by (service) (rate(http_requests_total[5m]))
- record: service:http_5xx_rate:sum
expr: sum by (service) (rate(http_requests_total{status=~"5.."}[5m]))
Дальше алерт может быть проще:
(
service:http_5xx_rate:sum
/
service:http_requests_rate:sum
) > 0.05
Это также помогает держать一致ность между дашбордом и алертами: вы не “слегка по-разному” считаете одно и то же.
Комментарии
Пока нет комментариев