Zum Inhalt springen
Serverküche
Suche

Die Suche wird geladen … (nur in der veröffentlichten Seite verfügbar).

Container Schwierigkeit: Profi

Monitoring mit Grafana & Prometheus: dein Server in Live-Dashboards

Ein vollständiger Monitoring-Stack aus Prometheus, node-exporter, cAdvisor und Grafana hinter Traefik – mit Live-Metriken zu Host und Containern.

· 10 Min. Lesezeit ·Dauer: ca. 60 Minuten
Inhaltsverzeichnis

Uptime Kuma sagt dir, ob ein Dienst läuft. Dieser Stack sagt dir, wie es ihm geht: CPU, RAM, Festplatte, Netzwerk und die Last jedes einzelnen Containers – als Live-Dashboards mit Verlauf. Wir bauen die Kombination, die im Selfhosting zum De-facto-Standard geworden ist: Prometheus sammelt, Grafana zeigt.

Was bauen wir?

Am Ende läuft ein kompletter Monitoring-Stack hinter deinem Traefik-Proxy, erreichbar unter https://grafana.DEINE_DOMAIN mit gültigem HTTPS. Vier Bausteine greifen ineinander:

  • Prometheus 3.13 – die Zeitreihen-Datenbank. Sie holt (scrapt) im Intervall Metriken von den anderen Diensten und speichert sie mit Zeitstempel.
  • node-exporter 1.12 – liefert die Host-Metriken: CPU, Arbeitsspeicher, Load, Festplatten, Netzwerk-Interfaces.
  • cAdvisor 0.55 – liefert die Container-Metriken: CPU und RAM pro laufendem Container, damit du siehst, welcher Dienst frisst.
  • Grafana 13.1 – die Oberfläche: fertige Dashboards, die aus den Prometheus-Daten Graphen und Anzeigen machen.

Der entscheidende Unterschied zu Uptime Kuma: Statt „grün/rot" bekommst du Trends – du erkennst, dass der RAM seit Tagen langsam vollläuft, bevor der Server swappt. Nur Grafana ist von außen erreichbar; Prometheus, node-exporter und cAdvisor bekommen keine öffentliche Route.

Wie groß muss der Server sein?

Der Stack selbst ist genügsam – im Test belegte er nur ein paar Hundert MB RAM. Der Speicherhunger kommt von der Vorhaltezeit (Retention) von Prometheus: Je länger du Metriken behältst und je mehr Dienste du überwachst, desto mehr Platz und RAM braucht die Zeitreihen-Datenbank. Weil Monitoring meist zusätzlich zu deinen eigentlichen Apps läuft, empfehlen wir den VPS 2000 mit mehr Reserve – für ein reines Test-Setup reicht auch der kleinere.
🍳 Empfehlung Anzeige

VPS 2000 G12

8 vCore · 16 GB RAM · 512 GB NVMe

ab 19,24 €/Monat

Genug RAM und Speicher, um Monitoring neben deinen Apps mitlaufen zu lassen.

Zu netcup →

💶 5 € Gutschein für netcup-Neukunden: 36nc17844976032 (nur Neukunden, keine Domains)

Voraussetzungen

  • Sicherheit im Umgang mit Docker Compose – Services, Volumes, Netzwerke. Falls das noch neu ist: Docker Compose verstehen.
  • Ein laufender Traefik-Reverse-Proxy mit dem gemeinsamen proxy-Netzwerk und dem Let’s-Encrypt-Resolver le – wie in Reverse Proxy mit Traefik aufgebaut.
  • Eine Subdomain grafana.DEINE_DOMAIN, deren DNS-Record (A/AAAA) auf deine Server-IP zeigt – siehe Domain mit Server verbinden.

Schritt für Schritt

Schritt 1: Die Architektur verstehen

Bevor wir Dateien anlegen, das Bild dahinter – sonst debuggst du blind. Prometheus arbeitet nach dem Pull-Prinzip: Nicht die Dienste schicken ihre Werte irgendwohin, sondern Prometheus fragt sie aktiv ab. Jeder „Exporter" stellt seine Metriken unter /metrics bereit, Prometheus holt sie alle 15 Sekunden ab und legt sie in seiner Zeitreihen-Datenbank ab. Grafana wiederum fragt Prometheus ab und zeichnet daraus die Graphen.

