OOM-Ereignisse beheben


Auf dieser Seite erfahren Sie, wie Sie Probleme mit Ereignissen vom Typ „Out of Memory“ (OOM) in Google Kubernetes Engine (GKE) beheben. Hier erfahren Sie, wie Sie die häufigsten Ursachen für OOM-Ereignisse ermitteln, zwischen Ereignissen auf Container- und Knotenebene unterscheiden und Lösungen anwenden.

Diese Seite richtet sich an Anwendungsentwickler, die überprüfen möchten, ob ihre Apps erfolgreich bereitgestellt wurden, sowie an Plattformadministratoren und ‑betreiber, die die Ursache von OOM-Ereignissen ermitteln und die Plattformkonfiguration überprüfen möchten. Weitere Informationen zu gängigen Rollen und Beispielaufgaben, auf die wir in Google Cloud Inhalten verweisen, finden Sie unter Häufig verwendete GKE Enterprise-Nutzerrollen und -Aufgaben.

Häufige Ursachen für OOM-Ereignisse

OOM-Ereignisse treten in der Regel bei Last- oder Traffic-Spitzen auf, wenn die Arbeitsspeichernutzung der App sprunghaft ansteigt und das für den Container konfigurierte Arbeitsspeicherlimit erreicht.

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

  • Unzureichendes Arbeitsspeicherlimit: Die Einstellung resources.limits.memory im Manifest des Pods ist zu niedrig für den typischen oder maximalen Arbeitsspeicherbedarf der App.
  • Nicht definierte Arbeitsspeicheranfragen oder ‑limits: Wenn sowohl resources.limits.memory als auch resources.requests.memory nicht definiert sind, ist die Arbeitsspeichernutzung des Containers unbegrenzt.
  • Hohe oder sprunghafte Last: Plötzliche, extreme Lastspitzen können die Ressourcen eines Systems, einschließlich des Arbeitsspeichers, überfordern, auch wenn die Limits normalerweise ausreichend sind.
  • Speicherleck: Die App hat möglicherweise einen Codefehler, der dazu führt, dass der Speicher nicht richtig freigegeben wird.

OOM-Ereignisse können einen kaskadierenden Fehler auslösen, da weniger Container zur Verarbeitung des Traffics verbleiben, was die Last auf den verbleibenden Containern erhöht. Diese Container werden dann möglicherweise auch geschlossen.

Umgang von Kubernetes mit OOM-Ereignissen

Der Linux OOM Killer verarbeitet jedes OOM-Ereignis. Der OOM-Killer ist ein Kernelprozess, der aktiviert wird, wenn das System nur noch sehr wenig Arbeitsspeicher hat. Es soll einen totalen Systemabsturz 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 Arbeitsspeichermangels 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. Damit werden Prozesse innerhalb dieser bestimmten Cgroup beendet, um die Ressourcengrenze zu erzwingen.

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 beendeten Zustand.

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 des OOM-Killers

Das Verhalten bei OOM-Kill 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 in 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 Zustand des Hauptcontainerprozesses überwacht, werden diese „teilweisen“ OOM-Vorgänge nicht erkannt. Der Hauptcontainerprozess wird möglicherweise weiter ausgeführt, auch wenn wichtige untergeordnete Prozesse beendet wurden. Dieses Verhalten kann zu subtilen App-Fehlern führen, die für Kubernetes oder Operatoren nicht sofort sichtbar sind, aber dennoch im Systemprotokoll des Knotens (journalctl) angezeigt werden.

  • 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 hat, die zu viel Arbeitsspeicher verwenden, 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 Ihnen eine genauere Steuerung, da einzelne Prozesse bei einem OOM-Ereignis beendet werden können und nicht die gesamte Cgroup.

OOM-Beendigung auf Systemebene

Ein OOM-Kill auf Systemebene ist ein schwerwiegenderes Ereignis, das auftritt, wenn der gesamte Knoten und nicht nur ein einzelner Container nicht mehr genügend Arbeitsspeicher 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 auf diesem Knoten der Arbeitsspeicher aufgebraucht 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 nutzt viel Arbeitsspeicher.

Um schwerwiegende OOM-Situationen zu vermeiden, verwendet Kubernetes Node-Pressure-Eviction, um Knotenressourcen zu verwalten. Bei diesem Vorgang werden weniger wichtige Pods von einem Knoten entfernt, wenn Ressourcen wie Arbeitsspeicher oder Speicherplatz kritisch niedrig werden. Ein OOM-Kill auf Systemebene weist darauf hin, dass bei diesem Bereinigungsvorgang nicht schnell genug Arbeitsspeicher freigegeben werden konnte, um das Problem zu verhindern.

Wenn der OOM Killer den Prozess eines Containers beendet, ist die Wirkung in der Regel identisch mit einem durch eine Cgroup ausgelösten Beenden: Der Container wird als OOMKilled markiert und entsprechend 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 können. Wir beginnen mit den einfachsten Kubernetes-Tools und gehen dann zur detaillierteren Loganalyse ü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 Status des Pods:

    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 im Feld Reason der Wert OOMKilled angezeigt wird, haben Sie die Veranstaltung bestätigt. Ein Exit Code von 137 weist ebenfalls auf einen OOM-Kill hin. Wenn das Feld Reason einen anderen Wert hat oder der Pod trotz App-Fehlern noch ausgeführt wird, fahren Sie mit dem nächsten Abschnitt fort, um das Problem weiter zu untersuchen.

In Protokollen nach unsichtbaren OOM-Ereignissen suchen

Ein OOM-Kill ist für Kubernetes „unsichtbar“, wenn ein untergeordneter Prozess beendet wird, der Hauptcontainerprozess aber 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 Bereich „Abfrage“ eine der folgenden Abfragen ein:

    • Wenn Sie bereits einen Pod haben, bei dem Sie vermuten, dass ein OOM-Ereignis aufgetreten ist, 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. Suchen Sie in den Logs nach Einträgen, die den String ContainerDied enthalten und kurz nach den OOM-Ereignissen aufgetreten sind, um Containerstopps zu identifizieren. Gleichen Sie die Container-ID aus dem OOM-Ereignis mit der entsprechenden ContainerDied-Zeile ab.

    3. Nachdem Sie die container IDs gefunden haben, enthält die ContainerDied-Zeile in der Regel den Pod-Namen, der dem fehlgeschlagenen 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 untersuchen 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:

    • Beenden 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 Meldung wie Out of memory: Killed process..., in der keine Cgroup erwähnt wird.

OOM-Ereignisse beheben

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

  • Speicherlimits erhöhen: Dies ist die direkteste Lösung. Bearbeiten Sie das Pod-Manifest, um einen höheren resources.limits.memory-Wert anzugeben, der der Spitzenlast der App entspricht. 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 Trafficlast 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 sich ihren Limits nähern, 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 Trafficspitzen große Mengen an Arbeitsspeicher verbraucht.
  • Problematische Arbeitslasten löschen: Als letzte Möglichkeit für nicht kritische Arbeitslasten können Sie den Pod löschen, um den Cluster sofort zu entlasten.

Nächste Schritte