DatabaseUnavailable (vidconf_db_up == 0, for: 30s, critical) и DbConnectionPoolNearExhaustion/RedisConnectionPoolNearExhaustion (занято > 80% дольше минуты, warning) — сигнал оператору, не автолечение: healthcheck backend'а по решению оператора остаётся мягким, рестарт при недоступной БД оборвал бы WS у всех, кто в конференциях. Дашборд Grafana «БД и пулы соединений» — занятость пулов на графике, следующий нагрузочный тест будут смотреть глазами.
202 lines
14 KiB
YAML
202 lines
14 KiB
YAML
# Правила алертинга Prometheus. Подключены через
|
||
# `rule_files` в prometheus.yml. Метрики — из `backend/api/metrics.py`
|
||
# (см. комментарий в prometheus.yml про контракт имён).
|
||
#
|
||
# Проверка: алерт на искусственно заваленном пайплайне — остановить `llm`
|
||
# при активном `summarize_session` (см. `docs/deploy/monitoring.md`).
|
||
groups:
|
||
- name: vidconf-pipeline
|
||
rules:
|
||
# Растёт число сеансов, застрявших в статусе `failed`
|
||
# (`conference_sessions.pipeline_status`).
|
||
# `delta()` — корректная функция PromQL именно для gauge (не
|
||
# `increase()`, которая рассчитана на монотонные counter'ы).
|
||
- alert: PipelineFailed
|
||
expr: delta(vidconf_pipeline_sessions{status="failed"}[15m]) > 0
|
||
for: 5m
|
||
labels:
|
||
severity: critical
|
||
annotations:
|
||
summary: "Растёт число упавших сеансов пайплайна пост-обработки"
|
||
description: >-
|
||
За последние 15 минут число сеансов в статусе failed выросло
|
||
на {{ $value }}. Смотреть логи worker/worker-transcriber и
|
||
таблицу conference_sessions (pipeline_status).
|
||
|
||
# Глубина хотя бы одной из очередей Celery устойчиво растёт 15 минут
|
||
# подряд — воркеры не успевают за потоком задач (см.
|
||
# docs/deploy/scaling.md — вынос очереди в отдельную реплику).
|
||
- alert: QueueGrowing
|
||
expr: delta(vidconf_celery_queue_depth[15m]) > 0 and vidconf_celery_queue_depth > 10
|
||
for: 15m
|
||
labels:
|
||
severity: warning
|
||
annotations:
|
||
summary: "Очередь Celery «{{ $labels.queue }}» растёт 15 минут подряд"
|
||
description: >-
|
||
Текущая глубина очереди {{ $labels.queue }}: {{ $value }} задач,
|
||
рост не прекращается 15 минут — см. docs/deploy/scaling.md
|
||
(вынос очереди в отдельную реплику/масштабирование).
|
||
|
||
# LLM-сервер (llama.cpp, job `llm` — см. комментарий в prometheus.yml
|
||
# про сетевой алиас `llm`/`llm-gpu`) недоступен. Актуально ТОЛЬКО на
|
||
# инсталляциях с включённым профилем `llm`/`llm-gpu` (пресеты 3–5,
|
||
# `install.sh`) — целевой набор профилей по умолчанию (`media,monitoring`,
|
||
# см. .env.example) их не включает, поэтому правило закомментировано
|
||
# вместе с job `llm` в prometheus.yml (иначе `up{job="llm"}` не находит
|
||
# ни одной серии — сам по себе закомментированный job уже не даёт этому
|
||
# алерту сработать, но держать активное правило на несуществующую
|
||
# метрику вводит в заблуждение). Раскомментируйте оба вместе на
|
||
# инсталляциях с профилем `llm`/`llm-gpu`.
|
||
# - alert: LlmDown
|
||
# expr: up{job="llm"} == 0
|
||
# for: 2m
|
||
# labels:
|
||
# severity: critical
|
||
# annotations:
|
||
# summary: "LLM-сервер (llama.cpp) недоступен"
|
||
# description: >-
|
||
# Prometheus не может достучаться до llm:8080 дольше 2 минут —
|
||
# суммаризация встанет (задачи будут копиться в очереди summarize,
|
||
# см. также алерт QueueGrowing). Проверить
|
||
# `docker compose ps llm` / `llm-gpu` и `docker compose logs llm`.
|
||
|
||
# Состояние БД и пулов соединений (сессия 33, разбор инцидента 07.08 —
|
||
# `.forcc/session-results/32-loadtest-07-08-debug.md`). `/api/health`
|
||
# отдавал 200 с `db: false` во время отказа — Prometheus его не скрейпит и
|
||
# не умеет разобрать JSON-тело, поэтому оба сигнала строятся на метриках
|
||
# `backend/api/metrics.py`, которые читаются вне основного пула.
|
||
- name: vidconf-db
|
||
rules:
|
||
# `vidconf_db_up` — отдельное соединение вне основного пула
|
||
# (`core/db.py::check_db_up`), поэтому 0 означает именно «БД не
|
||
# отвечает», а не «пул занят» (для второго см. DbConnectionPoolNearExhaustion
|
||
# ниже — раздельные метрики нарочно, см. «Главное требование» промпта
|
||
# сессии 33). `for: 30s` — два цикла скрейпа (`scrape_interval: 15s`),
|
||
# чтобы не среагировать на одиночный неудачный `connect()` (сеть/GC-пауза),
|
||
# но не тянуть с сигналом дольше: это самый критичный алерт в проекте.
|
||
- alert: DatabaseUnavailable
|
||
expr: vidconf_db_up == 0
|
||
for: 30s
|
||
labels:
|
||
severity: critical
|
||
annotations:
|
||
summary: "БД недоступна"
|
||
description: >-
|
||
vidconf_db_up == 0 дольше 30 секунд — backend не может открыть
|
||
отдельное (вне основного пула) соединение с Postgres. НЕ значит
|
||
автоматически «нужен рестарт контейнера» — по решению оператора
|
||
от 09.08 healthcheck backend'а остаётся мягким (не хардфейлится
|
||
на недоступной БД — рестарт-петля в разгар инцидента оборвала бы
|
||
WS у всех, кто в конференциях), это сигнал оператору, не
|
||
автолечение. Смотреть
|
||
`docker compose ps postgres`, `docker compose logs postgres`,
|
||
`pg_isready`.
|
||
|
||
# Раннее предупреждение — тот самый сигнал, которого не хватило
|
||
# 07.08: пул заполнялся постепенно (idle in transaction 3→8→16→26→35→
|
||
# 39→40 участников комнаты, см. session 32), а `up{job="backend"}`
|
||
# ничего не показывал, потому что backend отвечал исправно вплоть до
|
||
# самого потолка. Порог 80% — предложение из промпта сессии 33,
|
||
# `for: 1m` — фильтр от секундных всплесков (короткий пик параллельных
|
||
# запросов рассасывается за секунды, устойчивый рост участников
|
||
# комнаты — нет). На нагрузочном тесте 07.08 от пересечения 80% до
|
||
# исчерпания пула прошло по грубой оценке меньше двух минут — порог
|
||
# НЕ даёт большого запаса и это осознанный компромисс, а не идеал:
|
||
# цель — успеть до 500-х у пользователей, а не за много минут
|
||
# заранее. Перепроверить оба числа на следующем нагрузочном тесте
|
||
# (см. .forcc/session-results/33-db-health-alert.md) и подстроить,
|
||
# если реальный запас окажется у́же ожидаемого.
|
||
- alert: DbConnectionPoolNearExhaustion
|
||
expr: >-
|
||
(vidconf_db_pool_checked_out
|
||
/ (vidconf_db_pool_size + vidconf_db_pool_max_overflow)) * 100 > 80
|
||
for: 1m
|
||
labels:
|
||
severity: warning
|
||
annotations:
|
||
summary: "Основной пул соединений с БД близок к исчерпанию"
|
||
description: >-
|
||
Занято {{ $value | printf "%.0f" }}% основного пула БД дольше
|
||
минуты (порог 80%). Частая причина в этом проекте — долгоживущие
|
||
WS-подключения комнат (`api/chat.py`) держат соединение на
|
||
каждого сидящего в конференции; смотреть
|
||
`vidconf_db_pool_checked_out` и число открытых WS чата в логах,
|
||
не только текущую HTTP-нагрузку.
|
||
|
||
# Тот же класс отказа, что у пула БД (см. выше), только пул Redis —
|
||
# закрыт в 0.0.31 (`451c18e`) заданием явного max_connections, но без
|
||
# метрики занятости прошлый потолок нашёлся только руками на
|
||
# нагрузочном тесте. Бонус к задаче сессии 33 («потолки в этом
|
||
# проекте стоят лесенкой»), не отдельно запрошен промптом — пороги
|
||
# взяты по аналогии с пулом БД, не проверялись отдельным нагрузочным
|
||
# тестом именно на Redis.
|
||
- alert: RedisConnectionPoolNearExhaustion
|
||
expr: (vidconf_redis_pool_in_use / vidconf_redis_pool_max_connections) * 100 > 80
|
||
for: 1m
|
||
labels:
|
||
severity: warning
|
||
annotations:
|
||
summary: "Пул соединений Redis близок к исчерпанию"
|
||
description: >-
|
||
Занято {{ $value | printf "%.0f" }}% пула Redis дольше минуты
|
||
(порог 80%). Каждое WS-подключение комнаты держит собственную
|
||
pub/sub-подписку из этого же пула — смотреть число открытых WS
|
||
чата, не только команды Celery/кэша.
|
||
|
||
# Железо хоста (job `node` — node-exporter). Пороги подобраны под
|
||
# конкретный сервер 1gb: 8 ГБ RAM, 4 CPU, 50 ГБ диска — если сервер
|
||
# сменится, пересчитать.
|
||
- name: vidconf-host
|
||
rules:
|
||
# MemAvailable — уже честная оценка Linux с учётом того, что легко
|
||
# освобождаемый page cache/buffers не в счёт (в отличие от naive
|
||
# used = total - free). 10% от 8 ГБ ≈ 800 МБ — `for: 10m`, чтобы не
|
||
# дёргать на кратковременный всплеск (например, разовый всплеск
|
||
# transcribe/summarize), но успеть среагировать до OOM killer.
|
||
- alert: HostMemoryLow
|
||
expr: (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 < 10
|
||
for: 10m
|
||
labels:
|
||
severity: warning
|
||
annotations:
|
||
summary: "Мало свободной памяти на хосте"
|
||
description: >-
|
||
Свободно {{ $value | printf "%.1f" }}% RAM дольше 10 минут
|
||
(порог 10% ≈ 800 МБ из 8 ГБ). Смотреть, какой контейнер ест
|
||
память — дашборд «Хост и контейнеры», топ по памяти
|
||
(container-exporter, см. deploy/monitoring/container-exporter/).
|
||
|
||
# 50 ГБ диска — 10% ≈ 5 ГБ. `for: 15m` (не мгновенно): диск не растёт
|
||
# так же резко, как память, ложные срабатывания на всплеск не грозят,
|
||
# но и разовая проверка на границе некритична — 15 минут отсекает шум.
|
||
- alert: HostDiskLow
|
||
expr: (node_filesystem_avail_bytes{mountpoint="/",fstype!="tmpfs"} / node_filesystem_size_bytes{mountpoint="/",fstype!="tmpfs"}) * 100 < 10
|
||
for: 15m
|
||
labels:
|
||
severity: warning
|
||
annotations:
|
||
summary: "Мало места на диске хоста"
|
||
description: >-
|
||
Свободно {{ $value | printf "%.1f" }}% диска дольше 15 минут
|
||
(порог 10% ≈ 5 ГБ из 50 ГБ). Частые причины на этом проекте —
|
||
записи транскрибации (`recordings`), логи docker, образы/слои
|
||
после пересборки — проверить `docker system df`.
|
||
|
||
# 4 CPU. Порог 90% и `for: 15m` — сознательно строже по времени, чем
|
||
# у памяти/диска: кратковременные пики от пайплайна пост-обработки
|
||
# (транскрибация/суммаризация) — это ожидаемая, не аварийная нагрузка,
|
||
# алерт должен ловить именно устойчивую перегрузку, а не обычный всплеск.
|
||
- alert: HostCpuHigh
|
||
expr: 100 - (avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 90
|
||
for: 15m
|
||
labels:
|
||
severity: warning
|
||
annotations:
|
||
summary: "Устойчиво высокая загрузка CPU хоста"
|
||
description: >-
|
||
Загрузка CPU {{ $value | printf "%.1f" }}% дольше 15 минут
|
||
подряд (порог 90% из 4 ядер). Смотреть топ контейнеров по CPU
|
||
(дашборд «Хост и контейнеры») и латентность API — возможно,
|
||
не хватает уровня AI/ресурсов под нагрузку.
|