Grafana 8.3/8.4 и unified alerting: мигрируем правила, собираем грабли
Grafana 8.3/8.4 переводит unified alerting в default. Рассказываем о миграции правил для нескольких клиентов: типичные ловушки, скрипт конвертации, новая структура каналов.
Grafana 8.3/8.4: unified alerting стал движком по умолчанию, legacy alerting объявлен deprecated; новый редактор панелей; улучшения Loki datasource
Grafana 8.3 и следом 8.4 принесли одно принципиальное изменение: unified alerting теперь включён по умолчанию для новых инсталляций, а старый движок предупреждений объявлен legacy. Это не «старый добрый deprecated, живи ещё десять лет» - это реальный сигнал к действию, потому что миграция нетривиальна и тихо не пройдёт.
Мы ведём мониторинг для нескольких клиентов в рамках managed-сопровождения, и по части из них Grafana уже работала с alert-правилами на классическом движке. Решили не ждать пока это само рассосётся - пошли по стекам и перевели всё на unified alerting.
Что изменилось в архитектуре
Классический alerting был встроен прямо в Grafana: правила хранились в SQLite/PostgreSQL вместе с dashboard-ами, notification channels - там же. Unified alerting вводит отдельный слой - Grafana Alertmanager (или внешний Alertmanager, если уже есть). Правила теперь живут в папках-группах, привязаны к datasource через UID, а не к панели dashboard-а.
Ключевые структурные изменения:
- Notification channels исчезли. Вместо них - contact points и notification policies. Contact point - это куда слать (Telegram, email, PagerDuty). Policy - дерево маршрутизации по лейблам алертов, определяющее какой contact point получит какое уведомление и с каким silence/mute-поведением.
- Правила отвязаны от панелей. В классическом alerting правило жило прямо в панели и редактировалось там же. Теперь правила - отдельные сущности, панель может их отображать, но не содержит.
- Grafana-managed alerts vs datasource-managed alerts. Unified alerting поддерживает два режима: правила, которые Grafana вычисляет сама (Grafana-managed), и правила, которые живут непосредственно в Prometheus/Loki (datasource-managed, через Ruler API). Это важно - если уже есть правила в Prometheus recording rules, их можно подключить без дублирования.
Что нашли при миграции
Инструмент экспорта есть - в Grafana 8.3 появилась кнопка в UI для миграции классических алертов в unified. На деле это работает для простых случаев. Для реальных стеков начинаются нюансы.
Первое - notification channels не конвертируются автоматически в contact points. Grafana создаёт обёртку-заглушку, но маршрутизацию (кто получает что) нужно выстраивать руками. Если было пять каналов с разными получателями для разных dashboard-ов - придётся вручную выстраивать policies.
Второе - правила с template-переменными. В классических алертах можно было использовать переменные dashboard-а в threshold-запросах. В unified alerting это не работает - правила самодостаточны и независимы от dashboard-а. Такие правила нужно переписывать, жёстко зафиксировав значения или параметризировав через labels.
Третье - multi-dimensional alerts. Unified alerting нативно поддерживает multi-dimensional: одно правило может генерировать алерты для каждого инстанса по отдельности через label-ы из PromQL. Классический движок так не умел - там было одно правило, один алерт. При миграции это надо использовать, а не пытаться воспроизвести старое поведение «один монолитный алерт на всё».
Четвёртое - silence не переносятся. Если были активные silence в классическом alerting - они пропадают. Нужно их зафиксировать и воссоздать руками.
Скрипт конвертации
Для ускорения миграции написали небольшой Python-скрипт, который тянет классические алерты через Grafana API (/api/alerts), строит для каждого unified-правило в формате provisioning YAML и раскладывает по папкам согласно dashboard-у источника. Это не полная автоматизация - на выходе получается заготовка, которую надо проверить глазами, - но экономит часы ручной работы при переносе двадцати-тридцати правил.
Грубая структура скрипта:
import requests, yaml, pathlib
GRAFANA_URL = "http://localhost:3000"
AUTH = ("admin", "password")
alerts = requests.get(f"{GRAFANA_URL}/api/alerts", auth=AUTH).json()
for alert in alerts:
rule = {
"title": alert["name"],
"condition": "C",
"data": [
{
"refId": "A",
"datasourceUid": alert.get("dsUID", "__expr__"),
"model": {"expr": alert.get("query", ""), "refId": "A"},
}
],
"noDataState": "NoData",
"execErrState": "Alerting",
"for": f"{alert.get('for', 300)}s",
"labels": {},
"annotations": {"summary": alert.get("message", "")},
}
folder = alert.get("dashboardSlug", "migrated")
pathlib.Path(f"rules/{folder}").mkdir(parents=True, exist_ok=True)
with open(f"rules/{folder}/{alert['id']}.yaml", "w") as f:
yaml.dump({"apiVersion": 1, "groups": [{"name": folder, "rules": [rule]}]}, f)
Результат - набор YAML-файлов, которые можно положить в provisioning Grafana и загрузить без UI-кликов. Удобно особенно когда нужно воспроизвести правила на нескольких инсталляциях.
Улучшения Loki datasource в 8.3/8.4
Отдельно стоит отметить улучшения в Loki datasource - они напрямую влияют на алертинг по логам. В 8.3 появилась поддержка LogQL metric queries в alert-правилах: теперь можно писать правило вида «если за последние 5 минут в логах Nginx больше N строк с кодом 5xx - алерт». Раньше для этого нужно было либо тащить метрики отдельно через Prometheus recording rules, либо городить workaround через Loki Ruler.
Это закрывает реальный кейс: мониторинг по логам без дополнительной прослойки. На одном из клиентских стеков мы как раз убрали несколько вспомогательных recording rules в Prometheus, которые существовали только чтобы прокинуть агрегаты из логов в алерты.
Где сейчас
Несколько клиентских стеков переведены полностью, пара - в процессе. Основное время уходит не на конвертацию правил, а на перестройку notification policies: когда старая схема из пяти каналов «на глазок» превращается в явное дерево маршрутизации с лейблами, это требует разговора с клиентом о том, кто реально должен получать какие алерты. Хорошая возможность навести порядок, но трудоёмкая.
Unified alerting в целом - более правильная архитектура. Правила как код, provisioning через YAML, интеграция с внешним Alertmanager, multi-dimensional алерты - это то, чего в классическом движке не было и не появилось бы. Переход болезненный, но разовый.
- PostgreSQL 14 в production: два месяца с новым vacuum и наша конфигурация · 25 января 2022
- Kubernetes 1.23 и PSP: начинаем миграцию на Pod Security Standards · 14 января 2022