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
Load Average, User/System/iowait %
RAM
used/cache/available, Swap In/Out
Disk
% used, inode %, IOPS, throughput
Netzwerk
Throughput in/out, packet loss, conn count
Prozesse
Top 10 nach CPU/RAM, Zombie-Count
Uptime
System uptime, reboot history

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:

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:

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