Як моніторити Docker контейнери у production?
Моніторинг Docker у проді означає знати чотири речі весь час: чи container живий, чи healthy, чи не використовує забагато ресурсів, чи не рестартує надто часто. Інструменти для цього добре відомі; робота це зв'язати їх разом.
Теорія
TL;DR
- Три шари моніторити: host (Linux-метрики), Docker-daemon, самі container.
- Стандартний стек: cAdvisor (per-container метрики) + Prometheus (TSDB) + Grafana (дашборди) + Alertmanager (paging).
- Логи окремо: Loki / ELK / fluentd / Datadog Logs.
- На що alert: неочікувані рестарти, healthcheck-fails, OOM-kill, CPU/memory saturation.
- Критичний інсайт: container-рестарти це сигнали,
docker statsне зловить flapping container, що крутиться 30с і помирає.
Що міряти
Рівень host:
- CPU/пам'ять/диск/мережа на host-масштабі
- Docker-daemon uptime
Рівень container:
- CPU-використання (% від ліміту)
- Memory-використання (vs cgroup-ліміт)
- Network I/O
- Block I/O
- Restart count
- Health-статус (healthy/unhealthy)
- Uptime
Рівень застосунку (всередині container):
- HTTP latency / errors
- Request rate
- Кастомні business-метрикиDocker-специфічний моніторинг це шари 1-2. App-level метрики експортуються САМИМ застосунком (/metrics-endpoint, Prometheus-scraping).
Стандартний стек
cAdvisor — per-container метрики
Google cAdvisor читає cgroup-дані і експортує як Prometheus-метрики:
services:
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.49.0
container_name: cadvisor
privileged: true
devices:
- /dev/kmsg
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker:/var/lib/docker:ro
- /dev/disk:/dev/disk:ro
restart: unless-stoppedЗайди на http://localhost:8080 для швидкого UI; метрики на /metrics.
Ключові cAdvisor-метрики:
container_cpu_usage_seconds_total— CPU consumedcontainer_memory_working_set_bytes— реальна пам'ять у usecontainer_network_receive_bytes_total/container_network_transmit_bytes_total— network I/Ocontainer_fs_usage_bytes— disk-використання
Prometheus — зберігати і query'їти
prometheus:
image: prom/prometheus:v2.55.0
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- promdata:/prometheus
ports:
- "9090:9090"
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.retention.time=30d# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
- job_name: 'docker-daemon'
static_configs:
- targets: ['host.docker.internal:9323']
- job_name: 'app'
static_configs:
- targets: ['api:3000']
metrics_path: /metricsPrometheus скрейпить кожні 15 секунд і зберігає 30 днів метрик.
Grafana — дашборди
grafana:
image: grafana/grafana:11.3.0
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD}
volumes:
- grafana:/var/lib/grafanaІмпортуй pre-built дашборди: «Docker and System Monitoring» (ID: 893), «Docker Container & Host Metrics» (ID: 10619). П'ять кліків і у тебе повна container-observability.
Alertmanager — paging
alertmanager:
image: prom/alertmanager:v0.27.0
ports:
- "9093:9093"
volumes:
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml:roВизнач alert у Prometheus, route через Alertmanager у Slack/PagerDuty/email.
Essential alerts
# rules.yml
groups:
- name: docker
rules:
- alert: ContainerDown
expr: time() - container_last_seen{name!=""} > 300
for: 5m
annotations:
summary: "Container {{ $labels.name }} not seen for 5 minutes"
- alert: ContainerHighMemory
expr: container_memory_working_set_bytes / container_spec_memory_limit_bytes > 0.9
for: 10m
annotations:
summary: "Container {{ $labels.name }} above 90% memory"
- alert: ContainerOOMKilled
expr: increase(container_oom_events_total[5m]) > 0
annotations:
summary: "Container {{ $labels.name }} OOM-killed in last 5 minutes"
- alert: ContainerRestarting
expr: increase(container_start_time_seconds[15m]) > 2
annotations:
summary: "Container {{ $labels.name }} restarted >2 times in 15 minutes"
- alert: ContainerUnhealthy
expr: container_health_status == 0
for: 2m
annotations:
summary: "Container {{ $labels.name }} unhealthy for 2 minutes"Ці п'ять покривають більшість прод-failure-mode.
Логи
Метрики кажуть, що щось не так; логи кажуть чому. Стандартні прод-стеки:
- Loki + Promtail + Grafana — Prometheus-flavored, log-labels збігаються з metric-labels.
- ELK (Elasticsearch + Logstash + Kibana) — важкий, але потужний search.
- Fluentd / Fluent Bit — log-collector, шиппить будь-куди.
- Vector — сучасна альтернатива fluentd, менший overhead.
На Docker-рівні налаштуй log-driver:
services:
api:
image: myapp
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# Також: tag, labels для централізованого routingБез max-size дефолтні json-file логи ростуть нескінченно і заповнюють диск.
Health-driven моніторинг
Якщо твої container мають визначений healthcheck:, Prometheus / cAdvisor експозять container_health_status. Alert на нього. Сама логіка healthcheck це твій liveness-probe.
services:
api:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
retries: 3Без healthcheck = monitoring blind spot.
Типові помилки
Жодних alert
Дашборд, на який ніхто не дивиться, це не моніторинг. Постав Alertmanager + paging з першого дня. «Ми будемо перевіряти дашборд» ніколи не працює.
Логи без retention або ротації
services:
api:
image: myapp
# дефолтні json-file логи ростуть без межіЧерез 6 місяців /var/lib/docker/containers/<id>/*-json.log може бути сотні GB. Завжди ставлять max-size і max-file.
Моніторити host-метрики, але не container-метрики
Host з 50% CPU може бути одним container на 100% CPU. Per-container метрики дозволяють знайти noisy-neighbor.
Забути скрейпити сам daemon
Docker-daemon експозить Prometheus-метрики, якщо увімкнено (/etc/docker/daemon.json: { "metrics-addr": "0.0.0.0:9323", "experimental": true }). Daemon-level метрики показують daemon-health, image push/pull rate тощо.
Ігнорувати restart count
Container з --restart=unless-stopped, що flap'ить кожні 30 секунд, виглядає «up» більшість часу, але зламаний. Change-rate container_start_time_seconds ловить це.
Реальне застосування
- Малі/середні команди: Compose-based стек cAdvisor + Prometheus + Grafana + Loki. ~30 хвилин на setup, покриває 90% потреб.
- Cloud-провайдери: AWS CloudWatch Container Insights, GCP Cloud Monitoring, Azure Container Insights. Managed, без setup, billed per metric.
- Datadog / New Relic / Honeycomb: SaaS APM з Docker-інтеграцією. Платиш за зручність.
- Kubernetes-style: Prometheus Operator + kube-state-metrics + node-exporter. Та сама ідея, K8s-native.
Питання для поглиблення
Q: Чи можна використовувати docker stats для прод-моніторингу?
A: Ні. Це live-snapshot, не TSDB. Корисно для ad-hoc inspect («чому це повільне зараз?»), безкорисно для trend або alert.
Q: Чому cAdvisor замість просто docker stats?
A: cAdvisor експозить Prometheus-endpoint, тож метрики скрейпляться, зберігаються і queryable історично. docker stats ні.
Q: Чи Docker daemon експозить метрики сам?
A: Так, якщо увімкнеш: /etc/docker/daemon.json з "metrics-addr": "0.0.0.0:9323". Тоді скрейпиш host:9323/metrics.
Q: Яка різниця між метриками і логами?
A: Метрики це aggregate через час (CPU 75% at t=12:00). Логи це події («GET /api/users 200 in 12ms at t=12:00:01»). Обидва потрібні; метрики для alert і trend, логи для debug причини.
Q: (Senior) Як кореляційно зіставляти метрики, логи і трейси у проді Docker?
A: Додай labels скрізь: container-labels (com.docker.stack=myapp), match'й їх у cAdvisor-метриках, пропагуй через Loki-labels для логів і додавай OpenTelemetry trace ID до log-рядків. View «Explore» Grafana дозволяє кликати metric-сплеск, стрибнути у логи на цій timestamp з тими ж labels, потім стрибнути у trace по trace ID. Інфраструктура: cAdvisor + Prometheus + Loki + Tempo за одним Grafana. Складна частина: добре інструментувати застосунок, не deployment.
Приклади
Compose-based monitoring-стек
services:
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.49.0
privileged: true
devices: ["/dev/kmsg"]
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker:/var/lib/docker:ro
networks: [monitor]
prometheus:
image: prom/prometheus:v2.55.0
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ./rules.yml:/etc/prometheus/rules.yml:ro
- promdata:/prometheus
ports: ["9090:9090"]
networks: [monitor]
grafana:
image: grafana/grafana:11.3.0
ports: ["3001:3000"]
environment:
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD}
volumes:
- grafana:/var/lib/grafana
networks: [monitor]
alertmanager:
image: prom/alertmanager:v0.27.0
volumes:
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
ports: ["9093:9093"]
networks: [monitor]
loki:
image: grafana/loki:3.2.0
ports: ["3100:3100"]
networks: [monitor]
promtail:
image: grafana/promtail:3.2.0
volumes:
- /var/log:/var/log:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./promtail.yml:/etc/promtail/config.yml:ro
networks: [monitor]
volumes:
promdata:
grafana:
networks:
monitor:Чотири стовпи (метрики з cAdvisor, store у Prometheus, alert через Alertmanager, дашборди у Grafana) плюс log-shipping (Promtail → Loki). Один docker compose up.
Налаштування ротації логів скрізь
# У compose.yaml твого стеку
x-logging: &default-logging
driver: json-file
options:
max-size: "10m"
max-file: "3"
tag: "{{.Name}}"
services:
api:
image: myapp
logging: *default-logging
worker:
image: myworker
logging: *default-loggingYAML-anchor застосовує той самий logging-config між сервісами. Disk-використання лишається обмеженим.
Endpoint метрик daemon-рівня
# /etc/docker/daemon.json
{
"metrics-addr": "0.0.0.0:9323",
"experimental": true
}sudo systemctl restart docker
curl http://localhost:9323/metrics | head
# # HELP engine_daemon_engine_info ...
# # TYPE engine_daemon_engine_info gauge
# ...Тепер Prometheus може скрейпити daemon на host:9323 для engine-level метрик.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.
Коментарі
Ще немає коментарів