← Terug naar tutorials

Monitoring en alerting voor Docker-workloads: storingen detecteren vóór je gebruikers

dockermonitoringalertingdevopsobservability

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

  1. Wat je wilt meten (en waarom)
  2. Architectuur: metrics, logs, traces (en wat we hier doen)
  3. Voorbereiding: Docker host harden en basischecks
  4. Metrics verzamelen: cAdvisor + Node Exporter + Prometheus
  5. Dashboards: Grafana
  6. Alerting: Alertmanager + concrete alertregels
  7. Logs: Docker logs, JSON-driver, en Loki + Promtail
  8. Praktische SLO/SLI-alerts voor webservices
  9. Runbooks en incidentworkflow
  10. Troubleshooting: veelvoorkomende problemen
  11. 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”

Waarom: een container kan “up” zijn maar toch geen verkeer verwerken (bijv. deadlock). Daarom combineer je liveness met applicatiechecks.

2) Resourceverbruik (host en container)

Waarom: de meeste incidenten zijn resource-gerelateerd. Memory leaks, noisy neighbors, disk vol door logs, of throttling door CPU-limieten.

3) Applicatiegezondheid

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:

In deze tutorial bouwen we:

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

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?

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:

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:

Handige dashboards importeren

Je kunt community dashboards importeren (ID’s wijzigen soms). Zoek in Grafana “Dashboards → Import” en gebruik bijvoorbeeld:

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:

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

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:

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:

  1. Betekenis: wat zegt de alert?
  2. Impact: wat merken gebruikers?
  3. Eerste checks (2–5 minuten)
  4. Mitigatie (snel stabiliseren)
  5. Root cause hints
  6. 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):

docker image prune -a
docker volume prune
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

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)

4) Loki/Promtail ziet geen logs

ls -lah /var/lib/docker/containers/*/*-json.log | head
docker logs --tail 200 promtail

Checklist voor productie

Metrics

Alerts (minimaal)

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

Proces


Volgende stappen (aanbevolen uitbreidingen)

  1. 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).
  2. Docker events exporter of eigen script om restarts en healthcheck failures als metrics te exposen.
  3. Remote storage (Thanos/Cortex/Mimir) voor lange retentie en schaal.
  4. Traces (OpenTelemetry + Tempo/Jaeger) voor latency root cause in microservices.

Samenvatting

Je hebt nu een complete monitoring- en alertingbasis voor Docker-workloads:

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.