Модуль: Observability · Уровень: Middle+/Senior

TL;DR#

Grafana — слой визуализации и алертинга поверх множества источников данных (Prometheus, Loki, Tempo, и др.). Дашборд = набор panels, каждая панель выполняет запрос к data source и рендерит результат (time series, stat, gauge, table, heatmap). Сила Grafana — корреляция трёх сигналов: из метрики (Prometheus) по exemplar прыгнуть в трейс (Tempo), из трейса в логи (Loki) через derived fields. Templating ($variable) делает дашборды переиспользуемыми между сервисами/инстансами. Grafana Unified Alerting — отдельная подсистема (alert rules → notification policies → contact points), не путать с панельным «алертом». Senior-практика: дашборды как код (provisioning/Terraform/Grafonnet, не клики), иерархия overview → service → instance, аннотации деплоев, и осторожность с $__rate_interval, downsampling и вводящими в заблуждение average-панелями.

Теория#

Источники данных и корреляция#

Data sourceСигналЯзык запросов
Prometheus / MimirМетрикиPromQL
LokiЛогиLogQL
TempoТрейсыTraceQL
Postgres/MySQL, CloudWatch, ElasticsearchразноеSQL/нативный

Главная ценность — переходы между сигналами в одном UI:

  • Exemplars (Prometheus): точки на графике латенси несут trace_id → клик → трейс в Tempo.
  • Trace-to-logs (Tempo): из спана прыжок в Loki по trace_id.
  • Derived fields (Loki): регуляркой выдёргиваем trace_id из строки лога → кнопка «открыть трейс».

Это и есть «observability», а не три разрозненных инструмента.

Типы панелей#

ПанельКогда
Time seriesОсновная: тренды rate/latency/errors во времени
StatОдно число: текущий error budget, RPS, версия
GaugeЗначение относительно порога (utilization, SLO остаток)
TableТоп-N (по эндпоинтам, по подам), instant запросы
HeatmapВизуализация гистограммы во времени (распределение латенси) — нужен histogram data source
HistogramРаспределение значений (не во времени)
Logs / TracesПанели для Loki/Tempo
Bar gauge / State timelineСтатусы, up/down

Heatmap — лучший способ увидеть бимодальность латенси, которую average и даже p99 скрывают.

Templating / переменные#

$job        — Query variable: label_values(up, job)
$instance   — label_values(up{job="$job"}, instance)   (зависимый)
$interval   — Interval variable: 1m,5m,1h
# Панель использует переменные
sum by (le) (rate(http_request_duration_seconds_bucket{job="$job", instance=~"$instance"}[$__rate_interval]))
  • Query variables — значения из лейблов (label_values(...)), каскадно зависимые.
  • Multi-value + =~"$instance" — выбор нескольких значений (регэксп-матч).
  • Repeat panels/rows — одна панель размножается по каждому значению переменной (по инстансу/поду).
  • Custom/Interval/Textbox/Datasource переменные — для гибкости и мульти-кластерных дашбордов.

Transformations#

Постобработка результатов на стороне Grafana без изменения запроса: join нескольких queries, organize/rename fields, calculate (добавить вычисляемое поле), group by, filter, reduce. Полезно для table-панелей и комбинирования источников.

Dashboards as code#

Кликами собранный дашборд не версионируется и не воспроизводится. Senior-подход:

// provisioning/dashboards/payments.json (упрощённо)
{
  "title": "Payments / RED",
  "templating": { "list": [{ "name": "job", "query": "label_values(up, job)" }] },
  "panels": [
    { "type": "timeseries", "title": "Rate",
      "targets": [{ "expr": "sum(rate(http_requests_total{job=\"$job\"}[$__rate_interval]))" }] }
  ]
}
# provisioning/dashboards/provider.yaml — Grafana подхватывает из папки
apiVersion: 1
providers:
  - name: 'gitops'
    folder: 'Services'
    type: file
    options: { path: /etc/grafana/dashboards }

Варианты: file provisioning, Grafonnet (Jsonnet-библиотека для генерации JSON), Terraform provider (grafana_dashboard), хранение JSON в git с code review. Это даёт воспроизводимость, ревью, откат.

Grafana Alerting (Unified)#

Современный alerting — отдельная подсистема, единая для всех data sources:

  • Alert rule — запрос (PromQL/LogQL/…) + условие + for (как долго держится) → состояния Normal/Pending/Firing.
  • Contact points — куда слать (Slack, PagerDuty, email, webhook).
  • Notification policies — дерево маршрутизации по лейблам алерта (label matching), с grouping (схлопывание похожих в одно уведомление), таймингами (group_wait, repeat_interval).
  • Silences — временное глушение по матчерам (на время maintenance).
  • Mute timings — расписания без уведомлений.

Отличие от legacy alerting: старый был привязан к панели (один алерт = одна панель, только Prometheus-подобные источники). Unified — независимые rule groups, мульти-источники, multi-dimensional (один rule → много алертов по сериям).

Важно: «алерт на панели» в UI ≠ настоящий alert rule. Для прода — alert rules + notification policies, либо вообще Prometheus Alertmanager (Grafana может им рулить).

Что показывать#

Иерархия дашбордов (drill-down):

  1. Overview / fleet — health всех сервисов, золотые сигналы агрегатом, SLO/error budget.
  2. Service dashboard — RED (Rate/Errors/Duration) для конкретного сервиса + зависимости.
  3. Instance/pod — USE (CPU/mem/saturation), GC, goroutines, для дебага конкретного инстанса.

