Monitoring en alerting voor Docker-workloads: storingen detecteren vóór je gebruikers
Monitoring en alerting voor Docker is meer dan “kijken of een container nog draait”. In productie wil je vroegsignalering: je ziet dat iets misgaat (latency, foutpercentages, resource-uitputting, disk- of inode-problemen, netwerkissues, crashloops) voordat gebruikers het merken. In deze tutorial bouw je een complete basisstack voor observability rond Docker-workloads, inclusief metrics, logs en alerts. Je krijgt concrete commando’s, valkuilen, en praktische alertregels.
Inhoud
- Wat je wilt meten (en waarom)
- Architectuur: metrics, logs, traces (en wat we hier doen)
- Voorbereiding: Docker host harden en basischecks
- Metrics verzamelen: cAdvisor + Node Exporter + Prometheus
- Dashboards: Grafana
- Alerting: Alertmanager + concrete alertregels
- Logs: Docker logs, JSON-driver, en Loki + Promtail
- Praktische SLO/SLI-alerts voor webservices
- Runbooks en incidentworkflow
- Troubleshooting: veelvoorkomende problemen
- Checklist voor productie
Wat je wilt meten (en waarom)
Een effectieve monitoringset begint bij de juiste signalen. Voor Docker-workloads zijn dit de belangrijkste categorieën:
1) Beschikbaarheid en “liveness”
- Draait de container?
- Zit hij in een crashloop?
- Wordt hij continu opnieuw gestart?
Waarom: een container kan “up” zijn maar toch geen verkeer verwerken (bijv. deadlock). Daarom combineer je liveness met applicatiechecks.
2) Resourceverbruik (host en container)
- CPU: saturatie (throttling), load
- Memory: usage, OOM kills
- Disk: space, I/O latency, inode-uitputting
- Network: throughput, errors, drops
Waarom: de meeste incidenten zijn resource-gerelateerd. Memory leaks, noisy neighbors, disk vol door logs, of throttling door CPU-limieten.
3) Applicatiegezondheid
- HTTP status codes (5xx), latency p95/p99
- Queue length, worker backlog
- Database connect errors, timeouts
Waarom: gebruikers ervaren latency en errors, niet “CPU 80%”.
4) “Golden signals”
Klassiek: latency, traffic, errors, saturation.
Waarom: dit vertaalt direct naar gebruikersimpact en is geschikt voor SLO’s.
Architectuur: metrics, logs, traces (en wat we hier doen)
Je kunt observability zien als drie pijlers:
- Metrics: numerieke tijdreeksen (CPU, memory, request duration). Goed voor dashboards en alerts.
- Logs: detail en context (stacktraces, foutmeldingen). Goed voor root cause.
- Traces: request flows over services heen. Goed voor microservices, maar optioneel.
In deze tutorial bouwen we:
- Prometheus (metrics scraping + query)
- cAdvisor (container metrics)
- Node Exporter (host metrics)
- Grafana (dashboards)
- Alertmanager (alerts routeren)
- Loki + Promtail (log-aggregatie)
Alles draait als Docker-containers op één host. In productie kun je dit later opschalen (bijv. Prometheus HA, remote write, managed Grafana).
Voorbereiding: Docker host harden en basischecks
Vereisten
- Linux host met Docker Engine
- Poorten beschikbaar (voorbeeld):
- Prometheus: 9090
- Grafana: 3000
- Alertmanager: 9093
- Loki: 3100
Controleer Docker:
docker version
docker info
Maak een directorystructuur:
sudo mkdir -p /opt/monitoring/{prometheus,grafana,alertmanager,loki,promtail}
sudo chown -R $USER:$USER /opt/monitoring
Controleer of je host al “bijna vol” is (klassieke oorzaak van incidenten):
df -h
df -i
free -h
uptime
Bekijk Docker resourcegebruik:
docker system df
docker stats --no-stream
Metrics verzamelen: cAdvisor + Node Exporter + Prometheus
Waarom cAdvisor en Node Exporter?
- cAdvisor geeft container-level metrics: CPU, memory, network, filesystem per container.
- Node Exporter geeft host-level metrics: disk, filesystem, load, kernel, netwerkinterfaces.
Prometheus “scrapet” deze endpoints periodiek.
1) Maak een Docker network
docker network create monitoring
2) Start Node Exporter
docker run -d \
--name node-exporter \
--network monitoring \
--restart unless-stopped \
-p 9100:9100 \
--pid="host" \
-v /:/host:ro,rslave \
quay.io/prometheus/node-exporter:latest \
--path.rootfs=/host
Test:
curl -s http://localhost:9100/metrics | head
3) Start cAdvisor
docker run -d \
--name cadvisor \
--network monitoring \
--restart unless-stopped \
-p 8080:8080 \
--privileged \
-v /:/rootfs:ro \
-v /var/run:/var/run:rw \
-v /sys:/sys:ro \
-v /var/lib/docker/:/var/lib/docker:ro \
gcr.io/cadvisor/cadvisor:latest
Test:
curl -s http://localhost:8080/metrics | head
4) Prometheus configureren
Maak /opt/monitoring/prometheus/prometheus.yml:
cat > /opt/monitoring/prometheus/prometheus.yml <<'EOF'
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['prometheus:9090']
- job_name: 'node'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
EOF
Start Prometheus:
docker run -d \
--name prometheus \
--network monitoring \
--restart unless-stopped \
-p 9090:9090 \
-v /opt/monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro \
prom/prometheus:latest
Open Prometheus UI: http://<host>:9090
Controleer targets in Prometheus:
Ga naar Status → Targets en check of node en cadvisor “UP” zijn.
Belangrijke PromQL-queries (basis)
Host CPU usage (%) (gemiddeld over alle cores):
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
Host memory usage (%):
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100
Container restarts (cAdvisor): cAdvisor heeft niet altijd restart-metrics; vaak gebruik je Docker events of kube-state-metrics in Kubernetes. Voor pure Docker kun je ook via docker ps/docker inspect checken, maar voor metrics is het beter om een exporter te gebruiken. Toch kun je crashloops indirect zien via CPU/memory spikes en “container last seen” metrics. Een praktische aanpak is logs + healthchecks + blackbox probing (zie verder).
Container CPU usage (per container):
sum by (name) (rate(container_cpu_usage_seconds_total{image!=""}[5m])) * 100
Let op: labels verschillen per cAdvisor-versie; soms is container_label_com_docker_compose_service handiger als je Compose gebruikt.
Dashboards: Grafana
Grafana maakt metrics bruikbaar voor teams. Je bouwt dashboards voor:
- Host overview
- Container overview
- Latency/errors (app metrics)
- Disk & inode
- Network errors/drops
Start Grafana
docker run -d \
--name grafana \
--network monitoring \
--restart unless-stopped \
-p 3000:3000 \
-v /opt/monitoring/grafana:/var/lib/grafana \
grafana/grafana:latest
Open: http://<host>:3000
Default login is vaak admin/admin (wijzig direct).
Prometheus als datasource toevoegen
In Grafana:
- Connections → Data sources → Add data source → Prometheus
- URL:
http://prometheus:9090 - Save & test
Handige dashboards importeren
Je kunt community dashboards importeren (ID’s wijzigen soms). Zoek in Grafana “Dashboards → Import” en gebruik bijvoorbeeld:
- Node Exporter Full
- cAdvisor / Docker dashboards
Als je liever handmatig start, maak panels met de PromQL uit de vorige sectie.
Alerting: Alertmanager + concrete alertregels
Monitoring zonder alerts is “post-mortem observability”. Alerts moeten:
- Actiegericht zijn (“wat moet ik doen?”)
- Niet te noisy zijn
- Gebaseerd zijn op impact (SLO/SLI) of harde grenzen (disk bijna vol)
1) Alertmanager starten
Maak /opt/monitoring/alertmanager/alertmanager.yml:
cat > /opt/monitoring/alertmanager/alertmanager.yml <<'EOF'
global:
resolve_timeout: 5m
route:
receiver: 'default'
group_by: ['alertname', 'instance']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receivers:
- name: 'default'
# Voorbeeld: log naar stdout via webhook-echo of later Slack/Email/PagerDuty
# Hier laten we het leeg; Alertmanager UI toont alerts sowieso.
EOF
Start Alertmanager:
docker run -d \
--name alertmanager \
--network monitoring \
--restart unless-stopped \
-p 9093:9093 \
-v /opt/monitoring/alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro \
prom/alertmanager:latest
Open: http://<host>:9093
2) Prometheus koppelen aan Alertmanager
Pas /opt/monitoring/prometheus/prometheus.yml aan en voeg toe:
grep -q "alerting:" /opt/monitoring/prometheus/prometheus.yml || cat >> /opt/monitoring/prometheus/prometheus.yml <<'EOF'
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
EOF
Herstart Prometheus:
docker restart prometheus
3) Alertregels toevoegen
Maak /opt/monitoring/prometheus/alerts.yml:
cat > /opt/monitoring/prometheus/alerts.yml <<'EOF'
groups:
- name: host-alerts
rules:
- alert: HostHighCPU
expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 90
for: 10m
labels:
severity: warning
annotations:
summary: "Hoge CPU op {{ $labels.instance }}"
description: "CPU usage > 90% gedurende 10m. Check top/containers en throttling."
- alert: HostHighMemory
expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 90
for: 10m
labels:
severity: warning
annotations:
summary: "Hoog geheugengebruik op {{ $labels.instance }}"
description: "Memory usage > 90% gedurende 10m. Controleer OOM, leaks, caches."
- alert: HostDiskAlmostFull
expr: (1 - (node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"})) * 100 > 85
for: 15m
labels:
severity: critical
annotations:
summary: "Disk bijna vol op {{ $labels.instance }} ({{ $labels.mountpoint }})"
description: "Beschikbare disk < 15%. Controleer logs, images, volumes: docker system df."
- alert: HostInodesAlmostFull
expr: (1 - (node_filesystem_files_free{fstype!~"tmpfs|overlay"} / node_filesystem_files{fstype!~"tmpfs|overlay"})) * 100 > 85
for: 15m
labels:
severity: critical
annotations:
summary: "Inodes bijna op op {{ $labels.instance }} ({{ $labels.mountpoint }})"
description: "Inodes < 15% vrij. Vaak door veel kleine logfiles/cache. Check /var/lib/docker en app logs."
- name: docker-alerts
rules:
- alert: ContainerHighCPU
expr: sum by (name) (rate(container_cpu_usage_seconds_total{image!=""}[5m])) * 100 > 80
for: 15m
labels:
severity: warning
annotations:
summary: "Container hoge CPU: {{ $labels.name }}"
description: "CPU > 80% gedurende 15m. Check docker stats en app-profiel."
- alert: ContainerHighMemory
expr: (container_memory_working_set_bytes{image!=""} / container_spec_memory_limit_bytes{image!=""}) * 100 > 90
for: 10m
labels:
severity: warning
annotations:
summary: "Container hoge memory: {{ $labels.name }}"
description: "Memory > 90% van limit. Let op: zonder limit is deze metric misleidend."
EOF
Belangrijk: container_spec_memory_limit_bytes is alleen zinvol als je memory limits zet. Zonder limits kan dit 0 of extreem hoog zijn. In dat geval alarmeer je beter op absolute waarden of host memory pressure.
Koppel alerts.yml in Prometheus config. Voeg in prometheus.yml toe:
grep -q "^rule_files:" /opt/monitoring/prometheus/prometheus.yml || sed -i '1irule_files:\n - /etc/prometheus/alerts.yml\n' /opt/monitoring/prometheus/prometheus.yml
Start Prometheus met extra volume mount voor alerts:
docker rm -f prometheus
docker run -d \
--name prometheus \
--network monitoring \
--restart unless-stopped \
-p 9090:9090 \
-v /opt/monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro \
-v /opt/monitoring/prometheus/alerts.yml:/etc/prometheus/alerts.yml:ro \
prom/prometheus:latest
Controleer in Prometheus: Alerts tab.
Alerts testen (bewust CPU belasten)
Installeer stress op de host of gebruik een container:
docker run --rm -it --name stress --network monitoring alpine sh -lc "apk add --no-cache stress-ng && stress-ng --cpu 2 --timeout 120s"
Logs: Docker logs, JSON-driver, en Loki + Promtail
Metrics zeggen je dat er iets mis is; logs zeggen vaak waarom.
Docker logging driver checken
Meestal is json-file default. Check:
docker info --format '{{.LoggingDriver}}'
Bekijk logs van een container:
docker logs --tail 200 -f <containernaam>
Probleem: docker logs is lokaal en niet doorzoekbaar over tijd/containers heen. Daarom Loki.
Loki starten
docker run -d \
--name loki \
--network monitoring \
--restart unless-stopped \
-p 3100:3100 \
grafana/loki:latest
Test:
curl -s http://localhost:3100/ready
Promtail configureren (Docker logs scrapen)
Maak /opt/monitoring/promtail/promtail.yml:
cat > /opt/monitoring/promtail/promtail.yml <<'EOF'
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: docker
static_configs:
- targets:
- localhost
labels:
job: dockerlogs
__path__: /var/lib/docker/containers/*/*-json.log
EOF
Start Promtail:
docker run -d \
--name promtail \
--network monitoring \
--restart unless-stopped \
-v /var/lib/docker/containers:/var/lib/docker/containers:ro \
-v /opt/monitoring/promtail/promtail.yml:/etc/promtail/config.yml:ro \
grafana/promtail:latest \
-config.file=/etc/promtail/config.yml
Loki datasource in Grafana
- Data source: Loki
- URL:
http://loki:3100
In Explore kun je nu zoeken. Voorbeeld query:
{job="dockerlogs"} |= "error"
Tip: Voeg labels toe (containernaam, compose service) via Docker labels en promtail pipeline stages. Dat is wat complexer, maar loont enorm. Een simpele eerste stap is later uitbreiden.
Praktische SLO/SLI-alerts voor webservices
Host- en container-alerts zijn nuttig, maar echte waarde zit in service-level alerts: “gebruikers krijgen errors” of “latency is te hoog”.
Stap 1: instrumenteer je app met Prometheus metrics
Als je app (bijv. Go, Java, Python, Node) metrics kan exposen op /metrics, scrape die dan. Stel je hebt een service web op poort 8081 die metrics aanbiedt.
Run je container:
docker run -d --name web --network monitoring -p 8081:8081 myorg/web:latest
Voeg in Prometheus scrape_configs toe:
# Voeg toe in prometheus.yml onder scrape_configs:
# - job_name: 'web'
# static_configs:
# - targets: ['web:8081']
Herstart Prometheus.
Stap 2: definieer SLIs
Voor HTTP-services zijn typische metrics:
http_requests_total{status="500"}(counter)http_request_duration_seconds_bucket(histogram)
Als je histogram hebt, kun je p95/p99 berekenen.
Error rate (5xx) over 5m:
sum(rate(http_requests_total{job="web",status=~"5.."}[5m]))
/
sum(rate(http_requests_total{job="web"}[5m]))
p95 latency (voorbeeld met histogram):
histogram_quantile(
0.95,
sum by (le) (rate(http_request_duration_seconds_bucket{job="web"}[5m]))
)
Stap 3: alerts die bij impact passen
Voeg aan alerts.yml een groep toe:
cat >> /opt/monitoring/prometheus/alerts.yml <<'EOF'
- name: web-slo-alerts
rules:
- alert: WebHighErrorRate
expr: |
(
sum(rate(http_requests_total{job="web",status=~"5.."}[5m]))
/
sum(rate(http_requests_total{job="web"}[5m]))
) > 0.02
for: 10m
labels:
severity: critical
annotations:
summary: "Web service hoge error rate"
description: "5xx rate > 2% gedurende 10m. Check deployments, dependencies, logs in Loki."
- alert: WebHighLatencyP95
expr: |
histogram_quantile(
0.95,
sum by (le) (rate(http_request_duration_seconds_bucket{job="web"}[5m]))
) > 0.5
for: 10m
labels:
severity: warning
annotations:
summary: "Web service hoge latency (p95)"
description: "p95 latency > 500ms gedurende 10m. Check DB, downstream, CPU throttling."
EOF
Herstart Prometheus:
docker restart prometheus
Waarom dit beter is: CPU kan 95% zijn zonder impact (batch job), of 40% met enorme impact (lock contention). Error rate en latency zijn direct user-facing.
Runbooks en incidentworkflow
Alerts zonder runbook leiden tot stress en trial-and-error. Maak per alert minimaal:
- Betekenis: wat zegt de alert?
- Impact: wat merken gebruikers?
- Eerste checks (2–5 minuten)
- Mitigatie (snel stabiliseren)
- Root cause hints
- Escalatie (wanneer en naar wie)
Voorbeeld runbook: Disk bijna vol
Eerste checks:
df -h
df -i
docker system df
sudo du -xh /var/lib/docker | sort -h | tail -n 20
Snelle mitigatie (pas op in productie):
- Oude images opruimen:
docker image prune -a
- Ongebruikte volumes (alleen als je zeker weet dat ze niet nodig zijn):
docker volume prune
- Ongebruikte containers/netwerken:
docker container prune
docker network prune
Root cause: loggroei, ontbrekende logrotate, runaway container die data schrijft.
Troubleshooting: veelvoorkomende problemen
1) cAdvisor toont weinig of rare metrics
- Controleer mounts en privileges.
- Sommige distro’s/cgroups v2 kunnen andere paden gebruiken. Update cAdvisor image en check issues.
- Kijk naar cAdvisor UI:
http://<host>:8080
2) container_spec_memory_limit_bytes is 0
Dat betekent vaak: geen memory limit ingesteld. Zet limits:
docker run -d --name myapp --memory="512m" --cpus="1.0" myorg/myapp:latest
Zonder limits is “% van limit” niet bruikbaar; alarmeer dan op host memory pressure of absolute container usage.
3) Te veel alerts (noise)
- Voeg
for:toe (bijv. 10m) zodat korte spikes niet alarmeren. - Gebruik severity levels (warning vs critical).
- Alert op impact (SLO) in plaats van alleen resource.
4) Loki/Promtail ziet geen logs
- Check of Docker json logs bestaan:
ls -lah /var/lib/docker/containers/*/*-json.log | head
- Check promtail logs:
docker logs --tail 200 promtail
- Let op permissies: promtail container moet de logs kunnen lezen (ro mount is ok).
Checklist voor productie
Metrics
- Host metrics via Node Exporter
- Container metrics via cAdvisor
- App metrics via
/metrics(Prometheus client) - Dashboards voor CPU/memory/disk/inodes/network + app latency/errors
Alerts (minimaal)
- Disk space + inodes (critical)
- Host memory pressure (warning/critical)
- Service error rate (critical)
- Service latency p95/p99 (warning/critical)
- Target down (Prometheus “up == 0” alert)
Voorbeeld “target down” alert (voeg toe aan alerts.yml):
up == 0
Met een regel:
cat >> /opt/monitoring/prometheus/alerts.yml <<'EOF'
- name: meta-alerts
rules:
- alert: TargetDown
expr: up == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Scrape target down: {{ $labels.job }} / {{ $labels.instance }}"
description: "Prometheus kan target niet scrapen gedurende 5m. Check container/network/firewall."
EOF
Logs
- Centrale logsearch (Loki)
- Basis logretentie en diskplanning
- Correlatie: link vanuit alert naar logquery (in runbook)
Proces
- Runbooks per critical alert
- On-call routing (Slack/email/PagerDuty)
- Post-incident review: welke alert had eerder kunnen afgaan?
Volgende stappen (aanbevolen uitbreidingen)
- Blackbox Exporter voor echte endpoint checks (HTTP/TCP/DNS) vanaf buiten je service. Dit detecteert issues die container/host metrics missen (bijv. reverse proxy misconfig, TLS problemen).
- Docker events exporter of eigen script om restarts en healthcheck failures als metrics te exposen.
- Remote storage (Thanos/Cortex/Mimir) voor lange retentie en schaal.
- Traces (OpenTelemetry + Tempo/Jaeger) voor latency root cause in microservices.
Samenvatting
Je hebt nu een complete monitoring- en alertingbasis voor Docker-workloads:
- Prometheus verzamelt metrics van Node Exporter (host) en cAdvisor (containers), plus optioneel je app.
- Grafana visualiseert alles in dashboards.
- Alertmanager ontvangt alerts en kan ze routeren (later uitbreiden naar Slack/email).
- Loki + Promtail centraliseren logs zodat je snel root cause vindt.
De kern is: alert op impact (errors/latency) en bescherm jezelf met harde resource-alerts (disk/inodes/memory). Daarmee detecteer je storingen eerder dan je gebruikers — en heb je meteen de data om ze sneller op te lossen.