Warum Monitoring keine Option ist
Du merkst, dass dein Server ein Problem hat? Dann ist es schon zu spät. Ein Webserver, der bei 95% CPU-Last angeht, hat keine Reserven mehr — jede weitere Anfrage verzögert sich, und unter Last kollabiert er. Monitoring sagt dir das bevor es passiert.
Metriken sind auch dein Beweis, wenn etwas schief geht: "Der Server war die letzten 2 Wochen stabil bei 40% CPU — das Problem kam plötzlich und war nach dem neuesten Deployment" ist eine nützliche Diagnose. Ohne Monitoring ist das eine Hypothese, keine Tatsache.
Die dritte Funktion: Capacity Planning. Wenn deine CPU-Last über 6 Monate linear wächst, weisst du, dass du in 2 Monaten upgraden musst — statt eine Woche vorher im Chaos.
Monitoring ohne Alerting = Monitoring ohne Nutzen
Daten sammeln, in eine Grafana-Dashboard starren — das bringt nichts, wenn du nicht informiert wirst. Jede Metrik braucht einen Schwellenwert, bei dem du benachrichtigt wirst. Ohne Alerting ist Monitoring ein Rückspiegel — du siehst nur, wo du warst, nicht wo du hinfährst.
Die 5 wichtigsten Metriken: Linux-Basics
Bevor du ein komplexes Monitoring-Stack aufsetzt: die grundlegenden Linux-Metriken, die du immer im Blick haben solltest. Alle können mit top, htop, vmstat, iostat und free abgefragt werden:
CPU: Was bedeuten die Zahlen?
uptime zeigt den Load Average — der wichtigste Indikator, noch vor CPU-%:
$ uptime
14:23:01 up 45 days, 3:22, 2 users, load average: 0.52, 0.48, 0.41
Load Average 0.52 bedeutet: im Schnitt war die letzte Minute 52% der verfügbaren CPU-Kapazität ausgelastet. Ein Load von 4.0 auf einer 4-Kern-Maschine = 100% CPU-Auslastung. Regel: Load Average sollte dauerhaft unter der Anzahl der CPU-Kerne bleiben.
CPU-% im Detail mit mpstat -P ALL 1 1 (paket: sysstat): User% (deine App), System% (Kernel-Overhead), iowait% (Warten auf Disk) — wenn iowait hoch ist, ist die Disk der Flaschenhals, nicht die CPU.
RAM: Was heisst "used"?
free -h zeigt vermeintlich "used" — aber Linux cached aktiv Speicher als Datei-Cache und gibt ihn sofort zurück, wenn eine App ihn braucht. Die relevante Zahl ist "available", nicht "used". Wenn "available" sinkt und Swap-In anfängt, hast du ein RAM-Problem — nicht vorher.
$ free -h
total used free shared buff/cache available
Mem: 7.7Gi 2.1Gi 3.4Gi 89Mi 2.2Gi 5.4Gi
Swap: 2.0Gi 0B 2.0Gi
Swap = Evil (fast immer)
Wenn dein Server Swap nutzt, ist das ein Symptom — entweder du hast zu wenig RAM, oder eine App hat ein Memory-Leak. Swap-In bedeutet: der Kernel lagert RAM-Seiten aus, und wenn die App sie wieder braucht, muss er sie zurückholen — das verlangsamt alles. Swap-Nutzung ist kein normaler Betriebszustand. Behandle sie als Alert.
Disk: Nicht nur %, sondern IOPS und Throughput
df -h zeigt den Füllstand — aber auch iostat -x 1 5 zeigt, wie busy die Disks wirklich sind:
$ iostat -x 1 5
Device r/s w/s rkB/s wkB/s await %util
vda 12.00 8.50 480.00 340.00 2.3ms 4.2%
vdb 0.00 0.00 0.00 0.00 0.00ms 0.0%
%util über 70% = Disk wird zum Flaschenhals. Bei NVMe SSDs ist das weniger kritisch als bei HDDs (bei SSDs ist 100% util normal und unkritisch). await zeigt die durchschnittliche Latenz — über 10ms auf NVMe ist ein Problem.
Node Exporter: Prometheus-Metriken vom Server
Prometheus + Node Exporter ist der Standard-Stack für Server-Monitoring: Node Exporter exportiert Linux-Metriken im Prometheus-Format, Prometheus sammelt und speichert sie, Grafana visualisiert. Das ist für 1-5 Server overkill? Nein — Node Exporter braucht weniger als 50 MB RAM und lässt sich in 30 Minuten aufsetzen.
# Node Exporter installieren (als Systemd-Service)
# 1. Binary herunterladen
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-*.linux-amd64.tar.gz
sudo mv node_exporter-*.linux-amd64/node_exporter /usr/local/bin/
# 2. Systemd Service
sudo tee /etc/systemd/system/node_exporter.service << 'EOF'
[Unit]
Description=Node Exporter
After=network-online.target
[Service]
User=node_exporter
Group=node_exporter
ExecStart=/usr/local/bin/node_exporter
Restart=always
[Install]
WantedBy=multi-user.target
EOF
# 3. Service starten
sudo useradd -M -s /bin/false node_exporter
sudo systemctl daemon-reload
sudo systemctl enable node_exporter
sudo systemctl start node_exporter
sudo systemctl status node_exporter
Node Exporter läuft jetzt auf Port 9100. curl http://localhost:9100/metrics zeigt die Metriken im Prometheus-Format:
# HELP node_cpu_seconds_total Seconds the CPU spent in each mode.
# TYPE node_cpu_seconds_total counter
node_cpu_seconds_total{cpu="0",mode="user"} 12345.67
node_cpu_seconds_total{cpu="0",mode="system"} 234.56
node_memory_MemAvailable_bytes 5.4e+09
node_filesystem_avail_bytes{mountpoint="/",fstype="ext4"} 2.5e+11
Diese Metriken heissen node_cpu_seconds_total, node_memory_MemAvailable_bytes, node_filesystem_avail_bytes — und sind in Grafana direkt als Dashboard-Graphen verfügbar (Node Exporter Full Dashboard, Dashboard ID 1860).
Alerting: Wann schreibst du dir selbst?
Prometheus' alerting_rules definieren, wann ein Alert ausgelöst wird. Das sind die wichtigsten Regeln für einen typischen Webserver:
# /etc/prometheus/rules/server_alerts.yml
groups:
- name: server_alerts
interval: 30s
rules:
# CPU zu hoch (>80% über 5min)
- alert: HighCPU
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 5m
labels:
severity: warning
annotations:
summary: "Hohe CPU-Last auf {{ $labels.instance }}"
description: "CPU-Auslastung über 80% seit 5 Minuten. Aktueller Wert: {{ $value | printf \"%.1f\" }}%"
# RAM kritisch (<10% verfügbar)
- alert: LowMemory
expr: (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) < 0.1
for: 5m
labels:
severity: critical
annotations:
summary: "Wenig RAM auf {{ $labels.instance }}"
description: "Nur noch {{ $value | printf \"%.1f\" }}% RAM verfügbar"
# Disk fast voll (>85%)
- alert: DiskSpaceLow
expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) < 0.15
for: 5m
labels:
severity: warning
annotations:
summary: "Wenig Disk-Space auf {{ $labels.instance }}"
description: "Nur {{ $value | printf \"%.1f\" }}% des Root-FS verfügbar"
# Swap aktiv
- alert: SwapActive
expr: node_memory_SwapTotal_bytes > 0 and (node_memory_SwapFree_bytes / node_memory_SwapTotal_bytes) < 0.5
for: 1m
labels:
severity: warning
annotations:
summary: "Swap aktiv auf {{ $labels.instance }}"
description: "Server nutzt Swap. Mögliche Memory-Überlastung."
# Server down
- alert: ServerDown
expr: up{job="node"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Server nicht erreichbar: {{ $labels.instance }}"
description: "Prometheus kann den Server nicht erreichen. Mögliche Ursache: Service down, Netzwerkproblem."
Alertmanager leitet die Alerts weiter: an PagerDuty (24/7 on-call), Slack (Team-Kanal), Email (als Fallback). Das for: 5m heisst: der Alert wird erst nach 5 Minuten geschickt — das verhindert Alarm-Sturm bei kurzfristigen Lastspitzen (z.B. ein Backup-Job).
App-spezifisches Monitoring
Server-Metriken sagen dir, ob der Server gesund ist — aber nicht, ob deine App funktioniert. Zusätzlich brauchst du:
| Metrik | Was sie misst | Wie zu implementieren |
|---|---|---|
| HTTP Error Rate | % der Requests, die 5xx返回 | Middleware in Express: zähle 5xx vs total, exportiere als Prometheus-Metrik |
| Request Latency (p50/p95/p99) | Wie schnell antwortet dein Server? | histogram.observe() mit Pfad, Methode, Status-Code |
| DB Query Duration | Welche Queries sind langsam? | pg-metrics / PostgreSQL pg_stat_statements Extension |
| Background Job Queue | Warteschlangen-Länge, Job-Ausfall-Rate | PM2 Plus / Bull Board Dashboard |
| SSL Zertifikat-Ablauf | Wann läuft das Zertifikat ab? | Script: openssl s_client -connect domain:443 -servername domain 2>/dev/null | openssl x509 -noout -dates |
// Express-Middleware: Request-Latenz als Prometheus-Histogram
const promClient = require('prom-client');
const httpRequestDuration = new promClient.Histogram({
name: 'http_request_duration_seconds',
help: 'HTTP request duration in seconds',
labelNames: ['method', 'route', 'status_code'],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5],
});
app.use((req, res, next) => {
const end = httpRequestDuration.startTimer();
res.on('finish', () => {
end({
method: req.method,
route: req.route?.path || req.path,
status_code: res.statusCode,
});
});
next();
});
Externes Monitoring: UptimeRobot & BetterStack
Server-internes Monitoring kann nicht sehen, ob deine Seite von aussen erreichbar ist — Netzwerk-Routen, DNS, Cloudflare, Load Balancer dazwischen können alle Probleme verursachen. Deshalb brauchst du externe Uptime-Monitoring — ein Service, der regelmässig deine Seite aufruft und die Ergebnisse logged:
- UptimeRobot (kostenlos für 50 Monitore, Check alle 5 Min): HTTP(S)-Monitoring, Port-Check, Keyword-Check (ist der richtige Content da?).
- BetterStack (kostenlos für 1 Min-Check, 10 Monitore): Dasselbe, mit besserer Alerting-Integration (PagerDuty, Slack direkt aus der UI).
- Datadog / New Relic: Für professionelle Setups, besser für komplexe Microservices.
Konfiguration: Check auf /health (dein eigener Endpoint), HTTP-Status muss 200 sein, Response-Time unter 2 Sekunden, Check von mindestens 3 geographischen Standorten (Europa, USA, Asien).
SSL-Zertifikat-Ablauf automatisch überwachen
SSL-Zertifikate, die ablaufen, sind ein klassischer, vollständig vermeidbarer Ausfall. UptimeRobot und BetterStack haben beide SSL-Monitoring eingebaut: du gibst die Domain ein, und sie checken täglich, ob das Zertifikat noch mindestens 30 Tage gültig ist — und warnen dich, wenn nicht.
SLA-Tracking: Verfügbarkeit messen und reporten
Wenn du ein SLA (z.B. "99,9% uptime")承诺st, musst du es messen können. Prometheus berechnet das automatisch:
# Prometheus: Uptime in % über 30 Tage
# (Erreichbarkeit / Gesamte Zeit) * 100
avg_over_time(up{job="node"}[30d]) * 100
# Alert wenn SLA unter 99.5% sinkt
- alert: SLAViolation
expr: avg_over_time(up{job="node"}[30d]) * 100 < 99.5
for: 1h
labels:
severity: critical
annotations:
summary: "SLA-Verletzung auf {{ $labels.instance }}"
description: "Uptime der letzten 30 Tage unter 99.5%. Aktuell: {{ $value | printf \"%.2f\" }}%"
99.9% Uptime heisst maximal 43.8 Minuten Ausfall pro Monat. 99.99% heisst maximal 4.4 Minuten. Tracke das nicht nur intern — ein monatlicher SLA-Report zeigt deinen Kunden oder Stakeholdern, dass du deine Versprechen einhältst.
Grafana-Dashboard: Die wichtigste Ansicht
Ein gutes Monitoring-Dashboard zeigt auf einen Blick, ob alles in Ordnung ist — ohne dass du scrollen musst. Die wichtigsten Panels:
- System Overview (ganz oben): CPU%, RAM%, Disk% als einzelne Gauge-Werte. Rot wenn kritisch, grün wenn okay.
- Load Average über Zeit (Linien-Chart): zeigt Trends, nicht nur Momentaufnahmen.
- Network I/O: eingehend/ausgehend in MB/s. Ein plötzlicher Einbruch = Netzwerk-Problem.
- HTTP Request Rate + Error Rate: Requests/Sekunde und 5xx-Rate.
- Top 5 Processes nach CPU und RAM — direkt siehst du, welche Prozesse Ressourcen verbrauchen.
Grafana Dashboard "Node Exporter Full" (ID 1860) ist ein guter Start — du kannst ihn importieren und dann anpassen. Für eigene App-Metriken nutzt du denselben Prometheus-Server und fügst eigene Panels hinzu.
Fazit: Monitoring ist kein Luxus
Die Minimal-Lösung: UptimeRobot (kostenlos) + Server-SSH-Access mit einem 5-Minuten-Check-Script in Cron. Das reicht für einen einzelnen Server ohne kritische SLA.
Das professionelle Setup: Prometheus + Node Exporter + Grafana + AlertManager. Installation in unter 2 Stunden, RAM-Fussabdruck unter 100 MB, Daten für 90 Tage verfügbar. Alerting an Slack und/oder PagerDuty. SLA-Tracking als monatlicher Report.
Das Wichtigste: Jede Metrik braucht einen Alert. Eine Metrik, die du nie ansiehst, ist eine, die du nicht brauchst. Und ein Alert, der nie ausgelöst wird, ist ein Alert, den du nicht brauchst. Starte mit den 5 Schwellenwerten (CPU >80%, RAM <10%, Disk >85%, Swap aktiv, Server down), und erweitere von dort.
VPS mit Root-Zugang für eigenes Monitoring
Volle Kontrolle über System-Metriken, Prometheus, Node Exporter — kein Managed-Service nötig.
Zum Hosting-Vergleich