Warum überhaupt Pull statt Push? Weil Prometheus so jederzeit weiß, ob ein Ziel lebt: Antwortet ein Exporter nicht, ist das selbst eine Information (up = 0). Du brauchst keine Agenten, die aktiv „nach Hause telefonieren", und kannst jedes neue Ziel einfach in die Konfiguration eintragen. Ein Exporter ist dabei nichts Magisches – er ist ein winziger Webserver, der unter /metrics eine Textliste aktueller Messwerte ausliefert.

Daraus folgt die Sicherheitsregel dieses Setups: node-exporter, cAdvisor und Prometheus haben keine Authentifizierung. Sie dürfen deshalb niemals offen im Internet stehen. Prometheus und cAdvisor binden wir nur ins interne Docker-Netz monitoring; node-exporter läuft im Host-Netz (warum, erklärt Schritt 4), bleibt aber dank UFW und netcup-Firewall von außen unerreichbar. Ausschließlich Grafana hängt zusätzlich im proxy-Netz und bekommt eine Traefik-Route – der einzige Dienst mit Login und öffentlicher Adresse.

Schritt 2: Projektordner und Prometheus konfigurieren

Leg die Ordnerstruktur an:

Terminal
mkdir -p ~/monitoring/prometheus ~/monitoring/grafana/provisioning/datasources
cd ~/monitoring

Erstelle die Prometheus-Konfiguration. Sie legt fest, wen Prometheus in welchem Intervall abfragt:

YAML
# prometheus/prometheus.yml
global:
  scrape_interval: 15s
  scrape_timeout: 10s

scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets: ["localhost:9090"]

  - job_name: node
    static_configs:
      - targets: ["host.docker.internal:9100"]

  - job_name: cadvisor
    static_configs:
      - targets: ["cadvisor:8080"]

Wichtig: cadvisor:8080 steht als Servicename da – Docker löst ihn im gemeinsamen monitoring-Netz automatisch auf. Der node-exporter läuft dagegen im Host-Netz (Schritt 4 erklärt, warum) und wird deshalb über host.docker.internal:9100 angesprochen – diesen Namen bekommt Prometheus gleich per extra_hosts auf die IP des Docker-Hosts gemappt. scrape_interval: 15s ist ein guter Kompromiss – oft genug für aussagekräftige Kurven, selten genug, um kaum Last zu erzeugen.

Schritt 3: Grafana-Datenquelle automatisch provisionieren

Statt die Prometheus-Datenquelle später von Hand in Grafana anzuklicken, richten wir sie per Provisioning ein – als Datei. Das ist reproduzierbar: Nach einem Neuaufbau ist alles sofort wieder da, ohne Klickarbeit.

YAML
# grafana/provisioning/datasources/datasource.yml
apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    editable: false

access: proxy bedeutet, dass Grafana selbst (serverseitig) Prometheus abfragt – der Browser des Nutzers spricht nie direkt mit Prometheus. Genau deshalb kann Prometheus intern bleiben.

Schritt 4: Der Compose-Stack

Jetzt das Herzstück. Erstelle die compose.yaml. Ersetze grafana.DEINE_DOMAIN und das Grafana-Passwort:

