Маршрутизация Alertmanager в Telegram и Mattermost
Published: 2026-03-04
Alertmanager отправляет алерты кластера в два места: в Telegram — чтобы видеть их с телефона, в Mattermost — для командных тредов. Оба канала используют один кастомный Go-шаблон, встроенный прямо в values HelmRelease kube-prom-stack. Ключевое архитектурное решение: Alertmanager не обращается к Telegram или Mattermost напрямую — он отправляет всё в abot, небольшой fan-out-прокси.
Шаблон для Telegram
Шаблон живёт в alertmanager.templateFiles внутри values HelmRelease:
{{- define "telegram.default.message" -}}
{{- $alert := index .Alerts 0 -}}
{{- $desc := reReplaceAll ",?\\s*timestamp:.*$" "" $alert.Annotations.description -}}
{{- if eq .Status "resolved" }}✅
{{- else }}
{{- with .CommonLabels.severity }}
{{- if eq . "critical" }}🔴
{{- else if eq . "warning" }}🟡
{{- else if eq . "info" }}🔵
{{- else }}🟠{{ end }}
{{- else }}🔴{{ end }}
{{- end }} · {{ .CommonLabels.severity | toUpper }} · <a href="{{ .CommonLabels.grafana_url }}">dashboard</a>
🖥 {{ .CommonLabels.cluster }}
📦 {{ .CommonLabels.namespace }}{{ if .CommonLabels.pod }} · {{ .CommonLabels.pod }}{{ end }}
💬 {{ $desc }}
⏰ {{ $alert.StartsAt.Local.Format "2006-01-02 15:04:05" }}
{{- end -}}
Ключевые детали:
$descубирает суффиксыtimestamp: ...из описаний — Alertmanager иногда добавляет их из лейблов.- Severity отображается цветным эмодзи: 🔴 critical, 🟡 warning, 🔵 info, 🟠 остальное.
- Лейбл
clusterдобавляется черезexternalLabelsв spec Prometheus — каждый алерт знает, откуда пришёл. - Resolved-алерты всегда получают ✅ независимо от severity.
Шаблон для Mattermost
Идентичная логика, но Markdown вместо HTML:
{{- define "mattermost.default.message" -}}
{{- $alert := index .Alerts 0 -}}
{{- $desc := reReplaceAll ",?\\s*timestamp:.*$" "" $alert.Annotations.description -}}
{{- if eq .Status "resolved" }}✅{{ else }}
{{- with .CommonLabels.severity }}
{{- if eq . "critical" }}🔴{{ else if eq . "warning" }}🟡{{ else }}🔵{{ end }}
{{- end }}
{{- end }} **{{ .CommonLabels.severity | toUpper }}** · [dashboard]({{ .CommonLabels.grafana_url }})
🖥 `{{ .CommonLabels.cluster }}`
📦 `{{ .CommonLabels.namespace }}`
💬 {{ $desc }}
{{- end -}}
Настройка receiver
yamlalertmanager:
enabled: true
config:
global:
resolve_timeout: 5m
route:
group_by: [cluster, namespace, alertname]
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: telegram-mattermost
routes:
- matchers:
- severity = "critical"
repeat_interval: 1h
receiver: telegram-mattermost
- matchers:
- alertname = "Watchdog"
receiver: "null" # подавить heartbeat-алерт
receivers:
- name: "null"
- name: telegram-mattermost
webhook_configs:
- url: "http://abot.observability.svc.cluster.local:9444/alert"
send_resolved: true
Почему abot, а не нативные вебхуки
У нативного Telegram-ресивера Alertmanager есть ограничения, а Mattermost требует специфического формата. Что даёт abot:
- Единую точку входа для Alertmanager — не нужно N ресиверов на N бэкендов.
- Fan-out в Telegram, Mattermost и куда угодно ещё.
- Полный контроль над форматом сообщений через Go-шаблоны.
- Повторные отправки с экспоненциальной задержкой.
- Работу через HTTP-прокси — on-prem-кластеры не могут напрямую достучаться до
api.telegram.org.
Прокси для Telegram
On-prem-кластеры отправляют запросы к Telegram через внутренний HTTP-прокси:
yamlconfig:
telegram:
botToken: "..."
chatId: "-100..."
proxy: "http://10.16.91.109:3128"
mattermost:
webhookUrl: "https://mattermost.internal/hooks/..."
Прокси — это внутренний tinyproxy, который пробрасывает запросы во внешний интернет.
group_by и repeat_interval
group_by: [cluster, namespace, alertname] гарантирует, что алерты из разных кластеров образуют отдельные группы. Без cluster в group_by флаппинг алерта на dev может подавить тот же алерт на prod.
| Настройка | Значение | Смысл |
|---|---|---|
group_wait |
30s | Собрать связанные алерты перед отправкой |
group_interval |
5m | Минимальный интервал между обновлениями группы |
repeat_interval (warning) |
4h | Не спамить ночью |
repeat_interval (critical) |
1h | Напоминать каждый час, пока не решено |
Тестирование маршрутизации
bash# Отправить тестовый алерт
curl -X POST http://alertmanager.observability.svc.cluster.local:9093/api/v2/alerts \
-H 'Content-Type: application/json' \
-d '[{
"labels": {
"alertname": "TestAlert",
"severity": "warning",
"cluster": "dev",
"namespace": "app"
},
"annotations": {
"description": "Тестовый алерт"
}
}]'
# Логи abot
kubectl logs -n observability deployment/abot --context=dev-k8s -f
# Активные алерты
kubectl port-forward -n observability svc/alertmanager 9093:9093
curl -s http://localhost:9093/api/v2/alerts | jq '[.[] | {name: .labels.alertname, status: .status.state}]'
Типичные проблемы
Алерты не приходят:
group_wait— первый алерт ждёт 30 секунд перед отправкой.repeat_interval— если алерт уже отправлялся, он молчит до истечения интервала.- Inhibition rules — critical может подавлять warning от того же компонента.
Resolved не приходят:
- В
webhook_configдолжен стоятьsend_resolved: true.
Ошибки шаблона:
- Проверяйте конфигурацию командой
alertmanager --check-config.