Ich zeige, wie ich linux psi mit Prometheus erfasse und in Grafana sichtbar mache, um Ressourcendruck auf CPU, Speicher und I/O als echte Wartezeit zu messen. So erkenne ich Engpässe früh, ordne sie cgroups oder Containern zu und löse bei Bedarf automatisierte Gegenmaßnahmen aus.
Zentrale Punkte
- PSI-Metriken: some/full für CPU, Memory, I/O und gleitende Durchschnitte
- cgroup-Fokus: Container- und Service-spezifische Sicht statt nur global
- Alert-Trigger: Ereignisse bei Schwellwerten mit poll/epoll nutzen
- Grafana: Panels für Trends, Peaks und Top-N-Verursacher
- Best Practices: Schwellen, Zeitfenster, Korrelation mit Systemmetriken
PSI kurz erklärt: Druck als reale Wartezeit verstehen
PSI beantwortet die Frage, wie viel Wallclock-Zeit Tasks vergeblich auf CPU, RAM oder I/O warten. Die Dateien unter /proc/pressure/{cpu,memory,io,irq} liefern zwei Perspektiven: some zeigt Phasen, in denen einige Tasks warten, full markiert Momente, in denen alle nicht-idlen Tasks blockieren. Ich bewerte beide Werte getrennt, weil some früher anschlägt und full echte Stillstände sichtbar macht. Zusätzlich nutze ich avg10, avg60 und avg300, um kurzfristige Ausschläge und längerfristige Trends zu unterscheiden. Über die wachsende total-Zeit erkenne ich, wie stark sich Druck seit dem Boot summiert und wo Hotspots liegen.
Systemweite vs. cgroup-PSI: Die richtige Ebene wählen
Ich unterscheide bewusst zwischen globalen Druckwerten und der Sicht pro cgroup. Die Dateien unter /proc/pressure/ zeigen das System als Ganzes, während cgroup v2 zusätzlich cpu.pressure, memory.pressure und io.pressure je Gruppe anbietet. In Container-Umgebungen ordne ich damit Druck sauber Pods, Services oder Containern zu. Diese Zuordnung verhindert Blindflüge: Statt rätselraten zu müssen, sehe ich den Verursacher direkt in der Gruppe. Auf Multi‑Tenant-Hosts trenne ich so Shared- und dedizierte Lasten und steuere Limits gezielt.
Voraussetzungen prüfen: Kernel, PSI und cgroup v2 aktivieren
Bevor ich Metriken sammle, stelle ich sicher, dass die Plattform passt:
- Kernel-Version: PSI ist ab Linux 4.20 verfügbar. Ich prüfe mit
uname -rund kontrolliere viazcat /proc/config.gz | grep CONFIG_PSI, ob der Support einkompiliert ist. - PSI-Laufzeit-Flag: Einige Distributionen erlauben das optionale Boot-Flag
psi=1, um PSI vollständig zu aktivieren. Ich setze es bei Bedarf und verifiziere, dass/proc/pressure/*Inhalte liefert. - cgroup v2: Für per‑Service/Container‑Sicht nutze ich die unified hierarchy. Ich prüfe mit
mount | grep cgroup2und erwarte einencgroup2-Mount (oft/sys/fs/cgroup). Fehlt er, aktiviere ich ihn über den Kernel-Parametersystemd.unified_cgroup_hierarchy=1(Neustart erforderlich). - Berechtigungen: Exporter auf dem Host brauchen Leserechte auf
/proc/pressure/*und ggf. auf/sys/fs/cgroup/*/*.pressure. In Containern mounte ich diese Pfade read‑only ein.
PSI-Trigger nutzen: Automatisch reagieren statt nur beobachten
Neben Zeitreihen stelle ich auch Ereignisse über poll oder epoll ein, indem ich Schwellwerte und Zeitfenster in die PSI-Dateien schreibe. Sobald der Ressourcendruck den Grenzwert im Fenster übersteigt, feuert ein Event und ich starte Gegenmaßnahmen. Das kann ein zusätzlicher Pod, ein Cache-Flush oder die temporäre Drosselung eines Batch-Jobs sein. In Systemd-Units binde ich diese Reaktion direkt an Services und halte Latenzen flach. So wird Monitoring zur Steuerung, nicht bloß zur Anzeige.
In der Praxis setze ich ein kompaktes Watcher‑Programm ein, das die entsprechenden *pressure-Dateien öffnet, per write() einen Trigger (some oder full samt Schwellwert und Fenster in µs) registriert und anschließend mit epoll blockierend auf Ereignisse wartet. So spare ich Poll‑Zyklen und reagiere deterministisch. Ich halte die Fenster bewusst etwas länger (z. B. 10–30 s), um Transienten auszublenden, und unterscheide je Ressource: memory reagiert empfindlicher als io, cpu muss deutlicher sein, um zu feuern.
PSI nach Prometheus exportieren: Agenten, Metriken, Labels
Für die Zeitreihen sammle ich PSI über einen dedizierten Exporter oder integriere die Werte in bestehende Agenten wie den Node-Exporter. Entscheidend sind konsistente Labels für Host, cgroup und Container, damit Queries in Grafana sauber filtern. In Kubernetes verwende ich zusätzlich cAdvisor- und Kubelet-Metriken für *_pressure_*_waiting_seconds_total, damit Knoten-, Pod- und Container-Ebenen deckungsgleich bleiben. Für klassische Hosts lese ich /proc/pressure/* direkt und mappe some und full auf separate Metrik-Namen. Eine Anleitung zur Agenten-Integration hilft beim Start, etwa unter Node Exporter einrichten.
Exporter-Varianten im Detail: Node, cgroup und Kubernetes
Ich setze je nach Umgebung unterschiedliche Wege ein:
- Node-Exporter (Host-Ebene): Ich aktiviere den pressure-Collector (falls nicht per Default aktiv) z. B. über
--collector.pressure. Er liefert Metriken wienode_pressure_cpu_some_avg10,node_pressure_memory_full_avg60sowienode_pressure_io_waiting_seconds_total{state="some|full"}. Letztere eignen sich fürrate()-Analysen und Top‑N. - Eigener cgroup-Exporter (Service/Container-Ebene): Für feingranulare Sicht lese ich
/sys/fs/cgroup/<gruppe>/{cpu,memory,io}.pressureund emittiere Metriken wiecgroup_pressure_memory_waiting_seconds_total{state="full",cgroup="..."}sowie dieavg10/60/300‑Gauges. Ich normalisiere den cgroup‑Pfad als Label (cgroup) oder mappe aufservice/containerLabels. - Kubernetes: Auf Knotenebene scrapt Prometheus node‑exporter. Für Container‑Sicht nutze ich einen DaemonSet‑Exporter mit
hostPID:trueund Read‑Only‑Mounts von/sys/fs/cgroupund/proc, damit ich die cgroup‑Dateien des Hosts sehe. Zusätzlich ziehe ich Kubelet/cAdvisor‑Metriken heran, sofern sie die PSI‑Totals ausgeben; die Labelsnamespace,podundcontainerhalte ich dabei konsistent.
Mir hilft eine klare Label-Strategie: instance (Host oder NodeName), cgroup (Pfad), namespace/pod/container (bei K8s) sowie state (some/full) und resource (cpu/memory/io/irq). So kann ich stark aggregieren und zugleich tief zoomen.
Prometheus-Jobs, Recording Rules und Beispiel-Queries
Für saubere Auswertungen nutze ich zwei Muster: Prozent‑Gauges (avg10/60/300) und abgeleitete rate()‑Werte auf den *_waiting_seconds_total‑Zählern.
- Scrape: 15s ist ein guter Start. Kürzer erhöht Last, bringt bei
avg10aber selten Mehrwert. - Recording Rules: Ich berechne abgeleitete Zeitreihen, um Dashboards und Alerts zu vereinfachen:
record: psi:node_memory_full:avg60 = avg_over_time(node_pressure_memory_full_avg10[60s])record: psi:node_io_full:rate5m = rate(node_pressure_io_waiting_seconds_total{state="full"}[5m])record: psi:cgroup_memory_full:rate5m = sum by (cgroup) (rate(cgroup_pressure_memory_waiting_seconds_total{state="full"}[5m]))
Mit PromQL baue ich typische Sichten:
- Host‑Trend:
node_pressure_memory_full_avg60über Zeit, getrennt nach Knoten. - Top‑N‑Verursacher:
topk(5, psi:cgroup_memory_full:rate5m)zeigt die lautesten cgroups. - Impact auf Latenz:
(increase(http_request_duration_seconds_sum[5m]) / increase(http_request_duration_seconds_count[5m]))gegennode_pressure_io_full_avg60legen, um Korrelationen zu erkennen. - Plateaus erkennen:
clamp_min(psi:node_io_full:rate5m, 0.0)als Heatmap pro Knoten.
Grafana-Dashboards: Trends sichtbar machen und Hotspots finden
In Grafana zeige ich CPU-, Memory- und I/O-Druck getrennt an, jeweils für some und full als eigene Kurven. Bar-Gauges liefern mir den aktuellen Status, während Timeseries-Panels Peaks und Plateaus enthüllen. Für Ursachenanalyse setze ich Top‑N‑Ansichten nach cgroup, Container oder Pod ein und springe von dort in Detail-Panels. Wichtig bleibt die Kombination aus avg10, avg60 und avg300, um Übersteuerungen durch kurze Spikes zu vermeiden. Wer Dashboards vordenken will, findet hilfreiche Ideen rund um den Grafana und Prometheus Stack.
Alarmierung in der Praxis: Regeln, Fenster, Eskalation
Ich halte mich an ein zweistufiges Modell: Warnung für frühe Signale, Kritisch für dauerhaften Engpass. Beispielhaft setze ich:
- Memory
- Warnung:
node_pressure_memory_full_avg60 > 0.01für 10–30 s - Kritisch:
node_pressure_memory_full_avg60 > 0.05für ≥60 s
- Warnung:
- I/O
- Warnung:
rate(node_pressure_io_waiting_seconds_total{state="some"}[5m]) > 0.02 - Kritisch:
node_pressure_io_full_avg60 > 0.02für ≥120 s
- Warnung:
- CPU
- Warnung:
node_pressure_cpu_some_avg60 > 0.05 - Kritisch:
node_pressure_cpu_full_avg60 > 0.01(weil full hier besonders weh tut)
- Warnung:
In Annotations verknüpfe ich Kontexte (Top‑cgroups, Durchsatz, Latenz) und starte Playbooks: Scale‑Up, Limits anpassen, Cache‑Aktionen, Batch‑Drosselung. Bei wiederholten Events ziehe ich Kapazitätsentscheidungen vor.
Schwellen und Alarmierung: Zeitfenster richtig wählen
Ich definiere klare Schwellwerte pro Ressource und trenne harte Ausfälle von kurzen Lastspitzen. Beispielsweise werte ich memory full über 5 % länger als 60 Sekunden als kritisch, während 1–2 % über zehn Sekunden nur eine Warnung auslösen. Für CPU setze ich strengere Grenzen auf full, da flächendeckendes Warten dort spürbar bremst. Mehrwert bringt die Kopplung mit Durchsatz- und Latenzmetriken: Wenn Druck steigt und Requests langsamer werden, erhöht sich die Dringlichkeit. Ich lege Alerts stets auf Zeitfenster, nicht auf Einzelwerte, um Fehlalarme durch Bursts zu vermeiden.
Kubernetes: Scrape-Setup, Rechte und Korrelation
Im Cluster sammele ich PSI so, dass die Ebenen deckungsgleich werden:
- Node-Exporter als DaemonSet: Standard‑Scrape pro Node liefert globale PSI‑Werte.
- cgroup‑Exporter als Sidecar/DaemonSet: Liest die cgroup v2‑Dateien des Hosts, labelt nach
namespace/pod/container. Ich nutze minimal nötige Rechte und RO‑Mounts. - Kubelet/cAdvisor: Ich aktiviere die Ausgabe relevanter Container‑Metriken und scrappe den Kubelet‑Endpunkt. Ich halte die Label‑Join‑Keys (z. B.
containervs.container_name) konsistent, damit PromQL‑Joins ohne Friktion funktionieren. - Join mit Workload-Metriken: Ich korreliere Pod‑PSI mit App‑Latenzen (z. B. HTTP‑Metriken), CPU‑Limits‑Hits und Speicherfehlern. So erkenne ich, ob Limits, Scheduling oder Storage‑Bottlenecks die Ursache sind.
PSI-Dateien, Kennzahlen und Lesart: Kompakte Übersicht
Die folgende Tabelle fasst die wichtigsten Dateien, Kennzahlen und Einsatzzwecke zusammen, damit ich schneller interpretiere und passende Grafana-Panels baue. Ich nutze sie bei der Analyse, um die nächste Frage zu planen: Tuning, Skalierung oder Entstörung. Besonders relevant bleiben die Unterschiede zwischen some und full sowie die drei Durchschnittsfenster. So ordne ich Symptome zeitlich ein und prüfe, ob der Druck lokal oder flächig auftritt. Die Spalte „Einsatz“ hilft bei der schnellen Einordnung.
| Ressource | Datei | Kennzahlen | Bedeutung | Einsatz |
|---|---|---|---|---|
| CPU | /proc/pressure/cpu | some, full; avg10/60/300; total | Wartezeit auf freie Rechenzeit | Überlastete Hosts, zu enge CPU-Limits |
| Memory | /proc/pressure/memory | some, full; avg10/60/300; total | Wartezeit durch Reclaim, Swap, OOM-Nähe | RAM-Engpässe, Cache‑Druck, fehlerhafte Requests |
| I/O | /proc/pressure/io | some, full; avg10/60/300; total | Wartezeit auf Speichergeräte/Dateisystem | Langsame Datenträger, Sync-Stürme, Flush-Phasen |
| IRQ | /proc/pressure/irq | some, full; avg10/60/300; total | Druck durch Interrupt-Bearbeitung | Netzwerk-Last, Treiber-Tuning, Affinity |
| cgroup | */{cpu,memory,io}.pressure | some, full; avg10/60/300; total | Druck je Service/Container | Root-Cause‑Suche, gezielte Limits |
Praxis: Hosting und WordPress-Stacks effizient absichern
Auf stark genutzten WordPress-Hosts konkurrieren PHP‑FPM, Datenbank und Cache‑Layer regelmäßig um RAM und I/O, was ich über memory und io sofort sehe. Steigt full bei Memory, optimiere ich OpCache, erhöhe Pool‑Größen dosiert oder reduziere teure Plugins. Bei I/O‑Druck prüfe ich Query-Pläne, Journaling‑Einstellungen und asynchrones Schreiben. PSI pro cgroup macht sichtbar, ob Webserver, Worker oder Datenbank den Engpass verursachen. Wer tiefer einsteigen will, findet Hinweise im Linux-PSI Ratgeber, der Einstieg und Bewertung zusammenfasst.
Kapazitätsplanung und Tuning: Von Zahlen zu Aktionen
Ich verbinde PSI mit CPU‑Auslastung, Page‑Faults, I/O‑Durchsatz und Latenzen, um echte Ursachen zu erkennen. Bei anhaltendem memory full skaliere ich RAM, optimiere Reclaim‑Parameter oder ziehe Workloads auseinander. Zeigt io full lange Plateaus, erhöhe ich Queue‑Tiefen, aktiviere Write‑Back‑Strategien oder setze schnellere Datenträger ein. Für CPU‑Druck messe ich Runqueue‑Längen parallel, passe Scheduling‑Klassen an und verteile heiße Threads. Entscheidungen treffe ich nur, wenn Trends im avg60 und avg300 konsistent bleiben und nicht nur ein Spike vorliegt.
Troubleshooting und Validierung: Vom Host bis zum Container
Bei fehlenden PSI-Werten prüfe ich Kernel-Version, CONFIG_PSI und optional den Boot‑Parameter psi=1. Anschließend verifiziere ich die Ausgaben der Dateien unter /proc/pressure/* manuell und gleiche sie mit Exporter-Metriken ab. In cgroup v2 kontrolliere ich zusätzlich die *.pressure-Dateien innerhalb der Gruppenverzeichnisse. Ich teste Alerts mit Lastgeneratoren und beobachte Reaktionslogik via epoll, um Fehlkonfigurationen früh zu entdecken. Abschließend gleiche ich Grafana-Panels mit Logs, Traces und Profilergebnissen ab, damit Diagnose und Gegenmaßnahme sicher passen.
Typische Stolpersteine, die ich einkalkuliere:
- Swap-Interaktion: Leichte memory some-Werte sind normal bei aggressivem Reclaim. Kritisch wird es, wenn full steigt und gleichzeitig Latenzen anziehen.
- CPU-Isolation und Affinity: Pinned/isolierte Kerne können lokal cpu full erzeugen, obwohl der Host gesamt noch Luft hat. Ich prüfe irq-PSI zusätzlich, wenn Netzwerk/Storage stark interruptlastig ist.
- Virtuelle Umgebungen: In VMs spiegeln PSI‑Werte auch Hypervisor‑Einflüsse. Ich messe Host‑ und Gast‑Ebene getrennt, um Overcommitment eindeutig zuzuordnen.
- Scrape-Overhead: PSI selbst ist günstig, aber zu kurze Scrape‑Intervalle erhöhen Prometheus‑Last. 15 s ist oft der Sweet‑Spot.
- Label-Kardinalität: cgroup‑Pfade können explodieren. Ich reguliere mit labeldrop/keep und mappe nur die Ebenen, die ich analysiere (z. B. Service statt jeder kurzlebigen Task‑cgroup).
Kurz zusammengefasst
PSI misst echte Wartezeit auf CPU, RAM und I/O und liefert damit ein klares Signal für Ressourcendruck. Mit Prometheus-Export und Grafana-Dashboards baue ich eine Sicht, die Ursachen trennt und Hotspots schnell aufdeckt. Die Trennung von some und full plus die Fenster avg10/60/300 macht Entscheidungen belastbar. Alerts ziele ich auf Zeiträume, verknüpfe sie mit Latenzen und steuere automatische Reaktionen über Trigger. So treffe ich fundierte Kapazitätsentscheide, entschärfe Engpässe rechtzeitig und halte Dienste im Alltag reaktionsschnell.


