Tutoriel Linux

Cgroups Linux, die CPU- und Speicherkapazitäten eines Dienstes überprüfen

Débutant5 min de lecture

Ein Webdienst, der langsam wird, ein Worker, der ohne klare Nachricht beendet wurde, oder ein Server, der swappen muss, hat nicht unbedingt den gesamten RAM der Maschine erschöpft. Mit systemd kann eine festgelegte Grenze für eine Einheit ausreichen, um das Symptom zu erklären.

Die cgroups ermöglichen es dem Linux-Kernel, die Ressourcen einer Gruppe von Prozessen zu zählen, zu begrenzen und zu priorisieren. Bevor Sie Speicher hinzufügen oder zufällig neu starten, überprüfen Sie, was systemd auf den betreffenden Dienst anwendet.

Tux vor Servern mit einer CPU- und Speicherauslastungsanzeige
Cgroups und Ressourcengrenzen eines von systemd verwalteten Linux-Dienstes.

Cgroup eines Dienstes anzeigen

Bei aktuellen Distributionen platziert systemd jeden Dienst in seiner eigenen Cgroup. Sie müssen keine PIDs manuell verschieben, um eine klassische Einheit wie Nginx, PostgreSQL oder einen Anwendungs-Worker zu diagnostizieren.

Beginnen Sie damit, den Status des Dienstes und die mit den Ressourcen verbundenen Eigenschaften zu lesen. Ersetzen Sie nginx.service durch den tatsächlichen Namen Ihrer Einheit.

systemctl status nginx.service --no-pager
systemctl show nginx.service -p ControlGroup -p MainPID -p MemoryCurrent -p MemoryMax -p MemoryHigh -p CPUQuotaPerSecUSec

ControlGroup gibt den von systemd verwalteten Pfad an. MemoryCurrent zeigt den zum Zeitpunkt des Lesens für die Einheit verbrauchten Speicher an. Wenn MemoryMax den Wert infinity hat, hat systemd keine harte Speicherschwelle für diesen Dienst festgelegt.

Wenn Sie von einer PID ausgehen, lesen Sie ihre Cgroup von /proc. In cgroup v2 sehen Sie oft eine Zeile wie 0::/system.slice/nginx.service.

pid=$(systemctl show nginx.service -p MainPID --value)
cat /proc/$pid/cgroup

Wenn MainPID den Wert 0 hat, hat der Dienst keinen aktiven Hauptprozess. Kehren Sie zu systemctl status und journalctl zurück, anstatt aus einer leeren PID zu schließen.

Speicher- und CPU-Limits lesen

MemoryMax ist ein hartes Limit. Wenn die Cgroup diese Schwelle erreicht und der Kernel nicht genug Speicher freigibt, kann ein Prozess aus der Gruppe vom OOM-Killer getötet werden.

MemoryHigh ist weniger drastisch. Der Kernel drosselt die Zuweisungen und versucht, Speicher zurückzugewinnen, wenn die Cgroup diese Schwelle überschreitet. Um einen Dienst einzuschränken, ohne ihn beim ersten Anstieg zum Absturz zu bringen, ist es oft eine bessere erste Einstellung als MemoryMax.

CPUQuota begrenzt die verfügbare CPU-Zeit für den Dienst. CPUQuota=25% entspricht einem Viertel eines logischen Kerns. Auf einer Maschine mit mehreren Kernen erlaubt CPUQuota=200% bis zu zwei Kernen an CPU-Zeit.

systemctl show nginx.service 
  -p MemoryCurrent -p MemoryMax -p MemoryHigh 
  -p CPUUsageNSec -p CPUQuotaPerSecUSec

Verwechseln Sie nicht Quote und Priorität. Ein Limit verhindert, dass der Dienst eine bestimmte Menge an CPU überschreitet. Eine Priorität, mit nice oder CPUWeight, regelt hauptsächlich den Wettbewerb, wenn mehrere Aufgaben gleichzeitig CPU anfordern.

Herkunft eines Limits finden

Ein Limit kann aus der Datei stammen, die mit dem Paket geliefert wurde, aus einem systemd-Drop-in oder aus einem lokal erstellten Override. Lesen Sie die vollständige Konfiguration, bevor Sie den Dienst ändern.

systemctl cat nginx.service
systemctl show nginx.service -p FragmentPath -p DropInPaths

Eine Datei unter /etc/systemd/system/nginx.service.d/ hat normalerweise Vorrang vor der vom Paket bereitgestellten Einheit. Vermeiden Sie es, eine Datei direkt in /usr/lib/systemd/system/ oder /lib/systemd/system/ zu ändern, da ein Update sie überschreiben könnte.

