3. Alerting и алерты
Правила оповещений: от шума к действию
«Если системе требуется вмешательство человека просто ради того, чтобы выжить, это не автоматика. Это крайне дорогой домашний питомец».
— Брайан Керниган
В предыдущей статье мы увидели, как с помощью научного подхода и сквозной телеметрии локализовать и устранять скрытые дефекты бэкенда. Однако в идеальном мире инженер не должен сидеть перед экраном мониторинга и гипнотизировать графики в ожидании катастрофы. Система обязана сама позвать человека на помощь — и сделать это ровно тогда, когда ситуация действительно требует немедленного вмешательства.
Здесь вступает в силу ключевая дисциплина Observability — Алертинг (Alerting).
Главная цель алертинга — не просто отправить уведомление в мессенджер, а побудить дежурного инженера к конкретному действию (Actionable Alert). Если в три часа ночи звенит пейджер, а сонный инженер думает: «А, это опять этот ложный алерт, утром разберусь» — ваша система оповещений мертва. Это паразитный шум, который разрушает сон команды и неизбежно приводит к «усталости от алертов» (Alert Fatigue), из-за которой настоящую аварию пропустят.
От шума к действию
Зрелая культура эксплуатации формулирует базовый закон: каждый алерт, требующий пробуждения человека, должен быть вызван реальным влиянием на пользователей или неминуемой угрозой полной остановки сервиса.
Если проблему можно решить перезапуском пода — этим должен заниматься Kubernetes. Если диск заполняется медленно и места хватит еще на две недели — об этом создается тикет в трекере задач в рабочий день. Но если выгорает бюджет ошибок и клиенты интернет-магазина не могут завершить платеж — звенит боевой алерт.
Архитектура Алертинга в Prometheus
В экосистеме Prometheus алертинг разделен на две независимые части. Это классический пример разделения ответственности (Separation of Concerns).
flowchart LR
A["Prometheus Server"] -->|"1 - Evaluate Rules<br>Каждые N секунд"| B["Alert Rules"]
B -->|"2 - Fire Alert<br>Active State"| C["Alertmanager"]
subgraph D ["Alertmanager Duties"]
C --> E["Deduplication<br>Дедупликация"]
E --> F["Grouping<br>Группировка"]
F --> G["Routing<br>Маршрутизация"]
G --> H["Notify<br>Slack/Email/PagerDuty"]
end
style A fill:#1e293b,stroke:#38bdf8,stroke-width:2px,color:#fff
style C fill:#881337,stroke:#f43f5e,stroke-width:2px,color:#fff
style D fill:#0f172a,stroke:#475569,stroke-width:1px,color:#fff
1. Prometheus Server (Detection)
Сам Prometheus занимается только вычислениями. Он раз в минуту (по умолчанию) прогоняет ваши PromQL запросы.
- Если условие истинно (например,
up == 0), он переводит алерт в состояние Pending (Ожидание). - Если условие истинно в течение времени
for(например, 5 минут), он переводит алерт в состояние Firing (Стрельба) и отправляет его в Alertmanager.
Prometheus оперирует конечным автоматом состояний: Inactive -> Pending -> Firing.
2. Alertmanager (Notification)
Alertmanager — это отдельный бинарный файл (тоже написан на Go). Он не умеет считать метрики. Он умеет управлять потоками уведомлений.
- Deduplication: Если Prometheus отправит 100 одинаковых алертов (с одной меткой), Alertmanager отправит в Slack только одно сообщение. Он объединяет события от нескольких реплик Prometheus через протокол сплетен (Gossip Protocol на базе HashiCorp memberlist).
- Grouping: Если упало 100 сервисов одновременно (например, проблема в сети дата-центра), Alertmanager склеит их в одно сообщение, а не завалит вас спамом.
- Routing: Разные алерты направляются в разные каналы в зависимости от лейблов (критичные — дежурным в PagerDuty, предупреждения — в Slack команды).
- Silencing: Возможность временно выключить алерт (например, на время планового обслуживания).
Mechanical Sympathy: Цена алертинга
Алертинг не бесплатен для Prometheus.
Каждое правило алертинга — это запрос к базе данных TSDB.
- Если у вас сложный запрос с
rate()и большими окнами, он нагружает CPU Prometheus. - Если правил тысячи, Prometheus может не успевать их вычислить.
Best Practice: Не используйте слишком маленькие окна for.
for: 10s— почти бессмысленно, так как один «провал» в скрейпинге может вызвать ложный алерт.for: 5m— стандарт индустрии. Это фильтрует кратковременные выбросы (flapping).
Параметр for: 5m заставляет Prometheus выждать пять минут непрерывного выполнения аварийного условия в статусе Pending, прежде чем переводить его в Firing.
Anatomy of a Good Alert (Анатомия хорошего алерта)
Хороший алерт отвечает на три вопроса:
- What? Что сломалось? (Имя алерта и лейблы).
- Where? Где сломалось? (Instance, Job).
- So What? Насколько это критично? (Severity: Critical/Warning).
Пример плохого алерта:
# ПЛОХО
alert: HighCPU
expr: process_cpu_seconds_total > 0.8
- Не понятно, это 80% или 0.8 секунд?
- Нет контекста. Может, сервис просто обрабатывает нагрузку.
Пример хорошего алерта (Go Specific):
# ХОРОШО
alert: GoServiceHighGoroutines
expr: go_goroutines{job="my-go-service"} > 10000
for: 5m
labels:
severity: warning
annotations:
summary: "High Goroutine count on {{ $labels.instance }}"
description: "Instance {{ $labels.instance }} has {{ $value }} goroutines. Potential leak."
Специфика Go: Что нужно мониторить обязательно?
Для Go-бэкендов есть набор специфичных метрик, которые должны быть в алертах.
-
Goroutine Leak:
go_goroutines > 10000(или линейный рост).
Это самый частый баг в Go. Если горутин растет и не падает — приложение скоро упадет с OOM или перестанет отвечать. -
GC Pressure:
rate(go_gc_duration_seconds_sum[5m]) > 10.
Если GC запускается слишком часто, это значит, что приложение генерирует слишком много мусора. Это «тормозит» реальную работу (Stop-The-World pauses). -
SLO Violations:
Мы обсуждали SLO. Алерт должен срабатывать, когда Error Budget выгорает быстрее, чем мы готовы принять.
Используйте Multi-window, Multi-burn-rate Alerts (стандарт Google SRE).- Пример: Если за последний час потрачено 5% месячного бюджета — PagerDuty (Critical).
- Если за последние 3 дня потрачено 10% бюджета — Ticket (Warning).
Итог
- Prometheus вычисляет правила, Alertmanager отправляет уведомления.
- Алерты должны быть Actionable (требовать действия). Если действие не нужно — это график, а не алерт.
- Используйте
forclause, чтобы избежать шума (flapping). - В Go обязательно следите за
go_goroutinesиgo_gc_duration_seconds.
В следующей статье мы поговорим о главной проблеме алертинга — как отделить сигнал от шума: 4. Noise vs signal.