OOM-Ereignisse beheben

Wenn bei Ihren Google Kubernetes Engine-Apps (GKE-Apps) unerwartete Containerbeendigungen, Instabilität oder CrashLoopBackOff-Fehler auftreten, kann dies an OOM-Ereignissen (Out of Memory) liegen. OOM-Ereignisse treten auf, wenn ein Container oder Knoten nicht mehr genügend Arbeitsspeicher hat. In diesem Fall beendet der Linux-OOM-Killer Prozesse, um Ressourcen freizugeben.

Auf dieser Seite können Sie feststellen, ob ein OOM-Ereignis auf Container- oder Knotenebene aufgetreten ist, und wirksame Strategien zur Risikominderung anwenden, um ein erneutes Auftreten zu verhindern.

Diese Informationen sind wichtig für Anwendungsentwickler, die dafür sorgen müssen, dass ihre Anwendungen mit entsprechenden Speicheranforderungen und ‑limits konfiguriert sind und keine Speicherlecks aufweisen. Dies ist auch für Plattformadministratoren und ‑operatoren wichtig, die die Ressourcennutzung von Knoten überwachen und dafür sorgen, dass der Cluster über genügend Arbeitsspeicher verfügt, um die bereitgestellten Arbeitslasten zu unterstützen. Weitere Informationen zu gängigen Rollen und Beispielaufgaben, auf die wir in Google Cloud Inhalten verweisen, finden Sie unter Häufig verwendete GKE-Nutzerrollen und -Aufgaben.

Häufige Ursachen für OOM-Ereignisse

OOM-Ereignisse treten in der Regel bei Last- oder Trafficspitzen auf, wenn die Arbeitsspeichernutzung der Anwendung stark ansteigt und das für den Container konfigurierte Arbeitsspeicherlimit erreicht.

Die folgenden Szenarien können ein OOM-Ereignis verursachen:

  • Unzureichendes Speicherlimit: Die Einstellung resources.limits.memory im Manifest des Pods ist zu niedrig für den typischen oder maximalen Speicherbedarf der App.
  • Nicht definierte Arbeitsspeicheranfragen oder ‑limits: Wenn weder resources.limits.memory noch resources.requests.memory definiert sind, ist die Arbeitsspeichernutzung des Containers unbegrenzt.
  • Hohe oder unregelmäßige Last: Plötzliche, extreme Lastspitzen können die Ressourcen eines Systems, einschließlich des Arbeitsspeichers, überlasten, auch wenn die Limits normalerweise ausreichen.
  • Speicherleck: Die App hat möglicherweise einen Codefehler, der dazu führt, dass der Speicher nicht ordnungsgemäß freigegeben wird.

OOM-Ereignisse können einen kaskadierenden Fehler auslösen, da weniger Container für die Verarbeitung des Traffics übrig bleiben, was die Last auf den verbleibenden Containern erhöht. Diese Container werden dann möglicherweise ebenfalls beendet.

Umgang von Kubernetes mit OOM-Ereignissen

Der Linux-OOM-Killer verarbeitet jedes OOM-Ereignis. Der OOM-Killer ist ein Kernel-Prozess, der aktiviert wird, wenn der Arbeitsspeicher des Systems kritisch niedrig ist. Der Zweck besteht darin, einen vollständigen Systemabsturz zu verhindern, indem Prozesse strategisch beendet werden, um Ressourcen freizugeben. Der Kernel verwendet ein Punktesystem, um auszuwählen, welcher Prozess beendet werden soll. Ziel ist es, die Systemstabilität aufrechtzuerhalten und Datenverlust zu minimieren.

In einer Kubernetes-Umgebung ist der OOM Killer in zwei verschiedenen Bereichen aktiv: in der Kontrollgruppe (cgroup), die sich auf einen Container auswirkt, und im System, das sich auf den gesamten Knoten auswirkt.

Beendigung aufgrund von Arbeitsspeichermangel auf Containerebene

Ein OOM-Kill auf Containerebene tritt auf, wenn ein Container versucht, sein vordefiniertes Speicherlimit zu überschreiten. Kubernetes weist jeden Container einer bestimmten Cgroup mit einem festen Arbeitsspeicherlimit zu. Wenn die Arbeitsspeichernutzung eines Containers dieses Limit erreicht, versucht der Kernel zuerst, Arbeitsspeicher innerhalb dieser Cgroup freizugeben. Wenn der Kernel durch diesen Prozess nicht genügend Arbeitsspeicher freigeben kann, wird der OOM Killer der Cgroup aufgerufen. Es beendet Prozesse innerhalb dieser spezifischen cgroup, um die Ressourcengrenze durchzusetzen.

