fix(ops): убрать Watchdog из человеческой ленты и добавить эскалацию по длительности
All checks were successful
CI Trade-In / changes (pull_request) Successful in 18s
CI / changes (pull_request) Successful in 22s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m54s
CI / backend-tests (pull_request) Successful in 7m37s
All checks were successful
CI Trade-In / changes (pull_request) Successful in 18s
CI / changes (pull_request) Successful in 22s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m54s
CI / backend-tests (pull_request) Successful in 7m37s
Watchdog горел всегда по построению (10080/10080 интервалов за 7 суток) и раньше слался в Telegram раз в 12ч — 14 сообщений в неделю ни о чём. Это приучило пролистывать инфра-тему, и 17.09 настоящий DiskWillFillIn24h утонул между Watchdog и вечно горящим NoActiveCeleryWorkers: диск дошёл до 84% незамеченным. Watchdog переведён на внешний deadman-приёмник (webhook на METRICS_WATCHDOG_PING_URL, healthchecks.io-подобный сервис) вместо Telegram: человек не получает ни одного штатного сообщения, а живость канала по-прежнему подтверждается регулярным HTTP-пингом. Секрет на хосте пока не заведён — деплой не трогаем, добавление аккаунта и значения переменной на проде остаётся ручным шагом. Новое правило Prometheus AlertFiringTooLong эскалирует любую тревогу, непрерывно firing дольше 6 часов (кроме Watchdog и себя самой — оба исключения обязательны, иначе получаем второй вечный сигнал). Маршрутизирована в клиентскую тему (алертменеджер матчит по alertname, не по host — исходная тревога может быть про любой хост). Затронуты также два backend-теста ops/ (test_3078_alert_topic.py, test_3078_oncall_route.py), проверявших структуру шаблона Alertmanager — обновлены под новую форму (один прямой Telegram-получатель вместо двух, маркер watchdog-ping вместо telegram-heartbeat).
This commit is contained in:
parent
9b818c2029
commit
dc9a93c048
5 changed files with 188 additions and 30 deletions
|
|
@ -21,8 +21,9 @@
|
|||
переменной — и инфраструктурная тема «метрики» оставалась пустой, а весь трафик,
|
||||
и клиентский, и инфраструктурный, копился в теме «алерты». Владелец решил
|
||||
развести: инфраструктура — в «метрики», клиентские инциденты — в «алерты».
|
||||
Тесты ниже закрепляют именно это: `telegram`/`telegram-heartbeat` получают
|
||||
ИНФРАСТРУКТУРНУЮ тему, а не общую.
|
||||
Тест ниже закрепляет именно это для единственного оставшегося прямого
|
||||
получателя — `telegram` (Watchdog с 17.09 больше не шлёт в Telegram вовсе,
|
||||
у него теперь внешний webhook-приёмник `watchdog-ping`, см. шаблон).
|
||||
|
||||
Тесты рендерят шаблон обоими способами и разбирают результат как YAML —
|
||||
проверяется фактический конфиг, а не наличие нужных слов в тексте.
|
||||
|
|
@ -59,6 +60,7 @@ def _render(infra_topic_line: str) -> dict:
|
|||
text.replace("${METRICS_TELEGRAM_BOT_TOKEN}", "123:ABC")
|
||||
.replace("${METRICS_TELEGRAM_CHAT_ID}", "-100123")
|
||||
.replace("${METRICS_TELEGRAM_INFRA_TOPIC_LINE}", infra_topic_line)
|
||||
.replace("${METRICS_WATCHDOG_PING_URL}", "https://hc-ping.com/dummy")
|
||||
)
|
||||
assert "${" not in rendered, (
|
||||
"в отрендеренном конфиге остался литерал плейсхолдера — "
|
||||
|
|
@ -76,16 +78,16 @@ def _telegram_configs(cfg: dict) -> list[dict]:
|
|||
|
||||
|
||||
def test_topic_lands_in_every_telegram_receiver() -> None:
|
||||
"""Оба прямых получателя адресуют ИНФРАСТРУКТУРНУЮ тему, а не клиентскую (#3163).
|
||||
"""Единственный прямой получатель адресует ИНФРАСТРУКТУРНУЮ тему, а не клиентскую (#3163).
|
||||
|
||||
Получателей два — `telegram` и `telegram-heartbeat`. До разделения тем оба
|
||||
брали топик из одной переменной с клиентскими инцидентами, и тема «метрики»
|
||||
(245) оставалась пустой. Если heartbeat уйдёт не в ту тему, «мониторинг жив»
|
||||
будет капать мимо, и это заметят не сразу — сюда же попадёт и весь
|
||||
инфраструктурный шум.
|
||||
До разделения тем `telegram` и `telegram-heartbeat` брали топик из одной
|
||||
переменной с клиентскими инцидентами, и тема «метрики» (245) оставалась
|
||||
пустой. С 17.09 (устранение шума Watchdog) прямой Telegram-получатель
|
||||
остался один — `telegram`; Watchdog теперь пингует внешний
|
||||
deadman-приёмник вебхуком (`watchdog-ping`, без topic вовсе — не Telegram).
|
||||
"""
|
||||
cfgs = _telegram_configs(_render(INFRA_TOPIC_LINE))
|
||||
assert len(cfgs) >= 2, f"ожидалось минимум два получателя telegram, найдено {len(cfgs)}"
|
||||
assert len(cfgs) >= 1, f"ожидался хотя бы один получатель telegram, найдено {len(cfgs)}"
|
||||
for c in cfgs:
|
||||
assert c.get("message_thread_id") == 245, f"инфраструктурный топик не проставлен: {c}"
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ def test_klientskiy_marshrut_idyot_v_servis_knopki() -> None:
|
|||
"""
|
||||
text = TEMPLATE.read_text(encoding="utf-8")
|
||||
block = text[text.index("- name: telegram-clients") :]
|
||||
block = block[: block.index("- name: telegram-heartbeat")]
|
||||
block = block[: block.index("- name: watchdog-ping")]
|
||||
assert "webhook_configs" in block, "клиентский приёмник не переключён на сервис"
|
||||
assert "alert-ack:8080/alertmanager" in block, "вебхук указывает не на сервис кнопки"
|
||||
# У прочих приёмников прямой путь сохранён.
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@
|
|||
# клиентские инциденты МЕРЫ) и тему «метрики» (245, инфраструктурный шум —
|
||||
# диск, память, просевший экспортер). Инфраструктурный шум и клиентский
|
||||
# инцидент не равны по срочности, а смешанные в одной теме они обучают
|
||||
# пролистывать обе. Поэтому `telegram` и `telegram-heartbeat` ниже адресуют
|
||||
# ИНФРАСТРУКТУРНУЮ тему (`METRICS_TELEGRAM_INFRA_TOPIC_LINE`). Получателя
|
||||
# `telegram-clients` в этом списке нет: он не шлёт в Telegram напрямую, а
|
||||
# вебхуком уходит в alert-ack, и тему адресует сам, своей переменной
|
||||
# METRICS_TELEGRAM_TOPIC_ID.
|
||||
# пролистывать обе. Поэтому `telegram` ниже адресует ИНФРАСТРУКТУРНУЮ тему
|
||||
# (`METRICS_TELEGRAM_INFRA_TOPIC_LINE`). Получателя `telegram-clients` в этом
|
||||
# списке нет: он не шлёт в Telegram напрямую, а вебхуком уходит в alert-ack, и
|
||||
# тему адресует сам, своей переменной METRICS_TELEGRAM_TOPIC_ID. `watchdog-ping`
|
||||
# тоже не шлёт в Telegram вовсе — см. комментарий у его маршрута ниже.
|
||||
|
||||
global:
|
||||
resolve_timeout: 5m
|
||||
|
|
@ -35,14 +35,35 @@ route:
|
|||
repeat_interval: 6h
|
||||
|
||||
routes:
|
||||
# Watchdog не должен смешиваться с настоящими алертами и не должен молчать:
|
||||
# это «сторож сторожа», он горит всегда и подтверждает, что канал доставки жив.
|
||||
- receiver: telegram-heartbeat
|
||||
# Watchdog — «сторож сторожа», горит ВСЕГДА по построению (`vector(1)`,
|
||||
# см. infra.yml). Раньше уходил в Telegram раз в 12ч — 14 сообщений в
|
||||
# неделю ни о чём, и именно они приучили пролистывать инфра-тему: 17.09
|
||||
# настоящий DiskWillFillIn24h утонул между Watchdog и вечно горящим
|
||||
# NoActiveCeleryWorkers, диск дошёл до 84% незамеченным.
|
||||
#
|
||||
# ПОЧЕМУ ВНЕШНИЙ DEADMAN-ПРИЁМНИК, А НЕ «РЕЖЕ» И НЕ ОТДЕЛЬНАЯ ТЕМА.
|
||||
# Увеличенный интервал по-прежнему кладёт человеку регулярное сообщение —
|
||||
# просто реже, и его тоже рано или поздно начнут пролистывать. Отдельная
|
||||
# техническая тема — это ещё один chat_id/topic_id и ещё один канал,
|
||||
# за которым НАДО СПЕЦИАЛЬНО следить, то есть тот же человеческий цикл,
|
||||
# сдвинутый в другое место. Внешний deadman-приёмник (healthchecks.io и
|
||||
# аналоги) устроен наоборот: Alertmanager молча шлёт HTTP-пинг на каждый
|
||||
# Watchdog, и пока пинги идут — сервис МОЛЧИТ. Он заговорит (email/свой
|
||||
# alert) только когда пинг ПЕРЕСТАНЕТ приходить, то есть ровно когда
|
||||
# канал доставки умер, — это и есть смысл «сторожа сторожа», без единого
|
||||
# штатного сообщения человеку. Полностью выключать эту проверку нельзя —
|
||||
# remove бы всей ветки Watchdog это и сделал.
|
||||
#
|
||||
# METRICS_WATCHDOG_PING_URL пока НЕ заведён на хосте (нужен аккаунт
|
||||
# healthchecks.io/аналога) — до тех пор webhook будет молча падать по
|
||||
# DNS/сети, Alertmanager это тихо ретраит; человека это не касается ни
|
||||
# раньше, ни теперь.
|
||||
- receiver: watchdog-ping
|
||||
matchers:
|
||||
- alertname = "Watchdog"
|
||||
group_wait: 0s
|
||||
group_interval: 12h
|
||||
repeat_interval: 12h
|
||||
group_interval: 5m
|
||||
repeat_interval: 5m
|
||||
|
||||
# Клиентский инцидент. host="apps" — это продуктовая машина: если на ней
|
||||
# критично, значит МЕРА и Site Finder недоступны людям, а не «где-то в
|
||||
|
|
@ -63,6 +84,20 @@ route:
|
|||
group_wait: 10s
|
||||
repeat_interval: 30m
|
||||
|
||||
# Эскалация по длительности (AlertFiringTooLong, prometheus/rules/infra.yml)
|
||||
# — сигнал о том, что какую-то другую тревогу не заметили или на неё
|
||||
# забили дольше 6 часов. Она НЕ про клиентский инцидент, но обязана быть
|
||||
# заметнее обычной инфраструктуры, поэтому уходит в ту же тему, где
|
||||
# владелец бывает чаще, а не смешивается с общим потоком severity=critical
|
||||
# ниже. Матчим по имени, а не по host="apps": исходная тревога может
|
||||
# быть про любой хост, и врать в лейбле не стоит (см. инвариант host
|
||||
# у alert:app в infra.yml).
|
||||
- receiver: telegram-clients
|
||||
matchers:
|
||||
- alertname = "AlertFiringTooLong"
|
||||
group_wait: 10s
|
||||
repeat_interval: 30m
|
||||
|
||||
# Прочее критичное — инфраструктура, клиенты пока не затронуты.
|
||||
- receiver: telegram
|
||||
matchers:
|
||||
|
|
@ -117,14 +152,12 @@ ${METRICS_TELEGRAM_INFRA_TOPIC_LINE}
|
|||
- url: "http://alert-ack:8080/alertmanager"
|
||||
send_resolved: true
|
||||
|
||||
- name: telegram-heartbeat
|
||||
telegram_configs:
|
||||
- bot_token: "${METRICS_TELEGRAM_BOT_TOKEN}"
|
||||
chat_id: ${METRICS_TELEGRAM_CHAT_ID}
|
||||
${METRICS_TELEGRAM_INFRA_TOPIC_LINE}
|
||||
api_url: "https://api.telegram.org"
|
||||
parse_mode: HTML
|
||||
# Внешний deadman-приёмник вместо Telegram — см. комментарий у маршрута
|
||||
# Watchdog выше. URL — HTTPS-пинг вида healthchecks.io/ping/<uuid>,
|
||||
# значение живёт в METRICS_WATCHDOG_PING_URL на хосте (как и остальные
|
||||
# METRICS_* секреты, в git не попадает). `send_resolved: false`: сервису
|
||||
# не нужно resolved-событие, важен сам факт регулярного POST.
|
||||
- name: watchdog-ping
|
||||
webhook_configs:
|
||||
- url: "${METRICS_WATCHDOG_PING_URL}"
|
||||
send_resolved: false
|
||||
message: |
|
||||
⚪ <b>Мониторинг жив</b> — сторож отчитался, канал доставки работает.
|
||||
Если это сообщение перестало приходить дважды подряд, замолчал сам мониторинг.
|
||||
|
|
|
|||
|
|
@ -442,3 +442,44 @@ groups:
|
|||
annotations:
|
||||
summary: "WAL пишется быстрее 100 МБ/час"
|
||||
description: "{{ $labels.host }} / {{ $labels.db }}: {{ $value | humanize1024 }}B/с. Стоит сверить с реальной пользовательской нагрузкой — расхождение означает лишние записи."
|
||||
|
||||
# ── Эскалация ──────────────────────────────────────────────────────────────
|
||||
# Симптом 17.09: DiskWillFillIn24h пришёл вовремя и утонул между Watchdog
|
||||
# (10080 интервалов firing за 7 суток — горит всегда по построению) и
|
||||
# NoActiveCeleryWorkers (6229 интервалов) в общей ленте; диск дошёл до 84%
|
||||
# незамеченным. Alertmanager сам по длительности не эскалирует — это
|
||||
# правило Prometheus поверх служебной метрики ALERTS_FOR_STATE (unix-время
|
||||
# входа тревоги в pending/firing, см. Robust Perception "The
|
||||
# ALERTS_FOR_STATE metric").
|
||||
- name: escalation
|
||||
interval: 60s
|
||||
rules:
|
||||
# ИСКЛЮЧЕНИЯ В `alertname!~` ОБЯЗАТЕЛЬНЫ, а не для порядка:
|
||||
# - Watchdog горит всегда по построению (`vector(1)` выше) — без
|
||||
# исключения это правило унаследовало бы его вечный firing и стало
|
||||
# ВТОРЫМ таким сигналом, то есть тем самым шумом, который лечим.
|
||||
# - Сама AlertFiringTooLong — иначе, однажды сработав, она бы никогда
|
||||
# не погасла: собственная ALERTS_FOR_STATE тоже старше порога, и
|
||||
# правило продлевало бы себя бесконечно.
|
||||
# Что это НЕ значит: если NoActiveCeleryWorkers (её чинит параллельная
|
||||
# правка, здесь не трогаем) продолжит гореть дольше 6 часов, эта
|
||||
# тревога сработает сразу после мержа — это ожидаемо и верно: она
|
||||
# огонь реального незакрытого инцидента, а не вечная по построению.
|
||||
#
|
||||
# `label_replace(..., "stuck_alertname", "$1", "alertname", "(.+)")`
|
||||
# ОБЯЗАТЕЛЕН, а не косметика: `alertname` — зарезервированный лейбл,
|
||||
# Prometheus молча перезаписывает его именем ЭТОГО правила
|
||||
# (AlertFiringTooLong) на выходе, каким бы ни было значение в expr.
|
||||
# Без копии в `stuck_alertname` текст сообщения называл бы саму себя
|
||||
# виновником, а не исходную тревогу.
|
||||
- alert: AlertFiringTooLong
|
||||
expr: |
|
||||
label_replace(
|
||||
(time() - ALERTS_FOR_STATE{alertname!~"Watchdog|AlertFiringTooLong"}) > 6*3600,
|
||||
"stuck_alertname", "$1", "alertname", "(.+)"
|
||||
)
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Тревога держится дольше 6 часов"
|
||||
description: "{{ $labels.stuck_alertname }}{{ if $labels.host }} ({{ $labels.host }}){{ end }} непрерывно firing больше 6 часов — похоже, её не заметили или на неё забили."
|
||||
|
|
|
|||
|
|
@ -170,3 +170,85 @@ tests:
|
|||
exp_annotations:
|
||||
summary: "Бэкенд «Меры» не отвечает"
|
||||
description: "Агент на Poincare 5 минут не может снять /metrics с tradein-backend (up=0), либо цель пропала из скрейпа. Проверь `docker ps` и /health изнутри сети. Лэндинг meraocenka.ru может открываться из кэша и при мёртвом бэкенде — это не признак жизни."
|
||||
|
||||
# Эскалация по длительности. `promtool test rules` держит одну общую шкалу
|
||||
# времени и TSDB на весь файл — к 6.5 часам к этому моменту «зависшими»
|
||||
# (input series предыдущих сценариев кончились, но absent()-условия по ним
|
||||
# продолжают гореть) оказываются и другие тестовые тревоги файла, не только
|
||||
# NoActiveCeleryWorkers из этого блока. Список ниже — ровно то, что
|
||||
# реально вернул promtool (проверено запуском, не придумано): 8 тревог,
|
||||
# держащихся дольше 6 часов. ГЛАВНАЯ ПРОВЕРКА в этом списке — то, чего в
|
||||
# нём НЕТ: ни Watchdog (горит вечно с t=0 точно так же, но исключён
|
||||
# матчером), ни сама AlertFiringTooLong (иначе была бы там на восьмое
|
||||
# место и продлевала бы себя бесконечно). В 3 часа — рано, эскалации
|
||||
# ещё быть не должно вовсе.
|
||||
- interval: 1m
|
||||
input_series:
|
||||
- series: 'up{job="celery",host="apps"}'
|
||||
values: '1x420'
|
||||
alert_rule_test:
|
||||
- eval_time: 3h
|
||||
alertname: AlertFiringTooLong
|
||||
exp_alerts: []
|
||||
- eval_time: 6h30m
|
||||
alertname: AlertFiringTooLong
|
||||
exp_alerts:
|
||||
- exp_labels:
|
||||
severity: critical
|
||||
stuck_alertname: MeraBackendDown
|
||||
host: apps
|
||||
job: app
|
||||
app: mera
|
||||
exp_annotations:
|
||||
summary: "Тревога держится дольше 6 часов"
|
||||
description: "MeraBackendDown (apps) непрерывно firing больше 6 часов — похоже, её не заметили или на неё забили."
|
||||
- exp_labels:
|
||||
severity: critical
|
||||
stuck_alertname: HostAgentDown
|
||||
host: apps
|
||||
job: node
|
||||
exp_annotations:
|
||||
summary: "Тревога держится дольше 6 часов"
|
||||
description: "HostAgentDown (apps) непрерывно firing больше 6 часов — похоже, её не заметили или на неё забили."
|
||||
- exp_labels:
|
||||
severity: critical
|
||||
stuck_alertname: RemoteWriteStalled
|
||||
host: apps
|
||||
job: node
|
||||
exp_annotations:
|
||||
summary: "Тревога держится дольше 6 часов"
|
||||
description: "RemoteWriteStalled (apps) непрерывно firing больше 6 часов — похоже, её не заметили или на неё забили."
|
||||
- exp_labels:
|
||||
severity: critical
|
||||
stuck_alertname: CadvisorDown
|
||||
job: cadvisor
|
||||
exp_annotations:
|
||||
summary: "Тревога держится дольше 6 часов"
|
||||
description: "CadvisorDown непрерывно firing больше 6 часов — похоже, её не заметили или на неё забили."
|
||||
- exp_labels:
|
||||
severity: critical
|
||||
stuck_alertname: QueueExporterDown
|
||||
job: redis
|
||||
exp_annotations:
|
||||
summary: "Тревога держится дольше 6 часов"
|
||||
description: "QueueExporterDown непрерывно firing больше 6 часов — похоже, её не заметили или на неё забили."
|
||||
- exp_labels:
|
||||
severity: critical
|
||||
stuck_alertname: TradeInBackgroundContainerMissing
|
||||
name: tradein-scraper
|
||||
exp_annotations:
|
||||
summary: "Тревога держится дольше 6 часов"
|
||||
description: "TradeInBackgroundContainerMissing непрерывно firing больше 6 часов — похоже, её не заметили или на неё забили."
|
||||
- exp_labels:
|
||||
severity: critical
|
||||
stuck_alertname: TradeInBackgroundContainerMissing
|
||||
name: tradein-tgbot
|
||||
exp_annotations:
|
||||
summary: "Тревога держится дольше 6 часов"
|
||||
description: "TradeInBackgroundContainerMissing непрерывно firing больше 6 часов — похоже, её не заметили или на неё забили."
|
||||
- exp_labels:
|
||||
severity: critical
|
||||
stuck_alertname: NoActiveCeleryWorkers
|
||||
exp_annotations:
|
||||
summary: "Тревога держится дольше 6 часов"
|
||||
description: "NoActiveCeleryWorkers непрерывно firing больше 6 часов — похоже, её не заметили или на неё забили."
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue