Мониторинг контейнеров: stats, cAdvisor, Prometheus

Рейтинг: 78.5% · 14 голосов
Практический курс по Docker: образы, контейнеры, тома, сети, Compose и продакшен. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
Marina_DevOps
Сообщения: 54
Зарегистрирован: 11 май 2026, 05:31

Мониторинг контейнеров: stats, cAdvisor, Prometheus

Сообщение Marina_DevOps »

Оглавление курса (46)
  1. Что такое Docker и какие задачи он решает
  2. Установка Docker и запуск первого контейнера
  3. Образы: слои, теги и реестр Docker Hub
  4. Пишем свой Dockerfile
  5. Тома и хранение данных: volumes и bind mounts
  6. Сети в Docker: связываем контейнеры между собой
  7. Переменные окружения и конфигурация контейнеров
  8. Docker Compose: поднимаем многоконтейнерное приложение
  9. Оптимизация образов: multi-stage сборка, размер и кэш слоёв
  10. Логи, отладка и мониторинг контейнеров
  11. Базовая безопасность контейнеров
  12. Подготовка к продакшену: что важно учесть
  13. Dockerfile глубже: ENTRYPOINT и CMD, HEALTHCHECK, .dockerignore, запуск не от root
  14. Реестры образов: приватные registry, push и pull, теги и digest, imagePullSecrets
  15. BuildKit и buildx: multi-arch сборки, секреты сборки, экспорт кэша
  16. Docker в CI/CD: автосборка, сканирование образов (Trivy, Docker Scout), публикация
  17. Итоговый проект и куда расти: от Dockerfile до прода, обзор оркестрации (Kubernetes, Podman, OCI)
  18. Архитектура Docker: dockerd, containerd, runc и OCI
  19. Как работает изоляция: namespaces и cgroups
  20. Образы изнутри: слои, OverlayFS и storage driver
  21. Жизненный цикл контейнера и политики перезапуска
  22. Отладка контейнеров: exec, attach, nsenter, debug
  23. Сети Docker глубже: bridge, overlay, macvlan и iptables
  24. Docker Compose глубже: profiles, healthcheck-зависимости, override
  25. Данные и тома глубже: драйверы, бэкап, права
  26. Секреты и конфигурация: как не светить пароли
  27. Ограничение ресурсов: CPU, память, OOM и cgroups
  28. Здоровье и автозапуск: HEALTHCHECK, init и сигналы
  29. Логирование контейнеров: драйверы, ротация, сбор
  30. Мониторинг контейнеров: stats, cAdvisor, Prometheus (вы здесь)
  31. Безопасность глубже: rootless, user namespaces, seccomp, capabilities
  32. Supply chain: сканирование, SBOM и подпись образов
  33. Глубокая оптимизация образов: distroless, scratch, кэш
  34. Multi-arch и сборка под ARM: buildx и эмуляция
  35. Реестры в продакшене: Harbor и облачные registry
  36. Docker без Docker: Podman, containerd, nerdctl, Buildah
  37. От Compose к оркестрации: когда контейнеров становится много
  38. Docker Swarm: встроенная оркестрация
  39. Сборка образов в CI без демона: kaniko, BuildKit, Buildah
  40. Траблшутинг Docker: типичные ошибки и как чинить
  41. Производительность и эксплуатация Docker-хоста
  42. Docker Desktop, WSL2 и альтернативы на Windows и Mac
  43. Контейнеризация приложений: бэкенд, фронтенд, БД
  44. Docker и Kubernetes: containerd, миграция, kompose
  45. Лучшие практики и антипаттерны Docker
  46. Сквозной проект: production-ready стек и путь дальше
Зачем вообще нужен docker мониторинг

Представь типичную ночь: прод лёг, в чате паника, ты заходишь на хост и видишь, что контейнер с API то живой, то мёртвый, рестартится по кругу. Логи говорят только "Killed". Кто его убил? Сколько он жрал памяти перед смертью? Упирался ли в лимит CPU? Без метрик ты гадаешь на кофейной гуще. С метриками - открываешь дашборд и видишь: за 30 секунд до OOM рабочий набор памяти упёрся в 512 МБ лимита, ядро прислало SIGKILL. Диагноз за пять секунд вместо часа.

Мониторинг контейнеров - это не "красивые графики для отчёта". Это способность ответить на три вопроса: сколько ресурсов реально потребляет контейнер, упирается ли он в назначенные лимиты, и почему он умер. В этом уроке разберём путь от простого docker stats до полноценного стека Prometheus плюс Grafana плюс cAdvisor, поймём, откуда метрики берутся под капотом (спойлер - из cgroups), и подведём это к тому, как мониторинг устроен в Kubernetes.

Изображение

docker stats - первое, что под рукой, и его потолок

Самый быстрый инструмент - встроенный. Команда docker stats показывает живые метрики всех запущенных контейнеров: CPU, память, сеть, дисковый ввод-вывод.

Код: Выделить всё

docker stats --no-stream

CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O    PIDS
9f3a1b2c4d5e   api       142.50%   438.2MiB / 512MiB     85.59%    1.2GB / 880MB     0B / 4.1MB   38
7a8b9c0d1e2f   postgres  3.10%     210.5MiB / 2GiB       10.28%    44MB / 51MB       33MB / 1.2GB 27
Разберём поля, потому что в них скрыты грабли:
  • CPU % - может быть больше 100%. Это нормально: 142.50% означает, что контейнер использует почти полтора ядра. docker stats считает процент относительно одного ядра, а не относительно всей машины. На 8-ядерном хосте потолок тут 800%.
  • MEM USAGE / LIMIT - usage это рабочий набор (working set), а не сырой счётчик. Docker вычитает из общего использования кэш страниц, который ядро может выкинуть под давлением. Поэтому число тут заметно ниже, чем RSS из top. Если LIMIT показывает весь объём хоста - значит у контейнера лимит памяти не задан, и это уже сигнал.
  • MEM % - usage делёный на limit. Вот ЭТО поле надо ловить: подбирается к 100% - жди OOM kill.
  • NET I/O и BLOCK I/O - накопленные суммы с момента старта контейнера, а не скорость. Чтобы увидеть скорость, смотри как меняется число между обновлениями.
  • PIDS - число процессов/потоков внутри. Резко растёт - возможна форк-бомба или утечка потоков.
Где docker stats упирается в потолок:
  • Нет истории. Это live-режим. Контейнер умер минуту назад - данные испарились вместе с ним. Расследовать инцидент постфактум нечем.
  • Нет агрегации. Десять хостов - десять отдельных терминалов. Никакого общего вида на флот.
  • Нет алертов. Никто не разбудит тебя ночью, ты должен сам пялиться в экран.
  • Дорогой опрос. Под капотом stats дёргает API демона по каждому контейнеру, на сотнях контейнеров это ощутимая нагрузка.
Вывод: docker stats - это стетоскоп для быстрой проверки "что сейчас", но не система наблюдаемости. Для прода нужно хранить историю и агрегировать. Отсюда переходим к нормальному стеку.

Откуда метрики на самом деле: cgroups под капотом

Прежде чем ставить cAdvisor, надо понять, откуда вообще берутся цифры. Магии нет - всё лежит в псевдофайловой системе ядра. Контейнер это процесс, помещённый в cgroup (control group), и ядро ведёт по этой группе счётчики. На cgroup v2 (дефолт на современных дистрибутивах) они тут:

Код: Выделить всё

# найти cgroup контейнера и заглянуть в счётчики памяти
cat /sys/fs/cgroup/system.slice/docker-9f3a1b2c4d5e....scope/memory.current
459276288
cat /sys/fs/cgroup/.../memory.max
536870912
cat /sys/fs/cgroup/.../memory.stat | head
anon 312401920
file 98635776
inactive_file 71434240
...
Working set, который ты видел в docker stats и увидишь в Prometheus как container_memory_working_set_bytes, считается так: на cgroup v2 это memory.current минус inactive_file, на cgroup v1 это usage минус cache. Именно по working set ядро (и OOM killer, и kubelet) решает, кого убивать - потому что inactive_file можно выкинуть и освободить, а анонимную память некуда деть. Знать это критично: новички пугаются, что "память не падает после нагрузки" - а это просто кэш файлов, который никому не мешает.

Счётчики CPU там же: cpu.stat содержит usage_usec (сколько процессорного времени съедено), а главное - nr_throttled и throttled_usec. Вот это золото для прода, к нему вернёмся в разделе про троттлинг. Любой инструмент мониторинга контейнеров - cAdvisor, node-exporter, сам Docker - это всего лишь сборщик и переводчик этих файлов в формат Prometheus.

cAdvisor и метрики Docker для Prometheus

cAdvisor (Container Advisor от Google) - это демон, который сам находит все контейнеры на хосте, читает их cgroups и отдаёт метрики в формате Prometheus на эндпоинте /metrics. Это де-факто стандарт для per-container метрик. Запускают его самого в контейнере, монтируя нужные хостовые пути:

Код: Выделить всё

docker run -d --name cadvisor \
  --restart unless-stopped \
  -p 8080:8080 \
  --volume /:/rootfs:ro \
  --volume /var/run:/var/run:ro \
  --volume /sys:/sys:ro \
  --volume /var/lib/docker/:/var/lib/docker:ro \
  --volume /dev/disk/:/dev/disk:ro \
  --device /dev/kmsg \
  gcr.io/cadvisor/cadvisor:v0.49.1
Зачем столько монтирований: /sys нужен для чтения cgroups, /var/lib/docker - чтобы сопоставить ID контейнера с его именем и образом, /dev/kmsg - чтобы ловить события OOM из кольцевого буфера ядра. RU-ремарка: gcr.io в России бывает недоступен, держи зеркало в Yandex Container Registry или VK Cloud, либо тяни образ заранее и клади во внутренний registry.

Проверяем, что метрики льются:

Код: Выделить всё

curl -s localhost:8080/metrics | grep '^container_cpu_cfs_throttled_seconds_total' | head -2

container_cpu_cfs_throttled_seconds_total{id="/docker/9f3a...",image="myapp:1.4",name="api"} 8.41
container_cpu_cfs_throttled_seconds_total{id="/docker/7a8b...",image="postgres:16",name="postgres"} 0
Обрати внимание на лейблы name и image - cAdvisor сам их проставляет, так в Grafana ты фильтруешь по имени сервиса, а не по нечитаемому хешу.

Параллельно есть второй источник - встроенный metrics endpoint самого демона dockerd. Он отдаёт метрики про сам движок (операции с образами, состояние демона, длительность build). Включается в /etc/docker/daemon.json:

Код: Выделить всё

{
  "metrics-addr": "127.0.0.1:9323",
  "experimental": true
}
После рестарта демона метрики доступны на 127.0.0.1:9323/metrics. Это не замена cAdvisor: dockerd metrics это про здоровье движка, cAdvisor - про ресурсы контейнеров. На полном хосте обычно крутят оба плюс node-exporter для метрик самого хоста (CPU/память/диск/сеть машины), потому что контейнер, задыхающийся не из-за своего лимита, а из-за того что хост целиком исчерпан, ты увидишь только через node-exporter.

Стек docker prometheus grafana cAdvisor целиком

Соберём боевой стек одним compose. Prometheus тянет метрики (pull-модель: сам ходит и скрейпит), Grafana рисует, cAdvisor и node-exporter поставляют данные.

Код: Выделить всё

services:
  prometheus:
    image: prom/prometheus:v2.54.1
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prom_data:/prometheus
    ports:
      - "9090:9090"
    restart: unless-stopped

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:v0.49.1
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
    devices:
      - /dev/kmsg
    restart: unless-stopped

  node-exporter:
    image: prom/node-exporter:v1.8.2
    pid: host
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.sysfs=/host/sys'
    restart: unless-stopped

  grafana:
    image: grafana/grafana:11.2.0
    ports:
      - "3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana
    restart: unless-stopped

volumes:
  prom_data:
  grafana_data:
Конфиг Prometheus, который говорит "ходи скрейпь cadvisor и node-exporter раз в 15 секунд":

