排解 OOM 事件


本頁面說明如何排解及解決 Google Kubernetes Engine (GKE) 中的記憶體不足 (OOM) 事件。瞭解如何找出 OOM 事件的常見原因、區分容器層級和節點層級的發生情形,以及套用解決方案。

本頁適用於想確認應用程式是否已順利部署的應用程式開發人員,以及想瞭解 OOM 事件根本原因並驗證平台設定的平台管理員和營運人員。如要進一步瞭解 Google Cloud 內容中提及的常見角色和範例工作,請參閱「常見的 GKE Enterprise 使用者角色和工作」。

OOM 事件的常見原因

OOM 事件通常發生在負載或流量尖峰期間,此時應用程式的記憶體用量會暴增,達到為容器設定的記憶體上限。

下列情況可能會導致 OOM 事件:

  • 記憶體限制不足:Pod 資訊清單中的 resources.limits.memory 設定過低,無法滿足應用程式的正常或尖峰記憶體需求。
  • 未定義記憶體要求或限制:如果未定義 resources.limits.memoryresources.requests.memory,容器的記憶體用量就不會受到限制。
  • 負載過高或負載尖峰:負載突然大幅增加可能會導致系統資源 (包括記憶體) 不堪負荷,即使限制通常足夠也是如此。
  • 記憶體洩漏:應用程式可能存在程式碼缺陷,導致無法正確釋放記憶體。

OOM 事件可能會引發連鎖故障,因為可處理流量的容器減少,導致其餘容器的負載增加。這些容器也可能因此終止。

Kubernetes 如何處理 OOM 事件

Linux OOM Killer 會處理每個 OOM 事件。OOM Killer 是核心程序,會在系統記憶體嚴重不足時啟動。這項機制會策略性地終止程序,釋放資源,避免系統全面崩潰。核心會使用評分系統選取要停止的程序,以維持系統穩定性並盡量減少資料遺失。

在 Kubernetes 環境中,OOM Killer 會在兩個不同範圍運作:控制群組 (cgroup),影響一個容器;以及系統,影響整個節點。

容器層級的 OOM 終止

如果容器嘗試超出預先定義的記憶體限制,就會發生容器層級的 OOM 終止。Kubernetes 會將每個容器指派給具有硬性記憶體限制的特定 cgroup。當容器的記憶體用量達到這個限制時,核心會先嘗試回收該 cgroup 內的記憶體。如果核心無法透過這個程序回收足夠的記憶體,系統就會叫用 cgroup OOM Killer。系統會終止該特定 cgroup 中的程序,以強制執行資源界線。

當容器中的主要程序以這種方式終止時,Kubernetes 會觀察該事件,並將容器的狀態標示為 OOMKilled。Pod 的設定 restartPolicy 會決定結果:

  • AlwaysOnFailure:容器會重新啟動。
  • Never:容器不會重新啟動,且會維持終止狀態。

OOM Killer 會將故障隔離到違規容器,避免單一有問題的 Pod 導致整個節點停止運作。

控制群組版本對 OOM Killer 行為的影響

不同 cgroup 版本之間的 OOM 終止行為可能差異很大。如果不確定使用的 cgroup 版本,請檢查叢集節點的 cgroup 模式

  • cgroup v1 中,容器記憶體 Cgroup 內的 OOM 事件可能會導致無法預測的行為。OOM Killer 可能會終止該 cgroup 內的任何程序,包括不是容器主要程序 (PID 1) 的子程序。

    這對 Kubernetes 來說是一大挑戰。由於 Kubernetes 主要監控主要容器程序的健康狀態,因此不會察覺這些「部分」OOM 終止。即使重要子程序已終止,主要容器程序可能仍會繼續執行。這種行為可能會導致應用程式發生細微的故障,Kubernetes 或運算子不會立即發現,但節點的系統日誌 (journalctl) 仍會顯示。

  • cgroup v2 可更準確預測 OOM Killer 行為。

    為確保 cgroup v2 環境中的工作負載完整性,OOM killer 會防止部分終止,並確保出現下列兩種結果之一:屬於該 cgroup 及其子代的所有工作都會終止 (讓 Kubernetes 看到失敗),或者當工作負載沒有使用過多記憶體的工作時,工作負載會保持不變,並繼續執行,不會發生非預期的內部程序終止。

    如要在單一程序終止時使用 cgroup v1 行為,kubelet 會提供 cgroup v2singleProcessOOMKill 旗標。這個標記可讓您更精細地控管,在 OOM 事件期間終止個別程序,而非整個 cgroup。

系統層級 OOM 終止

系統層級的 OOM 終止是更嚴重的事件,發生時整個節點 (而不只是單一容器) 都會耗盡可用記憶體。如果所有程序 (包括所有 Pod 和系統精靈) 的記憶體用量總和超過節點容量,就可能發生這個事件。

當這個節點的記憶體不足時,全域 OOM Killer 會評估節點上的所有程序,並終止程序,為整個系統回收記憶體。選取的程序通常是生命週期短且使用大量記憶體的程序。

為避免發生嚴重的 OOM 情況,Kubernetes 會使用節點壓力驅逐來管理節點資源。當記憶體或磁碟空間等資源嚴重不足時,系統會從節點中逐出重要性較低的 Pod。系統層級的 OOM 終止表示這個逐出程序無法及時釋放記憶體,因此無法避免問題。

