From 7f5c88869ac09c1f948f5d4e17a84a56eebe8e36 Mon Sep 17 00:00:00 2001 From: Max Ronzhin Date: Tue, 28 Jul 2026 00:42:25 +0300 Subject: [PATCH] =?UTF-8?q?fix(monitoring):=20=D1=83=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D1=82=D1=8C=20cAdvisor=20=E2=80=94=20=D0=BD=D0=B5=D1=81=D0=BE?= =?UTF-8?q?=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=B8=D0=BC=20=D1=81=20container?= =?UTF-8?q?d-=D1=81=D0=BD=D0=B0=D0=BF=D1=88=D0=BE=D1=82=D1=82=D0=B5=D1=80?= =?UTF-8?q?=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit На сервере 1gb Docker Engine использует containerd-снапшоттер (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, job в prometheus.yml, панели «топ контейнеров» в host.json (Prometheus иначе резолвит cadvisor:8080 в никуда — Grafana показывала бы «No data» вечно). node-exporter метрики хоста (CPU/RAM/ диск/сеть) при этом покрывает полностью, без изменений. Правка конфигурации мониторинга, без изменения пользовательского поведения — версия не бампается. --- CHANGELOG.md | 14 +++--- deploy/docker-compose.yml | 47 +++++-------------- deploy/monitoring/alerts.yml | 12 +++-- .../monitoring/grafana/dashboards/host.json | 44 +---------------- deploy/monitoring/prometheus.yml | 14 ++---- docs/deploy/monitoring.md | 28 ++++++----- 6 files changed, 48 insertions(+), 111 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a19295a..435e6c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,15 +5,17 @@ ## [0.0.9] — 2026-07-28 -Метрики CPU/RAM/диска хоста и контейнеров в Grafana; дефолтные подвкладки -конференций и пользователей в админке. +Метрики CPU/RAM/диска хоста в Grafana; дефолтные подвкладки конференций и +пользователей в админке. ### Добавлено - Мониторинг железа: `node-exporter` (CPU/RAM/диск/сеть/load average хоста) - и `cAdvisor` (метрики по каждому контейнеру) — профиль compose - `monitoring`, без публикации портов наружу. Новый дашборд Grafana «Хост и - контейнеры». Три новых алерта Prometheus: `HostMemoryLow`, `HostDiskLow`, - `HostCpuHigh` (пороги — под сервер `1gb`: 8 ГБ RAM, 4 CPU, 50 ГБ диска). + — профиль compose `monitoring`, без публикации портов наружу. Новый + дашборд Grafana «Хост и контейнеры». Три новых алерта Prometheus: + `HostMemoryLow`, `HostDiskLow`, `HostCpuHigh` (пороги — под сервер `1gb`: + 8 ГБ RAM, 4 CPU, 50 ГБ диска). `cAdvisor` (разбивка по контейнерам) + сознательно не включён — несовместим с containerd-снапшоттером Docker + Engine на `1gb`, см. `docs/deploy/monitoring.md`. ### Изменено - Админка, вкладка «Конференции»: вкладка «Все» перенесена в конец списка diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index b3bd252..31de105 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -734,42 +734,17 @@ services: profiles: ["monitoring"] logging: *default-logging - cadvisor: - # Метрики по контейнерам (CPU/память каждого) — отвечает на вопрос - # «какой из контейнеров ест ресурсы», в отличие от node-exporter - # (только хост целиком). Дороже node-exporter по CPU/RAM (собственные - # ~60-120 МБ RSS + периодический обход cgroups) — на сервере 8 ГБ/4 CPU - # с ~13 контейнерами это приемлемо, но не бесплатно; `--docker_only` - # и `--disable_metrics` ниже отключают часть коллекторов сверх дефолта - # cAdvisor (который уже отключает cpu_topology/hugetlb/memory_numa/ - # process/sched/tcp/udp и т.п.) — здесь дополнительно per-core CPU, - # perf_event и дисковые метрики (диск и так покрыт node-exporter'ом, - # дублировать через cAdvisor незачем). Список допустимых значений — - # `cadvisor -help` (в версии образа меняется, не все имена стабильны - # между релизами — при апгрейде образа сверяться с `-help`). - image: gcr.io/cadvisor/cadvisor:v0.49.2 - restart: unless-stopped - volumes: - - /:/rootfs:ro - - /var/run:/var/run:ro - - /sys:/sys:ro - - /var/lib/docker/:/var/lib/docker:ro - # Только чтение — cAdvisor не должен иметь возможность управлять - # контейнерами, ему нужен только список и статистика. - - /var/run/docker.sock:/var/run/docker.sock:ro - command: - - '--docker_only=true' - - '--disable_metrics=percpu,perf_event,diskIO,disk,app' - # Без ports вообще — тот же принцип, что и у node-exporter: Prometheus - # достаёт метрики по имени сервиса (`cadvisor:8080`) внутри сети compose. - healthcheck: - test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:8080/healthz >/dev/null || exit 1"] - interval: 10s - timeout: 5s - retries: 10 - start_period: 15s - profiles: ["monitoring"] - logging: *default-logging + # cAdvisor сознательно НЕ используется: несовместим с Docker Engine на + # сервере `1gb` (containerd-снапшоттер вместо классического overlay2 — + # `docker info` показывает `driver-type: io.containerd.snapshotter.v1`). + # cAdvisor (проверено на v0.49.2 и v0.52.1) не может определить + # read-write layer контейнеров через ни docker.sock, ни напрямую через + # `--containerd`/`--containerd-namespace=moby` — падает с + # «failed to identify the read-write layer ID», отдаёт метрики только по + # корневому cgroup, без разбивки по контейнерам. Открытая проблема + # совместимости cAdvisor с containerd image store, не чинится флагами — + # см. `.forcc/JOURNAL.md` за детали и альтернативы. node-exporter + # (сервис выше) метрики хоста при этом покрывает полностью. grafana: image: grafana/grafana:13.1.0 diff --git a/deploy/monitoring/alerts.yml b/deploy/monitoring/alerts.yml index 46f8ba2..3fae135 100644 --- a/deploy/monitoring/alerts.yml +++ b/deploy/monitoring/alerts.yml @@ -80,8 +80,10 @@ groups: summary: "Мало свободной памяти на хосте" description: >- Свободно {{ $value | printf "%.1f" }}% RAM дольше 10 минут - (порог 10% ≈ 800 МБ из 8 ГБ). Смотреть, какой контейнер ест - память — дашборд «Хост и контейнеры», топ по памяти (cAdvisor). + (порог 10% ≈ 800 МБ из 8 ГБ). Разбивки по контейнерам в Grafana + нет (cAdvisor несовместим с containerd-снапшоттером на этом + сервере, см. deploy/docker-compose.yml) — смотреть вручную, + например `docker stats`. # 50 ГБ диска — 10% ≈ 5 ГБ. `for: 15m` (не мгновенно): диск не растёт # так же резко, как память, ложные срабатывания на всплеск не грозят, @@ -112,6 +114,6 @@ groups: summary: "Устойчиво высокая загрузка CPU хоста" description: >- Загрузка CPU {{ $value | printf "%.1f" }}% дольше 15 минут - подряд (порог 90% из 4 ядер). Смотреть топ контейнеров по CPU - (дашборд «Хост и контейнеры», cAdvisor) и латентность API — - возможно, не хватает уровня AI/ресурсов под нагрузку. + подряд (порог 90% из 4 ядер). Разбивки по контейнерам в Grafana + нет (см. HostMemoryLow) — смотреть `docker stats` и латентность + API — возможно, не хватает уровня AI/ресурсов под нагрузку. diff --git a/deploy/monitoring/grafana/dashboards/host.json b/deploy/monitoring/grafana/dashboards/host.json index b76c709..0b3b3d7 100644 --- a/deploy/monitoring/grafana/dashboards/host.json +++ b/deploy/monitoring/grafana/dashboards/host.json @@ -1,6 +1,6 @@ { "title": "Хост и контейнеры", - "description": "CPU/RAM/диск/сеть хоста (node-exporter) и разбивка по контейнерам (cAdvisor) — профиль compose monitoring. Пороги на панелях согласованы с deploy/monitoring/alerts.yml (группа vidconf-host).", + "description": "CPU/RAM/диск/сеть хоста (node-exporter) — профиль compose monitoring. Пороги на панелях согласованы с deploy/monitoring/alerts.yml (группа vidconf-host). Разбивки по контейнерам нет: cAdvisor несовместим с containerd-снапшоттером Docker Engine на сервере 1gb (см. комментарий у сервиса cadvisor в deploy/docker-compose.yml и .forcc/JOURNAL.md) — вручную смотреть `docker stats`.", "uid": "vidconf-host", "editable": false, "timezone": "browser", @@ -180,7 +180,7 @@ "title": "Сетевой трафик", "description": "rx/tx по физическим интерфейсам хоста (исключены lo/veth/docker/br — это внутренние интерфейсы контейнеров, а не реальный сетевой трафик сервера).", "type": "timeseries", - "gridPos": { "h": 8, "w": 12, "x": 0, "y": 20 }, + "gridPos": { "h": 8, "w": 24, "x": 0, "y": 20 }, "datasource": { "type": "prometheus", "uid": "prometheus" }, "fieldConfig": { "defaults": { "unit": "Bps", "custom": { "drawStyle": "line", "fillOpacity": 5 } }, @@ -200,46 +200,6 @@ "refId": "B" } ] - }, - { - "id": 10, - "title": "Топ контейнеров по CPU", - "description": "Источник — cAdvisor (job cadvisor). Отвечает на вопрос «кто из контейнеров грузит CPU» при срабатывании HostCpuHigh.", - "type": "timeseries", - "gridPos": { "h": 8, "w": 12, "x": 12, "y": 20 }, - "datasource": { "type": "prometheus", "uid": "prometheus" }, - "fieldConfig": { - "defaults": { "unit": "percentunit", "custom": { "drawStyle": "line", "fillOpacity": 10, "stacking": { "mode": "normal" } } }, - "overrides": [] - }, - "targets": [ - { - "datasource": { "type": "prometheus", "uid": "prometheus" }, - "expr": "topk(5, sum by (name) (rate(container_cpu_usage_seconds_total{name!=\"\"}[5m])))", - "legendFormat": "{{name}}", - "refId": "A" - } - ] - }, - { - "id": 11, - "title": "Топ контейнеров по памяти", - "description": "container_memory_working_set_bytes — это то, что реально учитывает cgroup OOM killer (в отличие от RSS), источник — cAdvisor.", - "type": "timeseries", - "gridPos": { "h": 8, "w": 24, "x": 0, "y": 28 }, - "datasource": { "type": "prometheus", "uid": "prometheus" }, - "fieldConfig": { - "defaults": { "unit": "decbytes", "custom": { "drawStyle": "line", "fillOpacity": 10, "stacking": { "mode": "normal" } } }, - "overrides": [] - }, - "targets": [ - { - "datasource": { "type": "prometheus", "uid": "prometheus" }, - "expr": "topk(5, container_memory_working_set_bytes{name!=\"\"})", - "legendFormat": "{{name}}", - "refId": "A" - } - ] } ] } diff --git a/deploy/monitoring/prometheus.yml b/deploy/monitoring/prometheus.yml index bfd48e7..7ca65f3 100644 --- a/deploy/monitoring/prometheus.yml +++ b/deploy/monitoring/prometheus.yml @@ -1,7 +1,7 @@ # Конфигурация Prometheus (devops) — сбор метрик backend, -# PostgreSQL, Redis, железа хоста, контейнеров и локального LLM-сервера. -# Поднимается compose-профилем `monitoring` (deploy/docker-compose.yml, -# сервис `prometheus`). +# PostgreSQL, Redis, железа хоста и локального LLM-сервера. Поднимается +# compose-профилем `monitoring` (deploy/docker-compose.yml, сервис +# `prometheus`). # # Имена метрик backend (`vidconf_http_request_duration_seconds`, # `vidconf_pipeline_sessions`, `vidconf_celery_queue_depth`) — КОНТРАКТ с @@ -42,14 +42,6 @@ scrape_configs: static_configs: - targets: ["node-exporter:9100"] - # Метрики по каждому контейнеру (CPU/память отдельно у backend, worker, - # postgres и т.д. — профиль monitoring, сервис cadvisor). Отвечает на - # вопрос «какой из контейнеров ест ресурсы», в отличие от job `node` - # (только хост целиком). - - job_name: cadvisor - static_configs: - - targets: ["cadvisor:8080"] - # Локальный LLM-сервер (llama.cpp, LLAMA_ARG_ENDPOINT_METRICS=1). Адрес # `llm:8080` разрешается ОДНИМ из двух compose-сервисов в зависимости от # выбранного при установке пресета — `llm` (CPU, профиль `llm`, уровни diff --git a/docs/deploy/monitoring.md b/docs/deploy/monitoring.md index a987d65..87c76db 100644 --- a/docs/deploy/monitoring.md +++ b/docs/deploy/monitoring.md @@ -11,19 +11,25 @@ | `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 | -| `cadvisor` | `gcr.io/cadvisor/cadvisor:v0.49.2` | — (внутренний) | метрики по каждому контейнеру (CPU/память) | | `grafana` | `grafana/grafana:13.1.0` | `3001` (внутри контейнера `3000`) | дашборды «Пайплайны пост-обработки» и «Хост и контейнеры» | -`node-exporter`/`cadvisor` не публикуют порт на хост вообще (не только -127.0.0.1) — Prometheus ходит к ним по имени сервиса во внутренней сети -compose, публикация на хост для этого не нужна. `cadvisor` смонтирован -к `/var/run/docker.sock` только на чтение (`:ro`) и запущен с -`--docker_only` + урезанным набором коллекторов (`--disable_metrics`) — -он заметно дороже `node-exporter` по CPU/RAM, урезание снижает накладные -расходы. Если на конкретном сервере это всё равно избыточно — -`cadvisor` можно убрать из `deploy/docker-compose.yml`, оставив только -`node-exporter` (метрики хоста при этом не пострадают, пропадёт только -разбивка по контейнерам). +`node-exporter` не публикует порт на хост вообще (не только 127.0.0.1) — +Prometheus ходит к нему по имени сервиса во внутренней сети compose, +публикация на хост для этого не нужна. + +**cAdvisor сознательно не используется** (разбивки метрик по контейнерам +в Grafana нет): 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`. Если на другом сервере используется +классический overlay2, cAdvisor можно добавить обратно по тому же +образцу, что и `node-exporter` (job `cadvisor` в `prometheus.yml`, +таргет `cadvisor:8080`, без публикации портов). Файлы: `deploy/monitoring/prometheus.yml`, `deploy/monitoring/alerts.yml`, `deploy/monitoring/grafana/provisioning/` (datasource + провайдер