core code helper

Моніторинг з Prometheus і Grafana: метрики, PromQL і алерти, які не дратують

Як влаштований Prometheus, які метрики збирати в першу чергу (RED і USE), базовий PromQL і правила для алертів, на які справді хочеться реагувати.

0 імпортували Імпортувати собі

Моніторинг потрібен, щоб дізнаватися про проблеми раніше за користувачів і швидко знаходити причину. Prometheus — стандарт де-факто для метрик у світі контейнерів і Kubernetes, а Grafana — для їх візуалізації.

Як працює Prometheus

Prometheus сам приходить по метрики: раз на 15–30 секунд робить HTTP-запит на /metrics кожної цілі (pull-модель) і зберігає часові ряди у своїй базі.

# Так виглядає ендпоінт /metrics
http_requests_total{method="GET",route="/api/orders",status="200"} 18421
http_requests_total{method="GET",route="/api/orders",status="500"} 37
http_request_duration_seconds_bucket{route="/api/orders",le="0.1"} 16890
http_request_duration_seconds_bucket{route="/api/orders",le="0.5"} 18302

Чотири типи метрик:

Тип Що це Приклад
Counter Лише зростає (скидається при рестарті) кількість запитів, помилок
Gauge Може рости і падати пам'ять, довжина черги, з'єднання
Histogram Розподіл значень по «кошиках» час відповіді
Summary Перцентилі, пораховані на клієнті рідко потрібен, краще histogram
Увага

Не кладіть у мітки значення з необмеженою кількістю варіантів: user_id, email, повний URL з ID. Кожна унікальна комбінація міток — окремий часовий ряд. Мільйон користувачів = мільйон рядів = Prometheus без пам'яті. Використовуйте шаблон маршруту (/orders/:id), а не реальний шлях.

Що вимірювати: RED і USE

RED — для сервісів (з точки зору користувача):

  • Rate — запитів за секунду;
  • Errors — частка помилок;
  • Duration — час відповіді (перцентилі).

USE — для ресурсів (CPU, диск, мережа, пули з'єднань):

  • Utilization — наскільки зайнятий;
  • Saturation — черга, що не встигає обробитись;
  • Errors — помилки ресурсу.

PromQL: п'ять запитів, які потрібні щодня

# 1. Запитів за секунду по сервісу (за останні 5 хвилин)
sum by (service) (rate(http_requests_total[5m]))

# 2. Частка помилок 5xx
sum(rate(http_requests_total{status=~"5.."}[5m]))
  / sum(rate(http_requests_total[5m]))

# 3. p95 часу відповіді з гістограми
histogram_quantile(0.95,
  sum by (le, route) (rate(http_request_duration_seconds_bucket[5m])))

# 4. Використання пам'яті контейнерами відносно ліміту
sum by (pod) (container_memory_working_set_bytes{container!=""})
  / sum by (pod) (kube_pod_container_resource_limits{resource="memory"})

# 5. Диск закінчиться протягом 24 годин (за поточним трендом)
predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[6h], 24 * 3600) < 0
Порада

До counter завжди застосовуйте rate() або increase(). Саме значення лічильника (18421 запит з моменту старту) майже нічого не означає і «падає» до нуля після кожного рестарту.

Алерти: на симптоми, а не на причини

Погані алерти будять людей через те, що ні на що не впливає: «CPU 85% на вузлі 7». Хороші — коли страждають користувачі або ось-ось почнуть.

groups:
  - name: api
    rules:
      - alert: ApiHighErrorRate
        expr: |
          sum(rate(http_requests_total{service="api",status=~"5.."}[5m]))
            / sum(rate(http_requests_total{service="api"}[5m])) > 0.02
        for: 5m
        labels:
          severity: page
        annotations:
          summary: "API: понад 2% помилок 5xx протягом 5 хвилин"
          runbook: "https://wiki.example.com/runbooks/api-errors"

      - alert: DiskWillFillIn24h
        expr: predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[6h], 86400) < 0
        for: 30m
        labels:
          severity: ticket

Правила хорошого алерту:

  • for: — умова має триматися кілька хвилин, щоб не реагувати на одиночні сплески;
  • severity: page (будити вночі) лише для того, що зачіпає користувачів зараз; решта — ticket;
  • runbook у кожному алерті: що перевірити і що робити;
  • регулярний перегляд: алерт, на який ніхто не реагував місяць, — видалити або переробити.
Важливо

Якщо черговий отримує 30 алертів за ніч, він перестає їх читати. Утома від алертів (alert fatigue) — головна причина, чому реальні інциденти пропускають.

SLO замість порогів «на око»

Ще кращий підхід — алертити на витрату бюджету помилок. Наприклад, SLO «99.9% запитів успішні за 30 днів» дає бюджет 0.1%. Алерт спрацьовує, коли бюджет витрачається занадто швидко: за годину згоріло 2% місячного бюджету — це сторінка черговому, за 6 годин 5% — тікет.

Дашборди в Grafana

Дашборд сервісу варто будувати зверху вниз:

  1. Верхній ряд — RED: запити, помилки, p50/p95/p99.
  2. Далі — залежності: БД, кеш, черги, зовнішні API.
  3. Внизу — ресурси: CPU, пам'ять, рестарти подів.

Зберігайте дашборди як код (JSON у Git або провіжинінг Grafana) — інакше після чергового «покращення» ніхто не знайде, як було.

Чому погано додавати мітку user_id до метрики http_requests_total?

Кожне унікальне значення мітки створює окремий часовий ряд. З тисячами або мільйонами користувачів кількість рядів вибухає (high cardinality), Prometheus з'їдає пам'ять і сповільнюється. Деталі на рівні окремого користувача — задача логів і трейсів, а не метрик.

Збережіть статтю собі

Увійдіть, щоб імпортувати її у власні нотатки, відмічати вивчене, ставити лайки й питати спільноту. Реєстрація безкоштовна: вхід через Google займає кілька секунд.

Увійти через Google Інші способи входу