von der ersten Scrape-Konfiguration bis zum Grafana-Dashboard
Wer Container-Metriken nur über docker stats beobachtet, verliert Verlaufsdaten und Alarmierung. cAdvisor liest CPU, Memory, Netzwerk und I/O jedes Containers aus, Prometheus speichert die Zeitreihen dauerhaft, und PromQL macht daraus belastbare Kennzahlen für Kapazitätsplanung und Fehlersuche im laufenden Betrieb.
Inhaltsverzeichnis
- 1. Warum docker stats für Container-Metriken nicht reicht
- 2. cAdvisor als Metriken-Quelle einrichten
- 3. Prometheus als Scraper konfigurieren
- 4. PromQL: die wichtigsten Container-Metriken abfragen
- 5. Labels und Relabeling für saubere Zuordnung
- 6. Dashboards in Grafana aufbauen
- 7. Speicherbedarf, Retention und Cardinality im Griff behalten
- 8. Typische Fehler bei der Metriken-Erfassung
- 9. Metriken-Quellen im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum docker stats für Container-Metriken nicht reicht
Der Befehl docker stats zeigt CPU, Memory, Netzwerk und Block I/O in Echtzeit an, speichert aber nichts. Sobald das Terminal geschlossen wird, sind die Werte verloren. Für die Fehlersuche nach einem nächtlichen Speicherleck oder für eine Kapazitätsplanung über mehrere Wochen sind Container-Metriken ohne Historie wertlos. Genau hier setzt die Kombination aus cAdvisor und Prometheus an: cAdvisor liest dieselben Kernel-Zähler wie docker stats, exponiert sie aber dauerhaft als HTTP-Endpunkt, und Prometheus schreibt jede Messung mit Zeitstempel in eine Zeitreihen-Datenbank.
Der zweite Schwachpunkt von docker stats ist die fehlende Aggregation. Wer wissen will, wie sich die Summe aller PHP-FPM-Container über eine Woche entwickelt hat, oder welcher Container in den letzten 24 Stunden am meisten Netzwerk-Traffic verursacht hat, braucht eine Abfragesprache. PromQL bietet genau das: Funktionen wie rate(), sum by() und quantile_over_time() verwandeln rohe Container-Metriken in Kennzahlen, auf denen Kapazitätsentscheidungen und Alarme aufgebaut werden können. Die folgenden Abschnitte zeigen den kompletten Weg von der ersten cAdvisor-Instanz bis zum fertigen Dashboard.
2. cAdvisor als Metriken-Quelle einrichten
cAdvisor, kurz für Container Advisor, ist ein von Google entwickelter Daemon, der auf jedem Docker Host als eigener Container läuft und die cgroups des Kernels ausliest. Jede Sekunde sammelt cAdvisor CPU-Zeit, Speicherverbrauch, Netzwerk-Bytes und Block-I/O für jeden laufenden Container und stellt die Werte im Prometheus-Textformat unter /metrics bereit. Der große Vorteil gegenüber eigenen Skripten: cAdvisor braucht keine Agenten innerhalb der Container, es liest ausschließlich auf Host-Ebene mit, was Overhead und Angriffsfläche gering hält.
Für den Betrieb reicht ein einzelner Container mit Zugriff auf /var/run, /sys und /var/lib/docker als Read-Only-Mounts. Auf Multi-Host-Setups läuft cAdvisor als DaemonSet-Äquivalent, also ein Container pro Host, damit lokale Container-Metriken auch lokal erfasst werden und kein zentraler Single Point of Failure entsteht. Die folgende Compose-Definition zeigt das minimale, produktionstaugliche Setup samt den nötigen Berechtigungen.
# docker-compose.yml — cAdvisor as a metrics source per host
services:
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.49.1
container_name: cadvisor
restart: unless-stopped
ports:
- "127.0.0.1:8080:8080" # bind to localhost, Prometheus scrapes internally
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
devices:
- /dev/kmsg
privileged: false
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/healthz"]
interval: 30s
timeout: 5s
retries: 3
# Quick verification: list all container-level metrics
# curl -s http://localhost:8080/metrics | grep container_cpu_usage_seconds_total
3. Prometheus als Scraper konfigurieren
Prometheus arbeitet als Pull-basiertes Monitoring-System: Es fragt periodisch die konfigurierten Ziele ab, statt dass die Ziele selbst Daten senden. Für Container-Metriken bedeutet das, cAdvisor als Scrape-Target in prometheus.yml einzutragen. Ein Scrape-Interval von 15 Sekunden ist für die meisten Docker-Umgebungen ein guter Kompromiss zwischen Auflösung und Speicherbedarf, kürzere Intervalle erhöhen die Genauigkeit bei kurzlebigen Lastspitzen, verbrauchen aber deutlich mehr Storage.
Wichtig ist die korrekte Job-Benennung, weil sie später in jeder PromQL-Abfrage als Filter dient. In Multi-Host-Setups listet man jeden Host einzeln unter static_configs oder nutzt Service Discovery, etwa über DNS oder eine Datei mit file_sd_configs, damit neue Hosts automatisch erkannt werden, ohne die Prometheus-Konfiguration manuell anzupassen. Die relative_timeout-Einstellung sollte kleiner als das Scrape-Interval bleiben, damit ein hängender cAdvisor-Endpunkt nicht den gesamten Scrape-Zyklus blockiert.
# prometheus.yml — scrape configuration for container metrics
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: "cadvisor"
scrape_interval: 15s
scrape_timeout: 10s
static_configs:
- targets:
- "host1:8080"
- "host2:8080"
labels:
environment: "production"
- job_name: "node-exporter"
static_configs:
- targets:
- "host1:9100"
- "host2:9100"
labels:
environment: "production"
rule_files:
- "alerts/*.yml"
4. PromQL: die wichtigsten Container-Metriken abfragen
Rohe Zähler wie container_cpu_usage_seconds_total sind kumulativ und für sich genommen wenig aussagekräftig. Erst die rate()-Funktion, angewendet über ein Zeitfenster, ergibt eine sinnvolle CPU-Auslastung in Kernen pro Sekunde. Für Speicherverbrauch nutzt man dagegen keinen Zähler, sondern das Gauge container_memory_working_set_bytes direkt, weil Memory zu jedem Zeitpunkt einen absoluten Wert darstellt und keine Rate braucht. Diese Unterscheidung zwischen Zählern und Gauges ist die Grundlage jeder korrekten PromQL-Abfrage für Container-Metriken.
Für die tägliche Praxis reichen wenige Abfrage-Muster: CPU-Auslastung pro Container über rate(...[5m]), Speicherverbrauch relativ zum gesetzten Limit über eine Division zweier Metriken, und Netzwerk-Durchsatz über die Ableitung von container_network_transmit_bytes_total. Die sum by (name)-Klausel gruppiert Ergebnisse nach Container-Namen, damit ein Dashboard nicht Hunderte einzelne Zeitreihen zeigt, sondern aggregierte, lesbare Kurven pro Service.
# PromQL — common container metrics queries
# CPU usage per container, in cores, averaged over 5 minutes
sum by (name) (rate(container_cpu_usage_seconds_total{name!=""}[5m]))
# Memory usage relative to the configured limit, as a percentage
100 * (
container_memory_working_set_bytes{name!=""}
/
container_spec_memory_limit_bytes{name!=""} > 0
)
# Network transmit rate in bytes per second, per container
sum by (name) (rate(container_network_transmit_bytes_total{name!=""}[5m]))
# Top 5 containers by CPU usage right now
topk(5, sum by (name) (rate(container_cpu_usage_seconds_total[1m])))
# Containers restarting more than twice in the last hour (needs cadvisor >= 0.47)
increase(container_last_seen{name!=""}[1h]) > 2
5. Labels und Relabeling für saubere Zuordnung
cAdvisor liefert von Haus aus viele nützliche Labels, darunter name, image und id, aber diese Rohdaten enthalten oft technische Container-IDs statt sprechender Service-Namen. Mit metric_relabel_configs in Prometheus lassen sich Labels vor dem Speichern umbenennen, zusammenfassen oder filtern, sodass Dashboards und Alarme auf sprechenden Namen wie service="magento-web" statt auf einer zufälligen Container-ID basieren.
Ein häufiges Muster ist das Filtern von Pause-Containern und Infrastruktur-Containern, die für die eigentliche Anwendungs-Observability irrelevant sind. Ohne diesen Filter blähen sich Container-Metriken-Dashboards mit Rauschen auf, und die Cardinality der Zeitreihen steigt unnötig. Ein Relabel-Regelwerk, das konsequent auf Compose-Projekt-Labels wie com.docker.compose.service zugreift, macht die Zuordnung zwischen Metrik und Anwendung sofort nachvollziehbar, auch für Kollegen, die die Infrastruktur nicht selbst aufgebaut haben.
6. Dashboards in Grafana aufbauen
Prometheus selbst bietet nur eine rudimentäre Oberfläche für Ad-hoc-Abfragen, für dauerhafte Dashboards ist Grafana die naheliegende Wahl. Nach dem Hinzufügen von Prometheus als Datenquelle lassen sich die PromQL-Abfragen aus Abschnitt 4 direkt in Panels einfügen. Ein sinnvolles Grund-Dashboard für Container-Metriken zeigt mindestens vier Panels: CPU-Auslastung pro Container als Zeitreihe, Memory relativ zum Limit als Balkendiagramm, Netzwerk-Durchsatz als gestapelte Fläche und eine Tabelle der Top-5-Container nach Ressourcenverbrauch.
Grafana-Variablen wie $container oder $environment, gespeist aus label_values()-Abfragen, machen ein einzelnes Dashboard für alle Container wiederverwendbar, statt für jeden Service ein eigenes Dashboard zu pflegen. Fertige Community-Dashboards für cAdvisor, etwa mit der ID 893 im Grafana-Dashboard-Katalog, liefern einen guten Ausgangspunkt, sollten aber an die eigenen Label-Namen angepasst werden, weil abweichende Relabeling-Regeln sonst zu leeren Panels führen.
7. Speicherbedarf, Retention und Cardinality im Griff behalten
Jede zusätzliche Label-Kombination erzeugt in Prometheus eine neue Zeitreihe, und jede Zeitreihe kostet Speicher und Abfragezeit. Bei Container-Metriken mit kurzlebigen Containern, etwa bei häufigen Deployments mit wechselnden Container-IDs, kann die Cardinality unbemerkt explodieren, wenn Labels wie id statt stabiler Service-Namen verwendet werden. Ein Blick auf prometheus_tsdb_symbol_table_size_bytes und die Anzahl aktiver Serien über count({__name__=~".+"}) zeigt frühzeitig, ob die Cardinality aus dem Ruder läuft.
Die Standard-Retention von 15 Tagen reicht für operative Fehlersuche, für Kapazitätstrends über Monate braucht es entweder eine höhere --storage.tsdb.retention.time, was den lokalen Speicherbedarf linear erhöht, oder ein Remote-Write in ein Long-Term-Storage-System wie Thanos oder Mimir. Für die meisten mittelgroßen Docker-Umgebungen ist eine Kombination aus 30 Tagen lokaler Retention plus wöchentlichen Snapshots als Backup ein praktikabler Mittelweg zwischen Aufwand und Nutzen.
8. Typische Fehler bei der Metriken-Erfassung
Der häufigste Fehler ist ein zu hoch gewähltes Scrape-Interval kombiniert mit kurzen rate()-Fenstern. Ein Scrape-Interval von 60 Sekunden mit rate(...[1m]) liefert instabile, verrauschte Werte, weil im Fenster oft nur ein oder zwei Datenpunkte liegen. Die Faustregel: Das rate()-Fenster sollte mindestens das Vierfache des Scrape-Intervals betragen, damit genügend Datenpunkte für eine stabile Ableitung vorliegen.
# Common mistakes when collecting container metrics
# WRONG: scrape interval too coarse combined with a narrow rate window
# scrape_interval: 60s
# rate(container_cpu_usage_seconds_total[1m]) # unstable, too few samples
# RIGHT: rate window at least 4x the scrape interval
# scrape_interval: 15s
# rate(container_cpu_usage_seconds_total[5m]) # smooth, reliable
# WRONG: querying memory as if it were a counter
# rate(container_memory_working_set_bytes[5m]) # meaningless for a gauge
# RIGHT: gauges are read directly, no rate needed
# container_memory_working_set_bytes{name="magento-web"}
# WRONG: relying on unstable container IDs as the grouping label
# sum by (id) (rate(container_cpu_usage_seconds_total[5m]))
# RIGHT: group by a stable label added via relabeling
# sum by (compose_service) (rate(container_cpu_usage_seconds_total[5m]))
Ein zweiter verbreiteter Fehler ist das Fehlen von Limits als Referenzwert. Ohne container_spec_memory_limit_bytes als Nenner sagt der absolute Speicherverbrauch wenig darüber aus, wie nah ein Container am OOM-Kill steht. Ein dritter Fehler betrifft die Netzwerk-Metriken: container_network_receive_bytes_total wird pro Netzwerk-Interface geführt, bei mehreren Netzwerken pro Container muss also über das Interface-Label aggregiert werden, sonst zeigt das Dashboard nur einen Bruchteil des tatsächlichen Traffics.
9. Metriken-Quellen im direkten Vergleich
Neben cAdvisor gibt es weitere Wege, an Container-Metriken zu kommen, mit unterschiedlichen Kompromissen bei Overhead, Granularität und Integrationsaufwand.
| Quelle | Granularität | Overhead | Empfehlung |
|---|---|---|---|
| docker stats | Live, keine Historie | Minimal | Nur für Ad-hoc-Checks |
| cAdvisor + Prometheus | Sekunden, mit Historie | Gering | Standard für Docker-Hosts |
| Docker Stats API (JSON) | Live, keine Historie | Minimal | Für eigene Skripte, kein Ersatz für Zeitreihen |
| In-App-Instrumentierung | Sehr fein, applikationsnah | Höher, pro Anwendung | Ergänzend zu cAdvisor, nicht alternativ |
In der Praxis schließen sich diese Quellen nicht aus. cAdvisor liefert die Host- und Container-Perspektive, während In-App-Metriken über eigene Prometheus-Client-Bibliotheken applikationsspezifische Kennzahlen wie Request-Latenzen oder Queue-Längen ergänzen. Die Kombination beider Ebenen ergibt erst ein vollständiges Bild der Container-Metriken einer produktiven Umgebung.
10. Zusammenfassung
Container-Metriken mit Prometheus und cAdvisor lösen das Kernproblem von docker stats: fehlende Historie und fehlende Aggregation. cAdvisor liest CPU, Memory, Netzwerk und I/O direkt aus den cgroups des Kernels und exponiert sie im Prometheus-Format. Prometheus scraped diese Werte periodisch und speichert sie als Zeitreihen. PromQL macht daraus aussagekräftige Kennzahlen, von der CPU-Rate über die Memory-Auslastung relativ zum Limit bis zu Top-Listen der ressourcenhungrigsten Container.
Der entscheidende Hebel für belastbare Dashboards liegt in sauberen Labels: stabile Service-Namen statt zufälliger Container-IDs, ein Scrape-Interval, das zum gewählten rate()-Fenster passt, und eine bewusste Retention-Strategie, die Speicherbedarf und Historie ausbalanciert. Wer diese Grundlagen beherzigt, hat mit cAdvisor und Prometheus ein Monitoring-Fundament, das sich nahtlos um Alerting und verteiltes Tracing erweitern lässt.
Container-Metriken mit Prometheus und cAdvisor — Das Wichtigste auf einen Blick
Erfassung
cAdvisor liest cgroups direkt aus dem Kernel, ein Container pro Host, kein Agent innerhalb der Anwendungscontainer nötig.
Speicherung
Prometheus scraped im 15 Sekunden Takt und speichert Zeitreihen mit konfigurierbarer Retention lokal oder per Remote-Write.
Auswertung
PromQL mit rate(), sum by() und topk() verwandelt rohe Zähler in CPU-, Memory- und Netzwerk-Kennzahlen pro Service.
Visualisierung
Grafana mit Variablen für Container und Environment macht ein Dashboard für die gesamte Flotte wiederverwendbar.