Мера: тревога, когда бэкенд лёг, а лэндинг ещё открывается из кэша #3553

Merged
bot-backend merged 1 commit from fix/mera-observability into main 2026-09-17 09:15:05 +00:00
Collaborator

Часть #2214 (эпик #2200). Issue этим PR не закрывается: остались два шага, которые делаются не кодом (см. ниже).

#2214: мёртвый бэкенд Меры не поднимал тревоги вне Poincare

Что было. Из четырёх пунктов приёмки три уже выполнены раньше и перепроверены 17.09:

  • проверка /health валит деплой: deploy-tradein.yml, после цикла стоит exit 1 (PR #2231);
  • лимиты памяти: на проде backend 768m, scraper 640m, postgres 3g, frontend 384m, browser 2560m;
  • ротация логов работает.

Открытым оставался пункт «внешняя проба на публичные эндпоинты Меры + алерт». Проверил 17.09, только чтение:

  • В GlitchTip на Beget шесть мониторов (select … from uptime_monitor). Из них к Мере относится один: id=6, https://meraocenka.ru/. Лэндинг отдаёт tradein-frontend из пререндер-кэша (x-nextjs-prerender: 1, x-nextjs-cache: HIT), поэтому при мёртвом бэкенде он всё равно отвечает 200.
  • Монитор id=2 на gendsgn.ru/health проверяет бэкенд Птицы: в apps.caddy handle /health ведёт на backend:8000.
  • AppHighErrorRate и AppHighLatencyP95 считаются по метрикам, которые отдаёт сам бэкенд. Когда бэкенд мёртв, метрик нет, и оба правила молчат.
  • TradeInBackgroundContainerMissing следит только за tgbot и scraper.
  • Серия up{job="app",app="mera",instance="tradein-backend:8000",host="apps"} в Prometheus на Beget есть, но правила на неё не было.

Итог: если хост и фронт живы, а бэкенд Меры упал или завис, вне Poincare не срабатывало ничего.

Что сделано. В ops/metrics/prometheus/rules/infra.yml, группа app, добавлено правило MeraBackendDown:

up{job="app", app="mera"} == 0 or absent(up{job="app", app="mera"})
for: 5m, severity: critical, host: apps
  • Пара critical + apps отправляет тревогу по маршруту telegram-clients: alert-ack на Beget, затем тема 158 в Telegram. Правило вычисляется и доставляется на Beget.
  • Если event loop завис, скрейп отваливается по таймауту 20s. Если контейнер удалён, скрейп получает ошибку DNS. В обоих случаях up=0.
  • absent() срабатывает, когда цель тихо пропала из alloy-apps.alloy.
  • Смерть всего хоста по-прежнему ловят HostAgentDown и RemoteWriteStalled.

Почему for: 5m. Выгрузил сырые точки up{app="mera"} за 26.08–17.09 (61 857 точек). Нулей было 140 серий, все короткие, это окна деплоя. Самая длинная серия: 4 нулевые точки подряд, около 2 минут до восстановления. 5 минут дают запас в 2.5 раза. Ни один из 140 провалов тревогу бы не поднял.

Чего правило не видит. Публичный путь до бэкенда (DNS, TLS, Caddy на meraocenka.ru) оно не проверяет, скрейп идёт изнутри docker-сети. Для лэндинга этот путь проверяет монитор 6.

Тесты

Добавлены три случая promtool test rules в ops/metrics/prometheus/tests/infra_test.yml. Деплой метрик прогоняет их перед reload.

  1. Бэкенд умер, агент жив: тревога с метками app=mera, instance=tradein-backend:8000.
  2. Окно деплоя (4 нулевые точки) и одновременно упавший бэкенд Птицы: тишина на 11-й и 30-й минуте.
  3. Цель пропала из скрейпа: тревога.

Прогон тем же образом, что на проде (prom/prometheus:v3.1.0):

  • promtool check rules rules/infra.yml: SUCCESS: 25 rules found, rc=0;
  • promtool test rules tests/infra_test.yml: SUCCESS, rc=0. Прежние случаи из #3493 тоже зелёные.

Проверки в CI для PR нет: deploy-metrics.yml гоняет promtool только на push в main.

Фальсификация

Каждый раз портил копию infra.yml, затем восстанавливал из копии (diff -q без различий) и получал зелёный прогон.

  • Убрал ветку absent():
    FAILED: alertname: MeraBackendDown, time: 10m, exp:[… app="mera", host="apps", job="app", severity="critical" …], got:[]   rc=1
    
  • for: 1m вместо 5m:
    FAILED: alertname: MeraBackendDown, time: 11m, exp:[], got:[ … instance="tradein-backend:8000" … ]   rc=1
    
  • Правило переименовано (фактически удалено): красные случаи на 15m и на 10m, got:[], rc=1.

Что остаётся в #2214 (делается не кодом)

  1. Внешний монитор на публичную ручку бэкенда. Завести в UI GlitchTip, проект Trade-In (id=3): GET https://meraocenka.ru/trade-in/api/public/mera/stats, ожидать 200, интервал 60 с. Эта ручка уже проксируется на meraocenka.ru и ходит в tradein-backend и БД: 17.09 ответила 200 за 0.2 с. Лимит 30 запросов в минуту с одного IP, минутный монитор в него укладывается. Caddy для этого менять не нужно.
  2. Один раз проверить доставку до человека. Увидеть сообщение в теме 158. Годятся тестовое уведомление GlitchTip или первая настоящая тревога MeraBackendDown.

Эпик #2200 закрывать рано: его последняя открытая дочерняя задача — #2214.

Приёмка на проде после мержа (дата проверки: не позже 19.09.2026)

  • Деплой метрик зелёный, в логе Prometheus: конфиг и правила проверены, reload подтверждён.
  • На Beget docker exec gendesign-prometheus wget -qO- 'http://localhost:9090/api/v1/rules?rule_name[]=MeraBackendDown' показывает правило с health: "ok" и state: "inactive".
  • Ближайший деплой tradein не переводит правило в firing: ALERTS{alertname="MeraBackendDown",alertstate="firing"} пуст.

Деплой: правка в ops/metrics/** запускает deploy-metrics.yml. Он делает штатный reload Prometheus и по своему обычному сценарию пересоздаёт alertmanager, alert-ack и tg-relay на Beget, а также alloy на обоих хостах. Контейнеры Меры (backend, scraper, browser, postgres) не трогаются, поэтому гейт по scrape_runs не нужен. Миграций нет.

🤖 Generated with Claude Code

Часть #2214 (эпик #2200). Issue этим PR **не закрывается**: остались два шага, которые делаются не кодом (см. ниже). ## #2214: мёртвый бэкенд Меры не поднимал тревоги вне Poincare **Что было.** Из четырёх пунктов приёмки три уже выполнены раньше и перепроверены 17.09: - проверка /health валит деплой: `deploy-tradein.yml`, после цикла стоит `exit 1` (PR #2231); - лимиты памяти: на проде backend 768m, scraper 640m, postgres 3g, frontend 384m, browser 2560m; - ротация логов работает. Открытым оставался пункт «внешняя проба на публичные эндпоинты Меры + алерт». Проверил 17.09, только чтение: - В GlitchTip на Beget шесть мониторов (`select … from uptime_monitor`). Из них к Мере относится один: id=6, `https://meraocenka.ru/`. Лэндинг отдаёт tradein-frontend из пререндер-кэша (`x-nextjs-prerender: 1`, `x-nextjs-cache: HIT`), поэтому при мёртвом бэкенде он всё равно отвечает 200. - Монитор id=2 на `gendsgn.ru/health` проверяет бэкенд Птицы: в apps.caddy `handle /health` ведёт на `backend:8000`. - `AppHighErrorRate` и `AppHighLatencyP95` считаются по метрикам, которые отдаёт сам бэкенд. Когда бэкенд мёртв, метрик нет, и оба правила молчат. - `TradeInBackgroundContainerMissing` следит только за tgbot и scraper. - Серия `up{job="app",app="mera",instance="tradein-backend:8000",host="apps"}` в Prometheus на Beget есть, но правила на неё не было. Итог: если хост и фронт живы, а бэкенд Меры упал или завис, вне Poincare не срабатывало ничего. **Что сделано.** В `ops/metrics/prometheus/rules/infra.yml`, группа `app`, добавлено правило `MeraBackendDown`: ``` up{job="app", app="mera"} == 0 or absent(up{job="app", app="mera"}) for: 5m, severity: critical, host: apps ``` - Пара `critical` + `apps` отправляет тревогу по маршруту `telegram-clients`: alert-ack на Beget, затем тема 158 в Telegram. Правило вычисляется и доставляется на Beget. - Если event loop завис, скрейп отваливается по таймауту 20s. Если контейнер удалён, скрейп получает ошибку DNS. В обоих случаях `up=0`. - `absent()` срабатывает, когда цель тихо пропала из `alloy-apps.alloy`. - Смерть всего хоста по-прежнему ловят HostAgentDown и RemoteWriteStalled. **Почему `for: 5m`.** Выгрузил сырые точки `up{app="mera"}` за 26.08–17.09 (61 857 точек). Нулей было 140 серий, все короткие, это окна деплоя. Самая длинная серия: 4 нулевые точки подряд, около 2 минут до восстановления. 5 минут дают запас в 2.5 раза. Ни один из 140 провалов тревогу бы не поднял. **Чего правило не видит.** Публичный путь до бэкенда (DNS, TLS, Caddy на meraocenka.ru) оно не проверяет, скрейп идёт изнутри docker-сети. Для лэндинга этот путь проверяет монитор 6. ## Тесты Добавлены три случая `promtool test rules` в `ops/metrics/prometheus/tests/infra_test.yml`. Деплой метрик прогоняет их перед reload. 1. Бэкенд умер, агент жив: тревога с метками `app=mera, instance=tradein-backend:8000`. 2. Окно деплоя (4 нулевые точки) и одновременно упавший бэкенд Птицы: тишина на 11-й и 30-й минуте. 3. Цель пропала из скрейпа: тревога. Прогон тем же образом, что на проде (`prom/prometheus:v3.1.0`): - `promtool check rules rules/infra.yml`: `SUCCESS: 25 rules found`, rc=0; - `promtool test rules tests/infra_test.yml`: `SUCCESS`, rc=0. Прежние случаи из #3493 тоже зелёные. Проверки в CI для PR нет: `deploy-metrics.yml` гоняет promtool только на push в main. ## Фальсификация Каждый раз портил копию `infra.yml`, затем восстанавливал из копии (`diff -q` без различий) и получал зелёный прогон. - **Убрал ветку `absent()`:** ``` FAILED: alertname: MeraBackendDown, time: 10m, exp:[… app="mera", host="apps", job="app", severity="critical" …], got:[] rc=1 ``` - **`for: 1m` вместо `5m`:** ``` FAILED: alertname: MeraBackendDown, time: 11m, exp:[], got:[ … instance="tradein-backend:8000" … ] rc=1 ``` - **Правило переименовано (фактически удалено):** красные случаи на 15m и на 10m, `got:[]`, rc=1. ## Что остаётся в #2214 (делается не кодом) 1. **Внешний монитор на публичную ручку бэкенда.** Завести в UI GlitchTip, проект Trade-In (id=3): `GET https://meraocenka.ru/trade-in/api/public/mera/stats`, ожидать 200, интервал 60 с. Эта ручка уже проксируется на meraocenka.ru и ходит в tradein-backend и БД: 17.09 ответила 200 за 0.2 с. Лимит 30 запросов в минуту с одного IP, минутный монитор в него укладывается. Caddy для этого менять не нужно. 2. **Один раз проверить доставку до человека.** Увидеть сообщение в теме 158. Годятся тестовое уведомление GlitchTip или первая настоящая тревога `MeraBackendDown`. Эпик #2200 закрывать рано: его последняя открытая дочерняя задача — #2214. ## Приёмка на проде после мержа (дата проверки: не позже 19.09.2026) - Деплой метрик зелёный, в логе `Prometheus: конфиг и правила проверены, reload подтверждён`. - На Beget `docker exec gendesign-prometheus wget -qO- 'http://localhost:9090/api/v1/rules?rule_name[]=MeraBackendDown'` показывает правило с `health: "ok"` и `state: "inactive"`. - Ближайший деплой tradein не переводит правило в firing: `ALERTS{alertname="MeraBackendDown",alertstate="firing"}` пуст. Деплой: правка в `ops/metrics/**` запускает `deploy-metrics.yml`. Он делает штатный reload Prometheus и по своему обычному сценарию пересоздаёт alertmanager, alert-ack и tg-relay на Beget, а также alloy на обоих хостах. Контейнеры Меры (backend, scraper, browser, postgres) не трогаются, поэтому гейт по `scrape_runs` не нужен. Миграций нет. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
bot-backend added 1 commit 2026-09-17 07:39:20 +00:00
fix(metrics): тревога, когда бэкенд Меры лёг, а лэндинг ещё открывается (#2214)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 21s
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 / changes (pull_request) Successful in 24s
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 3m23s
CI / backend-tests (pull_request) Successful in 8m17s
26a7f1c163
Мёртвый или зависший tradein-backend не давал ни одного сигнала вне Poincare.
GlitchTip-монитор 6 смотрит на лэндинг meraocenka.ru, а его отдаёт фронт из
пререндер-кэша (x-nextjs-cache: HIT) — при мёртвом бэкенде там 200. Монитор 2
(gendsgn.ru/health) проверяет бэкенд Птицы. AppHighErrorRate и
AppHighLatencyP95 считают метрики самого бэкенда и при его смерти молчат.

Новое правило MeraBackendDown: up{job="app",app="mera"} == 0 или серия
пропала, 5 минут, severity critical + host apps — маршрут telegram-clients,
тема 158. Считается и доставляется на Beget. Порог 5 минут откалиброван по
истории up за 26.08–17.09: 140 провалов, самый длинный — 4 нулевые точки
(~2 минуты, окна деплоя).

Юнит-тесты promtool: мёртвый бэкенд — тревога; окно деплоя и упавшая Птица —
тишина; цель пропала из скрейпа — тревога.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bot-backend merged commit d087e3076d into main 2026-09-17 09:15:05 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#3553
No description provided.