
🌐 Auch auf: English · Français · Español
Deep Dive aus der Reihe rund um die Ansible-Tour. Niveau: fortgeschrittene Linux-Administration. Die Tour erklärt die Rollen; die Deep Dives erklären die Technik darunter – wie sie funktioniert, warum sie hier ist, was genau dieses Setup damit macht, wie man sie im Alltag betreibt und was es sonst noch gegeben hätte.
Jeder Server hier hat sein eigenes Prometheus und sein eigenes Grafana (Teil 6 der Tour zeigt die Rollen). Dieser Beitrag ist die Leseanleitung: was die Zahlen sind, wie „normal“ auf diesen Maschinen aussieht, welche Panels zählen, wenn etwas nicht stimmt – und wie die Alarmierung gebaut ist, samt dem Fehler, der sie wochenlang stumm hielt.
🧭 Wie Prometheus und Grafana funktionieren
Pull, Zeitreihen, Labels
Exporter sind kleine Programme, die Zahlen per HTTP bereitstellen – der Node Exporter für den Host, mysqld_exporter für MariaDB, php-fpm_exporter für PHP, cAdvisor im Kubelet für Container, kube-state-metrics für Kubernetes-Objekte. Prometheus holt sie alle 15 Sekunden ab („scrapen“) und speichert jede Zahl als Zeitreihe, gekennzeichnet durch einen Namen und Labels wie container="nextcloud-fpm". Grafana fragt Prometheus in dessen Abfragesprache PromQL und zeichnet die Antwort.
Drei Funktionen, die die meisten Panels abdecken
rate(x[5m]): Anstieg eines Zählers pro Sekunde, gemittelt über fünf Minuten – Anfragen pro Sekunde, CPU-Sekunden pro Sekunde (= Kerne).increase(x[1h]): wie stark ein Zähler in einer Stunde gewachsen ist – Neustarts, OOM-Kills, gedrosselte Perioden.max_over_time(…[7d])undsum by (…): schlechtester Wert in einem Zeitraum, und Zusammenzählen von Serien entlang gewählter Labels. Beim zweiten passieren die meisten Lesefehler (siehe unten).
Alarme ohne Alertmanager
Grafana wertet Alarmregeln selbst aus: eine Abfrage (A), eine Schwelle (B), eine Dauer („für 5 Minuten“). Hält die Bedingung lange genug, schickt es eine Mail an den Kontaktpunkt. Für einen Administrator und drei Server ersetzt das einen eigenen Alertmanager.
🛡️ Warum es hier ist
- Trends statt Momente. 30 Tage Verlauf beantworten „seit wann?“ und „ist das normal?“, was
topnie kann. - Ein Ort für Host, Datenbank, PHP, Container – und seit Oktober 2026 die Logs daneben (Loki).
- Offen und Standard. Prometheus und Grafana sind die Sprache des Kubernetes-Monitorings; Dashboards für jeden Exporter gibt es schon.
🔧 Was dieses Repository macht
| Teil | Detail |
|---|---|
| Prometheus | common_prometheus: 15 s Abfrageintervall, 30 Tage Aufbewahrung, lauscht auf 127.0.0.1; 5 Scrape-Jobs pro Server, ~7.500 Serien, 0,5–0,8 GB auf der Platte |
| Grafana | common_grafana: Paket aus dem Grafana-Repository, hinter dem Ingress unter /grafana, Port 3000 nur für den Knoten selbst offen; Log-Level warn |
| Datenquellen | Prometheus und Loki, über die API mit festen UIDs angelegt |
| Dashboards | 1860 Node Exporter Full, 7362 MySQL Overview, 4912 PHP-FPM, dazu das eigene „Container-Ressourcen“ |
| Alarme | 11 Metrik-Regeln und 4 Log-Regeln, ein Mail-Kontaktpunkt |
| Härtung | eigene SELinux-Domain für Grafana, Prometheus und die Exporter; Grafana-Login geschützt durch ein fail2ban-Jail und Grafanas eigene Sperre |
Alles wird über Grafanas HTTP-API eingerichtet, von kleinen Python-Skripten, die Ansible schreibt und ausführt. Dashboards werden mit festen UIDs importiert, damit Links und Lesezeichen einen Neuimport überleben; Dashboard 1860 bekommt zusätzlich seine Variablen gepatcht, weil die Import-API Datenquelle und Node/Job-Variablen uninitialisiert lässt und jedes Panel beim ersten Öffnen „No data“ zeigte.
📊 Die Dashboards lesen
Node Exporter Full (1860) – der Host
| Panel | Normal hier | Genauer hinsehen, wenn |
|---|---|---|
| CPU Busy | im Mittel 5 % (Blog), 9 % und 16 % (Nextcloud); 95. Perzentil unter 30 % | dauerhaft über 70 % – dann zeigt „CPU Basic“, ob User, System oder iowait |
| Sys Load | unter der Zahl der Kerne, kurze Spitzen leicht darüber (Neustart, nächtliche Scans) | länger als ein paar Minuten über der Kernzahl |
| RAM Used | im Mittel 16–32 %, höchstens 39 % | steigt tagelang, ohne zurückzugehen – ein Leck, keine Last |
| SWAP Used | nahe null | wächst stetig |
| Root FS Used | deutlich unter 80 % | der Alarm löst bei 80 % aus |
| Disk IOps / Read/Write | kurze Spitzen nachts (Scans, Neustart) | hohes iowait zusammen mit viel I/O tagsüber |
| Network Traffic | niedrig, Spitzen durch Uploads und Backups | dauerhafter ausgehender Verkehr, den niemand erklären kann |
Ein Muster wiederholt sich jede Nacht: eine Lücke von ein paar Minuten um 03:30. Das ist kein Ausfall, sondern der geplante Neustart nach Updates; eine Lücke zu jeder anderen Zeit verdient einen Blick. Und der größere Nextcloud-Server zeigt für K3s selbst ein Mehrfaches der CPU-Zeit des Blogs, bei gleicher Arbeit – eine Eigenschaft seiner Prozessorgeneration, erklärt im K3s-Deep-Dive, keine Fehlkonfiguration.
MySQL Overview (7362) – die Datenbank
| Panel | Normal hier | Genauer hinsehen, wenn |
|---|---|---|
| MySQL Connections | eine Handvoll, Spitzen 6–15 | nahe an max_connections (50) |
| MySQL Questions / Queries | einige Dutzend pro Sekunde, folgt der Tageszeit | ein Sprung ohne mehr Benutzer |
| InnoDB Buffer Pool | großer Teil ungenutzt, Trefferquote > 99 % | freie Seiten nahe null und steigende Lesezugriffe auf die Platte |
| Temporary Objects | einige temporäre Tabellen auf der Platte (TEXT-Spalten) | Platten-Tabellen wachsen schneller als die Abfragen |
| Slow Queries | 0 | alles über null – dann sagt das Slow-Log, welche |
PHP-FPM (4912) – die Worker
| Panel | Normal hier | Genauer hinsehen, wenn |
|---|---|---|
| Active / idle processes | wenige aktiv, die meisten untätig | aktive nahe an max_children |
| Listen queue | 0 | länger als eine Minute über null |
| Max children reached | 0 | steigt – der Pool ist zu klein |
| Slow requests | 0 | steigt – das Pod-Log nennt das Skript |
Container-Ressourcen – das eigene Dashboard
Acht Panels pro Namespace: CPU und Speicher als Anteil am Limit des Containers, CPU-Drosselung, absolute Kerne und Working Set, Neustarts und OOM-Kills pro Stunde, der letzte Abbruchgrund je Container sowie die konfigurierten Limits und Requests. Es beantwortet die eine Frage, die die anderen Dashboards nicht beantworten können: Wird ein Container von seinem eigenen Limit gebremst, während der Host Luft hat? Drosselung von ein paar Prozent ist für stoßweises PHP normal; ein Container, der über 50 % gedrosselt wird, ist der, der mehr CPU braucht.
Der Lesefehler
Beim Schreiben des Performance-Deep-Dives zeigte eine Abfrage für den PHP-Container des Blogs eine Speicherspitze von 940 MiB bei 1 GiB Limit. Sie hatte alle Serien mit diesem Containernamen summiert – während eines Rollouts überlappen alter und neuer Pod, und nach einem Neustart meldet cAdvisor kurz die alte und die neue cgroup. Die echte Spitze eines einzelnen Pods lag bei 538 MiB. Panels, die sum by (namespace, pod, container) rechnen wie das eigene Dashboard, sind sicher; ein sum nur über den Containernamen ist es nicht.
🚨 Alarme – und warum sie stumm waren
| Alarm | Löst aus, wenn (für 5 Min.) |
|---|---|
| Platte root / Platte data | mehr als 80 % belegt |
| Speicher | mehr als 90 % belegt |
| CPU-Last | mehr als 85 % ausgelastet |
| Knoten ausgefallen | der Node Exporter antwortet nicht |
| Container-Speicher | ein Container über 90 % seines Speicherlimits |
| Container-Drosselung | mehr als 50 % der CPU-Perioden gedrosselt |
| Container-OOM / Crash-Loop | ein OOM-Kill oder mehr als 2 Neustarts in 15 Minuten |
| Trivy veraltet / fehlgeschlagen | kein Schwachstellen-Scan seit 36 Stunden oder der letzte Scan scheiterte |
| Logs (Loki) | SELinux-Verweigerung im Enforcing-Modus, fail2ban-Fehler, Kernel-OOM, gar keine Logs |
Im September 2026 zeigte eine Prüfung auf dem größeren Nextcloud-Server, dass keine dieser Regeln je ausgelöst hatte – nicht, weil nichts passiert war, sondern weil sie es nicht konnten. Die Regeln waren über die API ohne das Feld condition angelegt worden, das die Abfrage benennt, die über den Alarm entscheidet. Grafana schrieb bei jeder Auswertung „condition must not be empty“ ins Log, und die Regeln standen auf execErrState: OK, was jeden Auswertungsfehler in „alles in Ordnung“ verwandelte. Zwei Einstellungen, jede für sich vernünftig, ließen die Alarmierung zusammen gesund aussehen, während sie tot war. Seitdem nennt jede Regel condition: "B". Die Lehre: Nach einer Änderung den Zustand jeder Regel in Grafana prüfen – „Normal“ ohne Auswertungsfehler –, statt das Ausbleiben von Mails als Beweis zu nehmen.
Eine Lücke bleibt mit Absicht: noDataState und execErrState stehen weiter auf OK, damit ein Neustart von Prometheus nicht ein Dutzend Mails verschickt. Hielte Prometheus selbst an, blieben alle Regeln still grün. Diesen Fall deckt etwas außerhalb von Grafana ab: Monit überwacht Prometheus, Grafana, Node- und MySQL-Exporter, Loki und Alloy und schickt eine Mail, wenn einer davon stoppt.
🧰 Betrieb im Alltag
# Prometheus directly, from the host
curl -s 'http://127.0.0.1:9090/api/v1/targets' | jq '.data.activeTargets[] | {job: .labels.job, health}'
curl -s --get 'http://127.0.0.1:9090/api/v1/query' \
--data-urlencode 'query=max_over_time(sum(phpfpm_active_processes)[7d:1m])' | jq '.data.result'
# Grafana state
systemctl status grafana-server prometheus
journalctl -u grafana-server --since -1h # warnings and errors only (log level warn)- Eine Schwelle ändern heißt, sie in der Rolle zu ändern und das Playbook laufen zu lassen; eine Änderung in der Weboberfläche würde der nächste Lauf überschreiben, und das ist gewollt.
- Ein neues Panel, das bleiben soll, gehört ins Dashboard-JSON im Repository (das eigene Dashboard) – bei importierten Community-Dashboards in eine Kopie mit eigener UID.
- Backups: Alles, was zählt – Datenquellen, Dashboards, Alarmregeln –, entsteht aus dem Repository neu. Die Metriken selbst werden nicht gesichert; 30 Tage Verlauf sind der bewusste Horizont.
🆘 Spickzettel: Symptom → Befehl
Die Kurzfassung dieses Artikels für den Moment, in dem etwas nicht stimmt. Auf dem Host als root ausführen; <…> sind Platzhalter.
| Symptom | Befehl |
|---|---|
| Grafana oder Prometheus nicht erreichbar | systemctl status grafana-server prometheus |
| 403 auf /grafana | # country filter in the ingress: is the address in the list? |
| Eine Grafik ist leer | curl -s http://127.0.0.1:9090/api/v1/targets | jq '.data.activeTargets[] | {job: .labels.job, health}' |
| Prometheus direkt fragen | curl -s --get http://127.0.0.1:9090/api/v1/query --data-urlencode 'query=up' | jq '.data.result' |
| Keine Alarm-Mails, obwohl etwas nicht stimmt | # Grafana → Alerting → Alert rules: state "Error" or "No data"? |
| Prometheus selbst steht | monit status svc_prometheus # Monit mails, Grafana cannot |
| Schwelle oder Dashboard in der Weboberfläche geändert | # overwritten by the next playbook run - change it in the role |
🔭 Wie es weitergeht
- Logs und Metriken in einer Ansicht: Loki ist seit Oktober 2026 da; eine Spitze in einem Panel mit den Log-Zeilen derselben Minute zu verknüpfen ist der nächste Schritt.
- Erledigt am 20./21. Oktober 2026: ein Alarm auf die PHP-FPM-Listen-Queue (Schwelle aus 14 Tagen Messung) und
/grafanahinter derselben Länderliste wie SSH und der WordPress-Admin – von überall sonst antwortet der Ingress-Controller mit 403. - Dashboards als Code (Jsonnet/Grafonnet), sobald die eigenen Dashboards über eines hinauswachsen.
⚖️ Alternativen – und warum Grafana + Prometheus
| Option | Stärke | Warum nicht hier |
|---|---|---|
| Netdata | Keine Konfiguration, Auflösung im Sekundentakt | Pro Host, standardmäßig kurzer Verlauf, weniger geeignet für Kubernetes-Metriken. |
| Zabbix / Icinga | Klassisches Check-basiertes Monitoring, ausgereifte Alarmierung | Checks sagen „läuft oder nicht“, nicht „wie viel seit wann“; Container-Metriken brauchen Zusatzarbeit. Icinga wurde in einem Labor getestet. |
| Cloud-Monitoring-Dienste | Nichts zu betreiben | Metriken verlassen den Server; Kosten pro Host und Serie. |
| Prometheus + Grafana | Standard für Kubernetes, Pull-Modell, offen, Dashboards für jeden Exporter | Gewählt. |
Ein Dashboard ist eine Sammlung von Fragen, die jemand anders gestellt hat. Nützlich sind hier die, deren Normalzustand bekannt ist – deshalb steht in diesem Beitrag neben jedem Panel „normal hier“. Und Alarmierung ist nur so gut wie ihr letzter Test: Die wichtigste Zeile dieses Beitrags ist die über Regeln, die wochenlang grün aussahen, ohne je auslösen zu können.