Код: Выделить всё

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: cadvisor
    static_configs:
      - targets: ['cadvisor:8080']

  - job_name: node
    static_configs:
      - targets: ['node-exporter:9100']
Подняли через docker compose up -d, зашли на 9090 - в Status -> Targets оба таргета должны быть UP. Если cadvisor DOWN - почти всегда забыли смонтировать /sys или /var/lib/docker. В Grafana (порт 3000, дефолтный логин admin/admin) добавляешь Prometheus как источник данных и импортируешь готовый дашборд - для cAdvisor классика это дашборд с Grafana.com по ID 14282 (Cadvisor exporter) и 1860 (Node Exporter Full). Не изобретай велосипед, эти дашборды вылизаны сообществом годами.

Что реально мониторить: throttling, память vs лимит, OOM, рестарты

Графики ради графиков бесполезны. Вот четыре метрики, ради которых всё затевалось, с готовыми PromQL.

1. CPU throttling - тихий убийца latency. Это самая коварная штука. Контейнер с лимитом CPU не "падает", когда упирается в потолок - ядро просто притормаживает его планировщиком CFS. Снаружи это выглядит как загадочные задержки в ответах сервиса при низкой загрузке CPU. Ловится так:

Код: Выделить всё

rate(container_cpu_cfs_throttled_periods_total{name="api"}[5m])
  /
rate(container_cpu_cfs_periods_total{name="api"}[5m])
Это доля периодов, в которых контейнер был придушен. Больше 0.25 (четверть периодов троттлится) - лимит CPU занижен, поднимай или убирай. Я видел команды, которые неделями искали "тормоза в сети", а это был CPU throttling из-за лимита 0.5 ядра на нагруженном сервисе.

2. Память против лимита. Заранее видеть приближение к OOM:

Код: Выделить всё

container_memory_working_set_bytes{name="api"}
  /
container_spec_memory_limit_bytes{name="api"} > 0.9
Подобралось к 0.9 - сервис в одном чихе от смерти. Именно working_set, не usage: ядро убивает по working set.

3. OOM kill. Факт убийства по памяти cAdvisor отдаёт счётчиком:

Код: Выделить всё

increase(container_oom_events_total{name="api"}[10m]) > 0
Сработало - контейнер реально получил SIGKILL за превышение memory.max. Это не теория, это надгробие. Ремарка про cgroup v2: флаг docker run --oom-kill-disable там игнорируется, отключить OOM killer для контейнера больше нельзя, так что единственная защита - адекватный лимит и мониторинг.

4. Рестарты. Контейнер, который циклически перезапускается (crash loop), - симптом номер один больного сервиса. Метрику счётчика рестартов удобнее брать с dockerd-эндпоинта или через cAdvisor по container_start_time_seconds (время старта скакнуло - был рестарт). Резкий рост рестартов = алерт немедленно.

Алерты. Графики ты не смотришь в 4 утра - тебя должен будить алерт. Правило для Alertmanager выглядит так:

Код: Выделить всё

groups:
  - name: containers
    rules:
      - alert: ContainerHighCpuThrottling
        expr: |
          rate(container_cpu_cfs_throttled_periods_total[5m])
          / rate(container_cpu_cfs_periods_total[5m]) > 0.25
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Контейнер {{ $labels.name }} троттлится по CPU"

      - alert: ContainerOOMKilled
        expr: increase(container_oom_events_total[5m]) > 0
        labels:
          severity: critical
        annotations:
          summary: "Контейнер {{ $labels.name }} убит по OOM"
Конструкция for: 10m важна: она гасит ложные срабатывания на коротких всплесках. Алертим только когда проблема держится.

