Observability Monitoring Prometheus Grafana SRE

Observability im Hosting: Monitoring, Logging und Tracing richtig aufsetzen

Monitoring allein reicht nicht. Moderne Webanwendungen brauchen Observability — die Fähigkeit, aus Daten zu verstehen, warum etwas schiefläuft, nicht nur dass. Dieser Guide zeigt die Open-Source-Toolchain: Prometheus, Grafana, Loki und Jaeger.

Monitoring vs. Observability — Der Unterschied

Monitoring sagt dir, dass ein System down ist. Observability sagt dir, warum — und wie das System sich unter bisher ungesehenen Bedingungen verhält. Drei Säulen:

Monitoring ist eine Teilmenge von Observability. Observability schließt unknowables ein — Probleme, die du noch nicht kennst.

SLOs definieren: Bevor du Observability aufsetzt, definiere Service Level Objectives (SLOs): "99,9% der API-Requests antworten innerhalb 200ms." Ohne SLOs weißt du nicht, welche Alerts wirklich wichtig sind. Folge: Alert-Fatigue.

Prometheus: Metriken sammeln und abfragen

Prometheus scrapt Metriken von deinen Services über HTTP (/metrics-Endpoint) und speichert sie als Zeitreihen. Es gibt kaum ein System, das keinen Prometheus-Metriken-Export unterstützt.

# Beispiel: Node Exporter — Systemmetriken (CPU, RAM, Disk, Netzwerk) # Installation auf jedem Server: wget https://github.com/prometheus/node_exporter/releases/download/v1.7.0/node_exporter-1.7.0.linux-amd64.tar.gz tar xzf node_exporter-1.7.0.linux-amd64.tar.gz ./node_exporter

node_exporter läuft auf Port 9100 und exportiert Metriken wie node_cpu_seconds_total, node_memory_MemTotal_bytes, node_filesystem_avail_bytes.

Prometheus-Konfiguration

# prometheus.yml global: scrape_interval: 15s evaluation_interval: 15s alerting: alertmanagers: - static_configs: - targets: - alertmanager:9093 rule_files: - "alert_rules.yml" scrape_configs: # Prometheus itself - job_name: 'prometheus' static_configs: - targets: ['localhost:9090'] # Node Exporter — jeder Server im Cluster - job_name: 'node' static_configs: - targets: ['server1:9100', 'server2:9100', 'server3:9100'] # Deine Node.js/Express-App (mit prom-client) - job_name: 'webapp' metrics_path: /metrics static_configs: - targets: ['webapp:3000']

Metriken in Node.js/Express exportieren

// server.js (Express) import express from 'express'; import client from 'prom-client'; // Default-Registry inkl. HTTP-Metriken const register = new client.Registry(); client.collectDefaultMetrics({ register }); // Custom Metrics const httpRequestDuration = new client.Histogram({ name: 'http_request_duration_seconds', help: 'Duration of HTTP requests in seconds', labelNames: ['method', 'route', 'status_code'], buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5], registers: [register], }); const activeConnections = new client.Gauge({ name: 'http_active_connections', help: 'Number of active HTTP connections', registers: [register], }); app.use((req, res, next) => { activeConnections.inc(); const start = Date.now(); res.on('finish', () => { activeConnections.dec(); httpRequestDuration .labels(req.method, req.route?.path || req.path, res.statusCode) .observe((Date.now() - start) / 1000); }); next(); }); // /metrics-Endpoint für Prometheus app.get('/metrics', async (req, res) => { res.set('Content-Type', register.contentType); res.end(await register.metrics()); });

Grafana: Dashboards und Alerting

Grafana visualisiert Prometheus-Metriken und vieles mehr. Datasource hinzufügen: Prometheus → http://prometheus:9090.

Wichtige Panel-Typen für Webhosting

Request Rate

1.247 req/s
rate(http_requests_total[5m])

P99 Latency

87 ms
histogram_quantile(0.99, rate(...))

