Новый раздел про container-exporter/docker-socket-proxy: зачем свой экспортер вместо cAdvisor, какие метрики отдаёт, почему доступ к докер-сокету через прокси безопасен. Дополнена таблица метрик backend (vidconf_host_info).
13 KiB
Мониторинг (Prometheus + Grafana, профиль compose monitoring)
Независимый compose-профиль — можно поднимать вместе с любым пресетом инсталлятора (1–5) или отдельно.
1. Компоненты
| Сервис | Образ | Порт (хост) | Назначение |
|---|---|---|---|
prometheus |
prom/prometheus:v3.13.1 |
9090 |
сбор и хранение метрик, оценка правил алертинга |
postgres-exporter |
quay.io/prometheuscommunity/postgres-exporter:v0.20.1 |
— (внутренний) | метрики PostgreSQL |
redis-exporter |
oliver006/redis_exporter:v1.87.0-alpine |
— (внутренний) | метрики Redis |
node-exporter |
prom/node-exporter:v1.8.2 |
— (внутренний) | метрики хоста: CPU, память, диск, сеть, load average |
docker-socket-proxy |
tecnativa/docker-socket-proxy:v0.5.0 |
— (внутренний) | read-only прокси к Docker Engine API для container-exporter |
container-exporter |
сборка из deploy/monitoring/container-exporter/ |
— (внутренний) | метрики CPU/память/сеть в разбивке по контейнерам |
grafana |
grafana/grafana:13.1.0 |
3001 (внутри контейнера 3000) |
дашборды «Пайплайны пост-обработки» и «Хост и контейнеры» |
node-exporter/docker-socket-proxy/container-exporter не публикуют
порт на хост вообще (не только 127.0.0.1) — Prometheus и сам экспортер
ходят друг к другу по имени сервиса во внутренней сети compose,
публикация на хост для этого не нужна.
1.1. Метрики по контейнерам — свой экспортер вместо cAdvisor
cAdvisor не используется: Docker Engine на сервере 1gb использует
containerd-снапшоттер (docker info → driver-type: io.containerd.snapshotter.v1), а не классический overlay2-драйвер, и
cAdvisor (проверено на v0.49.2 и v0.52.1, с --docker_only и без, через
--containerd/--containerd-namespace=moby и без) не может определить
read-write layer контейнеров — падает с «failed to identify the
read-write layer ID», метрики есть только по корневому cgroup. Открытая
проблема совместимости cAdvisor с containerd image store, флагами не
чинится — см. .forcc/JOURNAL.md.
Вместо этого — свой минимальный экспортер (container-exporter,
deploy/monitoring/container-exporter/exporter.py, Python/aiohttp/
prometheus_client) поверх Docker Engine API: GET /containers/json +
GET /containers/{id}/stats?stream=false. Эти эндпойнты не зависят от
storage-driver'а — работают и с containerd-снапшоттером, и с overlay2.
Опрос контейнеров идёт параллельно в фоновой задаче (свой интервал,
не завязан на scrape Prometheus) — stats?stream=false не мгновенен
(Docker считает CPU-дельту по двум внутренним замерам с разницей ~1с),
линейный опрос дюжины контейнеров легко вышел бы за scrape-интервал.
Метрики (лейбл name — имя контейнера):
vidconf_container_cpu_percent— % от одного ядра, какdocker stats.vidconf_container_memory_usage_bytes/_limit_bytes— working set (без переиспользуемого page cache) и лимит памяти.vidconf_container_network_rx_bytes/_tx_bytes— суммарно по интерфейсам, счётчик со старта контейнера.
Безопасность: доступ к /var/run/docker.sock равносилен root на
хосте. Флаг :ro при монтировании сокета не спасает — он ограничивает
права на файл-ноду, а не на протокол: через сокет можно поднять
привилегированный контейнер и получить хост. Поэтому сокет НЕ
монтируется напрямую в container-exporter — между ними стоит
docker-socket-proxy (tecnativa/docker-socket-proxy), которому
разрешены только GET-запросы по контейнерам (CONTAINERS=1, POST=0
— дефолт образа, выписан явно). Даже полная компрометация
container-exporter (например, через уязвимость в его зависимостях) не
даёт управлять Docker — прокси физически не пропустит POST. Ни один
из двух сервисов не публикует портов наружу.
Если на другом сервере используется классический overlay2 и хочется
именно cAdvisor — можно добавить его по тому же образцу, что и
node-exporter (свой job в prometheus.yml, без публикации портов),
но тогда придётся решать ту же проблему монтирования сокета отдельно
(cAdvisor тоже требует доступ к Docker API).
Файлы: deploy/monitoring/prometheus.yml, deploy/monitoring/alerts.yml,
deploy/monitoring/grafana/provisioning/ (datasource + провайдер
дашбордов), deploy/monitoring/grafana/dashboards/pipelines.json,
deploy/monitoring/grafana/dashboards/host.json,
deploy/monitoring/container-exporter/ (код экспортера).
2. Запуск
docker compose -f deploy/docker-compose.yml --env-file .env --profile monitoring up -d
- Prometheus: http://localhost:9090
- Grafana: http://localhost:3001 (логин/пароль —
.env,GRAFANA_ADMIN_USER/GRAFANA_ADMIN_PASSWORD;install.shгенерирует пароль при первой установке)
⚠️ На боевом сервере оба порта публикуются ТОЛЬКО на 127.0.0.1
(deploy/docker-compose.yml) — открытые наружу admin-панели без
дополнительной аутентификации ранее уже приводили к компрометации сервисов
на этом проекте (см. .forcc/deploy/SESSION2-FINDINGS.md). С другой машины
используйте ssh-туннель:
ssh -L 9090:127.0.0.1:9090 -L 3001:127.0.0.1:3001 <user>@<host>
и открывайте http://localhost:9090 / http://localhost:3001 у себя.
Дашборды «Пайплайны пост-обработки» и «Хост и контейнеры» (папка VidConf в Grafana) появляются сразу — источник данных и дашборды провижинятся из файлов, без ручной настройки.
3. Метрики backend
GET /metrics (backend/api/metrics.py, без авторизации внутри
приложения — снаружи периметра закрыт явным return 403 в
deploy/nginx/nginx.conf; Prometheus ходит в backend напрямую по docker-сети,
backend:8000/metrics, минуя nginx):
vidconf_http_request_duration_seconds(histogram,method/path/status) — латентность HTTP по шаблону маршрута.vidconf_pipeline_sessions(gauge,status) — число сеансов конференций в каждом статусеpipeline_status(recording→transcribing→summarizing→notified|failed).vidconf_celery_queue_depth(gauge,queue) — глубина очередей Celery (transcription/summarize/notify/celery, redisLLEN), карта очередей —docs/deploy/scaling.md.vidconf_host_info(gauge, всегда1, лейблыcpus/ram_mb/gpu_name/vram_mb) — info-метрика обнаруженногоinstall.shжелеза (HW_*в.env, ADR-004). Источник плашки с характеристиками сервера в дашборде «Хост и контейнеры» — живые CPU/RAM/диск там же берутся из node-exporter, а GPU node-exporter не знает вообще, поэтому только эта метрика.«—»в лейбле —install.shне запускался (dev) либо видеокарта не обнаружена.
Job llm в prometheus.yml скрейпит llm:8080/metrics
(LLAMA_ARG_ENDPOINT_METRICS=1) — этот адрес резолвится ЛИБО сервисом
llm (CPU, профиль llm), ЛИБО llm-gpu (у него есть сетевой алиас llm,
см. deploy/docker-compose.yml) — профили llm/llm-gpu взаимоисключающи
по пресету, поэтому один job без дублирования.
По умолчанию job llm закомментирован в prometheus.yml (вместе с
алертом LlmDown в alerts.yml) — целевой набор профилей compose
(media,monitoring, см. .env.example) не включает llm/llm-gpu, и
таргет llm:8080 не резолвится вовсе. Раскомментируйте job и алерт вместе,
если инсталляция запущена с профилем llm/llm-gpu (пресеты 3–5
install.sh) — иначе панель «LLM-сервер доступен» дашборда «Пайплайны
пост-обработки» будет показывать «No data».
4. Алерты (deploy/monitoring/alerts.yml)
| Алерт | Условие | severity |
|---|---|---|
PipelineFailed |
рост числа сеансов в статусе failed за 15 минут |
critical |
QueueGrowing |
глубина очереди растёт 15 минут подряд и превышает 10 задач | warning |
LlmDown |
up{job="llm"} == 0 дольше 2 минут |
critical (закомментирован по умолчанию) |
HostMemoryLow |
свободно <10% RAM (≈800 МБ из 8 ГБ) дольше 10 минут | warning |
HostDiskLow |
свободно <10% диска (≈5 ГБ из 50 ГБ) дольше 15 минут | warning |
HostCpuHigh |
загрузка CPU >90% дольше 15 минут подряд | warning |
Пороги трёх алертов по железу подобраны под конкретный сервер 1gb
(8 ГБ RAM, 4 CPU, 50 ГБ диска) — при смене сервера пересчитать
(deploy/monitoring/alerts.yml, группа vidconf-host).
LlmDown актуален только на инсталляциях с профилем llm/llm-gpu
(пресеты 3–5) — по умолчанию (профили media,monitoring, без AI) правило
закомментировано в alerts.yml вместе с job llm в prometheus.yml.
Раскомментируйте оба, если поднимаете профиль llm/llm-gpu.
Проверка (после раскомментирования job/алерта, на инсталляции с профилем
llm/llm-gpu) — искусственно завалить пайплайн и убедиться, что алерт
срабатывает:
# Стек с профилями media, transcribe, llm, monitoring уже поднят,
# идёт активная суммаризация (сеанс в статусе summarizing).
docker compose -f deploy/docker-compose.yml --env-file .env stop llm
# Подождать > 2 минут → Prometheus (http://localhost:9090/alerts)
# должен показать LlmDown в состоянии firing, следом — QueueGrowing
# (очередь summarize перестаёт разбираться) и, если сеанс не восстановится
# за 15 минут (recover_stuck_summaries переставит задачу, workers/celery_app.py),
# PipelineFailed.
docker compose -f deploy/docker-compose.yml --env-file .env start llm
5. Хранение
prometheus_data/grafana_data — именованные тома, переживают
пересоздание контейнеров. Ретеншен Prometheus — дефолт образа (15 дней);
для прод-инсталляций с длинным горизонтом донастраивается флагом
--storage.tsdb.retention.time (не задан в deploy/docker-compose.yml —
осознанный dev/small-prod дефолт, донастраивается отдельно при необходимости).