TL;DR — Kurzzusammenfassung
Grafana Loki für zentralisierte Log-Aggregation mit Promtail einrichten. LogQL, Dashboards, Alerting und Multi-Tenant-Konfiguration für die Produktion erklärt.
Grafana Loki ist ein horizontal skalierbares Log-Aggregationssystem, das auf denselben Prinzipien wie Prometheus aufbaut — es indiziert nur Labels, nicht den vollständigen Log-Text, was es dramatisch günstiger als den ELK-Stack macht. Diese Anleitung behandelt Lokis Architektur, Installation via Docker Compose, Promtail-Konfiguration, die LogQL-Abfragesprache, Dashboard-Erstellung in Grafana, Alerting, Multi-Tenancy und Produktions-Sizing.
Voraussetzungen
- Docker und Docker Compose (oder ein Kubernetes-Cluster für Helm-basierte Installation).
- Grafana 10+ (oder Grafana Cloud) zur Visualisierung.
- Grundkenntnisse in YAML-Konfiguration und Kommandozeilen-Tools.
Loki-Architektur
Loki verteilt seine Arbeit auf vier Hauptkomponenten:
- Distributor — Empfängt Log-Streams von Promtail/Agenten, validiert Labels und verteilt sie über Consistent Hashing an die Ingester.
- Ingester — Hält aktuelle Logs im Arbeitsspeicher (konfigurierbare Chunk-Größe), schreibt komprimierte Chunks in den Objektspeicher und pflegt ein WAL für Absturzsicherheit.
- Querier — Führt LogQL-Abfragen sowohl auf in-memory (aktuellen) als auch auf gespeicherten (historischen) Chunks aus.
- Compactor — Zusammenführen und Deduplizieren der boltdb-shipper-Indextabellen und Durchsetzen von Aufbewahrungsrichtlinien.
Im monolithischen Modus (Standard für Einzelknoten-Installationen) laufen alle vier Komponenten in einem einzigen Binary. Wenn das Log-Volumen ~50 GB/Tag übersteigt, empfiehlt sich der Wechsel zum Microservices-Modus, in dem jede Komponente unabhängig skaliert.
Das zentrale Design-Konzept: Loki erstellt niemals einen invertierten Index des Log-Inhalts. Eine Abfrage mit |= "OutOfMemoryError" führt ein verteiltes Grep über die Chunks durch, die den ausgewählten Labels entsprechen.
Warum Loki statt ELK?
| Merkmal | Loki | Elasticsearch |
|---|---|---|
| Indizierungsmodell | Nur Labels | Invertierter Volltext-Index |
| Speicherkosten | ~10x weniger | Hoch (Index + Daten) |
| Abfragesprache | LogQL (ähnlich PromQL) | Lucene / KQL |
| Nativ in Grafana | Ja — erstklassig | Via Plugin |
| Horizontale Skalierung | Ja (Label-Sharding) | Ja (Shards) |
| Schema-Änderungen | Nicht erforderlich | Re-Indizierung nötig |
| Alerting | Loki Ruler + Alertmanager | ElastAlert / SIEM |
| Kubernetes-nativ | Ja (Helm Chart) | Ja (ECK Operator) |
Schritt 1: Installation mit Docker Compose
Erstellen Sie ein Projektverzeichnis und die folgende docker-compose.yml:
version: "3.8"
services:
loki:
image: grafana/loki:3.0.0
ports:
- "3100:3100"
volumes:
- ./loki-config.yaml:/etc/loki/local-config.yaml
- loki-data:/loki
command: -config.file=/etc/loki/local-config.yaml
promtail:
image: grafana/promtail:3.0.0
volumes:
- ./promtail-config.yaml:/etc/promtail/config.yaml
- /var/log:/var/log:ro
- /run/log/journal:/run/log/journal:ro
command: -config.file=/etc/promtail/config.yaml
grafana:
image: grafana/grafana:10.4.0
ports:
- "3000:3000"
environment:
GF_AUTH_ANONYMOUS_ENABLED: "true"
GF_AUTH_ANONYMOUS_ORG_ROLE: Admin
volumes:
loki-data:
Schritt 2: Loki konfigurieren
Erstellen Sie loki-config.yaml:
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
ingester:
lifecycler:
ring:
kvstore:
store: inmemory
replication_factor: 1
chunk_idle_period: 5m
chunk_retain_period: 30s
wal:
enabled: true
dir: /loki/wal
schema_config:
configs:
- from: 2024-01-01
store: boltdb-shipper
object_store: filesystem
schema: v12
index:
prefix: index_
period: 24h
storage_config:
boltdb_shipper:
active_index_directory: /loki/index
cache_location: /loki/boltdb-cache
shared_store: filesystem
filesystem:
directory: /loki/chunks
compactor:
working_directory: /loki/compactor
shared_store: filesystem
compaction_interval: 10m
retention_enabled: true
retention_delete_delay: 2h
limits_config:
retention_period: 30d
ingestion_rate_mb: 16
ingestion_burst_size_mb: 32
max_query_length: 721h
Schritt 3: 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: nginx
static_configs:
- targets: [localhost]
labels:
job: nginx
env: prod
__path__: /var/log/nginx/access.log
- job_name: docker
static_configs:
- targets: [localhost]
labels:
job: docker
__path__: /var/lib/docker/containers/*/*-json.log
pipeline_stages:
- json:
expressions:
log: log
stream: stream
- output:
source: log
Starten Sie den Stack:
docker compose up -d
docker compose logs -f loki
Schritt 4: LogQL-Abfragesprache
LogQL hat zwei Formen: Log-Abfragen und Metrik-Abfragen.
Stream-Selektoren (erforderlich, immer zuerst):
{job="nginx"}
{job="nginx", env="prod"}
{job=~"nginx|apache"}
Filterausdrücke (Pipe nach dem Selektor):
{job="nginx"} |= "error"
{job="nginx"} != "health_check"
{job="nginx"} |~ "5[0-9]{2}"
Parser-Ausdrücke — Felder aus strukturierten Logs extrahieren:
{job="app"} | json
{job="app"} | logfmt
{job="app"} | json | level="error"
Metrik-Abfragen — Logs in Zeitreihen umwandeln:
rate({job="nginx"} |= "error" [5m])
count_over_time({job="app"} | json | level="error" [1h])
sum by (status) (rate({job="nginx"} | logfmt | status=~"5.." [5m]))
topk(5, sum by (uri) (rate({job="nginx"}[10m])))
Schritt 5: Grafana-Datenquelle und Dashboards
- In Grafana gehen Sie zu Connections → Data sources → Add data source → Loki.
- Setzen Sie die URL auf
http://loki:3100(oder Ihre Loki-Adresse). - Klicken Sie auf Save & test — Sie sehen “Data source connected.”
Tipps für das Explore-Panel:
- Verwenden Sie Live tail für das Echtzeit-Streaming von Logs — ideal zum Debuggen von Deployments.
- Klicken Sie auf eine Log-Zeile, um den Log-Kontext zu sehen — Zeilen vor und nach dem Treffer.
- Wechseln Sie zum Metrics-Tab, um
rate()-Abfragen als Zeitreihen-Panels zu visualisieren.
Schritt 6: Alerting mit dem Loki Ruler
Fügen Sie einen ruler-Block zur loki-config.yaml hinzu:
ruler:
storage:
type: local
local:
directory: /loki/rules
rule_path: /loki/rules-temp
alertmanager_url: http://alertmanager:9093
ring:
kvstore:
store: inmemory
enable_api: true
Erstellen Sie /loki/rules/fake/rules.yaml:
groups:
- name: app-alerts
interval: 1m
rules:
- alert: HoheFehlerate
expr: |
sum(rate({job="app"} | json | level="error" [5m])) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "Hohe Fehlerrate in den App-Logs"
Loki vs. Alternativen
| Tool | Speichermodell | Self-hosted | Kosten | Nativ Grafana | Am besten geeignet für |
|---|---|---|---|---|---|
| Loki | Label-Index + komprimierte Chunks | Ja | Niedrig | Ja | Teams mit Grafana/Prometheus |
| Elasticsearch | Invertierter Volltext-Index | Ja | Hoch | Via Plugin | Volltextsuche, SIEM |
| Fluentd | Nur Agent (kein Speicher) | Ja | Kostenlos | Nein | Log-Routing und Transformation |
| Datadog Logs | SaaS, proprietär | Nein | Hoch | Nein | Enterprise, vollständige Observability |
| CloudWatch Logs | SaaS (AWS) | Nein | Mittel | Via Plugin | Native AWS-Workloads |
Multi-Tenancy
Aktivieren Sie Multi-Tenancy mit auth_enabled: true in Loki. Jede Anfrage muss X-Scope-OrgID: <tenant-id> enthalten. Konfigurieren Sie Promtail so, dass der Header gesendet wird:
clients:
- url: http://loki:3100/loki/api/v1/push
tenant_id: team-backend
Produktions-Sizing-Empfehlungen
| Log-Volumen | Architektur | Speicher | RAM |
|---|---|---|---|
| < 5 GB/Tag | Monolithisch, Einzelknoten | Lokales Filesystem | 2 GB |
| 5–50 GB/Tag | Monolithisch + S3/GCS | Objektspeicher | 4–8 GB |
| 50–500 GB/Tag | Microservices, 3 Ingester | Objekt + TSDB | 16–32 GB |
| > 500 GB/Tag | Microservices + Thanos Ruler | Objekt, Multi-AZ | 64 GB+ |
Aktivieren Sie immer das WAL (ingester.wal.enabled: true) in der Produktion, um Datenverlust bei Ingester-Neustarts zu verhindern.
Fehlerbehebung
| Problem | Lösung |
|---|---|
connection refused beim Senden | Prüfen Sie, ob der Loki-Container läuft und Port 3100 von Promtail erreichbar ist |
| Query-Timeout | Erhöhen Sie querier.query_timeout und teilen Sie Abfragen in kürzere Zeitbereiche auf |
| Fehlende Docker-Logs | Prüfen Sie Promtails positions.yaml für den Container-Log-Pfad |
| Hoher Ingester-Speicher | Reduzieren Sie chunk_idle_period, um Chunks früher in den Speicher zu schreiben |
| Aufbewahrung funktioniert nicht | Stellen Sie sicher, dass compactor.retention_enabled: true und limits_config.retention_period gesetzt sind |
Zusammenfassung
- Nur Label-Indizierung macht Loki 10x günstiger als Elasticsearch bei gleichem Volumen.
- Promtail sendet Logs aus Dateien, systemd-Journal und Docker mit Label-Anreicherung.
- LogQL unterstützt Stream-Filterung, JSON/logfmt-Parsing, Ratenberechnung und Aggregation.
- Der Loki Ruler wertet LogQL-Alerts nach einem Zeitplan aus und leitet sie an Alertmanager weiter.
- Für die Produktion ersetzen Sie den Filesystem-Speicher durch S3/GCS und aktivieren WAL und Chunk-Caching.