Перейти к содержанию

Мониторинг: Prometheus + Grafana

Библиотечный backend отдаёт метрики Prometheus на GET /metrics (защищён токеном METRICS_TOKEN).

Что собирается

Метрика Описание
http_requests_total Число HTTP-запросов (method, route, status_class)
http_errors_total Ответы 4xx/5xx
http_request_duration_ms Гистограмма латентности
library_outbox_pending_events Необработанные события outbox (очередь бронирований)
library_redis_up 1 = Redis OK, 0 = down, -1 = не настроен
library_redis_latency_ms Пинг Redis, мс (-1 если недоступен)
library_database_up 1 = PostgreSQL OK, 0 = down
library_database_latency_ms Пинг БД, мс (-1 если недоступна)
process_*, nodejs_* CPU, память, event loop Node.js (prom-client)

Готовый дашборд Library System Overview (v2) — секции:

  • Overview — Database, Redis, Outbox, Error rate %, DB latency, Node memory
  • HTTP traffic — RPS, ошибки (4xx/5xx), error rate %, latency p50/p95/p99
  • Infrastructure — outbox backlog, DB/Redis latency, Node CPU/memory
  • Top routes — топ маршрутов по ошибкам и по RPS

Файл: monitoring/grafana/provisioning/dashboards/json/library-overview.json.

Алерты: monitoring/alerts.yml (Prometheus) и monitoring/grafana/provisioning/alerting/rules.yaml (Grafana).


Предварительные требования

В /opt/library_system/.env обязательно:

METRICS_TOKEN=<длинный_случайный_токен>

Проверка с хоста:

cd /opt/library_system
TOKEN=$(grep '^METRICS_TOKEN=' .env | cut -d= -f2-)
curl -s -o /dev/null -w "%{http_code}\n" \
  -H "Authorization: Bearer $TOKEN" \
  http://127.0.0.1:3001/metrics

Ожидается 200 и текст в формате Prometheus (# HELP http_requests_total ...).

Без METRICS_TOKEN эндпоинт возвращает 404.


Вариант A: У вас уже есть Grafana и Prometheus на сервере

Типичная ситуация: контейнеры grafana и prometheus на портах 3000 и 9090 (как на mail).

Шаг 1. Подключить контейнеры к сети библиотеки

Чтобы Prometheus видел backend:3001, а Grafana — prometheus:9090:

cd /opt/library_system

# имя сети смотрите так:
docker network ls | grep library

# обычно library_system_library-network
docker network connect library_system_library-network prometheus
docker network connect library_system_library-network grafana

Шаг 2. Настроить scrape в Prometheus

Найдите, где лежит конфиг:

docker inspect prometheus --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{"\n"}}{{end}}'

Откройте prometheus.yml на хосте и добавьте в scrape_configs (пример — monitoring/prometheus-host-scrape.example.yml):

  - job_name: library-backend
    scrape_interval: 15s
    metrics_path: /metrics
    static_configs:
      - targets: ['backend:3001']
        labels:
          service: library-backend
    authorization:
      type: Bearer
      credentials: 'ВАШ_METRICS_TOKEN_ИЗ_.env'

Частая ошибка: target library-backend:3001 даёт no such host. В docker-compose.yml сервис называется backend, DNS в сети — backend:3001. Проверка: docker exec prometheus wget -qO- --header="Authorization: Bearer $TOKEN" http://backend:3001/metrics | head.

Если не подключали сеть и backend только на 127.0.0.1:3001 хоста:

    static_configs:
      - targets: ['host.docker.internal:3001']

На Linux при старте Prometheus добавьте: --add-host=host.docker.internal:host-gateway
или используйте IP шлюза Docker: 172.17.0.1:3001.

Перезагрузка конфига без рестарта:

curl -X POST http://127.0.0.1:9090/-/reload

(нужен флаг --web.enable-lifecycle при запуске Prometheus)

Или перезапуск контейнера:

docker restart prometheus

Шаг 3. Проверить target

Откройте: http://ВАШ_СЕРВЕР:9090/targets

Job library-backend должен быть UP (зелёный).
Если DOWN — смотрите Error: чаще всего 401 (неверный токен) или connection refused (сеть/порт).

# диагностика из контейнера prometheus
docker exec prometheus wget -qO- \
  --header="Authorization: Bearer $TOKEN" \
  http://backend:3001/metrics | head

Шаг 4. Datasource в Grafana

  1. Откройте Grafana: http://ВАШ_СЕРВЕР:3000
  2. Connections → Data sources → Add data source → Prometheus
  3. URL:
  4. если Grafana в сети library-network: http://prometheus:9090
  5. иначе: http://127.0.0.1:9090
  6. Save & test — должно быть зелёное «Successfully queried».

Шаг 5. Импорт дашборда

  1. Dashboards → New → Import
  2. Нажмите Upload JSON file (перетащите файл с ПК) — не вставляйте путь к файлу в поле «Find and import dashboards at grafana.com» (там нужен числовой ID с grafana.com).
  3. Файл: monitoring/grafana/provisioning/dashboards/json/library-overview.json из репозитория (скачайте на ПК через scp или скопируйте содержимое в блок Import via dashboard JSON model).
  4. Import (или Dashboard settings → JSON Model → Save для обновления уже импортированного)

После обновления репозитория переимпортируйте JSON или нажмите Overwrite при импорте с тем же uid library-overview.

