Два маленьких Go-сервиса поверх твоего Prometheus/Alertmanager, которые:
- Обогащают каждый алерт контекстом — метрики из Prometheus (с трендами) + статус подов/нод/событий из Kubernetes API.
- Связывают каскад алертов в один инцидент (по namespace + временному окну).
- Просят LLM написать черновик разбора — вероятная первопричина и что проверить.
- Кладут всё в Telegram: сырые алерты по одному + разбор.
Это не «ИИ вместо инженера». На выходе — черновик; решение принимает человек.
Cluster / ноды
│ метрики
▼
Prometheus ──► Alertmanager
│ webhook (сырой алерт)
▼
Data Enricher (Go)
│ ├─ идёт в Prometheus за метриками [startsAt−15m … +5m]
│ ├─ идёт в K8s API за статусом подов/нод/событий
│ └─ буферизует в Redis (окно корреляции)
▼
AI Worker (Go) ──► OpenRouter (LLM)
│
▼
Telegram: сырые алерты по одному → сводка «N связанных» → AI-разбор
- Alertmanager ничего не знает про Telegram/LLM — просто шлёт webhook на enricher.
- Enricher сам ходит за контекстом (pull), буферит в Redis для склейки.
- AI Worker — единственный, кто знает про Telegram и OpenRouter.
- Сырой алерт постится до обращения к модели → LLM не точка отказа.
- Кластер Kubernetes с kube-prometheus-stack (Prometheus + Alertmanager + kube-state-metrics)
- Redis в кластере (для корреляции; без него enricher шлёт алерты по одному)
- Аккаунт OpenRouter (https://openrouter.ai) — или любой OpenAI-совместимый эндпоинт
- Telegram-бот + канал + группа обсуждения
- Container registry для образов (или собери и запушь свои)
Порядок важен — комментарии от бота работают только в канале со связанной группой.
- Создай бота. Напиши @BotFather:
/newbot→ имя, username. Получишь токен вида123456:AA...→ этоTELEGRAM_BOT_TOKEN. Полезно:/setprivacy→ Disable (чтобы бот видел сообщения в группе). - Создай канал (например
IncidentGPT) и группу (IncidentGPT Group). - В настройках канала → Discussion → привяжи группу. (если не привязывается — сделай группу временно публичной, привяжи, потом верни приватной).
- Добавь бота админом и в канал, и в группу (право Post/Send Messages).
- Узнай chat_id. Напиши что-нибудь в канал, потом открой в браузере:
Найди:
https://api.telegram.org/bot<ТОКЕН>/getUpdates"chat":{"id":-100...,"type":"channel"}→TELEGRAM_CHANNEL_ID"chat":{"id":-100...,"type":"supergroup"}→TELEGRAM_THREAD_CHAT_ID
Заведи ключ на https://openrouter.ai/keys → это OPENROUTER_API_KEY.
Модель по умолчанию — google/gemini-2.5-flash (можно любую с OpenRouter).
Любой Redis в кластере. Например Bitnami, одиночный инстанс без персистентности:
helm repo add bitnami https://charts.bitnami.com/bitnami
helm upgrade --install redis bitnami/redis -n incidentgpt --create-namespace \
--set architecture=standalone --set auth.enabled=false --set master.persistence.enabled=falseСервис будет redis-master.incidentgpt.svc.cluster.local:6379 → это redisAddr.
# ai-worker
cd ai-worker
docker buildx build --platform linux/amd64 -t <твой-registry>/ai-worker:latest . --push
# enricher
cd ../enricher
docker buildx build --platform linux/amd64 -t <твой-registry>/incident-enricher:latest . --pushimage:
repository: <твой-registry>/ai-worker
tag: latest
imagePullSecret: "" # имя secret'а, если registry приватный
env:
OPENROUTER_API_KEY: "<ключ OpenRouter>" # 🔑
OPENROUTER_MODEL: "google/gemini-2.5-flash"
OPENROUTER_MAX_TOKENS: "2000"
TELEGRAM_BOT_TOKEN: "<токен бота>" # 🔑
TELEGRAM_CHANNEL_ID: "-100..." # id канала
TELEGRAM_THREAD_CHAT_ID: "-100..." # id группы обсужденияhelm upgrade -i ai-worker ./ai-worker/chart -n incidentgptimage:
repository: <твой-registry>/incident-enricher
tag: latest
env:
prometheusUrl: "http://<release>-kube-prometheus-stack-prometheus.monitoring:9090"
clusterName: "my-cluster"
redisAddr: "redis-master.incidentgpt.svc.cluster.local:6379"
# groupBackendUrl / rawBackendUrl уже указывают на ai-worker внутри кластера
corrWindow: "10m" # сколько держим группу в Redis
corrSettle: "40s" # сколько ждём следствий каскада перед склейкой
runbookBaseUrl: "" # опц. база ссылок на runbook: <url>/<alertname>Секция metrics: в этом же файле — какие PromQL enricher гоняет при обогащении.
Это примеры, адаптируй под свои метрики (см. комментарии в файле).
helm upgrade -i incidentgpt-enricher ./enricher/chart/incidentgpt-enricher -n incidentgptAlertmanager должен слать webhook на enricher (порт 9099, путь /alert). В values kube-prometheus-stack:
alertmanager:
config:
route:
receiver: incident-enricher
routes:
- receiver: incident-enricher
continue: true # чтобы алерты шли и сюда, и в другие ресиверы
receivers:
- name: incident-enricher
webhook_configs:
- url: "http://incidentgpt-enricher.incidentgpt.svc:9099/alert"
send_resolved: true
⚠️ Имя вroute.receiverдолжно точно совпадать сreceivers[].name. Для быстрого отклика на демо можно снизитьgroup_waitроута до2s.
kubectl port-forward -n incidentgpt deploy/incidentgpt-enricher 9099:9099
curl -s -XPOST -H "Content-Type: application/json" localhost:9099/alert -d '{
"receiver":"test","status":"firing",
"alerts":[{
"status":"firing",
"labels":{"alertname":"SyntheticHighCPU","severity":"critical","namespace":"monitoring","pod":"synthetic-pod-1","node":"worker-1","instance":"10.0.0.1:9100"},
"annotations":{"summary":"Synthetic test alert","expr":"sum(rate(container_cpu_usage_seconds_total{namespace=\"monitoring\"}[5m]))"},
"startsAt":"2025-11-25T10:00:00Z","endsAt":"0001-01-01T00:00:00Z"
}]
}'(больше примеров — в enricher/check_pod)
Заведи правило, которое гарантированно сработает (пример — ai-worker/cpu-alert.yaml),
и Alertmanager сам прогонит его через enricher.
kubectl apply -f ai-worker/cpu-alert.yaml # PrometheusRule с меткой release: <твой-release>Метка
release:в PrometheusRule должна совпадать с тем, что селектит твой Prometheus, иначе правило не подхватится.
Пример payload — в ai-worker/readme (POST на /incident).
kubectl logs -f -n incidentgpt deploy/incidentgpt-enricher # ALERT_RAW / ALERT_ENRICHED / ALERT_BUFFERED / GROUP_SENT
kubectl logs -f -n incidentgpt deploy/ai-worker # got incident / raw alert posted / group analysis postedСвязывание — грубое и детерминированное: алерты одного namespace, пришедшие в
окно CORR_SETTLE, считаются одним инцидентом.
- Каждый алерт enricher кладёт в Redis-хеш
grp:{namespace}(поле = fingerprint). - Первый алерт группы заводит debounce-таймер на
CORR_SETTLEсекунд. - Пока таймер идёт, любой алерт того же namespace добавляется в тот же хеш.
- Таймер сработал → вся группа уходит пачкой в ai-worker → один вызов LLM.
- Корень внутри группы определяет уже модель (не связывание).
Чего тут НЕТ: графа зависимостей. «Связаны» = один namespace + окно. Может ложно склеить несвязанное; не ловит кросс-namespace каскады. Зато дёшево и работает для каскадов внутри namespace (упала БД → сервисы того же ns посыпались).
Весь модуль корреляции — enricher/correlation.go (~200 строк, 6 функций).
ai-worker/ # Go: промпт, вызов LLM, постинг в Telegram
main.go
chart/ # Helm-чарт ai-worker
enricher/ # Go: обогащение + корреляция
main.go # приём алерта, обогащение метриками/K8s
correlation.go # Redis-буфер, окно, склейка группы
k8s.go # походы в Kubernetes API
chart/ # Helm-чарт enricher (+ секция metrics в values)
| Переменная | Дефолт | Смысл |
|---|---|---|
PROM_RANGE_BEFORE / PROM_RANGE_AFTER |
15m / 5m |
окно метрик вокруг startsAt алерта |
CORR_WINDOW |
10m |
TTL группы в Redis |
CORR_SETTLE |
40s |
сколько ждать следствий каскада перед склейкой |
RAW_DEDUP_TTL |
2m |
антиспам raw-фида: повторный fingerprint не постится в Telegram в пределах TTL; 0s отключает |
REDIS_ADDR |
— | адрес Redis (...-master) |
RUNBOOK_BASE_URL |
"" |
база ссылок на runbook; пусто → не добавляется |
ENRICH_*_CONTEXT |
true |
какие слои контекста собирать |
MIT (см. LICENSE).