Überprüfen Sie danach die Symptome. Ein langsamer Dienst kann auch das Ergebnis einer langsamen SQL-Anfrage, einer überlasteten Festplatte, einer vollen Warteschlange oder eines Anwendungsfehlers sein.

journalctl -u nginx.service -b --no-pager
journalctl -k -b | grep -Ei 'oom|out of memory|killed process|memory cgroup'

Wenn die Limits auf infinity bleiben und das Kernelprotokoll kein Speicherereignis anzeigt, suchen Sie anderswo, bevor Sie an den cgroups arbeiten: Anwendungsprotokolle, Festplattendaten, Netzwerklatenz oder der tatsächlichen Prozessverbrauch.

Testen, ohne den Dienst zu berühren

Um die Syntax zu validieren oder die Auswirkungen eines Limits zu messen, starten Sie eine temporäre Einheit mit systemd-run. Der Test ändert Nginx, PostgreSQL oder Ihren Geschäftsdienst nicht.

sudo systemd-run --unit=essai-limite-memoire 
  --property=MemoryMax=256M 
  --property=CPUQuota=25% 
  /usr/bin/bash -c 'sleep 300'

Lesen Sie danach die angewandten Eigenschaften erneut durch. Für CPUQuota=25% kann systemd einen Wert von 250ms pro Sekunde anzeigen.

systemctl show essai-limite-memoire.service 
  -p MemoryMax -p CPUQuotaPerSecUSec -p ControlGroup -p ActiveState
systemctl cat essai-limite-memoire.service

Beenden Sie die Testeinheit, sobald Sie fertig sind. Mit sleep ist das Vergessen unbedenklich, aber dieser Reflex vermeidet Überraschungen, wenn der Test einen echten Befehl ausführt.

sudo systemctl stop essai-limite-memoire.service

Ein systemd-Override anwenden

Wenn die Protokolle und Messungen bestätigen, dass eine Grenze sinnvoll ist, greifen Sie auf einen systemd-Override zu. Im folgenden Beispiel wird ein hohes Limit von 800 MB, ein hartes Limit von 1 GB und eine Begrenzung des Dienstes auf das Äquivalent eines CPU-Kerns festgelegt.

sudo systemctl edit mon-service.service

Fügen Sie nur die notwendigen Direktiven hinzu. Kopieren Sie diese Werte nicht in einen Produktionsdienst, ohne dessen normale Nutzung und Spitzen beobachtet zu haben.

[Service]
MemoryHigh=800M
MemoryMax=1G
CPUQuota=100%

Laden Sie systemd neu und starten Sie den Dienst nur neu, wenn eine Unterbrechung akzeptabel ist. Halten Sie beim Arbeiten an einer kritischen Komponente eine zweite SSH-Sitzung offen.

sudo systemctl daemon-reload
sudo systemctl restart mon-service.service
systemctl status mon-service.service --no-pager
systemctl show mon-service.service -p MemoryCurrent -p MemoryHigh -p MemoryMax -p CPUQuotaPerSecUSec

Wenn der Dienst nicht mehr startet oder zu langsam wird, entfernen Sie den Override vorsichtig. sudo systemctl revert mon-service.service löscht die lokalen Overrides der Einheit. Verwenden Sie es nicht, wenn im gleichen Verzeichnis andere Einstellungen zu bewahren sind.

Kontrollen nach Änderungen

  • Überprüfen Sie den Status mit systemctl is-active mon-service.service.
  • Lesen Sie MemoryCurrent, MemoryHigh, MemoryMax und CPUQuotaPerSecUSec nach dem Neustart durch.
  • Überwachen Sie journalctl -u mon-service.service -b und das Kernelprotokoll während des problematischen Lastspitzen.
  • Messen Sie die Auswirkungen auf die Anwendung: Antwortzeiten, verspätete Jobs, Warteschlangen, Benutzerfehler.
  • Notieren Sie den Grund für die Einstellung, das Datum, den festgelegten Wert und das behobene Symptom in Ihrem Betriebsverfahren.

Die cgroups schützen den Rest der Maschine und machen die Grenzen sichtbar. Sie beheben keine Speicherlecks, kostspieligen Anfragen oder nicht dimensionierte Worker. Wenn die Grenze erreicht ist, bewahren Sie das Protokoll, die beobachtete Last und den konfigurierten Wert auf, um zwischen dem Anpassen der Grenze und dem Beheben der Anwendung zu wählen.

Für weiterführende Informationen beschreibt die Seite systemd.resource-control(5) jede Direktive im Detail. Der Leitfaden Nice unter Linux hilft dabei, eine CPU-Priorität auszuwählen, wenn eine strenge Obergrenze nicht geeignet ist.

sudo apt update && sudo apt upgrade