systemd: Speicher und CPU für Prozesse begrenzen
Begrenzen Sie Prozesse mit MemoryHigh, MemoryMax, CPUQuota und TasksMax. Lernen Sie, warum ein VPS trotz Limit hängt und wie Sie den OOM-Kill prüfen.
Prozessspeicher und CPU-Zeit mit einem systemd-Drop-in begrenzen
Sie begrenzen den Speicher und die CPU-Zeit eines Prozesses auf einem Linux-VPS, indem Sie der Unit, die den Prozess startet, einige Zeilen hinzufügen. MemoryMax= ist die harte Obergrenze für den Speicher. CPUQuota= ist die Obergrenze für die Prozessorzeit. Beide Grenzen werden durch cgroup v2 (Control Groups, Version 2) erzwungen. Dabei handelt es sich um eine Kernel-Funktion, die systemd bereits verwendet, um jeden Dienst auf dem System zu erfassen.
sudo systemctl edit myapp.serviceDadurch wird eine Drop-in-Datei mit Anweisungen in Kommentaren geöffnet. Fügen Sie Folgendes oberhalb dieser Kommentare ein:
[Service]
MemoryHigh=512M
MemoryMax=768M
MemorySwapMax=0
CPUQuota=80%
TasksMax=128sudo systemctl daemon-reload
sudo systemctl restart myapp.service
systemctl show myapp.service -p MemoryHigh -p MemoryMax -p CPUQuotaPerSecUSec -p TasksMaxsystemctl show muss Ihre Werte in den vom Kernel verwendeten Einheiten erneut ausgeben: MemoryMax=805306368 und CPUQuotaPerSecUSec=800ms. Wenn MemoryMax=infinity ausgegeben wird, wurde das Drop-in nicht geladen. Prüfen Sie, ob die Datei unter /etc/systemd/system/myapp.service.d/override.conf gespeichert wurde und mit dem Header [Service] beginnt. Eine Einstellung ohne vorherigen Abschnitt veranlasst systemd, Assignment outside of section. Ignoring. zu protokollieren und den Dienst vollständig ohne Begrenzungen zu starten.
Im restlichen Leitfaden erfahren Sie, wie Sie diese Werte auswählen und welche Probleme nach dem Setzen der Begrenzungen weiterhin auftreten können.
Warum ein außer Kontrolle geratener Prozess einen VPS einfriert, obwohl er ihn nie vollständig belegt
Ein Prozess, der ein hartes Speicherlimit erreicht, wird nach etwa einer Sekunde beendet, und der Dienst wird neu gestartet. Das ist der gute Fall. Im schlechten Fall wird nichts beendet: Der Server antwortet auf Ping, SSH akzeptiert die Verbindung, aber die Shell-Eingabeaufforderung erscheint nie. Die Maschine läuft und ist beschäftigt, aber keine dieser Aktivitäten ist nützlich.
Der Mechanismus ist nicht offensichtlich. Wenn der freie Speicher knapp wird, gibt der Kernel keine neuen Seiten aus, sondern nimmt Seiten zurück. Am einfachsten lassen sich dateigestützte Seiten zurücknehmen, und der Page Cache enthält den ausführbaren Code aller laufenden Prozesse. Der Kernel entfernt daher die Textseiten von sshd. Die nächste Anweisung, die sshd ausführt, löst dann einen Page Fault aus, der diese Bytes erneut vom Speichergerät einlesen muss. Jeder Prozess wartet schließlich auf das Laufwerk, anstatt ausgeführt zu werden. Dieselben Seiten werden wiederholt ausgelagert und zurückgeholt. Dieser Zustand wird Thrashing genannt.
Zwei Faktoren verschärfen das Problem auf einem VPS gegenüber einem Laptop. Der Speicher ist häufig über das Netzwerk angebunden oder wird gemeinsam genutzt. Deshalb kostet jeder Page Fault mehr Millisekunden als auf einem lokalen NVMe-Gerät. Außerdem misst der Kernel nicht die benötigte Zeit, sondern den Erfolg: Solange die Rückgewinnung weiterhin eine Seite freigibt, glaubt der Kernel, dass er Fortschritte macht, auch wenn dieser Vorgang sehr langsam ist. Deshalb ruft er den Out-of-Memory-Killer (OOM-Killer) nicht auf. Ein Server kann viele Minuten in diesem Zustand bleiben, bevor ein Prozess beendet wird.
Sie können diesen Zustand beobachten. Der Kernel stellt unter Linux 4.20 und neuer Pressure Stall Information (PSI) bereit:
cat /proc/pressure/memory
cat /proc/pressure/iosome avg10=63.72 avg60=41.02 avg300=12.33 total=13729481
full avg10=48.15 avg60=30.44 avg300=8.90 total=9114233Die Zeile full ist entscheidend. full avg10=48.15 bedeutet, dass in den letzten zehn Sekunden während 48% der Zeit jede ausführbare Task auf dem Server wegen Speicheroperationen blockiert war und daher nichts ausgeführt wurde. Auf einem gesunden Server liegt full nahe null. Ab 10 wirkt das System für Menschen langsam. Werte von 40 oder mehr entsprechen dem Zustand, den Benutzer als eingefroren beschreiben.
Darum ist ein Limit allein auch keine Zusage. Eine Unit unter MemoryHigh= wird gedrosselt, statt beendet zu werden. Sie bleibt also aktiv und langsam. Nichts startet sie neu, weil sie aus Sicht von systemd nie fehlgeschlagen ist. Eine begrenzte Unit, für die weiterhin Swap verwendet werden darf, erzeugt Lese- und Schreibvorgänge, die dieser Unit zugerechnet, aber von einem gemeinsam genutzten Gerät verarbeitet werden. Dadurch kann sie /proc/pressure/io für alle anderen Dienste auf dem Server erhöhen. Limits legen fest, wer für einen Engpass bezahlt. Sie können jedoch keine zusätzliche Kapazität schaffen.
Prüfen, ob Ihre VPS cgroup v2 verwendet
stat -fc %T /sys/fs/cgroupcgroup2fs ist die einheitliche Hierarchie, die alle folgenden Einstellungen benötigen. tmpfs bedeutet, dass das System mit dem älteren v1-Layout gestartet wurde. Dort existieren MemoryHigh= und MemorySwapMax= nicht, und das OOM-Verhalten pro Unit ist anders. Ubuntu 22.04 und höher sowie Debian 11 und höher verwenden standardmäßig v2. Ein altes Image oder ein Kernel, der mit systemd.unified_cgroup_hierarchy=0 gestartet wurde, verwendet v2 nicht.
Unter cgroup v2 aktiviert systemd standardmäßig die Speicherbuchhaltung für jede Unit. Die Werte sind daher bereits verfügbar:
systemd-cgtop -mDamit werden die cgroups nach Speichernutzung sortiert aufgelistet. Das ist die schnellste Möglichkeit, die Ursache für den Ressourcenverbrauch auf diesem System zu ermitteln, solange es noch antwortet. Wenn der Server neu ist, hat die Konto- und Firewall-Konfiguration in den ersten zehn Minuten auf einer neuen VPS Vorrang.
MemoryHigh drosselt. MemoryMax beendet Prozesse.
Der Unterschied zwischen den beiden Speichereinstellungen bestimmt, wie sich ein Fehler auswirkt.
MemoryHigh=ist eine weiche Obergrenze. Oberhalb dieses Werts fordert der Kernel aus dieser Cgroup aggressiv Speicher zurück und verlangsamt absichtlich weitere Speicherzuweisungen. Die Nutzung kann den Wert trotzdem überschreiten, und es werden keine Prozesse beendet.MemoryMax=ist eine harte Obergrenze. Kann eine Speicherzuweisung unter dieser Grenze nicht erfüllt werden, startet der OOM-Killer innerhalb dieser Cgroup und beendet einen eigenen Prozess dieser Unit.
Der zweite Punkt ist der eigentliche Grund, MemoryMax= für alles zu setzen, dem Sie nicht vollständig vertrauen. Ohne Obergrenze betrifft ein Speichermangel das gesamte System, und der globale OOM-Killer wählt sein Opfer anhand von oom_score aus. Das bedeutet meist, dass der größte Prozess beendet wird. Der größte Prozess ist normalerweise Ihre Datenbank und nicht das Script, das den Speicherverlust verursacht hat. Mit einer Obergrenze wird der Prozess innerhalb der Unit beendet, die den Speichermangel verursacht hat.
Setzen Sie beide Werte und legen Sie MemoryHigh= etwa 20 bis 30 Prozent unter MemoryMax= fest. Der Abstand ist eine Warnzone: Ein langsamer Speicherverlust überschreitet High und zeigt sich als langsamer Dienst, während ein plötzlicher Spitzenwert Max direkt überschreitet und den Prozess beendet.
Prozentwerte beziehen sich auf den installierten physischen Speicher. MemoryMax=25% entspricht bei einem Tarif mit 4 GB also 1 GB und bleibt nach einer Vergrößerung des Tarifs bei einem Viertel des Systems. MemorySwapMax=0 hält diese Unit vollständig vom Swap fern. Dadurch wird ein lang andauerndes Ausbremsen zu einem schnellen, eindeutigen Abbruch.
Zu einer Obergrenze gehört eine Neustart-Richtlinie. Andernfalls bleibt nach dem Beenden lediglich ein gestoppter Dienst zurück.
[Unit]
StartLimitIntervalSec=300
StartLimitBurst=5
[Service]
Restart=on-failure
RestartSec=5sStartLimit* gehört in [Unit] und Restart= in [Service]. Wenn Sie einen der Werte in den falschen Abschnitt setzen, ignoriert systemd ihn. Fünf Neustarts innerhalb von fünf Minuten sind ein Speicherverlust und kein kurzzeitiger Aussetzer. Danach gibt systemd auf und lässt die Unit als fehlgeschlagen zurück. Diesen Zustand möchten Sie später vorfinden, statt eine Absturzschleife zu haben, die das Problem verbirgt.
CPU mit CPUQuota begrenzen oder mit CPUWeight anteilig zuteilen
CPUQuota= entspricht einem Prozentsatz der auf einem CPU-Kern verfügbaren Zeit. CPUQuota=50% entspricht der Hälfte eines Kerns. CPUQuota=200% entspricht zwei Kernen. Die Unit kann diese Rechenzeit auf beliebig viele Threads verteilen. Bei einem Tarif mit 2 vCPUs entspricht CPUQuota=200% dem gesamten System.
CPUWeight= ist für die meisten Dienste die bessere Standardeinstellung. Der Wert definiert einen relativen Anteil von 1 bis 10000. Der Kernel-Standardwert ist 100. Die Einstellung greift nur bei Konkurrenz um Rechenzeit: Ein Backup-Job mit CPUWeight=20 wird unter Last gegenüber einem Webserver mit 100 nachrangig behandelt. Wenn das System nicht ausgelastet ist, kann der Job weiterhin die gesamte Rechenleistung nutzen. Eine harte CPUQuota verwirft diese ungenutzte Kapazität.
Seien Sie realistisch, welchen Nutzen eine CPU-Begrenzung hat. Ein CPU-gebundener Prozess bringt Linux normalerweise nicht zum Stillstand, weil der Scheduler allen Prozessen weiterhin Rechenzeit zuweist. Arbeitsspeicher ist die Ressource, die ein System meist zum Absturz bringt. Verwenden Sie CPUQuota=, wenn Sie eine vorhersehbare Obergrenze benötigen, beispielsweise für einen Build oder einen Agenten, der sonst eine Stunde lang durchgehend die maximale CPU-Leistung nutzen würde. Die Dimensionierung solcher Workloads ist eine eigene Frage. Sie wird unter wie viel RAM und CPU ein Coding-Agent auf einem VPS benötigt behandelt.
Wenn die CPU-Auslastung hoch ist, obwohl keiner Ihrer Prozesse nennenswert arbeitet, kann die Ursache auf der anderen Seite des Hypervisors liegen. Das ist CPU-Steal-Time durch einen ausgelasteten Nachbarn. Keine von Ihnen gesetzte Quota kann daran etwas ändern.
TasksMax verhindert eine Fork-Schleife
TasksMax= gibt die maximale Anzahl der Prozesse und Threads an, die eine Unit enthalten darf. Threads werden mitgezählt. Ein Java- oder Go-Dienst benötigt daher mehr Spielraum, als die Prozessliste vermuten lässt. Dies ist der kostengünstigste Schutz gegen ein Skript, das in einer Schleife neue Prozesse erzeugt. Der Fork-Vorgang schlägt innerhalb der Unit fehl, bevor dem System die Prozess-IDs ausgehen.
TasksMax=128Wenn eine Unit das Limit erreicht, schreibt der Kernel eine Zeile mit dem Namen der Cgroup ins Log:
cgroup: fork rejected by pids controller in /system.slice/myapp.serviceDas Programm selbst meldet normalerweise fork: retry: Resource temporarily unavailable. Prüfen Sie mit systemctl show -p DefaultTasksMax, welche Vorgabe der Manager anwendet.
Einen einmaligen Job mit systemd-run begrenzen
Dafür benötigen Sie keine Unit-Datei. systemd-run erstellt für einen einzelnen Befehl eine temporäre Unit.
sudo systemd-run --scope -p MemoryMax=1G -p MemorySwapMax=0 -p CPUQuota=50% -p TasksMax=64 ./import-data.sh--scope führt den Befehl in Ihrem Terminal aus, nachdem Running scope as unit: run-r7c1a....scope ausgegeben wurde. Die Ausgabe bleibt auf dem Bildschirm sichtbar. Die Begrenzungen werden aufgehoben, sobald der Befehl beendet ist. Jede Eigenschaft aus systemd.resource-control funktioniert nach -p.
Für einen längeren Job lassen Sie --scope weg und vergeben einen Namen. Der Job läuft dann als temporärer Dienst im Hintergrund und schreibt seine Logs in das Journal:
sudo systemd-run --unit=nightly-import -p MemoryMax=1G -p CPUWeight=20 ./import-data.sh
journalctl -u nightly-import -fDieselben Optionen funktionieren mit --user auch ohne root-Rechte. Der User Manager verfügt jedoch nur über die ihm zugewiesenen Controller. Deshalb kann eine Eigenschaft dort abgelehnt werden. Führen Sie den Befehl in diesem Fall mit sudo aus. Wenn ein Job dauerhaft eingerichtet werden soll, übernehmen Sie die Einstellungen unverändert in eine echte Unit: siehe ein Script als systemd-Dienst und Timer ausführen.
Die Swap-Frage, ehrlich beantwortet
Swap verändert die Art des Fehlers, verhindert ihn aber nicht.
Ohne Swap erreicht ein Leak die Obergrenze, und innerhalb von Sekunden fällt ein Prozess aus. Der Ausfall ist deutlich, kurz und lässt sich anschließend im Journal leicht nachvollziehen. Mit Swap schreibt der Kernel selten verwendete anonyme Pages auf die Festplatte und gewinnt dadurch Zeit. Wenn sich der Prozess stabilisiert hätte, hilft Swap. Bei einem außer Kontrolle geratenen Prozess verlängert Swap jedoch einen fünf Sekunden langen Ausfall zu einem zwanzig Minuten langen Stillstand. Dieser Stillstand ist schlimmer, weil ein beendeter Prozess eine funktionierende Shell zurücklässt, ein System unter massivem Swapping jedoch nicht.
swapon --show
free -hEin praktikabler Mittelweg auf einem kleinen VPS ist eine moderate Swap-Datei für Pages, die einmal reserviert und danach nie wieder verwendet werden. Setzen Sie außerdem MemorySwapMax=0 für die Units, auf deren Ausfall Sie vorbereitet sind. Die wichtigen Dienste behalten ihren Swap. Die unvorhersehbaren Dienste erreichen schnell die Obergrenze und werden neu gestartet.
vm.swappiness zu senken, ist ein schwacher Stellhebel. Der Grund ist wichtig: Der Wert verschiebt nur das Verhältnis zwischen dem Verdrängen des Page Cache und dem Auslagern anonymer Pages. Beides verursacht später einen Lesezugriff auf die Festplatte. Geändert wird also, welche Pages unter Thrashing leiden, nicht, ob das System unter Thrashing leidet.
Ein früher OOM-Daemon beendet Prozesse vor dem Stillstand
Der Kernel wartet, bis die Rückgewinnung vollständig fehlschlägt. Auf einem kleinen VPS ist genau dieses Warten das Zeitfenster, in dem Sie den Rechner verlieren. Zwei Daemons im Userspace verkürzen dieses Zeitfenster, indem sie den Speicher selbst überwachen und früher Prozesse beenden.
earlyoom überwacht den verfügbaren Speicher und den freien Swap. Sobald einer der beiden Werte unter einen Schwellenwert fällt, beendet er den Prozess mit der höchsten Bewertung.
sudo apt install earlyoom
systemctl status earlyoomDas Debian- und Ubuntu-Paket startet den Dienst bei der Installation. Seine Optionen befinden sich in /etc/default/earlyoom:
EARLYOOM_ARGS="-m 5,2 -s 5,2 --avoid '^(sshd|systemd)$' --prefer '^(node|python3)$'"-m PERCENT legt das Minimum für den verfügbaren Speicher fest und -s PERCENT das Minimum für den freien Swap. Beide Werte stehen standardmäßig auf 10 Prozent. Die zweite Zahl in jedem Wertepaar bezeichnet den SIGKILL-Schwellenwert: earlyoom sendet SIGTERM, sobald der erste Wert unterschritten wird, und SIGKILL beim Unterschreiten des zweiten Werts. Dieser steht standardmäßig auf der Hälfte des ersten Werts. Übernehmen Sie eine Änderung mit sudo systemctl restart earlyoom. Mit journalctl -u earlyoom sehen Sie, welchen Prozess der Dienst beendet hat und wie viel Speicher dieser Prozess belegt hatte.
systemd-oomd ist die andere Option. Die Manpage beschreibt sie als „einen Systemdienst, der cgroups-v2 und Pressure Stall Information (PSI) verwendet, um den Zustand zu überwachen und Korrekturmaßnahmen einzuleiten, bevor im Kernel-Space ein OOM auftritt“. Der Dienst arbeitet mit vollständigen cgroups statt mit einzelnen Prozessen. Er beendet daher eine Unit und nicht nur einen verwaisten Kindprozess. Units werden mit ManagedOOMMemoryPressure=kill oder ManagedOOMSwap=kill aktiviert. Die Schwellenwerte stehen in /etc/systemd/oomd.conf.
systemctl status systemd-oomd
oomctloomctl zeigt an, was der Dienst derzeit überwacht. Auf einem Server-Image ist das häufig nichts, weil die Einstellung pro Unit aktiviert werden muss. Wählen Sie einen der beiden Daemons und belassen Sie es dabei. Wenn beide laufen, konkurrieren zwei Dienste bei der Auswahl eines Prozesses. Dadurch lässt sich der Grund für eine Beendigung schwerer nachvollziehen.
Welche Unit war dafür verantwortlich?
Beginnen Sie mit dem Kernel, weil er jeden von ihm ausgelösten Kill protokolliert.
journalctl -k --grep "Killed process" --since "2 hours ago"Ein Kill durch den globalen OOM-Killer sieht folgendermaßen aus:
Out of memory: Killed process 4127 (node) total-vm:2731084kB, anon-rss:1874232kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:4212kB oom_score_adj:0anon-rss ist der Speicher, den der Prozess beim Beenden im RAM belegt hatte, hier etwa 1.8 GB. Lesen Sie den Namen in den Klammern mit Vorsicht. Das ist das Opfer, das der Kernel ausgewählt hat. Der Kernel wählt den größten Prozess. Das ist nicht immer der Prozess, der den Speichermangel verursacht hat.
Ein Kill aufgrund eines cgroup-Limits hat ein anderes Präfix. Der darüber ausgegebene Bericht nennt die cgroup, die ihr eigenes Limit erreicht hat:
Memory cgroup out of memory: Killed process 8811 (python3) total-vm:1044320kB, anon-rss:769112kB, file-rss:0kB, shmem-rss:0kB, UID:998 pgtables:1720kB oom_score_adj:0Dieses Präfix liefert bereits den größten Teil der Diagnose. Memory cgroup out of memory bedeutet, dass eine Unit das von Ihnen gesetzte MemoryMax= erreicht hat und der restliche Server ausreichend Speicher hatte. Ein einfaches Out of memory bedeutet, dass der gesamte Rechner keinen Speicher mehr hatte. Ihre Limits fehlten also entweder oder waren in Summe zu großzügig.
Fragen Sie anschließend systemd ab:
systemctl status myapp.service
journalctl -u myapp.service -n 50myapp.service: A process of this unit has been killed by the OOM killer.
myapp.service: Main process exited, code=killed, status=9/KILL
myapp.service: Failed with result 'oom-kill'.systemctl status sagt dasselbe in einer Zeile aus, und zwar als Active: failed (Result: oom-kill).
Die cgroup-Zähler sind die dritte Quelle. Nur sie protokollieren auch Drosselungen, die niemals eine Logzeile erzeugen:
cat /sys/fs/cgroup/system.slice/myapp.service/memory.events
cat /sys/fs/cgroup/system.slice/myapp.service/memory.peaklow 0
high 4213
max 118
oom 12
oom_kill 12high zählt, wie oft die Unit MemoryHigh= überschritten hat und gedrosselt wurde. max zählt, wie oft sie das harte Limit erreicht hat. oom_kill zählt die tatsächlich beendeten Prozesse. Ein hoher Wert von high zusammen mit oom_kill 0 kennzeichnet den bereits beschriebenen stillen Fall: Der Dienst läuft, wird aber extrem langsam und meldet keinen Fehler. memory.peak (Linux 5.19 und neuer) enthält den höchsten von der cgroup erreichten Speicherverbrauch. An diesem Wert sollten Sie MemoryMax= ausrichten. Beide Dateien werden beim Neustart der Unit zurückgesetzt, weil systemd die cgroup erneut erstellt.
Eine Voraussetzung liegt dem gesamten Ablauf zugrunde. Wenn /var/log/journal nicht existiert, liegt das Journal im RAM. Nach dem Reboot, den Sie zur Wiederherstellung des Servers benötigt haben, sind dann alle Zeilen verschwunden.
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
journalctl --list-bootsWenn journalctl --list-boots mehr als nur den aktuellen Boot anzeigt, bleibt die Historie erhalten. Dann kann journalctl -k -b -1 die Kernelmeldungen des fehlgeschlagenen Boots anzeigen.
Ein Ausgangspunkt für einen kleinen VPS
Bei einem Tarif mit 2 GB sollten Sie 300 bis 400 MB für Kernel und Page Cache reservieren. Die Limits dürfen zusammen nicht die gesamten 2 GB ergeben, weil jede Unit gleichzeitig ihren Spitzenverbrauch erreichen kann. Geben Sie dem wichtigsten Dienst den größten Anteil. Begrenzen Sie anschließend alle weniger wichtigen Dienste in seiner Umgebung.
[Service]
MemoryHigh=256M
MemoryMax=384M
MemorySwapMax=0
CPUWeight=20
TasksMax=64
Restart=on-failure
RestartSec=5sEine zusätzliche Einstellung kann den Zugang sichern. OOMScoreAdjust=-500 in einem Drop-in für ssh.service sorgt dafür, dass der globale OOM-Killer Ihren SSH-Daemon deutlich seltener als Opfer auswählt. Das kann den Unterschied zwischen einer Reparatur des Systems und einem Neustart über das Control Panel ausmachen. Die Einstellung ändert nur die Auswahl des Opfers durch den Kernel. Sie verkürzt den Stillstand nicht.
Container laufen in eigenen cgroups. Diese werden von der Container-Runtime und nicht von Ihren Unit-Dateien erstellt. Daher wird ein Limit für docker.service nicht automatisch zu einem Limit für einen einzelnen Container. Die Entsprechungen für einzelne Container von MemoryMax= und CPUQuota= werden unter Speicher- und CPU-Limits in Docker Compose festlegen beschrieben.
FAQ
Warum ist mein VPS eingefroren, statt den außer Kontrolle geratenen Prozess zu beenden?
Weil der Kernel den Fortschritt daran misst, ob durch Reclaim Seiten freigegeben werden, nicht daran, wie lange das dauert. Bei knappem Arbeitsspeicher verdrängt er den Page Cache, einschließlich der ausführbaren Seiten laufender Programme, und liest sie bei der nächsten Instruktion wieder ein. Dadurch wartet alles auf den Speicher, und keine Speicherzuweisung ist technisch fehlgeschlagen. Der OOM-Killer wird daher nie aufgerufen. Prüfen Sie währenddessen /proc/pressure/memory: Ein full avg10 über 40 bedeutet, dass in den letzten zehn Sekunden fast kein Task ausgeführt werden konnte. Ein Userspace-Daemon wie earlyoom beendet Prozesse, bevor der Server diesen Zustand erreicht.
Was ist der Unterschied zwischen MemoryHigh und MemoryMax?
MemoryHigh= ist eine weiche Obergrenze, die den Prozess drosselt. Der Kernel führt für die Unit aggressives Reclaim durch und verlangsamt ihre Speicherzuweisungen. Die Nutzung kann jedoch über diesen Wert steigen, und es wird nichts beendet. MemoryMax= ist eine harte Obergrenze: Eine Speicherzuweisung, die darunter nicht erfüllt werden kann, ruft den OOM-Killer innerhalb des eigenen cgroup der Unit auf. Dadurch wird der Prozess beendet, der das Problem verursacht hat, statt des größten Prozesses auf dem Server. Setzen Sie MemoryHigh= unter MemoryMax= und behandeln Sie den Abstand zwischen beiden Werten als Warnbereich.
Wie finde ich heraus, welchen Dienst der OOM-Killer getroffen hat?
Führen Sie journalctl -k --grep "Killed process" --since "2 hours ago" aus. Eine mit Memory cgroup out of memory beginnende Zeile bedeutet, dass eine Unit ihr eigenes MemoryMax= erreicht hat. Ein einfaches Out of memory bedeutet dagegen, dass der Arbeitsspeicher des gesamten Rechners erschöpft war. Führen Sie anschließend journalctl -u <unit> -n 50 aus und suchen Sie nach Failed with result 'oom-kill'. Falls /var/log/journal auf Ihrem Server nicht vorhanden ist, wurde das Journal im RAM gehalten. Die Belege gingen dann beim Reboot verloren. Erstellen Sie dieses Verzeichnis daher vor dem nächsten Vorfall.
Sollte ich einem kleinen VPS Swap hinzufügen?
Eine kleine Swap-Datei hilft bei selten verwendeten Seiten, die einmal zugewiesen und danach nicht mehr verwendet werden. Bei einem außer Kontrolle geratenen Prozess hilft sie nicht. Sie verzögert das Beenden und ersetzt einen kurzen Ausfall durch einen langen Stillstand, bei dem Sie sich nicht anmelden können, um das Problem zu beheben. Halten Sie den Swap-Bereich klein und setzen Sie MemorySwapMax=0 für die Units, auf deren Ausfall Sie verzichten können. Diese erreichen dann ihre Obergrenze und starten schnell neu, während die wichtigen Dienste ihren Swap behalten.
Kann ich einen Befehl begrenzen, ohne eine Unit-Datei zu schreiben?
Ja. sudo systemd-run --scope -p MemoryMax=1G -p CPUQuota=50% ./script.sh führt den Befehl in Ihrem Terminal innerhalb eines temporären Scopes mit diesen Limits aus. Die Limits verschwinden, sobald der Befehl beendet wird. Nach -p steht jede Eigenschaft aus systemd.resource-control zur Verfügung. Daher funktionieren dort auch MemorySwapMax=, TasksMax= und CPUWeight=. Lassen Sie --scope weg und fügen Sie --unit=name hinzu, um den Auftrag im Hintergrund auszuführen und seine Ausgabe im Journal zu protokollieren.