Алертинг и дашборды

Алертинг и дашборды

Метрики и трейсы бесполезны, если на них никто не смотрит вовремя. Алерты автоматически сообщают команде о проблемах, дашборды дают мгновенный обзор здоровья сервиса. Грамотный алертинг - баланс между молчанием и шумом.

SLI / SLO / SLA - основа измерений

Пирамида 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 для сервиса

Минимальный полезный дашборд:

  1. Request Rate: sum(rate(http_requests_total[5m])) by (service)
  2. Error Rate: sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))
  3. Latency p50/p95/p99: histogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))
  4. Active Goroutines: go_goroutines
  5. Heap Memory: process_resident_memory_bytes
  6. 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)

  1. Алерт на симптомы, не на причины. Симптом - «error rate > 1%». Причина - «CPU 90%, OOM, медленный диск». Симптом видит пользователь; пока пользователь не страдает, причина - фоновый шум.

  2. Каждый алерт требует действия. Если на алерт нет известной реакции - это не алерт, это лог. Уберите его или превратите в дашборд.

  3. Severity - это обещание. Critical = разбуди в 3 ночи. Warning = посмотрим утром. Если warning надо смотреть сейчас - делайте critical.

  4. 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-а.

Зарегистрируйтесь бесплатно, чтобы пройти квиз, решить задание с автопроверкой и вести прогресс.