OOM-Richtlinien beherrschen: Systemds Reaktion auf Out-of-Memory-Ereignisse optimieren
Erfahren Sie, wie Sie das Out-of-Memory (OOM)-Killer-Verhalten von Linux mit systemd steuern können. Dieser Leitfaden untersucht die Direktiven `OOMScoreAdjust` und `OOMPolicy`, um kritische Dienste zu schützen, indem beeinflusst wird, welche Prozesse bei niedrigem Arbeitsspeicher beendet werden. Meistern Sie die OOM-Optimierung von systemd für verbesserte Systemstabilität und -resilienz.
OOM-Richtlinien beherrschen: Systemds Reaktion auf Out-of-Memory-Ereignisse optimieren
Out-of-Memory-Fehler treten selten zu einem günstigen Zeitpunkt auf. Ein Batch-Import erhält eine größere Datei als üblich, ein Dienst verliert über Nacht Speicher, ein Backup überschneidet sich mit einem Verkehrsspitze, oder ein Deployment verdoppelt die Anzahl der Worker-Prozesse. Wenn Linux nicht genügend Speicher für eine Allokation freigeben kann, ruft der Kernel möglicherweise den OOM-Killer auf und beendet einen Prozess, damit die Maschine weiterlaufen kann.
Das Unangenehme daran ist, dass das Standard-Opfer möglicherweise nicht der Dienst ist, den Sie gewählt hätten. Auf einem gemeinsam genutzten Host bevorzugen Sie vielleicht einen wiederholbaren Queue-Worker, der vor der Haupt-API stirbt. Auf einem Datenbankserver möchten Sie vielleicht, dass SSH und Überwachung am Leben bleiben, damit Sie die Maschine wiederherstellen können. Systemd gibt Ihnen zwei Stellschrauben für diese Art von Entscheidung: OOMScoreAdjust= und OOMPolicy=.
OOMScoreAdjust= beeinflusst, welcher Prozess ausgewählt wird. OOMPolicy= steuert, was systemd tut, nachdem ein Prozess im Dienst getötet wurde. Sie lösen unterschiedliche Probleme, und ihre Verwechslung führt zu schlechten Runbooks.
Was der Kernel bewertet
Jeder Linux-Prozess hat einen OOM-Score, der unter /proc/<pid>/oom_score sichtbar ist. Ein höherer Score bedeutet, dass der Prozess ein wahrscheinlicheres OOM-Opfer ist. Der Kernel leitet diesen Score aus der Speichernutzung und anderen Kontexten ab und wendet dann den Anpassungswert aus /proc/<pid>/oom_score_adj an.
Systemds OOMScoreAdjust= schreibt diese Anpassung für die von ihm gestarteten Prozesse. Der Bereich liegt zwischen -1000 und 1000.
-1000bietet den stärksten Schutz und deaktiviert effektiv das OOM-Killing für diesen Prozess.- Negative Werte machen den Prozess weniger wahrscheinlich, getötet zu werden.
- Positive Werte machen den Prozess wahrscheinlicher, getötet zu werden.
0belässt die Anpassung neutral.
Der sicherste Ansatz ist normalerweise nicht, "alles Wichtige zu schützen". Wenn jeder Dienst geschützt ist, hat der Kernel weniger nützliche Optionen, wenn der Host bereits wenig Speicher hat. Schützen Sie eine kleine Anzahl von Diensten und machen Sie entbehrliche Arbeit leichter zu töten.
Für einen primären API-Dienst ist eine moderate Anpassung oft ausreichend:
[Service]
OOMScoreAdjust=-300
Für einen Queue-Worker, der Jobs wiederholen kann:
[Service]
OOMScoreAdjust=500
Dieser Worker könnte bei Speicherdruck zuerst sterben, aber das ist der Punkt. Ein fehlgeschlagener Job kann zurück in die Warteschlange. Eine tote Datenbank oder ein nicht erreichbarer Host ist ein größerer Vorfall.
Was OOMPolicy tatsächlich tut
OOMPolicy= markiert eine Einheit nicht als "kritisch" und wählt nicht den ersten zu tötenden Prozess aus. Die unterstützten Werte sind continue, stop und kill.
continue: systemd protokolliert das OOM-Ereignis und lässt die Einheit laufen, wenn noch Prozesse vorhanden sind.stop: systemd protokolliert das Ereignis und stoppt die Einheit sauber.kill: Wenn ein Prozess in der Einheit durch OOM getötet wird, werden die verbleibenden Prozesse in dieser Einheit als Gruppe getötet.
Verwenden Sie diese Einstellung, um halb lebendige Dienste zu vermeiden. Wenn ein Multi-Prozess-Webdienst einen Worker verliert und weiterhin Verkehr in einem defekten Zustand akzeptiert, kann continue den Fehler verbergen. OOMPolicy=kill macht den Fehler offensichtlich und lässt Restart=on-failure den Dienst in einem sauberen Zustand zurückbringen.
[Service]
OOMPolicy=kill
Restart=on-failure
RestartSec=5s
Für einen Batch-Job mit Hilfsprozessen kann stop für die verbleibenden Prozesse weniger abrupt sein:
[Service]
OOMPolicy=stop
Der vom Kernel ausgewählte Prozess ist bereits weg. stop beeinflusst nur, was systemd mit dem Rest des Dienstes tut, also verlassen Sie sich nicht darauf als einen sanften Sicherungspunkt. Lang laufende Jobs sollten ihre eigene Arbeit checkpointen.
Ein praktisches Optimierungsmuster
Beginnen Sie damit, Dienste in drei Gruppen zu sortieren.
Identifizieren Sie zunächst Dienste, die den Host wiederherstellbar halten: SSH, Netzwerk, Überwachung und die primäre Arbeitslast. Geben Sie nur den wichtigsten bescheidene negative Anpassungen.
Zweitens identifizieren Sie Dienste, die wiederholt werden können: Worker, Importeure, Berichtsgeneratoren, Bildprozessoren, Cache-Wärmer und Entwicklungshilfen. Geben Sie diesen positive Anpassungen.
Drittens entscheiden Sie, ob jeder Dienst sicher weiterlaufen kann, nachdem ein Prozess getötet wurde. Wenn nicht, verwenden Sie OOMPolicy=kill und eine Neustartrichtlinie.
Ein realistischer Worker-Override könnte so aussehen:
# /etc/systemd/system/image-worker.service.d/oom.conf
[Service]
OOMScoreAdjust=500
OOMPolicy=kill
Restart=on-failure
RestartSec=10s
Ein primärer Anwendungsdienst könnte so aussehen:
# /etc/systemd/system/api.service.d/oom.conf
[Service]
OOMScoreAdjust=-300
OOMPolicy=kill
Restart=on-failure
RestartSec=5s
Ich würde OOMScoreAdjust=-1000 vermeiden, es sei denn, Sie haben den Fehlermodus getestet. Wenn dieser geschützte Dienst derjenige ist, der Speicher verliert, benötigt die Maschine immer noch eine Möglichkeit zur Wiederherstellung.
Anwenden und Überprüfen der Änderung
Verwenden Sie Drop-Ins anstelle der Bearbeitung von gepackten Unit-Dateien:
sudo systemctl edit api.service
Nach dem Speichern des Overrides laden Sie systemd neu und starten den Dienst neu:
sudo systemctl daemon-reload
sudo systemctl restart api.service
Überprüfen Sie die zusammengeführte Unit und die Werte, die systemd sieht:
systemctl cat api.service
systemctl show api.service -p OOMPolicy -p OOMScoreAdjust
Überprüfen Sie dann den laufenden Prozess:
PID=$(systemctl show api.service -p MainPID --value)
cat /proc/$PID/oom_score_adj
cat /proc/$PID/oom_score
oom_score_adj sollte Ihrer konfigurierten Anpassung entsprechen. oom_score kann sich ändern, wenn der Prozess mehr oder weniger Speicher verwendet.
Überprüfen Sie nach einem Vorfall sowohl die Unit-Protokolle als auch das Kernel-Protokoll:
journalctl -u api.service --since "1 hour ago"
journalctl -k --since "1 hour ago" | grep -i oom
Auf Systemen, die systemd-oomd verwenden, überprüfen Sie auch:
systemctl status systemd-oomd
oomctl
OOM-Richtlinie ist keine Kapazitätsplanung
OOM-Optimierung ist eine letzte Verteidigungslinie. Sie benötigen immer noch Speichergrenzen, Warnungen und genügend Spielraum für normale Spitzen. Für Dienste mit vorhersehbaren Grenzen sollten Sie cgroup-Speicherkontrollen in Betracht ziehen:
[Service]
MemoryHigh=1500M
MemoryMax=2G
MemoryHigh= übt Druck vor der harten Grenze aus. MemoryMax= ist eine Obergrenze. Das genaue Verhalten hängt von der systemd-Version und dem cgroup-Setup ab, aber die operative Idee ist einfach: einen Dienst begrenzen, bevor er den Host verbraucht.
Swap verdient die gleiche Art von Überlegung. Kein Swap kann kurze Spitzen in abrupte OOM-Kills verwandeln. Zu viel langsamer Swap kann den Host am Leben erhalten, während die Latenz nutzlos wird. Überprüfen Sie die OOM-Richtlinie zusammen mit Swap, Speichergrenzen, Neustartverhalten und Warnungen.
Beispiel: Ein Host, drei Dienste
Angenommen, ein kleiner Produktionshost betreibt eine API, einen Redis-Cache und einen Hintergrundberichts-Worker. Der Berichts-Worker ist nützlich, aber er kann Arbeit wiederholen. Redis verbessert die Latenz, aber die Anwendung kann immer noch einige Anfragen bedienen, indem sie zur Datenbank geht. Die API ist der kundenorientierte Dienst.
Ein vernünftiger erster Durchgang könnte sein:
# api.service
[Service]
OOMScoreAdjust=-300
OOMPolicy=kill
Restart=on-failure
# redis.service drop-in, falls diese Redis-Instanz nur Cache ist
[Service]
OOMScoreAdjust=0
OOMPolicy=kill
# report-worker.service
[Service]
OOMScoreAdjust=600
OOMPolicy=kill
Restart=on-failure
Das garantiert nicht, dass der Worker in jedem möglichen Fall zuerst stirbt, aber es macht Ihre Absicht klar. Wenn der Berichts-Worker zu groß wird, ist er ein leichteres Ziel. Wenn die API einen ihrer Prozesse verliert, tötet systemd den Rest und startet ihn sauber neu. Wenn Redis nur ein Cache ist, können Sie sich entscheiden, ihn nicht stark zu schützen; wenn Redis Ihr primärer Datenspeicher ist, würden Sie eine andere Entscheidung treffen.
Deshalb sollte die OOM-Richtlinie an die Dienstrolle gebunden sein, nicht an den Produktnamen. "Redis" ist nicht automatisch kritisch oder entbehrlich. "Der Cache, den wir neu aufbauen können" und "die einzige Kopie des Sitzungszustands" sind unterschiedliche operative Objekte.
Testen ohne eine Katastrophe zu verursachen
Sie müssen keinen Produktionsserver zum Absturz bringen, um zu erfahren, ob die Einstellungen angewendet werden. Beginnen Sie mit der Inspektion:
systemctl show report-worker.service -p OOMScoreAdjust -p OOMPolicy
systemctl status report-worker.service
Überprüfen Sie dann den laufenden Prozess:
PID=$(systemctl show report-worker.service -p MainPID --value)
cat /proc/$PID/oom_score_adj
Für tiefere Tests verwenden Sie einen Staging-Host oder eine entbehrliche virtuelle Maschine mit derselben systemd-Version und demselben cgroup-Modus. Führen Sie dort ein kontrolliertes Speicherdruck-Tool aus, nicht auf einer gemeinsam genutzten Produktionsbox. Das Ziel ist es, das allgemeine Verhalten zu bestätigen: der Worker ist leichter zu töten, der Hauptdienst bleibt nicht halb am Leben, und das Neustartverhalten ist im Journal sichtbar.
Wenn Sie Container verwenden, testen Sie in derselben Form, in der Sie bereitstellen. Ein Dienst, der direkt unter systemd läuft, verhält sich nicht genau wie ein Prozess innerhalb eines Containers mit eigenem Speicherlimit. Der Kernel kann das Container-Limit durchsetzen, bevor der Host global keinen Speicher mehr hat. In diesem Fall können Ihre Container-Laufzeit, Kubernetes oder cgroup-Einstellungen die erste Schicht sein, die entscheidet, was stirbt.
Den Vorfall danach lesen
Nach einem OOM-Ereignis vermeiden Sie es, direkt zu "wir brauchen mehr RAM" zu springen. Manchmal tun Sie das. Manchmal hat ein Cache TTLs vergessen. Manchmal hat ein Deployment die Worker-Konkurrenz geändert. Manchmal haben Persistenz- oder Backup-Aktivitäten Copy-on-Write-Speicher ansteigen lassen.
Suchen Sie nach drei Dingen:
journalctl -k --since "2026-05-24 01:00" | grep -i oom
journalctl -u api.service --since "2026-05-24 01:00"
systemctl show api.service -p Result -p NRestarts
Das Kernel-Protokoll sagt Ihnen normalerweise, welcher Prozess getötet wurde. Das Unit-Protokoll sagt Ihnen, wie systemd reagiert hat. Neustartzähler sagen Ihnen, ob sich der Dienst sauber erholt hat oder geflattert ist.
Vergleichen Sie dann den getöteten Prozess mit Ihrer beabsichtigten Priorität. Wenn ein geschützter Dienst vor einem entbehrlichen Worker gestorben ist, überprüfen Sie, ob der Worker tatsächlich unter der von Ihnen optimierten Unit lief, ob der Override geladen war und ob eine andere Speichergrenze zuerst ausgelöst hat. Wenn das ausgewählte Opfer der Richtlinie entspricht, der Vorfall aber dennoch Benutzer beeinträchtigt hat, muss Ihre Dienstklassifizierung möglicherweise geändert werden.
Dokumentieren Sie den Grund, nicht nur den Wert
OOM-Einstellungen sind leicht zu vergessen, da sie ruhig in Unit-Drop-Ins sitzen, bis ein schlechter Tag kommt. Hinterlassen Sie einen kurzen Kommentar im Override oder in Ihrem Infrastruktur-Repository, der den Grund für die Anpassung erklärt.
[Service]
# Wiederholbarer Queue-Worker. Bevorzugen Sie das Töten dieses vor api.service bei Host-Druck.
OOMScoreAdjust=600
OOMPolicy=kill
Dieser Kommentar spart Zeit während einer Vorfallüberprüfung. Ohne ihn könnte jemand einen positiven OOM-Score sehen und ihn auf Null "korrigieren", ohne zu erkennen, dass es eine absichtliche Prioritätsentscheidung war.
Notieren Sie auch, wann Sie die Einstellung zuletzt überprüft haben. Ein Dienst kann im Laufe der Zeit seine Rolle ändern. Ein Worker, der einmal entbehrliche Thumbnails verarbeitet hat, könnte später Zahlungen, Exporte oder kunden sichtbare Jobs verarbeiten. Die OOM-Richtlinie sollte dem aktuellen Risiko folgen, nicht dem ursprünglichen Zweck des Dienstes.
Häufige schlechte Konfigurationen
Eine schlechte Konfiguration ist es, die Datenbank, die API, den Worker, den Cache, den Log-Shipper und den Überwachungsagenten auf einmal zu schützen. Das fühlt sich vorsichtig an, gibt dem Kernel aber weniger Optionen. Wählen Sie Prioritäten.
Eine weitere schlechte Konfiguration ist das Setzen von OOMPolicy=continue auf einen Dienst, der keine fehlenden Kindprozesse tolerieren kann. Ein Prozessmanager, Webserver oder benutzerdefinierter Daemon kann die Unit aktiv halten, selbst nachdem ein Teil der Arbeitslast verschwunden ist. Wenn Ihr Load-Balancer nur prüft, ob der Port offen ist, kann der Verkehr weiterhin zu einem degradierten Dienst fließen.
Eine dritte schlechte Konfiguration ist eine positive Anpassung ohne Wiederholungsverhalten. Wenn Sie einen Dienst leicht zu töten machen, stellen Sie sicher, dass das Töten akzeptabel ist. Für einen Queue-Worker bedeutet das, dass Jobs nur nach erfolgreicher Verarbeitung bestätigt werden. Für einen Batch-Job bedeutet das Checkpoints. Für einen Cache-Wärmer bedeutet das, dass der Cache später neu aufgebaut werden kann.
Vermeiden Sie schließlich, OOM-Ereignisse mit automatischen Neustarts allein zu verbergen. Das Neustarten eines leckenden Dienstes kann Zeit kaufen, aber es kann auch eine Schleife erzeugen, in der der Speicher ansteigt, der Dienst stirbt und Benutzer periodische Ausfälle sehen. Fügen Sie Warnungen zur Neustartanzahl und zum Speicherwachstum hinzu, nicht nur zum Prozessstatus.
Ein kurzes Runbook
Wenn Sie einen echten Server optimieren, verwenden Sie eine wiederholbare Checkliste:
- Listen Sie Dienste auf, die für die Wiederherstellung und den Benutzerverkehr erforderlich sind.
- Listen Sie wiederholbare Dienste auf, die zuerst getötet werden können.
- Fügen Sie positive
OOMScoreAdjust-Werte zu entbehrlicher Arbeit hinzu. - Fügen Sie moderate negative Werte nur zu den wenigen Diensten hinzu, die Schutz verdienen.
- Verwenden Sie
OOMPolicy=killfür Dienste, die nicht teilweise laufen sollten. - Überprüfen Sie die angewendeten Werte durch
systemctl showund/proc. - Warnen Sie vor Speicherdruck, bevor OOM-Ereignisse auftreten.
Das Ziel ist nicht, OOM-Ereignisse harmlos zu machen. Das Ziel ist, sie verständlich zu machen. OOMScoreAdjust= hilft, das Opfer auszuwählen. OOMPolicy= hilft zu definieren, was mit dem Rest der Einheit passiert. Zusammen geben sie Ihnen eine vorhersehbarere Fehlerreihenfolge, wenn der Speicher bereits erschöpft ist.