Wenn der Hauptprozess in einem Container auf diese Weise beendet wird, beobachtet Kubernetes das Ereignis und kennzeichnet den Status des Containers als OOMKilled. Die konfigurierte restartPolicy des Pods bestimmt dann das Ergebnis:

  • Always oder OnFailure: Der Container wird neu gestartet.
  • Never: Der Container wird nicht neu gestartet und bleibt im Status „Beendet“.

Durch die Isolierung des Fehlers auf den betreffenden Container verhindert der OOM Killer, dass ein einzelner fehlerhafter Pod den gesamten Knoten zum Absturz bringt.

Auswirkungen der cgroup-Version auf das Verhalten von OOM Killer

Das OOM-Kill-Verhalten kann sich zwischen den cgroup-Versionen erheblich unterscheiden. Wenn Sie nicht sicher sind, welche cgroup-Version Sie verwenden, prüfen Sie den cgroup-Modus der Clusterknoten.

  • In cgroup v1 kann ein OOM-Ereignis in der Memory-Cgroup eines Containers zu unvorhersehbarem Verhalten führen. Der OOM-Killer kann jeden Prozess innerhalb dieser cgroup beenden, einschließlich untergeordneter Prozesse, die nicht der Hauptprozess des Containers (PID 1) sind.

    Dieses Verhalten stellt eine erhebliche Herausforderung für Kubernetes dar. Da Kubernetes in erster Linie den Status des Hauptcontainerprozesses überwacht, werden diese „teilweisen“ OOM-Vorgänge nicht erkannt. Der Hauptcontainerprozess wird möglicherweise weiter ausgeführt, auch wenn kritische untergeordnete Prozesse beendet wurden. Dieses Verhalten kann zu subtilen App-Fehlern führen, die für Kubernetes oder Operatoren nicht sofort sichtbar sind, aber im Systemjournal des Knotens (journalctl) sichtbar sind.

  • cgroup v2 bietet ein besser vorhersehbares Verhalten des OOM-Killers.

    Um die Integrität von Arbeitslasten in einer cgroup v2-Umgebung zu gewährleisten, verhindert der OOM-Killer das teilweise Beenden von Prozessen und sorgt für eines von zwei Ergebnissen: Entweder werden alle Aufgaben, die zu dieser cgroup und ihren untergeordneten Elementen gehören, beendet (wodurch der Fehler für Kubernetes sichtbar wird), oder wenn die Arbeitslast keine Aufgaben mit zu hohem Speicherverbrauch hat, bleibt die Arbeitslast unverändert und wird ohne unerwartete interne Prozessbeendigungen weiter ausgeführt.

    Für Szenarien, in denen Sie das cgroup v1-Verhalten zum Beenden eines einzelnen Prozesses wünschen, bietet das Kubelet das Flag singleProcessOOMKill für cgroup v2. Dieses Flag bietet eine detailliertere Steuerung, da einzelne Prozesse bei einem OOM-Ereignis beendet werden können und nicht die gesamte Cgroup.

OOM-Kill auf Systemebene

Ein OOM-Kill auf Systemebene ist ein schwerwiegenderes Ereignis, das auftritt, wenn der gesamte Knoten und nicht nur ein einzelner Container keinen verfügbaren Arbeitsspeicher mehr hat. Dieses Ereignis kann auftreten, wenn die kombinierte Arbeitsspeichernutzung aller Prozesse (einschließlich aller Pods und System-Daemons) die Kapazität des Knotens überschreitet.

Wenn dieser Knoten Out of Memory ist, bewertet der globale OOM Killer alle Prozesse auf dem Knoten und beendet einen Prozess, um Arbeitsspeicher für das gesamte System freizugeben. Der ausgewählte Prozess ist in der Regel kurzlebig und beansprucht viel Arbeitsspeicher.

