Docker-Logs mehrerer Hosts an einer Stelle durchsuchen
Wer Logs mit docker logs auf jedem Host einzeln durchsucht, verliert bei einem Ausfall wertvolle Minuten. Zentrales Logging mit Loki und Promtail sammelt jede Container-Ausgabe strukturiert ein, indexiert nur Labels statt des vollen Textes und macht Fehlersuche über die gesamte Docker-Flotte mit einer einzigen LogQL-Abfrage möglich.
Inhaltsverzeichnis
- 1. Warum zentrales Logging bei Containern unverzichtbar wird
- 2. Architektur: Promtail, Loki und Grafana im Zusammenspiel
- 3. Promtail einrichten und Docker-Logs erfassen
- 4. Loki konfigurieren: Speicher, Retention und Limits
- 5. LogQL: Logs gezielt filtern und durchsuchen
- 6. Label-Strategie: wenig Cardinality, viel Nutzen
- 7. Logs und Metriken gemeinsam in Grafana auswerten
- 8. Typische Fehler beim Aufbau von zentralem Logging
- 9. Zentrales Logging im direkten Vergleich der Ansätze
- 10. Zusammenfassung
- 11. FAQ
1. Warum zentrales Logging bei Containern unverzichtbar wird
Solange ein Docker-Host alle Container einer Anwendung trägt, reicht docker logs für die Fehlersuche meistens aus. Sobald mehrere Hosts, mehrere Umgebungen oder eine wachsende Anzahl an Services ins Spiel kommen, wird diese Methode unpraktikabel. Zentrales Logging löst genau dieses Skalierungsproblem: Statt sich per SSH auf jeden einzelnen Host zu verbinden und Logs manuell zu durchsuchen, landen alle Ausgaben an einer zentralen Stelle, durchsuchbar über eine einzige Oberfläche.
Der zweite Grund für zentrales Logging ist die Nachvollziehbarkeit über Container-Neustarts hinweg. Docker rotiert und verwirft Logs standardmäßig nach den Einstellungen des Log-Drivers, und ein neu erstellter Container startet mit leerem Log. Ohne zentrale Sammlung sind ältere Fehler nach einem Deployment oder Neustart unwiderruflich verloren. Loki, entwickelt von Grafana Labs, wurde speziell für diesen Anwendungsfall konzipiert: kostengünstige, horizontal skalierbare Speicherung von Logs mit minimalem Indexierungsaufwand.
2. Architektur: Promtail, Loki und Grafana im Zusammenspiel
Der Loki-Stack besteht aus drei Komponenten mit klar getrennten Aufgaben. Promtail läuft als Agent auf jedem Docker-Host, entdeckt laufende Container automatisch und liest deren Log-Dateien oder den Docker-Socket aus. Loki selbst ist die Speicher- und Abfrage-Engine, die eingehende Logs entgegennimmt, mit Labels versieht und persistiert. Grafana schließlich dient als Oberfläche für Abfragen und Visualisierung, dieselbe Oberfläche, die bereits für Prometheus-Metriken genutzt wird.
Der entscheidende Architektur-Unterschied zu klassischen Log-Systemen wie Elasticsearch: Loki indexiert nicht den vollständigen Log-Inhalt, sondern nur die Labels, etwa container, compose_service oder level. Der eigentliche Text wird komprimiert in Chunks abgelegt und erst bei einer Abfrage durchsucht. Dieser Ansatz reduziert den Speicherbedarf und die Betriebskosten erheblich, verlangt im Gegenzug aber eine durchdachte Label-Strategie, damit Abfragen nicht zu langsam werden.
# docker-compose.yml — minimal Loki + Promtail + Grafana stack
services:
loki:
image: grafana/loki:2.9.6
ports:
- "3100:3100"
volumes:
- ./loki-config.yml:/etc/loki/local-config.yaml:ro
- loki-data:/loki
command: -config.file=/etc/loki/local-config.yaml
promtail:
image: grafana/promtail:2.9.6
volumes:
- ./promtail-config.yml:/etc/promtail/config.yml:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
command: -config.file=/etc/promtail/config.yml
depends_on:
- loki
grafana:
image: grafana/grafana:10.4.2
ports:
- "3000:3000"
depends_on:
- loki
volumes:
loki-data:
3. Promtail einrichten und Docker-Logs erfassen
Promtail nutzt die Docker Service Discovery, um automatisch alle laufenden Container zu erkennen, ohne für jeden Service eine eigene Konfiguration zu pflegen. Über docker_sd_configs fragt Promtail regelmäßig den Docker-Socket ab und liest die zugehörigen Log-Dateien unter /var/lib/docker/containers. Relabeling-Regeln, syntaktisch identisch zu Prometheus, extrahieren aus den Docker-Labels sprechende Werte wie den Compose-Service-Namen oder den Projektnamen.
Ein wichtiges Detail: Standardmäßig protokolliert Docker JSON-formatierte Logzeilen mit einem eingebetteten log-Feld. Die Pipeline-Stage json in Promtail parst diese Struktur, extrahiert den eigentlichen Log-Text und trennt Stdout von Stderr. Wer strukturierte Anwendungs-Logs im JSON-Format schreibt, etwa aus PHP oder Node.js, kann eine zweite json-Stage verschachteln, um Felder wie level oder request_id direkt als Loki-Label zu extrahieren.
# promtail-config.yml — Docker service discovery + relabeling
server:
http_listen_port: 9080
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: docker
docker_sd_configs:
- host: unix:///var/run/docker.sock
refresh_interval: 5s
relabel_configs:
- source_labels: ["__meta_docker_container_label_com_docker_compose_service"]
target_label: "compose_service"
- source_labels: ["__meta_docker_container_label_com_docker_compose_project"]
target_label: "compose_project"
- source_labels: ["__meta_docker_container_name"]
regex: "/(.*)"
target_label: "container"
pipeline_stages:
- docker: {}
- json:
expressions:
level: level
request_id: request_id
- labels:
level:
4. Loki konfigurieren: Speicher, Retention und Limits
Loki unterstützt mehrere Storage-Backends, von lokalem Dateisystem für kleine Setups bis zu S3-kompatiblem Object Storage für produktive, skalierende Umgebungen. Für einzelne Docker-Hosts ist der Filesystem-Store völlig ausreichend, für Multi-Host-Umgebungen mit mehreren Loki-Instanzen empfiehlt sich Object Storage, damit alle Instanzen auf denselben Datenbestand zugreifen können. Die Retention wird über limits_config und compactor gesteuert, ein Wert von 30 Tagen ist für die meisten zentrales Logging-Setups ein sinnvoller Ausgangspunkt.
Die Ingestion-Limits verdienen besondere Aufmerksamkeit. Standardmäßig begrenzt Loki die Rate eingehender Logs pro Stream und Tenant, um einen einzelnen fehlerhaften Container davor zu bewahren, mit einer Endlosschleife von Fehlermeldungen die gesamte Loki-Instanz zu überlasten. Diese Limits sollten bewusst auf die erwartete Log-Menge der eigenen Container-Flotte angepasst werden, statt sie unreflektiert zu erhöhen, sobald die ersten Rate-Limit-Fehler auftreten.
5. LogQL: Logs gezielt filtern und durchsuchen
LogQL, die Abfragesprache von Loki, kombiniert Label-Selektoren mit optionalen Text- und Parsing-Filtern, ähnlich der Syntax von PromQL. Eine typische Abfrage beginnt mit einem Label-Selektor wie {compose_service="magento-web"}, der die Suche auf einen bestimmten Service eingrenzt, bevor überhaupt der Log-Text durchsucht wird. Erst danach kommen Text-Filter wie |= "error" oder Pattern-Filter mit regulären Ausdrücken zum Einsatz.
Für Alarme und Dashboards unterstützt LogQL auch Metrik-Abfragen direkt auf Logs, etwa count_over_time(), um die Anzahl der Fehlerzeilen pro Zeitfenster zu zählen, ohne eine separate Metrik-Pipeline zu benötigen. Diese Fähigkeit macht Loki zu mehr als einem reinen Log-Speicher: Aus Logs lassen sich direkt Kennzahlen für Alertmanager oder Grafana-Alerting ableiten, ganz ohne zusätzliche Instrumentierung der Anwendung.
# LogQL — common queries for centralized container logging
# All logs from one service in the last hour
{compose_service="magento-web"}
# Only error-level lines, across all containers of a project
{compose_project="shop"} | json | level="error"
# Text search combined with a label filter
{compose_service="magento-web"} |= "MySQL server has gone away"
# Rate of error lines per minute, usable as a Grafana panel or alert
sum(count_over_time({compose_project="shop"} |= "error" [1m]))
# Extract and filter on a JSON field without pre-defining it as a label
{compose_service="checkout-api"} | json | duration_ms > 2000
6. Label-Strategie: wenig Cardinality, viel Nutzen
Der größte konzeptionelle Unterschied zwischen Loki und klassischen Volltext-Suchsystemen liegt in der bewussten Beschränkung der Labels. Jedes zusätzliche Label mit hoher Kardinalität, etwa eine eindeutige Request-ID oder ein Zeitstempel als Label statt als Feld, erzeugt einen neuen Stream in Loki und vervielfacht damit den Indexierungsaufwand. Die Empfehlung lautet: Labels nur für Werte mit begrenzter, stabiler Wertemenge, wie Service-Name, Environment oder Log-Level, während variable Werte wie Request-IDs als durchsuchbarer Text oder JSON-Feld im Log selbst verbleiben.
Diese bewusste Trennung zwischen Labels und Inhalt ist der Kern jeder erfolgreichen zentrales Logging-Strategie mit Loki. Wer stattdessen versucht, Loki wie Elasticsearch zu behandeln und jedes Feld als Label zu extrahieren, erzeugt Tausende Streams und erlebt spürbare Performance-Einbrüche bei Abfragen sowie einen unnötig aufgeblähten Index. Die Faustregel: lieber weniger, stabilere Labels und dafür leistungsfähige Text- und JSON-Filter in der Abfrage selbst.
7. Logs und Metriken gemeinsam in Grafana auswerten
Der eigentliche Mehrwert von zentrales Logging mit Loki entsteht, wenn Logs und Metriken in derselben Grafana-Oberfläche kombiniert werden. Ein Dashboard-Panel mit der CPU-Auslastung eines Containers aus Prometheus lässt sich direkt neben ein Log-Panel mit den zugehörigen Fehlermeldungen aus Loki platzieren, beide gefiltert auf dieselbe Zeitspanne und denselben Service-Namen. Diese Korrelation spart in der Praxis erhebliche Zeit bei der Fehlersuche, weil der Zusammenhang zwischen einer Lastspitze und den dazugehörigen Fehlermeldungen sofort sichtbar wird.
Grafanas Explore-Ansicht unterstützt zusätzlich das direkte Springen von einer Metrik zu den korrelierenden Logs über sogenannte Derived Fields, sofern Trace- oder Request-IDs konsistent in Logs und Metriken vorhanden sind. Für Teams, die noch kein verteiltes Tracing einsetzen, ist diese Log-Metrik-Korrelation oft der pragmatischste erste Schritt in Richtung vollständiger Observability, ohne gleich eine komplette Tracing-Infrastruktur aufbauen zu müssen.
8. Typische Fehler beim Aufbau von zentralem Logging
Der häufigste Fehler ist das unreflektierte Extrahieren jedes JSON-Feldes als Loki-Label, was, wie in Abschnitt 6 beschrieben, die Cardinality explodieren lässt. Ein zweiter verbreiteter Fehler betrifft die Positions-Datei von Promtail: Wird sie beim Neustart des Promtail-Containers versehentlich gelöscht, statt in einem persistenten Volume zu liegen, liest Promtail alle Logs erneut von vorne ein und erzeugt massenhaft Duplikate in Loki.
# Common mistakes when building centralized logging with Loki
# WRONG: every JSON field becomes a label — cardinality explosion
# pipeline_stages:
# - json:
# expressions:
# request_id: request_id
# - labels:
# request_id: # thousands of unique streams
# RIGHT: high-cardinality fields stay in the log line, not as labels
# pipeline_stages:
# - json:
# expressions:
# request_id: request_id
# # no labels stage for request_id — filter with | json | request_id="..."
# WRONG: positions file lost on container restart — full log re-read
# volumes:
# - promtail-tmp:/tmp # ephemeral, resets on recreate
# RIGHT: persistent volume for the positions file
# volumes:
# - promtail-positions:/data
# positions:
# filename: /data/positions.yaml
Ein dritter Fehler ist das Fehlen von Ingestion-Limits in Loki, kombiniert mit einer fehlerhaften Anwendung, die in einer Schleife Tausende identischer Fehlermeldungen pro Sekunde schreibt. Ohne konfigurierte Rate-Limits kann ein einzelner Container die gesamte Loki-Instanz überlasten und damit das zentrales Logging für alle anderen Services lahmlegen. Sinnvolle Limits pro Tenant und Stream verhindern genau dieses Szenario, ohne normale Log-Volumina einzuschränken.
9. Zentrales Logging im direkten Vergleich der Ansätze
Loki ist nicht die einzige Möglichkeit für zentrales Logging von Containern. Die Wahl zwischen den verbreitetsten Ansätzen hängt von Datenmenge, Suchanforderungen und vorhandener Infrastruktur ab.
| Ansatz | Indexierung | Ressourcenbedarf | Empfehlung |
|---|---|---|---|
| docker logs pro Host | Keine, nur lokal | Minimal | Nur Einzel-Host-Setups |
| Loki + Promtail | Nur Labels | Gering | Standard für Docker-Umgebungen |
| Elasticsearch/ELK | Volltext, jedes Feld | Hoch | Bei komplexer Volltextsuche über große Datenmengen |
| Managed SaaS-Logging | Anbieterabhängig | Kein eigener Betrieb | Wenn Betriebsaufwand vermieden werden soll |
Für die meisten Docker-basierten Projekte ist Loki der beste Kompromiss zwischen Betriebskosten und Funktionsumfang, weil es sich nahtlos in eine ohnehin vorhandene Prometheus- und Grafana-Landschaft einfügt. Elasticsearch bleibt die richtige Wahl, wenn tatsächlich komplexe Volltextsuchen über riesige, unstrukturierte Log-Mengen benötigt werden, etwa bei Security-Audits mit sehr variablen Suchmustern.
10. Zusammenfassung
Zentrales Logging mit Loki und Promtail löst das Problem verstreuter, flüchtiger Container-Logs auf mehreren Hosts. Promtail entdeckt Container automatisch über Docker Service Discovery, liest deren Logs und versieht sie mit sprechenden Labels. Loki speichert die Logs kostengünstig, weil nur Labels indexiert werden, während der eigentliche Text komprimiert in Chunks liegt. LogQL kombiniert Label-Selektoren mit Text- und JSON-Filtern und erlaubt sogar Metrik-Abfragen direkt auf Logs.
Der entscheidende Erfolgsfaktor ist eine disziplinierte Label-Strategie: stabile, niedrig-kardinale Werte als Labels, variable Werte wie Request-IDs im durchsuchbaren Log-Text. Wer diese Trennung konsequent umsetzt, bekommt mit Loki eine Log-Plattform, die sich problemlos neben Prometheus-Metriken in Grafana integrieren lässt und die Grundlage für spätere Erweiterungen wie Alerting oder verteiltes Tracing bildet.
Zentrales Container-Logging mit Loki und Promtail — Das Wichtigste auf einen Blick
Erfassung
Promtail erkennt Container automatisch über Docker Service Discovery und liest deren Logs ein.
Speicherung
Loki indexiert nur Labels, der Log-Text liegt komprimiert vor, das senkt Speicherbedarf und Kosten deutlich.
Abfrage
LogQL kombiniert Label-Selektoren mit Text- und JSON-Filtern sowie Metrik-Funktionen wie count_over_time().
Label-Strategie
Nur stabile, niedrig-kardinale Werte als Labels, Request-IDs und ähnliche Felder bleiben im Log-Text.