Best practices:

  • На каждый сервис — стандартный RED-дашборд (генерируй из шаблона).
  • Латенси — перцентили (p50/p90/p99), не average; heatmap для распределения.
  • Аннотации деплоев (vertical lines) — мгновенно видно «сломалось после релиза».
  • Consistent time ranges и единые единицы.
  • Не перегружать: 6–12 осмысленных панелей лучше 40 шумных.

Подводные камни / gotchas#

  • Дашборды кликами не версионируются — без provisioning/git теряются и не воспроизводятся.
  • $__interval vs $__rate_interval: $__interval зависит от ширины графика и может стать меньше scrape interval → rate() даёт дыры/нули. Для rate() всегда $__rate_interval (учитывает scrape interval).
  • max data points и downsampling: Grafana просит у источника ограниченное число точек; при широком окне точки агрегируются и скрывают пики (max-spike сглаживается). Для алерт-валидации смотри сырьё.
  • Heatmap требует правильного формата: histogram-метрика с бакетами и правильный режим панели, иначе каша.
  • Алерт на панели ≠ alert rule — панельная индикация не шлёт уведомления через notification policies.
  • Misleading average panel: avg(latency) или mean легко рисует «всё хорошо», пряча хвост; ревьюверы дашбордов это ловят.
  • Перегруженный дашборд + много панелей → тормоза браузера и когнитивная перегрузка дежурного.
  • Перцентиль из готовых summary-квантилей (если источник — summary) нельзя агрегировать в панели по инстансам — рисует неправду.
  • Variables с All и regex могут раздуть запрос и положить Prometheus тяжёлым PromQL.

Вопросы на собеседовании#

В: В чём разница $__interval и $__rate_interval и почему это важно? О: $__interval вычисляется из ширины панели и max data points, может оказаться меньше scrape interval, тогда rate() не находит ≥2 точек в окне и рисует дыры. $__rate_interval гарантирует окно ≥ ~4× scrape interval, поэтому для rate()/increase() нужно использовать его. Это та же проблема «правила 4×» из Prometheus, но на стороне Grafana.

В: Как организовать корреляцию метрик, трейсов и логов в Grafana? О: Exemplars в Prometheus-метриках несут trace_id → клик с графика латенси открывает трейс в Tempo; в Tempo trace-to-logs ведёт в Loki по trace_id; в Loki derived fields выдёргивают trace_id из строки лога и дают кнопку в Tempo. Так в одном UI идёшь metric → trace → log, что и есть смысл observability.

В: Почему дашборды надо хранить как код? О: Кликами собранные дашборды не версионируются, не ревьюятся и не воспроизводятся при пересоздании Grafana. Provisioning из git (JSON/Grafonnet/Terraform) даёт code review, откат, единые шаблоны на все сервисы и устойчивость к потере инстанса. Это GitOps-подход к observability.

В: Чем Unified Alerting отличается от legacy и от Prometheus Alertmanager? О: Legacy alerting привязан к панели (один алерт = одна панель, ограниченные источники). Unified — независимые rule groups для любых data sources, multi-dimensional (один rule → много алертов по сериям), с notification policies и contact points. Prometheus Alertmanager делает маршрутизацию/дедуп/группировку на стороне Prometheus; Grafana Unified может его дублировать или управлять им — выбор зависит от того, где живёт источник истины алертов.

В: Почему стат-панель с average latency опасна? О: Среднее скрывает хвост распределения и чувствительно к выбросам; при бимодальном трафике mean может лежать в «пустой» зоне между двумя пиками и показывать «всё хорошо», когда часть пользователей страдает. Нужны перцентили (p99) и heatmap распределения.

В: Что показывать на дашборде сервиса? О: RED: Rate (RPS), Errors (доля 5xx/бизнес-ошибок), Duration (p50/p90/p99 + heatmap). Плюс зависимости (latency/errors downstream), saturation ресурсов (USE), SLO/error budget, и аннотации деплоев. Иерархия overview → service → instance для drill-down.

В: Как сделать один дашборд для всех инстансов сервиса? О: Query variables: $job = label_values(up, job), зависимый $instance = label_values(up{job="$job"}, instance), multi-value с =~"$instance" в запросах. Опционально repeat панелей по инстансу. Это шаблонизирует дашборд под любой сервис/под без копипасты.

На что копают на senior+#

  • Dashboards-as-code / GitOps: Grafonnet/Terraform/provisioning, генерация стандартных RED-дашбордов из библиотек, версионирование и ревью.
  • Механика $__rate_interval и взаимодействие с scrape interval, max data points и downsampling — почему графики «врут» при больших окнах.
  • Корреляция трёх сигналов в UI (exemplars, trace-to-logs, derived fields) и как это настраивается на уровне data sources.
  • Архитектура алертинга: Grafana Unified vs Prometheus Alertmanager, где источник истины, multiwindow burn-rate алерты, дедуп при HA-паре Prometheus.
  • Масштабирование и multi-tenancy: организация дашбордов/папок, RBAC, datasource per tenant, производительность при сотнях дашбордов/панелей.
  • Provisioning безопасности: read-only provisioned дашборды, секреты data sources, service accounts вместо API keys.