feat(ops): измеряем очередь Celery и Redis, до сих пор слепая зона
All checks were successful
CI Trade-In / changes (pull_request) Successful in 13s
CI / changes (pull_request) Successful in 15s
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 / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
All checks were successful
CI Trade-In / changes (pull_request) Successful in 13s
CI / changes (pull_request) Successful in 15s
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 / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
Prometheus не видел ни одной серии celery_*/redis_* — переполнение очереди Site Finder и залипший воркер снаружи выглядели одинаково, тишиной (issue #3471). Добавлено (только на продуктовом хосте, профиль apps): - redis-exporter (oliver006/redis_exporter) — здоровье общего Redis (db0 celery-брокер Site Finder, db1 SearchCache trade-in, db2 glitchtip), адрес через alias gendesign-redis на сети shared, без нового сетевого доступа. - celery-exporter (danihodovic/celery-exporter) — глубина очереди, число живых воркеров, счётчик неуспешных задач. Выбран вместо redis-exporter --check-keys, потому что дефолтная очередь "celery" дала бы только глубину, но не воркеров и не failures. - Скрейп обоих в alloy-apps.alloy. - Алерты в infra.yml: RedisDown, NoActiveCeleryWorkers, CeleryQueueGrowing (порог 150 предварительный — реальных данных по глубине очереди ещё нет, пересмотр через неделю наблюдений). Имена метрик celery-exporter (celery_queue_length, celery_worker_up, celery_task_failed_total) — по документации проекта, без прогона на реальном брокере; сверить после первого деплоя, см. комментарий у сервиса. promtool check rules — 23 правила, SUCCESS. Refs #3471
This commit is contained in:
parent
1254294ac3
commit
62a560387c
3 changed files with 143 additions and 0 deletions
|
|
@ -231,6 +231,71 @@ services:
|
||||||
mem_limit: 128m
|
mem_limit: 128m
|
||||||
logging: *default-logging
|
logging: *default-logging
|
||||||
|
|
||||||
|
# ── redis-exporter: здоровье общего Redis (только на Poincare) ───────────────
|
||||||
|
# #3471. Redis — один инстанс на три потребителя: db0 celery-брокер Site
|
||||||
|
# Finder, db1 SearchCache trade-in, db2 glitchtip (см. комментарий у сервиса
|
||||||
|
# `redis` в docker-compose.prod.yml). Один `redis_up` покрывает риск для всех
|
||||||
|
# трёх разом — до этой правки Redis не измерялся вообще, переполнение
|
||||||
|
# брокера и обычная недоступность снаружи выглядели одинаково — тишиной.
|
||||||
|
#
|
||||||
|
# Адрес — через alias `gendesign-redis`, который `redis` регистрирует на
|
||||||
|
# сети `shared` (см. #2709 в docker-compose.prod.yml) — джойнить ещё и
|
||||||
|
# `product` не нужно, тем же путём уже идёт postgres-exporter-tradein.
|
||||||
|
#
|
||||||
|
# Пароль — из окружения, не хардкод: сегодня на Redis нет requirepass (нет
|
||||||
|
# переменной ни в docker-compose.prod.yml, ни здесь), но если он появится,
|
||||||
|
# значение подставляется через METRICS_REDIS_PASSWORD в /opt/gendesign/.env
|
||||||
|
# на хосте, а не в этот файл.
|
||||||
|
redis-exporter:
|
||||||
|
image: oliver006/redis_exporter:v1.65.0
|
||||||
|
container_name: gendesign-redis-exporter
|
||||||
|
restart: unless-stopped
|
||||||
|
profiles: ["apps"]
|
||||||
|
environment:
|
||||||
|
REDIS_ADDR: ${METRICS_REDIS_ADDR:-redis://gendesign-redis:6379}
|
||||||
|
REDIS_PASSWORD: ${METRICS_REDIS_PASSWORD:-}
|
||||||
|
expose:
|
||||||
|
- "9121"
|
||||||
|
networks:
|
||||||
|
- shared
|
||||||
|
mem_limit: 64m
|
||||||
|
logging: *default-logging
|
||||||
|
|
||||||
|
# ── celery-exporter: очередь Site Finder (только на Poincare) ────────────────
|
||||||
|
# #3471. Слепая зона: глубина очереди, число живых воркеров и счётчик
|
||||||
|
# неуспешных задач нигде не измерялись — залипший воркер и переполненная
|
||||||
|
# очередь снаружи неотличимы от тишины.
|
||||||
|
#
|
||||||
|
# ПОЧЕМУ ОТДЕЛЬНЫЙ ОБРАЗ, А НЕ redis-exporter --check-keys. check-keys дал
|
||||||
|
# бы LLEN дефолтной очереди "celery" (в backend/app/workers/celery_app.py
|
||||||
|
# НЕТ task_routes — все таски идут в один дефолтный queue, имя буквально
|
||||||
|
# "celery") без нового образа вообще. Но он НЕ умеет считать живых
|
||||||
|
# воркеров и неуспешные таски — то есть закрыл бы только треть минимума
|
||||||
|
# из задачи. celery-exporter слушает событийную шину Celery через тот же
|
||||||
|
# брокер и даёт все три метрики разом, поэтому выбран он, а не комбинация
|
||||||
|
# check-keys + что-то ещё для остальных двух чисел.
|
||||||
|
#
|
||||||
|
# ⚠️ ИМЕНА МЕТРИК НИЖЕ (celery_queue_length, celery_worker_up,
|
||||||
|
# celery_task_failed_total) — по документации проекта на момент правки, БЕЗ
|
||||||
|
# прогона на реальном брокере (агент писал этот файл без доступа к проду).
|
||||||
|
# Сверить с `curl http://gendesign-celery-exporter:9808/metrics` на хосте
|
||||||
|
# после первого деплоя и поправить `ops/metrics/prometheus/rules/infra.yml`
|
||||||
|
# при расхождении — иначе алерты будут молча ничего не ловить.
|
||||||
|
celery-exporter:
|
||||||
|
image: danihodovic/celery-exporter:0.13.0
|
||||||
|
container_name: gendesign-celery-exporter
|
||||||
|
restart: unless-stopped
|
||||||
|
profiles: ["apps"]
|
||||||
|
command:
|
||||||
|
- "--broker-url=${METRICS_CELERY_BROKER_URL:-redis://gendesign-redis:6379/0}"
|
||||||
|
- "--queue=celery"
|
||||||
|
expose:
|
||||||
|
- "9808"
|
||||||
|
networks:
|
||||||
|
- shared
|
||||||
|
mem_limit: 128m
|
||||||
|
logging: *default-logging
|
||||||
|
|
||||||
# ── postgres-exporter: инфраструктурная БД (только на Beget) ─────────────────
|
# ── postgres-exporter: инфраструктурная БД (только на Beget) ─────────────────
|
||||||
# forgejo + glitchtip. Нужен и сам по себе, и как страховка: рост базы glitchtip
|
# forgejo + glitchtip. Нужен и сам по себе, и как страховка: рост базы glitchtip
|
||||||
# ничем не ограничен — политики ретенции у GlitchTip нет вообще.
|
# ничем не ограничен — политики ретенции у GlitchTip нет вообще.
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,27 @@ prometheus.scrape "postgres" {
|
||||||
scrape_interval = "60s"
|
scrape_interval = "60s"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ═══ REDIS И ОЧЕРЕДЬ CELERY (#3471) ═════════════════════════════════════════════
|
||||||
|
// До этой правки ни одной серии redis_* / celery_* в Prometheus не было: глубина
|
||||||
|
// очереди, число живых воркеров и потеря соединения с брокером были невидимы —
|
||||||
|
// переполнение очереди и залипший воркер снаружи выглядели одинаково, тишиной.
|
||||||
|
|
||||||
|
prometheus.scrape "redis" {
|
||||||
|
targets = [
|
||||||
|
{ __address__ = "gendesign-redis-exporter:9121", job = "redis" },
|
||||||
|
]
|
||||||
|
forward_to = [prometheus.remote_write.central.receiver]
|
||||||
|
scrape_interval = "30s"
|
||||||
|
}
|
||||||
|
|
||||||
|
prometheus.scrape "celery" {
|
||||||
|
targets = [
|
||||||
|
{ __address__ = "gendesign-celery-exporter:9808", job = "celery" },
|
||||||
|
]
|
||||||
|
forward_to = [prometheus.remote_write.central.receiver]
|
||||||
|
scrape_interval = "30s"
|
||||||
|
}
|
||||||
|
|
||||||
// ═══ МЕТРИКИ ПРИЛОЖЕНИЙ ════════════════════════════════════════════════════════
|
// ═══ МЕТРИКИ ПРИЛОЖЕНИЙ ════════════════════════════════════════════════════════
|
||||||
// Эндпоинты появляются в части 3. До этого скрейп просто отдаёт `up 0` — и это
|
// Эндпоинты появляются в части 3. До этого скрейп просто отдаёт `up 0` — и это
|
||||||
// правильно: цель видна как недоступная, а не отсутствует молча.
|
// правильно: цель видна как недоступная, а не отсутствует молча.
|
||||||
|
|
|
||||||
|
|
@ -232,6 +232,63 @@ groups:
|
||||||
summary: "p95 задержки ответа выше 5 секунд"
|
summary: "p95 задержки ответа выше 5 секунд"
|
||||||
description: "{{ $labels.app }}: p95 за 10 минут — {{ $value | humanizeDuration }}."
|
description: "{{ $labels.app }}: p95 за 10 минут — {{ $value | humanizeDuration }}."
|
||||||
|
|
||||||
|
# ── Redis и очередь Celery (#3471) ───────────────────────────────────────────
|
||||||
|
# Слепая зона: до этих правил ни redis_*, ни celery_* не собирались вовсе.
|
||||||
|
# Redis — общий инстанс на три потребителя (celery-брокер Site Finder, кэш
|
||||||
|
# trade-in, glitchtip — см. docker-compose.prod.yml), поэтому его смерть
|
||||||
|
# клиентская, отсюда severity: critical без явного host: apps — серия
|
||||||
|
# приходит только с продуктового alloy (alloy-apps.alloy), host в неё
|
||||||
|
# проставляется через external_labels уже на месте.
|
||||||
|
#
|
||||||
|
# ⚠️ Имена метрик celery_queue_length / celery_worker_up /
|
||||||
|
# celery_task_failed_total — по документации celery-exporter на момент
|
||||||
|
# написания правил, без проверки на реальном брокере (см. комментарий у
|
||||||
|
# сервиса celery-exporter в docker-compose.metrics-agent.yml). Сверить после
|
||||||
|
# первого деплоя.
|
||||||
|
- name: redis-celery
|
||||||
|
interval: 60s
|
||||||
|
rules:
|
||||||
|
- alert: RedisDown
|
||||||
|
expr: up{job="redis"} == 0 or redis_up == 0
|
||||||
|
for: 5m
|
||||||
|
labels:
|
||||||
|
severity: critical
|
||||||
|
annotations:
|
||||||
|
summary: "Redis недоступен"
|
||||||
|
description: "redis_exporter не может достучаться до Redis (или сам процесс лёг). Разом теряют связь celery-брокер Site Finder, SearchCache trade-in и glitchtip."
|
||||||
|
|
||||||
|
# `absent()` — как у CadvisorDown: если сам celery-exporter не поднялся,
|
||||||
|
# серии celery_worker_up не будет вообще, а не будет со значением 0.
|
||||||
|
- alert: NoActiveCeleryWorkers
|
||||||
|
expr: count(celery_worker_up == 1) == 0 or absent(celery_worker_up)
|
||||||
|
for: 5m
|
||||||
|
labels:
|
||||||
|
severity: critical
|
||||||
|
annotations:
|
||||||
|
summary: "Ни одного живого воркера Celery"
|
||||||
|
description: "celery-exporter не видит ни одного heartbeat от воркера Site Finder. Все periodic-таски (парсинг, аналитика, синк слоёв) встали."
|
||||||
|
|
||||||
|
# Порог 150 ПРЕДВАРИТЕЛЬНЫЙ: реальных данных по глубине очереди нет (до
|
||||||
|
# этой правки метрика не собиралась). beat_schedule.py на момент правки
|
||||||
|
# содержит 44 periodic-задачи с разным временем срабатывания — даже
|
||||||
|
# маловероятный залп всех разом даёт кратно меньше 150. Порог взят с
|
||||||
|
# запасом сознательно и требует пересмотра через неделю наблюдений по
|
||||||
|
# факту `celery_queue_length`.
|
||||||
|
#
|
||||||
|
# `delta(...) >= 0` — очередь не УМЕНЬШАЕТСЯ за 15 минут (тот же приём,
|
||||||
|
# что и "растёт и не разгребается" в тексте задачи): просто высокое
|
||||||
|
# значение без этого условия поймало бы и здоровый кратковременный всплеск.
|
||||||
|
- alert: CeleryQueueGrowing
|
||||||
|
expr: |
|
||||||
|
celery_queue_length{queue_name="celery"} > 150
|
||||||
|
and delta(celery_queue_length{queue_name="celery"}[15m]) >= 0
|
||||||
|
for: 15m
|
||||||
|
labels:
|
||||||
|
severity: warning
|
||||||
|
annotations:
|
||||||
|
summary: "Очередь Celery растёт и не разгребается"
|
||||||
|
description: "В очереди {{ $value }} задач, за 15 минут меньше не стало. Похоже на залипший воркер или устойчивый рост нагрузки."
|
||||||
|
|
||||||
# ── Postgres ────────────────────────────────────────────────────────────────
|
# ── Postgres ────────────────────────────────────────────────────────────────
|
||||||
- name: postgres
|
- name: postgres
|
||||||
interval: 60s
|
interval: 60s
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue