cgroups v2 Ressourcenbegrenzungen mit systemd

11 Min. Lesezeit - 3. Juni 2026

hero section cover
Inhaltsverzeichnis
  • cgroups v2-Ressourcenbeschränkungen mit systemd
  • Aktivieren von cgroups v2
  • Wie systemd cgroups organisiert
  • CPU-Beschränkungen
  • Speichergrenzen mit cgroups v2
  • I/O-Grenzen
  • Isolierung von Mandanten mit Slices
  • Überwachung mit systemd-cgtop und PSI
Teilen

Festlegen von CPU-, Speicher- und E/A-Limits mit cgroups v2 und systemd. Praktische Konfiguration für mandantenfähige Linux-Hosts, mit PSI-Überwachung und Slice-Isolierung.

cgroups v2-Ressourcenbeschränkungen mit systemd

cgroups v2 ist das einheitliche Framework zur Ressourcensteuerung des Linux-Kernels. Es ersetzt die fragmentierte v1-Hierarchie durch einen einzigen Baum, der CPU, Speicher und E/A einheitlich verwaltet und die Container-Isolation in Docker, Kubernetes und systemd unterstützt. Dieser Beitrag behandelt, wie man cgroups v2 aktiviert, Limits über systemd festlegt und es auf reale Multi-Tenant-Hosting-Szenarien anwendet.

Aktivieren von cgroups v2

Moderne Distributionen werden standardmäßig mit aktivierten cgroups v2 ausgeliefert: Ubuntu 21.10+, Debian 11+, Fedora 31+ und RHEL/Rocky 9+. Ältere Systeme verwenden möglicherweise eine hybride Hierarchie oder standardmäßig weiterhin v1. Überprüfen Sie dies mit:

stat -fc %T /sys/fs/cgroup/

Die Ausgabe von cgroup2fs bestätigt, dass v2 aktiv ist. tmpfs bedeutet in der Regel v1.

Um ein Hybridsystem auf reines v2 umzustellen, bearbeiten Sie /etc/default/grub und fügen Sie Folgendes an GRUB_CMDLINE_LINUX_DEFAULT:

systemd.unified_cgroup_hierarchy=1 cgroup_no_v1=all

Erstellen Sie anschließend GRUB neu und starten Sie den Rechner neu:

sudo update-grub
sudo reboot

Für den Produktionsbetrieb sollten Sie Kernel 5.2 oder neuer verwenden, damit Sie den cgroup-Freezer für v2 erhalten, sowie systemd 244+ für die vollständige cpuset Delegierung. Unter Rocky Linux 8 und RHEL 8 müssen Sie möglicherweise auch das Accounting explizit aktivieren, indem Sie diese Zeilen zu /etc/systemd/system.conf:

DefaultCPUAccounting=yes
DefaultMemoryAccounting=yes
DefaultIOAccounting=yes

Laden Sie mit sudo systemctl daemon-reexec. Überprüfen Sie nach dem Neustart, welche Controller verfügbar sind:

cat /sys/fs/cgroup/cgroup.controllers

Sie sollten cpu, memory, iound pids aufgelistet sehen. Diese Controller sind standardmäßig für untergeordnete cgroups nicht aktiviert. Um sie zu aktivieren, schreiben Sie in die Root-Subtree-Control-Datei:

echo "+cpu +memory +io" | sudo tee /sys/fs/cgroup/cgroup.subtree_control

Für einen umfassenden Überblick darüber, wie sich v2 intern von v1 unterscheidet, ist Michael Kerrisks Vortrag auf der NDC TechTown die beste Quelle:

Wie systemd cgroups organisiert

systemd erstellt für jeden Dienst, den es startet, eine cgroup, die nach der Unit benannt ist. nginx.service erhält /sys/fs/cgroup/system.slice/nginx.service/, und jeder Prozess, den es startet, befindet sich innerhalb dieser cgroup. Drei Unit-Typen sind direkt der Hierarchie zugeordnet:

Unit-TypRolleBeschreibung
.sliceInnerer KnotenGruppiert verwandte Dienste und definiert gemeinsame Grenzwerte
.serviceEndknotenVerwaltet von systemd gestartete Prozesse
.scopeBlattknotenVerfolgt extern gestartete Prozesse (Container-Payloads, Anmeldesitzungen)

Vier Standard-Slices sind ab Werk enthalten: -.slice (root), system.slice, user.sliceund machine.slice. Jede für ein Slice festgelegte Beschränkung gilt automatisch für jeden darin enthaltenen Dienst.

Eine wichtige Regel der Version 2: Prozesse können nur in Leaf-Knoten laufen. Eine cgroup mit untergeordneten cgroups kann keine Prozesse direkt hosten, weshalb systemd Dienste niemals in den Trunk eines Slices einordnet.

Legen Sie Beschränkungen immer über systemd fest, anstatt direkt in /sys/fs/cgroup/ direkt. Manuelle Einträge bleiben bei Neustarts nicht erhalten und stehen im Konflikt mit systemds exklusivem Besitz der Hierarchie. Verwenden Sie systemctl set-property für einmalige Änderungen und Unit-Drop-Ins (systemctl edit nginx.service) für dauerhafte Änderungen.

CPU-Beschränkungen

cgroups v2 bietet Ihnen zwei CPU-Steuerungsmöglichkeiten: eine harte Obergrenze (cpu.max, verfügbar als CPUQuota in systemd) und eine proportionale Gewichtung (cpu.weight / CPUWeight).

CPUQuota ist eine absolute Obergrenze. CPUQuota=50% erlaubt die Hälfte eines Kerns; CPUQuota=200% erlaubt die Zeit von zwei vollen Kernen. Der Dienst wird gedrosselt, wenn er versucht, diesen Wert zu überschreiten, unabhängig davon, wie wenig ausgelastet der Rest der CPU ist.

CPUWeight spielt nur bei Konkurrenz um Ressourcen eine Rolle. Der Bereich liegt zwischen 1 und 10.000, Standardwert ist 100. Drei Dienste mit Gewichten von 150, 100 und 50 erhalten etwa 50 %, 33 % und 17 % der CPU-Zeit, wenn sie alle gleichzeitig darauf zugreifen wollen. Wenn die CPU ansonsten im Leerlauf ist, schränken die Gewichte nichts ein.

Bei latenzempfindlichen Workloads sollten Prozesse mit AllowedCPUs=. Dies reduziert Kontextwechsel und hält den Cache pro Kern aktiv:

[Service]
CPUQuota=200%
CPUWeight=150
AllowedCPUs=0-3

Verwenden Sie eine feste Quote, wenn Sie vorhersehbare Kosten benötigen (Abrechnung bei Multi-Tenant-Umgebungen, Isolierung von „Noisy Neighbors“). Verwenden Sie Gewichte, wenn Sie eine maximale Hardwareauslastung anstreben und lediglich eine Prioritätsreihenfolge bei Spitzenlasten benötigen.

Speichergrenzen mit cgroups v2

Der Speicher hat zwei Ebenen: memory.high (soft, Drosselung) und memory.max (hart, OOM). Hintergrundinformationen zu Swap, Page Reclaim und dem Kernel-OOM-Killer finden Sie in unserem Begleitbeitrag zur Speicherverwaltung unter Linux.

Stellen Sie memory.high etwa 10 bis 20 % unter memory.max. Der Kernel beginnt mit der Rückgewinnung von Seiten und der Drosselung von Zuweisungen, sobald memory.high dieser Wert überschritten wird, wodurch sich die Arbeitslast in der Regel erholen kann, bevor der OOM-Killer ausgelöst wird. Erreicht die Auslastung memory.max, beendet der Kernel Prozesse in der cgroup.

Eine typische Konfiguration:

[Service]
MemoryHigh=400M
MemoryMax=512M
MemorySwapMax=0

MemorySwapMax=0 deaktiviert den Swap für diese cgroup. Dies lohnt sich bei latenzempfindlichen Workloads (Datenbanken, Echtzeit-Streaming), bei denen Swap-I/O die Tail-Latenz in die Höhe treiben würde.

Für Worker-Pools, bei denen das Zurücklassen verwaister Geschwister den gemeinsamen Status beschädigen würde, schreiben Sie 1 in die memory.oom.group Datei. Wenn ein Prozess wegen OOM beendet wird, beendet der Kernel alle Prozesse in der cgroup gemeinsam.

Überprüfen memory.events , um zu sehen, wie oft ein Dienst gedrosselt oder per OOM-Kill beendet wurde:

cat /sys/fs/cgroup/system.slice/nginx.service/memory.events

Die high und oom_kill Zähler zeigen an, ob Ihre Grenzwerte richtig dimensioniert sind. Anhaltende Werte ungleich Null bedeuten, dass die Arbeitslast mehr Spielraum benötigt.

I/O-Grenzen

Der I/O-Controller verfügt über dasselbe Zwei-Modi-Design: absolute Obergrenzen über io.max und proportionale Aufteilung über io.weight.

Die Limits gelten pro Blockgerät und werden durch Major-Minor-Nummern identifiziert. Sie finden sie mit lsblk -o NAME,MAJ:MIN. Eine typische systemd-Konfiguration:

[Service]
IOReadBandwidthMax=/dev/sda 50M
IOWriteBandwidthMax=/dev/sda 30M
IOReadIOPSMax=/dev/sda 1000
IOWriteIOPSMax=/dev/sda 500

io.weight funktioniert wie cpu.weight: Bereich 1 bis 10.000, Standardwert 100. Die Zuweisung von 500 an einen kundenorientierten Dienst und 50 an ein nächtliches Backup verhindert, dass das Backup die Festplatte während der Spitzenzeiten überlastet, lässt es aber die volle Bandbreite nutzen, wenn nichts anderes sie benötigt.

I/O-Limits gelten nur, wenn Sie das richtige Gerät anvisieren. Der Kernel verfolgt I/O nach Blockgeräten, daher bewirkt ein Limit für /dev/sda hat keine Auswirkungen auf die I/O, die an /dev/nvme0n1. Legen Sie auf Hosts mit mehreren Festplatten die Begrenzungen pro Gerät fest.

Isolierung von Mandanten mit Slices

Definieren Sie für gemeinsam genutzte Umgebungen ein Slice pro Mandant. Legen Sie /etc/systemd/system/tenant-a.slice:

[Slice]
CPUQuota=200%
CPUWeight=150
MemoryHigh=3584M
MemoryMax=4096M
MemorySwapMax=0
IOReadBandwidthMax=/dev/sda 200M
TasksMax=512

TasksMax=512 begrenzt die Gesamtzahl der Prozesse und Threads, wodurch verhindert wird, dass eine Fork-Bombe in einem Mandanten den Host lahmlegt. Legen Sie die Mandantendienste in diesem Slice ab (über Slice=tenant-a.slice in ihren Unit-Dateien), und sie erben automatisch alle Einstellungen.

Dieses Muster eignet sich auch, um ressourcenintensive Hintergrundaufgaben von benutzerseitigen Diensten zu trennen. Platzieren Sie Backups, Protokollrotation und Batch-Jobs in einem background.slice mit geringer CPUWeight und io.weight . Sie erhalten volle Ressourcen, wenn das System im Leerlauf ist, und treten beiseite, wenn Produktionsdatenverkehr eintrifft.

Für Container-Laufzeitumgebungen wie Docker und Podman fügen Sie Delegate=yes zu ihren systemd-Unit-Dateien hinzu. Dadurch können sie ihre eigenen Sub-Cgroups ohne Root-Rechte verwalten, und die für die übergeordnete Ebene festgelegten Grenzen gelten weiterhin für alle darunter liegenden Ebenen.

Überwachung mit systemd-cgtop und PSI

Für eine Live-Ansicht im Top-Stil von CPU, Speicher und E/A pro cgroup führen Sie Folgendes aus:

systemd-cgtop

Für die statische Hierarchie und um zu sehen, welche Prozesse sich wo befinden, verwenden Sie systemd-cgls.

Die nützlichste v2-Funktion für die Produktionsüberwachung ist Pressure Stall Information (PSI). PSI meldet den prozentualen Anteil der Zeit, in der Aufgaben in einer cgroup beim Warten auf eine Ressource blockiert waren, und stellt diese Informationen in drei Dateien pro cgroup bereit:

cat /sys/fs/cgroup/tenant-a.slice/cpu.pressure
cat /sys/fs/cgroup/tenant-a.slice/memory.pressure
cat /sys/fs/cgroup/tenant-a.slice/io.pressure

Eine CPU mit 100 % Auslastung und 0 % Druck ist in einem gesunden Zustand. Jede Aufgabe, die CPU-Leistung benötigt, erhält diese. Dieselbe CPU mit 80 % Auslastung, aber 30 % Druck bedeutet, dass Aufgaben auf ihre Ausführungszeit warten. Warten Sie auf PSI-Werte, nicht auf Auslastungsdaten: PSI erfasst Konflikte, die Auslastungsmetriken völlig übersehen.

Passen Sie die Grenzwerte live an, ohne etwas neu zu starten:

sudo systemctl set-property tenant-a.slice MemoryMax=6144M

Die Änderung wird sofort wirksam und bleibt auch nach einem Neustart bestehen. In Kombination mit PSI-basierten Warnmeldungen können Sie so auf Lastverschiebungen reagieren, bevor diese zu OOM-Kills oder außer Kontrolle geratenen Latenzen führen.

Wenn Sie hochdichte Multi-Tenant-Workloads ausführen und einen Host mit ausreichendem Spielraum benötigen, um diese Richtlinien sauber anzuwenden, sind unsere dedizierten Server genau dafür ausgelegt.

Blog

Diese Woche im Blickpunkt

Weitere Artikel
Warum es wichtig ist, einen leistungsstarken und ungemessenen VPS zu haben

Warum es wichtig ist, einen leistungsstarken und ungemessenen VPS zu haben

Ein ungemessener VPS bietet eine pauschale Bandbreite mit einer festen Portgeschwindigkeit. Wie er sich von gebührenpflichtigen Tarifen unterscheidet, wann er sich lohnt und worauf Sie vor dem Kauf achten sollten.

7 Min. Lesezeit - 9. Mai 2025

Linux-Speicherverwaltung: Swap, OOM Killer & Cgroups

12 Min. Lesezeit - 31. Mai 2026

Weitere Artikel
background image

Haben Sie Fragen oder benötigen Sie eine individuelle Lösung?

icon

Flexible Optionen

icon

Globale Reichweite

icon

Sofortige Bereitstellung

icon

Flexible Optionen

icon

Globale Reichweite

icon

Sofortige Bereitstellung