Типичные грабли и антипаттерны
  • Путать RSS из top с working set. Цифры не сойдутся - они меряют разное. Для решений про лимиты смотри working_set, его же видит OOM killer.
  • CPU % больше 100 принимают за баг. Это не баг, это многоядерность. На 4 ядрах потолок 400%.
  • Запускать контейнеры вообще без лимитов памяти, а потом удивляться, что один сервис сожрал хост и положил соседей. Без лимита нет порога OOM, и под раздачу попадёт случайный процесс по выбору ядра.
  • Скрейпить cAdvisor раз в секунду. cAdvisor сам по себе прожорлив, агрессивный интервал плюс куча контейнеров - и мониторинг ест больше, чем приложение. 15s достаточно.
  • Хранить метрики локально без диска под Prometheus. Перезапустил стек без volume - история сгорела. Volume под /prometheus обязателен, а для долгого хранения смотри в сторону VictoriaMetrics или Thanos.
  • Игнорировать node-exporter. Контейнер может задыхаться из-за исчерпания хоста, а не своего лимита. Без метрик хоста этого не увидишь.
Мини-лаба: подними мониторинг руками
  • Запусти cAdvisor одной командой docker run из урока, открой localhost:8080 - там есть и встроенный веб-интерфейс с живыми графиками.
  • Подними нагруженный контейнер с жёстким лимитом: docker run -d --name load --cpus=0.5 --memory=128m progrium/stress --cpu 2. Он намеренно упрётся в лимиты.
  • В соседнем терминале запусти docker stats - посмотри, как CPU % застывает у потолка ~50%, а MEM % растёт.
  • Открой curl -s localhost:8080/metrics | grep throttled и убедись, что container_cpu_cfs_throttled_periods_total для load растёт - вот он, троттлинг вживую.
  • Подними полный compose-стек, импортируй в Grafana дашборд 14282 и найди свой контейнер load на графиках. Сравни working set из Grafana с числом из docker stats - должны сойтись.
Контрольные вопросы
  • Почему docker stats не годится для расследования инцидента, который случился час назад, и что решает эту проблему?
  • Чем working set отличается от полного usage памяти, и почему именно по working set ядро решает, кого убить по OOM?
  • Что такое CPU throttling, по какой метрике cAdvisor ты его ловишь и почему он опасен при низкой загрузке CPU?
  • Зачем в стеке держать одновременно cAdvisor, node-exporter и dockerd metrics endpoint - что меряет каждый?
Итог

docker stats - быстрый стетоскоп "что сейчас", но без истории, агрегации и алертов. Настоящая наблюдаемость контейнеров строится на cgroups как источнике правды, cAdvisor как переводчике этих счётчиков в Prometheus, и связке Prometheus плюс Grafana для хранения, визуализации и алертов. Мониторь не графики ради графиков, а четыре вещи: CPU throttling, память против лимита, OOM kills и рестарты. Всё, что ты тут собрал руками, почти один в один переезжает в Kubernetes - там те же cAdvisor (встроенный в kubelet) и Prometheus, только метрики собираются со всего кластера через kube-state-metrics и Prometheus Operator. Логика та же, масштаб больше. В следующих уроках по оркестрации это пригодится сразу.
👍2 ❤️3 🔥2 😄 🤔1
Аватара пользователя
golang21
Сообщения: 1
Зарегистрирован: 05 июн 2026, 11:18

Re: Мониторинг контейнеров: stats, cAdvisor, Prometheus

Сообщение golang21 »

Блин, годами думал что CPU больше 100% это глюк docker stats. А это просто несколько ядер. Спасибо, дошло наконец.
👍 ❤️2 🔥 😄 🤔1
Аватара пользователя
tor_dev
Сообщения: 1
Зарегистрирован: 23 май 2026, 16:21

Re: Мониторинг контейнеров: stats, cAdvisor, Prometheus

Сообщение tor_dev »

Вопрос: а container_oom_events_total надёжно ловит OOM, или бывает что контейнер умер, а метрика не дёрнулась? У меня на cgroup v2 пару раз рестарт был без события oom
👍 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Логирование контейнеров: драйверы, ротация, сбор
Следующая глава →
Безопасность глубже: rootless, user namespaces, seccomp, capabilities

Все главы курса «Docker: контейнеризация от основ до продакшена»

Поделиться темой: ✈ Telegram VK
Похожие запросы: docker stats мониторинг контейнеров как смотретьМониторинг и метрики nginxdocker network как связать контейнеры между собой по имени

Вернуться в «Docker с нуля»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость