Алертинг и дашборды
Алертинг и дашборды
Метрики и трейсы бесполезны, если на них никто не смотрит вовремя. Алерты автоматически сообщают команде о проблемах, дашборды дают мгновенный обзор здоровья сервиса. Грамотный алертинг - баланс между молчанием и шумом.
SLI / SLO / SLA - основа измерений
- SLI (Service Level Indicator) - конкретная метрика. «Доля запросов, обработанных быстрее 200ms».
- SLO (Service Level Objective) - внутренняя цель. «99.9% запросов за 200ms».
- SLA (Service Level Agreement) - договорное обязательство перед клиентом, обычно мягче SLO. «99.5%».
SLI: rate(http_requests_total{le="0.2"}) / rate(http_requests_total)
текущее значение: 99.2%
SLO: 99.9%
Бюджет ошибок (error budget): 100% - 99.9% = 0.1%
= 43 минуты простоя в месяц
Концепция error budget от Google SRE: если бюджет не исчерпан, можно деплоить новые фичи; если исчерпан - фокус на надёжности. Это объективный механизм торможения релизов без drama.
Grafana Dashboard - RED для сервиса
Минимальный полезный дашборд:
- Request Rate:
sum(rate(http_requests_total[5m])) by (service) - Error Rate:
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) - Latency p50/p95/p99:
histogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket[5m]))) - Active Goroutines:
go_goroutines - Heap Memory:
process_resident_memory_bytes - DB connections:
pg_pool_active_connections
Группируйте панели логически: «бизнес-метрики» наверху, «runtime» снизу. Используйте variables ($service, $environment) - один дашборд для всех сервисов.
Alert Rules в Prometheus
groups:
- name: api-alerts
rules:
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m])) > 0.01
for: 5m
labels:
severity: critical
team: backend
annotations:
summary: "Error rate above 1%"
description: "Service {{ $labels.service }} has {{ $value | humanizePercentage }} errors"
runbook: "https://wiki.company.com/runbooks/high-error-rate"
- alert: HighLatency
expr: |
histogram_quantile(0.95,
sum by (le) (rate(http_request_duration_seconds_bucket[5m]))
) > 1
for: 10m
labels:
severity: warning
annotations:
summary: "p95 latency above 1s"
- alert: ServiceDown
expr: up{job="api"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "API instance is down"
for: 5m - алерт срабатывает только если условие держится 5 минут. Это убирает шум от коротких всплесков.
Alertmanager: маршрутизация и группировка
route:
receiver: default
group_by: [alertname, service]
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
- matchers: [severity="critical"]
receiver: pagerduty
repeat_interval: 1h
- matchers: [severity="warning"]
receiver: slack-warnings
receivers:
- name: pagerduty
pagerduty_configs:
- service_key: "..."
- name: slack-warnings
slack_configs:
- api_url: "..."
channel: "#alerts"
Группировка: 100 алертов «pod restart» от одного outage слепляются в одно уведомление. Разные severity → разные каналы: critical → PagerDuty/звонок, warning → Slack.
Принципы алертинга (Google SRE)
-
Алерт на симптомы, не на причины. Симптом - «error rate > 1%». Причина - «CPU 90%, OOM, медленный диск». Симптом видит пользователь; пока пользователь не страдает, причина - фоновый шум.
-
Каждый алерт требует действия. Если на алерт нет известной реакции - это не алерт, это лог. Уберите его или превратите в дашборд.
-
Severity - это обещание. Critical = разбуди в 3 ночи. Warning = посмотрим утром. Если warning надо смотреть сейчас - делайте critical.
-
Runbook обязателен. К каждому алерту - ссылка на инструкцию: что проверить, как диагностировать, как чинить. Иначе on-call дежурный паникует.
Alert fatigue и борьба с ним
Если алертов слишком много, команда перестаёт на них реагировать. Это страшнее, чем отсутствие алертов: вы платите за ложное чувство безопасности.
Метрики качества алертинга:
- Time to Acknowledge (TTA) - сколько минут до первой реакции
- False positive rate - процент алертов, не приведших к действию
- Alert volume per week - сколько алертов в неделю на дежурного
Ежеквартально пересматривайте алерты: что часто срабатывает без действий - удаляйте или ослабляйте пороги.
On-call: PagerDuty, OpsGenie, VictorOps
Эти сервисы решают:
- Эскалация: если первый дежурный не ответил за 15 минут → второй.
- Расписание: rotation между членами команды.
- Подавление: planned maintenance не должен будить.
- Аналитика: TTA, MTTR, частота инцидентов.
Бесплатные альтернативы для маленьких команд: Grafana OnCall, Slack-only с упоминанием @oncall.
Runbooks: формат
Хороший runbook содержит:
# Runbook: HighErrorRate
## Симптомы
- Алерт HighErrorRate firing
- В дашборде видим > 1% 5xx ошибок
## Диагностика
1. Открыть Jaeger, фильтр status=error → найти ошибочные spans
2. `kubectl logs deployment/api -n prod --tail=200`
3. Проверить дашборд DB: pg_pool_active_connections, slow queries
## Возможные причины
- Деплой за последний час → откат
- БД лочится на VACUUM → подождать или kill query
- Внешний API упал → включить fallback flag
## Эскалация
Если за 15 минут не разрешено - вызывать tech lead.
SLO в коде
Концепция «error budget burn rate» - насколько быстро мы тратим бюджет ошибок. Алерт на быстрый burn срабатывает, когда за 1 час потрачено 2% месячного бюджета - это тревожный сигнал ещё до полного исчерпания.
# 1h burn rate = errors_per_hour / monthly_budget
1h_error_rate / (1 - 0.999)
Multi-window multi-burn-rate alerts
Простой алерт «error rate > 1% за 5 минут» имеет проблемы: на маленьком RPS вы либо ловите false positives при единичных ошибках, либо пропускаете медленное сжигание SLO. Google SRE Workbook предлагает решение - алерты по нескольким окнам с разными burn rate.
Burn rate - насколько быстрее, чем заложено в SLO, тратится бюджет ошибок. Если SLO 99.9%, бюджет 0.1%. Текущий error rate 0.5% = burn rate 5× (тратим бюджет в 5 раз быстрее, исчерпаем за 30/5 = 6 дней вместо 30).
groups:
- name: slo-burn-rate
rules:
# Быстрое сжигание: пейджер
- alert: ErrorBudgetBurnFast
expr: |
(
job:error_rate:ratio5m{job="api"} > (14.4 * 0.001)
and
job:error_rate:ratio1h{job="api"} > (14.4 * 0.001)
)
for: 2m
labels: {severity: critical}
annotations:
summary: "SLO budget burns at 14.4× rate (1h window)"
# Медленное сжигание: тикет
- alert: ErrorBudgetBurnSlow
expr: |
(
job:error_rate:ratio30m{job="api"} > (6 * 0.001)
and
job:error_rate:ratio6h{job="api"} > (6 * 0.001)
)
for: 15m
labels: {severity: warning}
annotations:
summary: "SLO budget burns at 6× rate (6h window)"
Конкретные коэффициенты из SRE Workbook: 14.4× за 1h (page) - означает «при таком burn 2% бюджета будет потрачено за 1 час»; 6× за 6h (ticket) - «5% за 6 часов». Условие AND с коротким окном фильтрует point-in-time всплески: если за 5 минут плохо, но за 1 час всё ОК - это, скорее всего, шум.
Inhibition rules в Alertmanager
Когда сервис лежит, одновременно срабатывают десятки алертов: error rate, latency, no traffic, downstream errors. Если все пойдут в pager - это flood. Inhibition rules говорят: «если firing-алерт X, не отправлять алерт Y».
inhibit_rules:
# Если сервис целиком down - не слать всё остальное про него
- source_matchers:
- severity = critical
- alertname = ServiceDown
target_matchers:
- severity =~ "warning|critical"
equal: [service]
# Если деплой в процессе - гасим алерты на дeplym-related метрики
- source_matchers:
- alertname = DeploymentInProgress
target_matchers:
- alertname =~ "HighErrorRate|HighLatency"
equal: [service]
equal - список labels, которые должны совпадать в source и target, чтобы inhibition применилось (обычно service, cluster, env). Inhibition существенно снижает alert volume в крупных инцидентах, не теряя сигнал - первопричина остаётся в pager-е.
Recording rules для alerting
В уроке про Prometheus уже обсуждали recording rules как ускорение дашбордов. Для alerting они критичнее: алерт-правило вычисляется каждые 30s, и дорогой PromQL умножается на множество правил. Кроме того, evaluation должно быть детерминированным - если рaspрос таймаутит, алерт не выстрелит.
# Сначала - recording rule
- record: job:error_rate:ratio5m
expr: |
sum by (job) (rate(http_requests_total{status=~"5.."}[5m]))
/
sum by (job) (rate(http_requests_total[5m]))
# Потом - простой alert на готовом ряду
- alert: HighErrorRate
expr: job:error_rate:ratio5m{job="api"} > 0.01
for: 5m
Выгода двойная: алерт читается мгновенно (одно сравнение, не агрегация по миллионам points), и burn-rate-формулы переиспользуют те же ряды, что и дашборды. Один источник правды для всех потребителей метрики.
Capacity-based alerting: predict_linear
Реактивный алерт «диск 95%» сработает за 5 минут до полного. За это время дежурный успеет только проснуться. Предиктивный алерт по тренду даёт часы на реакцию.
- alert: DiskWillFillIn4Hours
expr: |
predict_linear(node_filesystem_avail_bytes{mountpoint="/data"}[1h], 4*3600) < 0
for: 10m
labels: {severity: warning}
annotations:
summary: "Disk {{ $labels.instance }} will fill in 4 hours"
- alert: ConnectionPoolExhausting
expr: |
predict_linear(pg_pool_available_connections[30m], 1800) < 5
for: 5m
labels: {severity: warning}
predict_linear(metric[window], seconds_ahead) экстраполирует линейный тренд: «по текущей скорости, какое значение будет через N секунд». Подходит для метрик с относительно гладкой динамикой: диск, утечка памяти, нарастающая очередь, пул соединений. Не подходит для метрик с резкими циклами (RPS, у которых дневной паттерн) - экстраполяция ночного нуля даст бессмыслицу.
Post-mortem culture: blameless и actionable
Алертинг ловит инциденты. Post-mortem - что делать с ними, чтобы они не повторялись. Без культуры разбора каждый инцидент учит только тех, кто на нём был; через год команда меняется и сервис снова падает на тех же граблях.
Шаблон blameless post-mortem:
# Incident 2026-05-12: API latency spike
## Summary
13:42-14:08 UTC, p99 latency on /api/users поднялась до 5s, error rate 2%.
Затронуто ~3% пользователей.
## Timeline
- 13:42 - alert HighLatency fired
- 13:45 - oncall acknowledged, начал диагностику
- 13:51 - обнаружен медленный SQL-запрос после ALTER TABLE
- 13:58 - kill query, latency восстановилась
- 14:08 - alert resolved
## Contributing factors
1. ALTER TABLE прошёл без проверки lock duration
2. Slow query не имел timeout
3. Алерт сработал на симптом, а не на причину - диагностика заняла 6 минут
## What went well
- Алерт сработал быстро (3 минуты после начала)
- Runbook содержал команды диагностики БД
## Action items
- [ ] Добавить statement_timeout на pool (owner: @alice, by 2026-05-19)
- [ ] CI-проверка миграций на lock duration (owner: @bob, by 2026-05-26)
- [ ] Recording rule для slow queries (owner: @carol, by 2026-05-15)
## SLO impact
Error budget spend: 0.02% from monthly 0.1%. Remaining 0.08%.
Ключевые принципы:
- Blameless - никаких «Иван виноват». Виновата система, не позволившая Ивану принять правильное решение. Это снимает страх и даёт команде честно описывать что произошло.
- Action items имеют owner и deadline, попадают в backlog. Без этого post-mortem превращается в ритуал.
- MTTR vs MTBF - Mean Time To Recovery (как быстро чиним) и Mean Time Between Failures (как часто ломается). Цель - снижать MTTR и увеличивать MTBF одновременно.
- Error budget impact - отчёт о том, сколько бюджета съел инцидент. Если за квартал бюджет потрачен - заморозка фич, фокус на надёжности.
Мини-практика
Создай Grafana dashboard с RED-метриками для HTTP-сервера: RPS, error rate, p50/p95/p99 latency. Добавь Prometheus rule на error rate > 1% для 5 минут. Подключи Alertmanager с двумя receivers: critical → email, warning → Slack webhook. Проверь срабатывание алерта, ускорив его через временное добавление failing endpoint-а.