Zentrales Logging wird unverzichtbar, sobald Sie mehr als eine Handvoll Server, Container oder Microservices verwalten. Ohne es bedeutet das Debuggen eines Produktionsvorfalls, sich per SSH in einzelne Maschinen einzuloggen und in verstreuten Log-Dateien zu suchen. Log-Aggregation mit Loki, Promtail und Grafana — der PLG-Stack — löst dieses Problem, indem alle Logs in einen einzigen abfragbaren Speicher übertragen werden und die Ressourcenkosten dabei deutlich niedriger bleiben als bei traditionellen Lösungen wie Elasticsearch.
Voraussetzungen
- Ein Linux-Server (Ubuntu 22.04 oder Debian 12 empfohlen) mit mindestens 2 GB RAM
- Docker 24+ und Docker Compose v2 installiert
- Grundlegende Kenntnisse der Grafana-Dashboards (siehe Grafana Dashboards für Infrastruktur-Monitoring)
- Ports 3000 (Grafana), 3100 (Loki) und 9080 (Promtail) verfügbar
- Sudo-Zugriff auf dem Host-System
Den PLG-Stack verstehen
Der PLG-Stack besteht aus drei Komponenten, die zusammen als leichte Observability-Pipeline arbeiten:
Promtail ist der Log-Sammel-Agent. Er läuft neben Ihren Anwendungen, liest Log-Dateien oder das systemd-Journal, hängt Labels (Schlüssel-Wert-Metadaten) an und überträgt Log-Streams per HTTP an Loki.
Loki ist das Log-Aggregations-Backend. Anders als Elasticsearch indiziert Loki den Log-Inhalt nicht — es indiziert nur die von Promtail angehängten Labels. Die rohen Log-Zeilen werden komprimiert und als Chunks gespeichert. Diese Architektur macht Loki deutlich günstiger im Betrieb: Ein Cluster, der täglich Gigabytes an Logs verarbeitet, kann mit einigen hundert Megabytes RAM auskommen.
Grafana bietet die Abfrage- und Visualisierungsschicht. Es verbindet sich mit Loki als Datenquelle und ermöglicht LogQL-Abfragen, Dashboard-Erstellung und Alert-Konfiguration — alles in derselben Oberfläche wie für Prometheus-Metriken (siehe Prometheus und Grafana für Server-Monitoring einrichten).
Der Datenfluss ist: Anwendung → Log-Datei/Journal → Promtail → Loki → Grafana.
Loki mit Docker Compose installieren
Erstellen Sie ein Arbeitsverzeichnis und die folgende docker-compose.yml:
version: "3.8"
networks:
logging:
driver: bridge
volumes:
loki_data:
grafana_data:
services:
loki:
image: grafana/loki:3.0.0
container_name: loki
ports:
- "3100:3100"
volumes:
- ./loki-config.yaml:/etc/loki/loki-config.yaml
- loki_data:/loki
command: -config.file=/etc/loki/loki-config.yaml
networks:
- logging
promtail:
image: grafana/promtail:3.0.0
container_name: promtail
volumes:
- ./promtail-config.yaml:/etc/promtail/promtail-config.yaml
- /var/log:/var/log:ro
- /run/log/journal:/run/log/journal:ro
- /etc/machine-id:/etc/machine-id:ro
command: -config.file=/etc/promtail/promtail-config.yaml
networks:
- logging
depends_on:
- loki
grafana:
image: grafana/grafana:11.0.0
container_name: grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=changeme
volumes:
- grafana_data:/var/lib/grafana
networks:
- logging
depends_on:
- loki
Erstellen Sie loki-config.yaml im selben Verzeichnis:
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
common:
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
instance_addr: 127.0.0.1
kvstore:
store: inmemory
schema_config:
configs:
- from: 2024-01-01
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
limits_config:
retention_period: 30d
compactor:
working_directory: /loki/compactor
retention_enabled: true
delete_request_store: filesystem
Stack starten:
docker compose up -d
docker compose ps
Alle drei Dienste sollten innerhalb von 30 Sekunden den Status healthy oder running anzeigen.
Promtail konfigurieren
Erstellen Sie promtail-config.yaml:
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: journal
journal:
max_age: 12h
labels:
job: systemd-journal
host: ${HOSTNAME}
relabel_configs:
- source_labels: [__journal__systemd_unit]
target_label: unit
- job_name: varlogs
static_configs:
- targets:
- localhost
labels:
job: varlogs
host: ${HOSTNAME}
__path__: /var/log/*.log
- job_name: docker
docker_sd_configs:
- host: unix:///var/run/docker.sock
refresh_interval: 5s
relabel_configs:
- source_labels: [__meta_docker_container_name]
regex: "/(.*)"
target_label: container
- source_labels: [__meta_docker_container_label_com_docker_compose_service]
target_label: service
Die positions-Datei verfolgt, wie weit Promtail in jeder Log-Datei gelesen hat, und verhindert nach Neustarts doppelte Ingestion.
Promtail neu starten, um die Konfiguration anzuwenden:
docker compose restart promtail
docker compose logs promtail --tail=20
Logs mit LogQL abfragen
LogQL ist Lokis Abfragesprache, inspiriert von PromQL. Abfragen haben zwei Teile: einen Stream-Selektor in geschweiften Klammern und optionale Pipeline-Stufen.
Einfacher Stream-Selektor — alle Logs des varlogs-Jobs abrufen:
{job="varlogs"}
Zeilenfilter — Zeilen mit “error” finden:
{job="varlogs"} |= "error"
Regex-Filter — Zeilen nach einem Muster filtern:
{job="systemd-journal"} |~ "fail(ed)?"
Label-Filter nach Parsing — JSON-Logs parsen und nach Level filtern:
{job="varlogs"} | json | level="error"
Metrik-Abfrage — Fehlerrate über Zeit:
rate({job="varlogs"} |= "error" [5m])
Fehler nach Host zählen:
sum by (host) (count_over_time({job="varlogs"} |= "error" [1h]))
Für komplexere Log-Parsing-Muster vergleichen Sie den Ansatz Journalctl: Linux-Systemlogs abfragen und analysieren für rein lokale Szenarien.
Grafana-Dashboards einrichten
Öffnen Sie Grafana unter http://ihr-server:3000 und melden Sie sich mit admin / changeme an.
Loki als Datenquelle hinzufügen:
- Gehen Sie zu Verbindungen > Datenquellen > Datenquelle hinzufügen
- Wählen Sie Loki
- Setzen Sie die URL auf
http://loki:3100 - Klicken Sie auf Speichern und testen — Sie sollten “Datenquelle verbunden und Labels gefunden” sehen
Log-Explorations-Panel erstellen:
- Erstellen Sie ein neues Dashboard und fügen Sie ein Panel hinzu
- Wählen Sie Loki als Datenquelle
- Wechseln Sie die Visualisierung zu Logs
- Geben Sie eine LogQL-Abfrage ein:
{job="varlogs"} |= "error" - Aktivieren Sie Deduplizierung und Zeilenumbruch in den Panel-Optionen
Rate-Dashboard aufbauen:
Fügen Sie ein Zeitreihen-Panel mit der Abfrage hinzu:
sum by (host) (rate({job="varlogs"} |= "error" [5m]))
Dies liefert ein Echtzeit-Fehlerrate-Diagramm aufgeteilt nach Server.
Alerts auf Log-Muster einrichten
Loki enthält eine Ruler-Komponente, die LogQL-Metrikabfragen nach einem Zeitplan auswertet und Alerts an Alertmanager sendet.
Ruler-Konfiguration zur loki-config.yaml hinzufügen:
ruler:
storage:
type: local
local:
directory: /loki/rules
rule_path: /loki/rules-temp
alertmanager_url: http://alertmanager:9093
enable_api: true
Erstellen Sie /loki/rules/fake/rules.yaml:
groups:
- name: log-alerts
rules:
- alert: HighErrorRate
expr: |
sum(rate({job="varlogs"} |= "error" [5m])) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "Hohe Fehlerrate in System-Logs erkannt"
description: "Die Fehler-Log-Rate hat 0.1 Zeilen/Sek. für 5 Minuten überschritten."
- alert: SSHAuthFailures
expr: |
sum(count_over_time({unit="sshd.service"} |= "Failed password" [10m])) > 10
for: 2m
labels:
severity: critical
annotations:
summary: "Mehrfache SSH-Authentifizierungsfehler"
description: "Mehr als 10 SSH-Auth-Fehler in 10 Minuten — möglicher Brute-Force-Angriff."
Loki vs. ELK vs. Graylog — Vergleich
| Merkmal | Loki | ELK Stack | Graylog |
|---|---|---|---|
| Indizierungsstrategie | Nur Labels | Volltext-Index | Volltext-Index |
| RAM-Nutzung (10 GB/Tag) | ~512 MB | 8–16 GB | 4–8 GB |
| Abfragesprache | LogQL | KQL / Lucene | GELF / Elasticsearch |
| Grafana-Integration | Nativ | Plugin erforderlich | Plugin erforderlich |
| Setup-Komplexität | Gering | Hoch | Mittel |
| Horizontale Skalierbarkeit | Gut (Microservices-Modus) | Hervorragend | Gut |
| Kosten bei Skalierung | Sehr gering | Hoch | Mittel |
| Am besten für | Kubernetes-/Container-Logs | Volltext-Suche in Logs | Compliance, SIEM |
Loki gewinnt bei Kosten und Einfachheit, wenn Ihr Hauptbedarf Log-Aggregation und Korrelation mit Metriken ist. Für einen Vergleich mit der Elasticsearch-Einrichtung siehe Elasticsearch für Log-Analyse einrichten.
Praxisbeispiel
Sie verwalten 15 Produktionsserver mit einer Mischung aus Nginx, PostgreSQL und benutzerdefinierten Python-APIs. Vorfälle passieren, aber die Ursachenanalyse dauert Stunden, weil Logs auf 15 Maschinen verteilt sind. So löst PLG dieses Problem:
- Stellen Sie Promtail auf jedem Server über ein Konfigurationsverwaltungstool (Ansible/Puppet) bereit und richten Sie alle Instanzen auf einen einzigen Loki-Endpunkt aus.
- Fügen Sie Labels
hostundenvhinzu, sodass jeder Log-Stream mit dem Ursprungsserver und der Umgebung (Produktion/Staging) markiert ist. - Erstellen Sie in Grafana ein Dashboard mit einer Variable
$host, die an diehost-Label-Werte gebunden ist. Jetzt zeigt ein einziges Dashboard Logs aller 15 Server mit einem Dropdown zum Filtern nach Host. - Fügen Sie eine Alert-Regel hinzu, die auslöst, wenn ein Host mehr als 50 Fehlerzeilen pro Minute produziert — Sie erhalten eine Slack-Benachrichtigung, bevor Benutzer das Problem melden.
- Verwenden Sie bei Vorfällen die Explore-Ansicht, um Logs und Prometheus-Metriken nebeneinander zu korrelieren: Fehler-Spike in Logs um 14:32 → CPU-Spike auf demselben Host um 14:31.
Dieser Workflow ersetzt eine 45-minütige manuelle SSH-Untersuchung durch einen 3-minütigen Grafana-Drill-Down.
Stolperfallen und Sonderfälle
Label-Kardinalitätsexplosion. Jede eindeutige Label-Kombination erstellt einen separaten Log-Stream. Verwenden Sie niemals hochkardinale Werte (Benutzer-IDs, UUIDs, Anfrage-IDs) als Labels. Halten Sie Labels bei stabilen, wenig variierenden Werten: host, job, env, service.
Aufbewahrungskonfiguration. Der retention_period in limits_config erfordert, dass der Compactor mit retention_enabled: true aktiviert ist. Ohne dies speichert Loki Logs unbegrenzt, bis der Speicher voll ist.
Chunk-Cache und Arbeitsspeicher. Loki speichert unkomprimierte Chunks während der Ingestion im Arbeitsspeicher. Auf Servern mit weniger als 2 GB RAM setzen Sie chunk_target_size: 1048576 und max_chunk_age: 1h, um den Speicherdruck zu begrenzen.
Verlust der Promtail-Positions-Datei. Wenn der Promtail-Container ohne persistentes Volume für /tmp/positions.yaml neu erstellt wird, liest er alle Log-Dateien von Anfang an und ingestiert Duplikate. Binden Sie die Positions-Datei immer auf einem persistenten Volume ein.
Log-Reihenfolge. Loki erfordert, dass Log-Zeilen innerhalb eines einzelnen Streams in Timestamp-Reihenfolge ingestiert werden. Wenn Ihre Anwendung Logs mit ungeordneten Timestamps schreibt, aktivieren Sie unordered_writes: true in der Loki-Ingester-Konfiguration.
Docker-Socket-Zugriff. Die Docker-SD-Konfiguration in Promtail benötigt Zugriff auf den Docker-Socket. Der Promtail-Container muss entweder als root laufen oder der docker-Gruppe hinzugefügt werden.
Zusammenfassung
- Der PLG-Stack (Promtail + Loki + Grafana) bietet zentrale Log-Aggregation zu einem Bruchteil der ELK-Kosten
- Loki indiziert nur Labels, nicht den Log-Inhalt — minimaler Speicher- und RAM-Verbrauch
- LogQL Stream-Selektoren (
{job="varlogs"}) plus Pipeline-Stufen (|= "error",| json) ermöglichen leistungsstarke Log-Filterung - Promtail scrapt systemd-Journal, Dateien und Docker-Container-Logs mit automatischer Label-Zuordnung
- Die Loki-Datenquelle in Grafana ermöglicht einheitliche Dashboards mit Logs (Loki) und Metriken (Prometheus)
- Hochkardinale Labels sind der häufigste Loki-Performance-Fehler — halten Sie Label-Werte stabil und gering
- Loki-Alert-Regeln verwenden LogQL-Metrikabfragen, die vom Ruler-Komponenten ausgewertet werden