YAML
services:
  prometheus:
    image: prom/prometheus:v3.13.1
    container_name: prometheus
    command:
      - "--config.file=/etc/prometheus/prometheus.yml"
      - "--storage.tsdb.path=/prometheus"
      - "--storage.tsdb.retention.time=30d"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prom_data:/prometheus
    extra_hosts:
      - "host.docker.internal:host-gateway"
    networks: [monitoring]
    restart: unless-stopped

  node-exporter:
    image: prom/node-exporter:v1.12.1
    container_name: node-exporter
    command:
      - "--path.rootfs=/host"
    network_mode: host
    pid: host
    volumes:
      - "/:/host:ro,rslave"
    restart: unless-stopped

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:v0.55.1
    container_name: cadvisor
    privileged: true
    devices:
      - /dev/kmsg
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro
    networks: [monitoring]
    restart: unless-stopped

  grafana:
    image: grafana/grafana:13.1.0
    container_name: grafana
    depends_on: [prometheus]
    environment:
      GF_SECURITY_ADMIN_USER: admin
      GF_SECURITY_ADMIN_PASSWORD: DEIN_GRAFANA_PASSWORT
      GF_SERVER_ROOT_URL: https://grafana.DEINE_DOMAIN
      GF_USERS_ALLOW_SIGN_UP: "false"
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning:ro
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.grafana.rule=Host(`grafana.DEINE_DOMAIN`)"
      - "traefik.http.routers.grafana.entrypoints=websecure"
      - "traefik.http.routers.grafana.tls.certresolver=le"
      - "traefik.http.services.grafana.loadbalancer.server.port=3000"
    networks: [monitoring, proxy]
    restart: unless-stopped

volumes:
  prom_data:
  grafana_data:

networks:
  monitoring:
  proxy:
    external: true

Was hier wichtig ist:

  • --storage.tsdb.retention.time=30d hält Metriken 30 Tage vor. Mehr = mehr Aussagekraft, aber auch mehr Speicher. 30 Tage sind ein guter Start.
  • node-exporter braucht das Host-Wurzelverzeichnis (/:/host:ro,rslave), pid: host und network_mode: host, um echte Host-Werte statt Container-Werte zu lesen. --path.rootfs=/host sagt ihm, wo der Host-Blick liegt (nur lesend, :ro). Das Host-Netz ist kein Nice-to-have: /proc/net ist an den Netzwerk-Namespace gebunden – in einem normalen Bridge-Netz sähe node-exporter nur sein eigenes Container-Interface (praktisch nur den Scrape-Traffic), nicht die echten Host-Interfaces. Der Preis: node-exporter lauscht damit auf Port 9100 des Hosts – deine Firewall hält den Port von außen zu, und Prometheus erreicht ihn über den extra_hosts-Eintrag (host-gateway).
  • cAdvisor braucht privileged: true, das Gerät /dev/kmsg und Lese-Mounts auf /sys und /var/lib/docker, um alle Container zu erkennen. Das ist für ein Container-Monitoring unvermeidlich – deshalb bleibt cAdvisor strikt intern.
  • Nur grafana trägt Traefik-Labels und hängt im proxy-Netz. Der interne Port ist 3000. GF_SERVER_ROOT_URL muss die öffentliche HTTPS-Adresse sein, sonst brechen Logins und Weiterleitungen hinter dem Proxy.
  • GF_USERS_ALLOW_SIGN_UP: "false" verhindert, dass sich Fremde selbst ein Konto anlegen. Setz ein starkes Admin-Passwort – Grafana steht öffentlich.

Schritt 5: Starten und die Prometheus-Targets prüfen

Starte den kompletten Stack:

Terminal
docker compose up -d

Beim ersten Mal lädt Docker vier Images – das dauert einen Moment. Prüfe, dass alle Container laufen:

Terminal
docker compose ps

Alle vier Dienste (node-exporter, cadvisor, prometheus, grafana) sollten running sein. Jetzt der entscheidende Test: Sieht Prometheus seine Ziele? Da Prometheus intern bleibt, fragen wir es aus einem kurzlebigen Container heraus, der im selben Netz hängt (Compose nennt das Netz monitoring_monitoring – Projektordner plus Netzname):

Terminal
docker run --rm --network monitoring_monitoring curlimages/curl:8.21.0 \
  -s http://prometheus:9090/api/v1/query?query=up

In der JSON-Antwort steht für jedes Ziel ein "value" mit "1" – das heißt „erreichbar". Im Test lieferten alle drei Jobs (prometheus, node, cadvisor) eine 1 zurück. Eine 0 bedeutet, dass Prometheus das Ziel nicht scrapen kann – dann hilft der Abschnitt „Wenn es nicht funktioniert".

Schritt 6: Grafana aufrufen und Dashboards importieren

Ruf https://grafana.DEINE_DOMAIN auf. Traefik holt beim ersten Zugriff das Zertifikat (ein paar Sekunden). Melde dich mit admin und deinem Passwort aus der compose.yaml an. Dank Provisioning ist die Prometheus-Datenquelle bereits verbunden – du findest sie unter Connections → Data sources:

Die provisionierte Prometheus-Datenquelle in Grafana mit der internen URL http://prometheus:9090
Die Datenquelle ist per Datei provisioniert – Grafana meldet sie als vorkonfiguriert

Jetzt die Dashboards. Statt Panels selbst zu bauen, importieren wir zwei erprobte aus der Grafana-Community. Geh auf Dashboards → New → Import, trag die ID ein und klick auf Load:

Der Grafana-Import-Dialog lädt das Dashboard „Node Exporter Full" von Grafana.com
Dashboard per ID importieren – hier „Node Exporter Full“ (ID 1860)

Importiere diese beiden:

  • 1860 – „Node Exporter Full": das umfassende Host-Dashboard.
  • 19792 – „cAdvisor Dashboard": Metriken pro Container.

Wähle beim Import jeweils die Prometheus-Datenquelle aus. Danach zeigt „Node Exporter Full" deinen Host in Echtzeit – CPU-Auslastung, belegter RAM, Festplatte, Netzwerk, Uptime:

Das Grafana-Dashboard „Node Exporter Full" zeigt Live-Werte des Servers: CPU 1 Prozent, RAM 14,7 Prozent belegt, 4 CPU-Kerne, 8 GB RAM, Uptime 2 Tage
Node Exporter Full mit echten Live-Daten des Test-Servers

Das cAdvisor-Dashboard bricht dieselbe Last auf einzelne Container herunter – hier siehst du sofort, welcher Dienst wie viel CPU und RAM zieht:

Das cAdvisor-Dashboard zeigt CPU-Verbrauch pro Container – jede Farbe ist ein Container wie grafana, prometheus oder traefik
cAdvisor: CPU- und Speicherverbrauch je Container, aufgeschlüsselt nach Name

Dashboards auch provisionieren

Wie die Datenquelle lassen sich auch Dashboards per Datei provisionieren (unter grafana/provisioning/dashboards/ eine .yml plus die Dashboard-JSONs). Für den Einstieg ist der Import über die ID schneller; wenn dein Setup steht, lohnt sich das Provisionieren, damit nach einem Neuaufbau alle Dashboards automatisch wieder da sind.

Schritt 7: Die wichtigsten Metriken richtig lesen

Ein Dashboard voller Kurven nützt wenig, wenn du nicht weißt, worauf du achten musst. Diese fünf Werte im „Node Exporter Full"-Dashboard verraten dir fast alles über die Gesundheit deines Servers:

  • CPU Busy. Kurze Spitzen sind normal. Kritisch wird es, wenn die Auslastung dauerhaft hoch bleibt – dann ist der Server der Flaschenhals.
  • Sys Load. Die Load sollte im Mittel unter der Anzahl deiner CPU-Kerne liegen (im Test: 4 Kerne → Load dauerhaft über 4 ist ein Warnzeichen für Überlast).
  • RAM Used. Wichtig: Linux nutzt freien Speicher als Datei-Cache – „belegt" ist also nicht gleich „knapp". Erst wenn neben dem Cache wenig frei bleibt und SWAP Used steigt, wird der Server zäh. Genau diesen schleichenden Anstieg willst du früh sehen.
  • Root FS Used. Der Klassiker unter den Serverausfällen ist die volle Festplatte. Der Verlauf zeigt dir die Füllrate – so erkennst du Tage im Voraus, wann es eng wird.
  • Network Traffic. Ungewöhnliche Ausschläge, obwohl gerade nichts läuft, können auf ein Backup, ein Update oder – im schlechten Fall – auf unerwünschten Datenverkehr hindeuten.

Im cAdvisor-Dashboard ist die spannendste Frage: Welcher Container zieht die Last? Wenn dein Server plötzlich langsam wird, siehst du hier auf einen Blick, ob es an einer einzelnen App liegt – und musst nicht raten. Damit wird aus „irgendwas ist langsam" ein gezieltes „der Foto-Import in Container X frisst gerade die CPU".

Wenn es nicht funktioniert