Um schwerwiegende OOM-Situationen zu vermeiden, verwendet Kubernetes die Entfernung bei Knotendruck, um Knotenressourcen zu verwalten. Bei diesem Vorgang werden weniger kritische Pods von einem Knoten entfernt, wenn Ressourcen wie Arbeitsspeicher oder Speicherplatz kritisch niedrig werden. Ein OOM-Kill auf Systemebene zeigt an, dass dieser Bereinigungsvorgang nicht schnell genug Arbeitsspeicher freigeben konnte, um das Problem zu verhindern.

Wenn der OOM-Killer den Prozess eines Containers beendet, ist der Effekt in der Regel identisch mit einem von einer cgroup ausgelösten Beenden: Der Container wird als OOMKilled markiert und gemäß seiner Richtlinie neu gestartet. Wenn jedoch ein kritischer Systemprozess beendet wird (was selten vorkommt), kann der Knoten selbst instabil werden.

OOM-Ereignisse untersuchen

In den folgenden Abschnitten erfahren Sie, wie Sie ein OOM-Ereignis erkennen und bestätigen. Dabei beginnen wir mit den einfachsten Kubernetes-Tools und gehen dann zu einer detaillierteren Protokollanalyse über.

Pod-Status auf sichtbare OOM-Ereignisse prüfen

Der erste Schritt zur Bestätigung eines OOM-Ereignisses besteht darin, zu prüfen, ob Kubernetes das OOM-Ereignis erkannt hat. Kubernetes beobachtet das Ereignis, wenn der Hauptprozess des Containers beendet wird. Das ist das Standardverhalten in cgroup v2-Umgebungen.

  • Prüfen Sie den Pod-Status:

    kubectl describe pod POD_NAME
    

    Ersetzen Sie POD_NAME durch den Namen des Pods, den Sie sich prüfen möchten.

    Wenn ein sichtbares OOM-Ereignis aufgetreten ist, sieht die Ausgabe in etwa so aus:

    ...
      Last State:     Terminated
        Reason:       OOMKilled
        Exit Code:    137
        Started:      Tue, 13 May 2025 19:05:28 +0000
        Finished:     Tue, 13 May 2025 19:05:30 +0000
    ...
    

Wenn Sie im Feld Reason den Wert OOMKilled sehen, haben Sie das Ereignis bestätigt. Ein Exit Code-Wert von 137 weist ebenfalls auf einen OOM-Kill hin. Wenn das Feld Reason einen anderen Wert hat oder der Pod trotz Anwendungsfehlern weiterhin ausgeführt wird, fahren Sie mit dem nächsten Abschnitt fort, um weitere Informationen zu erhalten.

Protokolle nach unsichtbaren OOM-Ereignissen durchsuchen

Ein OOM-Kill ist für Kubernetes „unsichtbar“, wenn ein untergeordneter Prozess beendet wird, der Hauptcontainerprozess jedoch weiterhin ausgeführt wird. Das ist ein häufiges Szenario in cgroup v1-Umgebungen. Sie müssen in den Protokollen des Knotens nach Beweisen für diese Ereignisse suchen.

So finden Sie unsichtbare OOM-Kills mit dem Log-Explorer:

  1. Rufen Sie in der Google Cloud Console den Log-Explorer auf.

    Zum Log-Explorer

  2. Geben Sie im Abfragebereich eine der folgenden Abfragen ein:

    • Wenn Sie bereits einen Pod haben, bei dem Sie ein OOM-Ereignis vermuten, fragen Sie diesen bestimmten Pod ab:

      resource.type="k8s_node"
      jsonPayload.MESSAGE:(("POD_NAME" AND "ContainerDied") OR "TaskOOM event")
      resource.labels.cluster_name="CLUSTER_NAME"
      

      Ersetzen Sie Folgendes:

      • POD_NAME: Der Name des Pods, den Sie abfragen möchten.
      • CLUSTER_NAME: Der Name des Clusters, zu dem der Pod gehört.
    • Wenn Sie herausfinden möchten, bei welchen Pods oder Knoten ein OOM-Ereignis aufgetreten ist, fragen Sie alle GKE-Arbeitslasten ab:

      resource.type="k8s_node"
      jsonPayload.MESSAGE:("ContainerDied" OR "TaskOOM event")
      resource.labels.cluster_name="CLUSTER_NAME"
      
  3. Klicken Sie auf Abfrage ausführen.

  4. Suchen Sie in der Ausgabe nach OOM-Ereignissen, indem Sie nach Logeinträgen mit dem String TaskOOM suchen.

  5. Optional: Wenn Sie nach OOM-Ereignissen für alle GKE-Arbeitslasten gesucht haben und den spezifischen Pod ermitteln möchten, bei dem die OOM-Ereignisse aufgetreten sind, führen Sie die folgenden Schritte aus:

    1. Notieren Sie sich für jedes Ereignis die zugehörige Container-ID.
    2. Identifizieren Sie Containerstopps, indem Sie in den Logs nach Logeinträgen suchen, die den String ContainerDied enthalten und kurz nach den OOM-Ereignissen aufgetreten sind. Suchen Sie in der ContainerDied-Zeile nach der Container-ID aus dem OOM-Ereignis.

    3. Nachdem Sie die container IDs abgeglichen haben, enthält die Zeile ContainerDied normalerweise den Pod-Namen, der dem fehlerhaften Container zugeordnet ist. Dieser Pod war vom OOM-Ereignis betroffen.