Дашборд покажет: - Статусы Database / Redis / Outbox / Error rate % - HTTP RPS, ошибки 4xx/5xx, latency p50/p95/p99 - Outbox backlog, DB и Redis latency - Node CPU и memory - Топ маршрутов по ошибкам и RPS

Шаг 6. Алерты (рекомендуется)

Prometheus на сервере (/opt/library_system/prometheus.yml):

  1. Смонтируйте правила в контейнер (добавьте volume при docker run или пересоздайте контейнер):
-v /opt/library_system/monitoring/alerts.yml:/etc/prometheus/alerts.yml:ro
  1. В prometheus.yml добавьте:
global:
  evaluation_interval: 15s

rule_files:
  - /etc/prometheus/alerts.yml
  1. Перезапустите Prometheus: docker restart prometheus

  2. Проверка: http://СЕРВЕР:9090/alerts — правила в состоянии Inactive (норма) или Pending/Firing.

Правила в monitoring/alerts.yml:

Алерт Условие
DatabaseUnavailable БД недоступна 1 мин
OutboxBacklogHigh outbox > 50, 5 мин
RedisUnavailable Redis down, 2 мин
HighHttpErrorRate > 0.05 err/s, 5 мин
HighHttpErrorRatePercent > 5% ошибок, 5 мин
HighNodeMemory Node > 512 MB, 10 мин
DatabaseSlow DB ping > 500 ms, 5 мин
SlowHttpResponses p95 > 2s, 10 мин

Grafana Alerting: Alerting → Alert rules → Import или вручную по выражениям из таблицы выше.

Уведомления в Telegram (встроенный profile):

TELEGRAM_BOT_TOKEN=...
TELEGRAM_CHAT_ID=...

Email:

GF_SMTP_ENABLED=true
GF_SMTP_HOST=smtp.example.com:587
GF_SMTP_USER=...
GF_SMTP_PASSWORD=...
ALERT_EMAIL_TO=ops@example.com

Вариант B: Встроенный monitoring profile (из docker-compose)

Отдельные контейнеры library-prometheus и library-grafana, порты только на localhost.

1. Переменные в .env

METRICS_TOKEN=<токен>
GRAFANA_ADMIN_PASSWORD=<надёжный_пароль>
# опционально:
GRAFANA_ROOT_URL=http://127.0.0.1:3000
TELEGRAM_BOT_TOKEN=
TELEGRAM_CHAT_ID=
ALERT_EMAIL_TO=

2. Запуск

cd /opt/library_system
docker compose --profile monitoring up -d prometheus grafana

Контейнеры: library-prometheus, library-grafana на 127.0.0.1:9090 и 127.0.0.1:3000.

Конфликт портов: если уже работают prometheus и grafana на 9090/3000, остановите их или не используйте profile — см. вариант A.

3. Доступ с вашего ПК (SSH-туннель)

ssh -L 9090:127.0.0.1:9090 -L 3000:127.0.0.1:3000 root@mail
  • Prometheus: http://127.0.0.1:9090
  • Grafana: http://127.0.0.1:3000 (логин admin, пароль из GRAFANA_ADMIN_PASSWORD)

Дашборд Library System Overview появится в папке Library автоматически.

4. Публикация Grafana наружу (опционально)

Рекомендуется не открывать :3000 в интернет. Если нужно:

  • отдельный поддомен за nginx + basic auth + HTTPS
  • или только VPN / SSH-туннель

Вариант C: Несколько реплик backend

При docker compose -f docker-compose.yml -f docker-compose.scale.yml up -d --scale backend=2:

PROMETHEUS_BACKEND_DNS_NAME=backend

Prometheus (profile monitoring) использует DNS SD Docker и собирает метрики со всех реплик.


Частые проблемы

Симптом Причина Решение
Target DOWN, 401 Неверный METRICS_TOKEN Синхронизировать токен в .env и prometheus.yml
Target DOWN, no such host Неверное имя target (library-backend вместо backend) В prometheus.yml: targets: ['backend:3001']
Target DOWN, connection refused Prometheus не в сети library-network docker network connect ...
/metrics 404 METRICS_TOKEN не задан в backend Добавить в .env, docker compose up -d backend
Grafana «No data» Неверный URL datasource http://prometheus:9090 или 127.0.0.1:9090
Outbox pending растёт outbox-worker не работает docker compose ps outbox-worker, REDIS_URL
library_redis_up = 0 Redis недоступен docker compose ps redis, REDIS_URL=redis://redis:6379

Полезные PromQL-запросы

# RPS всего API
sum(rate(http_requests_total[5m]))

# Ошибки в секунду
sum(rate(http_errors_total[5m]))

# p95 латентность
histogram_quantile(0.95, sum(rate(http_request_duration_ms_bucket[5m])) by (le))

# Топ маршрутов по ошибкам
topk(10, sum by (route) (rate(http_errors_total[15m])))

# Outbox backlog
max(library_outbox_pending_events)

Ссылки в репозитории

Файл Назначение
monitoring/prometheus.yml.template Scrape через Docker DNS (backend:3001)
monitoring/prometheus-host-scrape.example.yml Пример для внешнего Prometheus
monitoring/alerts.yml Правила Prometheus
monitoring/grafana/provisioning/ Datasource, дашборд, алерты Grafana
docker-compose.yml (profile monitoring) Сервисы prometheus + grafana

См. также: DEPLOYMENT.md, DOCKER.md.