如果 OOM Killer 終止容器的程序,效果通常與 cgroup 觸發的終止相同:容器會標示為 OOMKilled,並根據其政策重新啟動。不過,如果重要系統程序遭到終止 (極少發生),節點本身可能會變得不穩定。

調查 OOM 事件

以下各節將說明如何偵測及確認 OOM 事件,從最簡單的 Kubernetes 工具開始,逐步進行更詳細的記錄分析。

檢查 Pod 狀態,找出可見的 OOM 事件

確認 OOM 事件的第一步,是檢查 Kubernetes 是否觀察到 OOM 事件。當容器的主要程序遭到終止時,Kubernetes 會觀察到該事件,這是 cgroup v2 環境中的標準行為。

  • 檢查 Pod 的狀態:

    kubectl describe pod POD_NAME
    

    POD_NAME 替換為要調查的 Pod 名稱。

    如果發生可見的 OOM 事件,輸出內容會與下列內容相似:

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

如果「Reason」欄位顯示「OOMKilled」,表示你已確認活動。Exit Code 也表示 OOM 終止。137如果 Reason 欄位的值不同,或 Pod 仍在執行中 (即使應用程式發生錯誤),請前往下一節進行深入調查。

搜尋記錄中不可見的 OOM 事件

如果子程序遭到終止,但主要容器程序持續執行 (cgroup v1環境中的常見情況),Kubernetes 就會「看不到」OOM 終止。您必須搜尋節點的記錄,找出這些事件的證據。

如要找出隱藏的 OOM 終止作業,請使用記錄檔探索工具:

  1. 前往 Google Cloud 控制台的「Logs Explorer」。

    前往記錄檔探索工具

  2. 在查詢窗格中,輸入下列其中一個查詢:

    • 如果現有 Pod 發生 OOM 事件,請查詢該 Pod:

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

      更改下列內容:

      • POD_NAME:要查詢的 Pod 名稱。
      • CLUSTER_NAME:Pod 所屬叢集的名稱。
    • 如要找出發生 OOM 事件的 Pod 或節點,請查詢所有 GKE 工作負載:

      resource.type="k8s_node"
      jsonPayload.MESSAGE:("ContainerDied" OR "TaskOOM event")
      resource.labels.cluster_name="CLUSTER_NAME"
      
  3. 點選「執行查詢」

  4. 在輸出內容中,搜尋包含 TaskOOM 字串的記錄項目,找出 OOM 事件。

  5. 選用:如果您搜尋了所有 GKE 工作負載的 OOM 事件,並想找出發生 OOM 事件的特定 Pod,請完成下列步驟:

    1. 針對每個事件,記下與其相關聯的容器 ID。
    2. 找出含有 ContainerDied 字串的記錄項目,這些項目會在 OOM 事件發生後不久出現,藉此判斷容器停止運作的原因。將 OOM 事件中的容器 ID 與對應的 ContainerDied 行相符。

    3. 比對 container IDs 後,ContainerDied 行通常會包含與失敗容器相關聯的 Pod 名稱。這個 Pod 受到 OOM 事件影響。

使用 journalctl 取得即時資訊

如要即時分析系統,請使用 journalctl 指令。

  1. 使用 SSH 連線至節點:

    gcloud compute ssh NODE_NAME --location ZONE
    

    更改下列內容:

    • NODE_NAME:要檢查的節點名稱。
    • ZONE:節點所屬的 Compute Engine 可用區。
  2. 在 Shell 中,瀏覽節點系統日誌中的核心訊息:

    journalctl -k
    
  3. 分析輸出內容,區分事件類型:

    • 容器層級終止:記錄項目包含 memory cgroupmem_cgroupmemcg 等字詞,表示系統強制執行 cgroup 限制。
    • 系統層級終止:記錄檔項目是類似 Out of memory: Killed process... 的一般訊息,未提及 cgroup。

解決 OOM 事件

如要解決 OOM 事件,請嘗試下列解決方案:

  • 提高記憶體限制:這是最直接的解決方案。編輯 Pod 資訊清單,提供較高的 resources.limits.memory 值,以因應應用程式的尖峰用量。如要進一步瞭解如何設定限制,請參閱 Kubernetes 說明文件的「Pod 和容器的資源管理」一節。
  • 新增或調整記憶體要求:在 Pod 的資訊清單中,確認 resources.requests.memory 欄位已設為一般用量的實際值。這項設定有助於 Kubernetes 將 Pod 排定在記憶體充足的節點上。
  • 水平調度工作負載:如要分配流量負載並減輕單一 Pod 的記憶體壓力,請增加備用資源數量。如要讓 Kubernetes 主動調度工作負載資源,請考慮啟用水平自動調度 Pod 資源
  • 垂直調整節點大小:如果節點上的許多 Pod 都接近限制,則節點本身可能太小。如要增加節點大小,請將工作負載遷移至記憶體較多的節點集區。如要讓 Kubernetes 主動調整節點大小,請考慮啟用垂直 Pod 自動調度資源功能
  • 最佳化應用程式:檢查應用程式,找出並解決記憶體流失問題,並最佳化程式碼,避免在流量高峰期間耗用大量記憶體。
  • 刪除有問題的工作負載:如果工作負載不重要,可以刪除 Pod,立即減輕叢集壓力。

後續步驟