Error Rate

0.12%
rate(http_requests_total{status_code=~"5.."}[5m])

CPU Usage

34%
avg(rate(node_cpu_seconds_total[5m])) * 100

Alerting-Regeln

# alert_rules.yml groups: - name: webapp_alerts rules: - alert: HighErrorRate expr: rate(http_requests_total{status_code=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.01 for: 5m labels: severity: critical annotations: summary: "Hohe 5xx-Fehlerrate" description: "Fehlerrate {{ $value | printf \"%.2f\" }}% über 5 Minuten." - alert: HighLatency expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 1 for: 10m labels: severity: warning annotations: summary: "Hohe P99-Latenz" description: "P99-Latenz {{ $value }}s über 10 Minuten." - alert: HighMemoryUsage expr: (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) < 0.15 for: 5m labels: severity: warning annotations: summary: "Wenig freier Arbeitsspeicher" description: "Nur {{ $value | printf \"%.1f\" }}% RAM frei auf {{ $labels.instance }}." - alert: InstanceDown expr: up == 0 for: 2m labels: severity: critical annotations: summary: "Instance nicht erreichbar" description: "{{ $labels.job }} auf {{ $labels.instance }} ist seit 2 Minuten down."

Loki: Zentralisierte Logs ohne Elasticsearch

Loki ist Log-Aggregation ohne die Komplexität von Elasticsearch. Es speichert Log-Streams indiziert nach Labeln (nicht nach Fulltext) — dadurch extrem günstig. Drei Komponenten:

# promtail-config.yml (auf jedem Node) server: http_listen_port: 9080 grpc_listen_port: 0 positions: filename: /var/log/positions.yaml clients: - url: http://loki:3100/loki/api/v1/push scrape_configs: # Node.js/Express Logs - job_name: webapp static_configs: - targets: - localhost labels: job: webapp env: production __path__: /var/log/webapp/*.log # Nginx Logs - job_name: nginx static_configs: - targets: - localhost labels: job: nginx __path__: /var/log/nginx/*.log # Logging format in Node.js (JSON für Loki-Parsing): # {"level":"error","timestamp":"2026-06-04T10:00:00Z","message":"Connection timeout","requestId":"abc123","service":"api"}

Log-Label-Strategie: Halt die Zahl der Labels niedrig. Zu viele Labels führen zu "churn rate"-Problemen in Loki (zu viele Streams). Nutze job, env, service, region — nicht user_id oder request_id als Label.

LogQL: Loki-Metriken aus Logs erzeugen

Das Geniale an Loki: du kannst Logs in Metriken umwandeln. LogQL ist das Query-Language-Äquivalent zu PromQL.

# Fehlerrate aus Logs berechnen (LogQL → Metrik) sum by (job) ( rate( {job="webapp"} | json | level="error" [5m] ) ) # Latenzen aus Access-Logs extrahieren avg by (path) ( rate( {job="nginx"} | json | status_code >= 200 | status_code < 400 | unwrap latency_ms [5m] ) ) # Fehler-Streams mit mehr Kontext {job="webapp"} | json | level="error" | status_code >= 500 | line_format "{{.timestamp}} {{.message}} {{.requestId}}"

Distributed Tracing mit Jaeger

Bei Microservices oder vielen verbundenen Services brauchst du Distributed Tracing — um zu sehen, wo ein Request wie lange braucht. OpenTelemetry ist der Standard für Instrumentation, Jaeger der Backend-Store.

