gendesign/ops/metrics/alertmanager/alertmanager.yml.tmpl
bot-backend 5b7ef161e3
All checks were successful
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m2s
CI / backend-tests (pull_request) Successful in 17m24s
feat(observability): алерты адресуются в топик форумной группы (#3078)
Бот, которым шлются тревоги, — тот же, что пересылает сообщения поддержки, а
его чат форумный. Без message_thread_id Alertmanager кладёт тревоги в общую
тему, вперемешку с клиентской перепиской.

Поле поддерживается: проверено amtool check-config на том же образе, что
поднимается в проде (prom/alertmanager:v0.28.0). Схема Alertmanager строгая и
неизвестные поля отвергает, так что успешная проверка означает именно
поддержку, а не молчаливое игнорирование.

Подставляется ЦЕЛАЯ СТРОКА, а не значение: envsubst не умеет условий, и при
шаблоне вида `message_thread_id: ${TOPIC_ID}` незаданный топик дал бы
`message_thread_id:` без значения. Это не деградация - Alertmanager с таким
конфигом не стартует вовсе, то есть алертинг исчезает целиком. Деплой
формирует либо всю строку с отступом, либо пустую.

Топик необязателен: без него поле отсутствует, алерты уходят в общую тему,
поведение прежнее.

Попутно добавлена проверка конфига через amtool ДО подъёма стека - по образцу
`caddy validate` ниже в этом же файле. amtool берётся из того же образа, что и
сам Alertmanager, иначе проверялась бы не та версия схемы. Битый конфиг теперь
роняет деплой громко, а не выключает алертинг тихо.

Тесты (4) рендерят шаблон обоими способами и разбирают результат как YAML -
проверяется фактический конфиг, а не наличие нужных слов в тексте. Отдельно
проверено, что переменная объявлена в списке envsubst: забыть её - значит
оставить в конфиге литерал плейсхолдера.

Фальсификация: на исходных файлах краснеют 3 из 4; проходит только тест,
фиксирующий сохранённое поведение при незаданном топике. tests/ops целиком -
35 passed.
2026-08-26 14:10:56 +03:00

76 lines
3.7 KiB
Cheetah
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Шаблон конфигурации Alertmanager. Боевой файл собирается на хосте при деплое
# (`envsubst` в deploy-metrics.yml) и в репозиторий не попадает — токен бота и
# идентификатор чата живут в окружении хоста, а не в git.
#
# ПОЧЕМУ TELEGRAM НАПРЯМУЮ, А НЕ ЧЕРЕЗ БЭКЕНД МЕРЫ. У МЕРЫ уже есть рабочий путь
# доставки (вебхук GlitchTip → бот), и соблазн переиспользовать его велик. Но тогда
# сообщение о том, что лёг Poincare, шло бы через сервис НА Poincare. Алерт обязан
# уметь уйти без участия хоста, про который он написан.
#
# Telegram с Beget работает — проверено серией замеров 25.08: TLS-рукопожатие
# 18 из 20, getMe 4 из 4. Ломается только длинный long-poll (30 с), а отправка
# сообщения — короткий запрос.
global:
resolve_timeout: 5m
route:
receiver: telegram
# Группируем по алерту и хосту: пятнадцать контейнеров одного хоста, упавших
# разом, — это одно событие, а не пятнадцать сообщений.
group_by: ["alertname", "host"]
group_wait: 45s
group_interval: 5m
# Повтор раз в 6 часов. Чаще — приучает игнорировать, реже — можно проспать.
repeat_interval: 6h
routes:
# Watchdog не должен смешиваться с настоящими алертами и не должен молчать:
# это «сторож сторожа», он горит всегда и подтверждает, что канал доставки жив.
- receiver: telegram-heartbeat
matchers:
- alertname = "Watchdog"
group_wait: 0s
group_interval: 12h
repeat_interval: 12h
# Критичное — без задержки на группировку.
- receiver: telegram
matchers:
- severity = "critical"
group_wait: 10s
repeat_interval: 3h
inhibit_rules:
# Если хост целиком недоступен, не сыпать отдельно про каждый его сервис.
- source_matchers: [alertname = "HostAgentDown"]
target_matchers: [severity =~ "warning|critical"]
equal: ["host"]
receivers:
- name: telegram
telegram_configs:
- bot_token: "${METRICS_TELEGRAM_BOT_TOKEN}"
chat_id: ${METRICS_TELEGRAM_CHAT_ID}
${METRICS_TELEGRAM_TOPIC_LINE}
api_url: "https://api.telegram.org"
parse_mode: HTML
send_resolved: true
message: |
{{ if eq .Status "firing" }}🔴{{ else }}🟢{{ end }} <b>{{ .CommonLabels.alertname }}</b>{{ if .CommonLabels.host }} · {{ .CommonLabels.host }}{{ end }}
{{ range .Alerts }}
{{ .Annotations.summary }}
{{ if .Annotations.description }}{{ .Annotations.description }}{{ end }}
{{ end }}
- name: telegram-heartbeat
telegram_configs:
- bot_token: "${METRICS_TELEGRAM_BOT_TOKEN}"
chat_id: ${METRICS_TELEGRAM_CHAT_ID}
${METRICS_TELEGRAM_TOPIC_LINE}
api_url: "https://api.telegram.org"
parse_mode: HTML
send_resolved: false
message: |
⚪ <b>Мониторинг жив</b> — сторож отчитался, канал доставки работает.
Если это сообщение перестало приходить дважды подряд, замолчал сам мониторинг.