journalctl für Echtzeitinformationen verwenden

Wenn Sie eine Echtzeitanalyse Ihres Systems durchführen müssen, verwenden Sie journalctl-Befehle.

  1. Stellen Sie über SSH eine Verbindung zum Knoten her:

    gcloud compute ssh NODE_NAME --location ZONE
    

    Ersetzen Sie Folgendes:

    • NODE_NAME: der Name des Knotens, den Sie prüfen möchten.
    • ZONE: die Compute Engine-Zone, zu der Ihr Knoten gehört.
  2. Sehen Sie sich in der Shell die Kernel-Meldungen aus dem Systemjournal des Knotens an:

    journalctl -k
    
  3. Analysieren Sie die Ausgabe, um den Ereignistyp zu unterscheiden:

    • Beendigung auf Containerebene: Der Logeintrag enthält Begriffe wie memory cgroup, mem_cgroup oder memcg, die darauf hinweisen, dass ein cgroup-Limit erzwungen wurde.
    • Beenden auf Systemebene: Der Logeintrag ist eine allgemeine Nachricht wie Out of memory: Killed process..., ohne dass eine cgroup erwähnt wird.

OOM-Ereignisse beheben

Versuchen Sie Folgendes, um ein OOM-Ereignis zu beheben:

  • Arbeitsspeicherlimits erhöhen: Das ist die direkteste Lösung. Bearbeiten Sie das Pod-Manifest, um einen höheren resources.limits.memory-Wert anzugeben, der die Spitzenauslastung der Anwendung berücksichtigt. Weitere Informationen zum Festlegen von Limits finden Sie in der Kubernetes-Dokumentation unter Ressourcenverwaltung für Pods und Container.
  • Speicheranforderungen hinzufügen oder anpassen: Prüfen Sie im Manifest des Pods, ob das Feld resources.requests.memory auf einen realistischen Wert für die typische Nutzung festgelegt ist. Mit dieser Einstellung kann Kubernetes den Pod auf einem Knoten mit ausreichend Arbeitsspeicher planen.
  • Arbeitslast horizontal skalieren: Um die Traffic-Last zu verteilen und den Arbeitsspeicherbedarf für einzelne Pods zu reduzieren, erhöhen Sie die Anzahl der Replikate. Wenn Kubernetes die Arbeitslast proaktiv skalieren soll, sollten Sie horizontales Pod-Autoscaling aktivieren.
  • Knoten vertikal skalieren: Wenn viele Pods auf einem Knoten nahe an ihren Limits sind, ist der Knoten möglicherweise zu klein. Wenn Sie die Größe der Knoten erhöhen möchten, migrieren Sie Ihre Arbeitslasten zu einem Knotenpool mit mehr Arbeitsspeicher. Wenn Kubernetes die Knoten proaktiv skalieren soll, sollten Sie vertikales Pod-Autoscaling aktivieren.
  • App optimieren: Überprüfen Sie Ihre App, um Speicherlecks zu erkennen und zu beheben, und optimieren Sie Code, der bei Traffic-Spitzen große Mengen an Arbeitsspeicher verbraucht.
  • Problematische Arbeitslasten löschen: Als letzten Ausweg für nicht kritische Arbeitslasten können Sie den Pod löschen, um den Cluster sofort zu entlasten.

Nächste Schritte