Перейти к содержимому
К программе курса
Архитектура высоконагруженных веб-приложений
10 / 1191%

Мониторинг: метрики как оружие, алерты как контракты

100 минут

Для кого: инженеры, которых бесит узнавать о падении из Twitter. Если у вас нет SLO, алерты либо молчат, либо орут без причины — эта глава заставит вас сделать мониторинг, который работает.

Провокация №1: вы знаете, кто сейчас down?

  • Откройте http://prometheus/api/v1/query?query=up. Если есть сервисы со значением 0 и вы об этом не знаете — мониторинг мёртв.
  • Минимум: дашборд «up/down» по всем службам + алерт up == 0 > 1 минута.

1. Три столпа Observability — но за деньги

1.1 Metrics

  • Prometheus + exporters.
  • prometheus.yml обязательно содержит scrape_interval, evaluation_interval, rule files.
global:
  scrape_interval: 15s
  evaluation_interval: 15s
 
rule_files:
  - "recording_rules.yml"
  - "alerting_rules.yml"
 
scrape_configs:
  - job_name: "api"
    static_configs:
      - targets: ["api:3000"]
    metrics_path: "/metrics"
  • Recording rules для «золотых сигналов».
# recording_rules.yml
groups:
  - name: api_recordings
    rules:
      - record: job:http_request_duration_seconds:p95
        expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
      - record: job:http_request_error_rate
        expr: sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))

1.2 Logs

  • Структурированные, JSON. Корелляционный ID обязателен.
logger.info("order_created", {
  traceId: req.headers["x-trace-id"],
  userId: req.user.id,
  orderId: order.id,
  amount,
});

1.3 Traces

  • OpenTelemetry SDK + Jaeger/Tempo. Пробрасываем заголовок traceparent.
const span = tracer.startSpan("checkout", { attributes: { userId } });
res.set("traceparent", span.context().traceId);

Провокация №2: у вас есть SLO?

  • Если нет числа «мы гарантируем X% latency», вы не можете говорить о качестве.

2. SLI/SLO/SLА

СервисSLISLOSLA
APIP95 latency < 400ms99.5%99.9%
CheckoutError rate < 0.3%99.7%99.95%

Error budget:

def error_budget(total_requests: int, slo: float) -> int:
    return int(total_requests * (1 - slo))
 
# Пример: 1M запросов/месяц, SLO 99.9% → 1000 ошибок в месяц
  • Используйте error budget, чтобы решать, можно ли выпускать релиз.

Провокация №3: у вас алерты привязаны к SLO?

  • «CPU > 80%» — мусор. Алерт должен говорить: «SLO нарушается через 2 часа».

3. Alerting Rules

# alerting_rules.yml
groups:
  - name: api_alerts
    rules:
      - alert: APIErrorBudgetBurn
        expr: rate(http_requests_total{status=~"5.."}[5m]) > 5 * (1 - 0.999) * rate(http_requests_total[5m])
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "API error budget burning >5x"
          runbook: "https://wiki/runbooks/api-error-budget"
  • Severity уровни: page (critical), ticket (warning), log (info).

Провокация №4: алерты доставляются?

  • Проверяйте: curl -XPOST https://pagerduty/... — работает? Нет? Значит, PagerDuty вас не спасёт.

4. Grafana как боевой экран

  • Four Golden Signals: latency, traffic, errors, saturation.
{
  "dashboard": {
    "title": "API Golden Signals",
    "panels": [
      {
        "title": "RPS",
        "targets": [{ "expr": "rate(http_requests_total[5m])" }]
      },
      {
        "title": "P95 Latency",
        "targets": [{ "expr": "job:http_request_duration_seconds:p95" }]
      },
      {
        "title": "Error Rate",
        "targets": [{ "expr": "job:http_request_error_rate" }]
      },
      {
        "title": "CPU",
        "targets": [{ "expr": "rate(process_cpu_seconds_total[5m])" }]
      }
    ]
  }
}

Провокация №5: у вас есть runbooks?

  • Алерт без runbook — издевательство над on-call.

Runbook пример

Alert: Redis lag > 500ms
Steps:
1. Проверить `info replication`
2. Если `master_link_status=down` > 30s — переключить трафик на master-only
3. Уведомить #oncall-redis
4. Создать инцидент в Jira/StatusPage

Провокация №6: тестируете ли вы мониторинг?

  • Chaos engineering: «выключите dependency» и убедитесь, что алерт сработал, runbook помог.

Chaos сценарий

# Уронить dependency
kubectl scale deploy payment-api --replicas=0
# Должен сработать алерт "Payment API down"

Провокация №7: логирование не утопит вас в счёте?

  • Управляйте retention, sampling.
loki:
  chunk_target_size: 1048576
  retention_period: 7d

Провокация №8: trace sampling?

  • 100% sampling = дорого. Используйте dynamic sampling (например, ошибки всегда 100%, нормальные 10%).
const sampler = new TraceIdRatioBasedSampler({ ratio: 0.1 });

Практика

  1. SLI/SLO расчёт:
    • Выберите ключевой сервис, посчитайте monthly error budget, оформите SLO документ.
  2. Golden Signals Dashboard:
    • Создайте Grafana дашборд с latency/traffic/errors/saturation + алерты, привязанные к runbook.
  3. Alert storm simulation:
    • Намеренно вызовите 5 разных алертов на staging, оцените шум, оптимизируйте правила (aggregation, deadman switch).
  4. Tracing drill:
    • Инструментируйте цепочку из 3 сервисов через OpenTelemetry, найдите trace конкретного запроса.
  5. Runbook + Chaos:
    • Напишите runbook для «DB lag > 500ms». Проведите столбовую проверку: отключите реплику на staging, следуйте runbook, проверьте, что всё работает.

Безопасность: логи и трейсы не должны содержать PII без шифрования. Chaos-тесты только на staging, и только после согласования.

Что дальше

Архитектурные паттерны и решения: теперь, когда вы умеете измерять и реагировать, пора принимать системные решения (финальная глава).

Мониторинг: метрики как оружие, алерты как контракты — Архитектура высоконагруженных веб-приложений — Potapov.me