# Node.js mit OpenTelemetry + Jaeger import { NodeSDK } from '@opentelemetry/sdk-node'; import { JaegerExporter } from '@opentelemetry/exporter-jaeger'; import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'; import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express'; import { Resource } from '@opentelemetry/resources'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; const sdk = new NodeSDK({ resource: new Resource({ [SemanticResourceAttributes.SERVICE_NAME]: 'webapp', [SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0', }), traceExporter: new JaegerExporter({ endpoint: 'http://jaeger:14268/api/traces', }), instrumentations: [ new HttpInstrumentation(), new ExpressInstrumentation(), ], }); sdk.start(); // HTTP-Request-Tracing in Express // Jeder Request bekommt automatisch einen Trace-Span // Kinder: Datenbank-Call, External API, Cache Lookup process.on('SIGTERM', () => { sdk.shutdown() .then(() => console.log('Tracing terminated')) .catch((err) => console.error('Error terminating tracing', err)) .finally(() => process.exit(0)); });

In Jaeger UI siehst du jeden Request als Trace mit allen Spans: vom HTTP-Endpoint über Datenbankabfragen bis zu Downstream-API-Aufrufen. Das macht Performance-Bottlenecks sichtbar.

Die vollständige Toolchain: Prometheus + Grafana + Loki + Jaeger

ToolEinsatzbereichSpeicherort MetrikenSkalierung
PrometheusMetriken / AlertingTSDB (lokal)Horizontal (föderiert)
GrafanaVisualisierung + Alerting UI— (nur Queries)Stateless, multi-Tenant
LokiLog-AggregationObject Store (S3/GCS/MinIO)Read/Write-Scalable
JaegerDistributed TracingElasticsearch/CassandraMulti-Tenant-Sampler
Grafana OnCallAlert-Routing (PagerDuty-Ersatz)Cloud oder Self-hosted

Alert-Fatigue vermeiden

Die häufigste Fehlerquelle in Monitoring: zu viele Alerts, zu wenig Kontext. Regeln:

OpenTelemetry: Der Standard-Collector's Ansatz

Anstatt jeden Service einzeln zu instrumentieren, nutze den OpenTelemetry Collector — ein zentraler Sidecar, der Metriken, Logs und Traces normalisiert und an Backend-Dienste weiterleitet.

# otel-collector-config.yaml receivers: otlp: protocols: grpc: http: prometheus: config: scrape_configs: - job_name: 'webapp' static_configs: - targets: ['webapp:3000'] # Logs von überall journald: units: ['webapp.service', 'nginx.service'] processors: batch: timeout: 10s memory_limiter: check_interval: 1s limit_percentage: 75 exporters: prometheus: endpoint: "0.0.0.0:8889" namespace: "otel" const_labels: env: production loki: endpoint: http://loki:3100/loki/api/v1/push jaeger: endpoint: jaeger:14250 tls: insecure: true service: pipelines: traces: receivers: [otlp] exporters: [jaeger] metrics: receivers: [prometheus, otlp] exporters: [prometheus] logs: receivers: [journald, otlp] exporters: [loki]

Dein Observability-Spickzettel

  • Prometheus scrapt Metriken über HTTP — exportiere sie von jedem Service über den /metrics-Endpoint
  • Grafana Dashboards = Metriken visualisieren. Nutze P99-Latenz, nicht Durchschnitt — der Median ist im Hosting irrelevant
  • SLOs zuerst: Definiere Fehlerbudgets, bevor du Alerts baust. Ziel: MTTR senken, nicht Alert-Count maximieren
  • Loki statt Elasticsearch für Logs — Label-basiert, deutlich günstiger, und gut mit Grafana integriert
  • OpenTelemetry ist der Zukunftsstandard — instrumentiere einmal, sende an Prometheus/Loki/Jaeger gleichzeitig
  • Alert nur bei Action-Bedarf. Alle anderen Events → Runbook automatisieren oder ins Dashboard verlagern
  • Runbooks schreiben: Jeder Alert braucht eine klare Response-Prozedur. Ein Alert ohne Runbook ist nur Lärm.

Das könnte Sie auch interessieren

Mehr erfahren

Server-Monitoring und Metriken: So behältst du die Kontrolle

Mehr erfahren

.htaccess & Nginx-Konfiguration: Perfekte Webserver-Einstellungen

Mehr erfahren

API Gateway Hosting: Microservices richtig aufsetzen