Symptom: Ein Target steht in Prometheus auf up = 0 bzw. „DOWN".

Ursache & Lösung: Prometheus erreicht den Exporter nicht. Bei cadvisor prüfe, dass er im selben monitoring-Netz läuft und Name und Port exakt stimmen (cadvisor:8080). Beim node-Job prüfe den extra_hosts-Eintrag am Prometheus-Service und das Target host.docker.internal:9100 – ohne beides findet Prometheus den node-exporter im Host-Netz nicht. Nach Änderungen an der prometheus.yml den Prometheus-Container neu starten: docker compose restart prometheus.

Symptom: Das cAdvisor-Dashboard zeigt teils „No data" oder cAdvisor startet nicht.

Ursache & Lösung: Fehlen die Mounts oder privileged: true, sieht cAdvisor die Container nicht. Kontrolliere den cadvisor-Block (Mounts, devices: /dev/kmsg). Einzelne „No data"-Panels sind normal – manche Metriken (etwa CPU-Drosselung) gibt es nur, wenn du den Containern CPU-Limits gesetzt hast.

Symptom: Grafana lädt hinter Traefik nicht richtig – Login schlägt fehl oder das Layout ist kaputt.

Ursache & Lösung: Fast immer stimmt GF_SERVER_ROOT_URL nicht. Sie muss exakt die öffentliche Adresse sein (https://grafana.DEINE_DOMAIN). Prüfe außerdem, dass die Traefik-Label loadbalancer.server.port=3000 gesetzt ist – Grafana lauscht intern auf 3000.

Symptom: Grafana zeigt in den Panels „No data", obwohl die Targets up sind.

Ursache & Lösung: Meist der Zeitbereich. Ein frischer Stack hat noch keine Historie – stell oben rechts „Last 15 minutes" ein und warte ein paar Minuten, bis Daten aufgelaufen sind. Prüfe zudem, dass beim Dashboard-Import die Prometheus-Datenquelle gewählt war.

Symptom: Der Speicherplatz auf dem Server wächst stetig.

Ursache & Lösung: Das ist die Prometheus-TSDB. Reduziere --storage.tsdb.retention.time (z. B. auf 15d) oder überwache weniger Ziele. Das prom_data-Volume wächst mit Anzahl der Metriken × Vorhaltezeit.

Wartung & Backups

  • Was du sichern musst. Wichtig ist das grafana_data-Volume: Es enthält deine Nutzer, Einstellungen und selbst gebauten Dashboards. Sichere es verschlüsselt und off-site mit Restic. Die Prometheus-Metriken (prom_data) sind reine Messwerte – ein Verlust ist verschmerzbar, ein Backup optional.
  • Updates. Die Image-Tags sind gepinnt (prometheus:v3.13.1, grafana:13.1.0 …). Zum Aktualisieren die Tags hochsetzen, dann docker compose pull && docker compose up -d. Lies vorher die Release-Notes – vor allem Grafana ändert zwischen Hauptversionen gelegentlich Standards. cAdvisor beziehst du von gcr.io; dort ist v0.55.1 derzeit die aktuellste verfügbare Image-Version.
  • Sicherheit. Grafana steht öffentlich – vergib ein starkes Admin-Passwort und lass GF_USERS_ALLOW_SIGN_UP auf false. Prometheus und cAdvisor bleiben intern; exponiere sie nicht „mal eben" zum Debuggen, sie haben keine Authentifizierung.
  • Der nächste Schritt: Alarme. Ein Dashboard hilft nur, wenn du hineinschaust. Grafana kann bei Schwellwerten (RAM > 90 %, Platte fast voll) selbst Alarme auslösen und z. B. per Push melden. Damit wird aus dem hübschen Dashboard ein Frühwarnsystem – ein guter Anschluss an dein bestehendes Uptime Kuma.
  • Ehrlich zum Aufwand: Der Stack läuft nach dem Aufsetzen weitgehend allein. Plane gelegentlich einen Blick auf die Vorhaltezeit und den Speicherverbrauch ein und zieh die Versionen etwa quartalsweise nach.

Feedback per E-Mail: feedback@serverkueche.de

Das könnte dir auch schmecken