如果 Google Kubernetes Engine (GKE) 應用程式發生非預期的容器終止、不穩定或 CrashLoopBackOff 錯誤,可能是因為記憶體不足 (OOM) 事件。如果容器或節點記憶體不足,就會發生 OOM 事件,導致 Linux OOM Killer 終止程序,以回收資源。
您可以在這個頁面中,判斷 OOM 事件是發生在容器層級還是節點層級,並套用有效的緩解策略,防止事件再次發生。
應用程式開發人員必須確保應用程式已設定適當的記憶體要求和限制,且沒有記憶體洩漏問題,因此這項資訊非常重要。此外,平台管理員和運算子也必須監控節點資源用量,確保叢集有足夠的記憶體容量來支援已部署的工作負載。如要進一步瞭解我們在 Google Cloud 內容中提及的常見角色和範例工作,請參閱「常見的 GKE 使用者角色和工作」。
OOM 事件的常見原因
OOM 事件通常發生在負載或流量尖峰期間,此時應用程式的記憶體用量會暴增,達到為容器設定的記憶體上限。
下列情況可能會導致 OOM 事件:
- 記憶體限制不足:Pod 資訊清單中的
resources.limits.memory設定過低,無法滿足應用程式的典型或尖峰記憶體需求。 - 未定義記憶體要求或限制:如果未定義
resources.limits.memory和resources.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會決定結果:
Always或OnFailure:容器重新啟動。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 v2的singleProcessOOMKill旗標。這個標記可讓您更精細地控管,在 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 的 137 也表示 OOM 終止。如果 Reason 欄位的值不同,或 Pod 仍在執行中,但應用程式發生錯誤,請前往下一節進行深入調查。
搜尋記錄中不可見的 OOM 事件
如果子程序遭到終止,但主要容器程序持續執行 (cgroup v1環境中的常見情況),Kubernetes 就會「看不到」OOM 終止。您必須搜尋節點的記錄,找出這些事件的證據。
如要找出隱藏的 OOM 終止作業,請使用記錄檔探索工具:
前往 Google Cloud 控制台的「Logs Explorer」。
在查詢窗格中,輸入下列其中一個查詢:
如果現有 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"
點選「執行查詢」
在輸出內容中,搜尋包含
TaskOOM字串的記錄項目,找出 OOM 事件。選用:如果您搜尋了所有 GKE 工作負載的 OOM 事件,並想找出發生 OOM 事件的特定 Pod,請完成下列步驟:
- 針對每個事件,記下與其相關聯的容器 ID。
找出含有
ContainerDied字串的記錄項目,這些項目發生在 OOM 事件後不久,即可判斷容器停止運作。將 OOM 事件中的容器 ID 與對應的ContainerDied行相符。比對
container IDs後,ContainerDied行通常會包含與失敗容器相關聯的 Pod 名稱。這個 Pod 受到 OOM 事件影響。
使用 journalctl 取得即時資訊
如要即時分析系統,請使用 journalctl 指令。
使用 SSH 連線至節點:
gcloud compute ssh NODE_NAME --location ZONE更改下列內容:
NODE_NAME:要檢查的節點名稱。ZONE:節點所屬的 Compute Engine 可用區。
在 Shell 中,瀏覽節點系統日誌中的核心訊息:
journalctl -k分析輸出內容,區分事件類型:
- 容器層級終止:記錄項目包含
memory cgroup、mem_cgroup或memcg等字詞,表示系統強制執行 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,立即減輕叢集壓力。
後續步驟
如果無法在說明文件中找到問題的解決方法,請參閱「取得支援」一文,尋求進一步的協助, 包括下列主題的建議:
- 與 Cloud 客戶服務聯絡,建立支援案件。
- 在 StackOverflow 上提問,並使用
google-kubernetes-engine標記搜尋類似問題,向社群尋求支援。你也可以加入#kubernetes-engineSlack 頻道,取得更多社群支援。 - 使用公開版 Issue Tracker 開啟錯誤或功能要求。