本文已超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

Kubernetes 1.26:Job 追蹤功能正式發佈,支援大規模平行批次工作負載

Kubernetes 1.26 版本包含 Job 控制器的穩定實作,可以可靠地追蹤大量具有高度並行性的 Job。SIG AppsWG Batch 自 Kubernetes 1.22 以來一直致力於此基礎性改進。經過多次迭代和規模驗證,這現在是 Job 控制器的預設實作。

與索引 完成模式 配對,Job 控制器可以處理大規模並行批次 Job,最多支援 10 萬個並行 Pod。

新的實作也使得 Pod 失敗策略 的開發成為可能,該策略在 1.26 版本中處於 beta 階段。

我該如何使用此功能?

若要使用帶有 Finalizers 的 Job 追蹤,請升級到 Kubernetes 1.25 或更新版本並建立新的 Job。如果您有能力啟用 JobTrackingWithFinalizers 功能閘道,您也可以在 v1.23 和 v1.24 中使用此功能。

如果您的叢集運行 Kubernetes 1.26,則帶有 Finalizers 的 Job 追蹤是一項穩定功能。對於 v1.25,它位於該功能閘道之後,您的叢集管理員可能已明確禁用它 - 例如,如果您有不使用 beta 功能的策略。

升級之前建立的 Job 仍將使用舊版行為進行追蹤。這是為了避免追溯地將 Finalizers 新增到正在運行的 Pod,這可能會引入競爭條件。

為了在大型 Job 上獲得最佳效能,Kubernetes 專案建議使用 索引完成模式。在此模式下,控制平面能夠以更少的 API 呼叫追蹤 Job 進度。

如果您是批次、HPCAIML 或相關工作負載的運算符開發人員,我們鼓勵您使用 Job API 將準確的進度追蹤委派給 Kubernetes。如果 Job API 中缺少某些東西迫使您管理純 Pod,Working Group Batch 歡迎您的回饋和貢獻。

棄用通知

在功能開發期間,當啟用該功能時,控制平面將註解 batch.kubernetes.io/job-tracking 新增到建立的 Job。這允許舊版 Job 的安全轉換,但它從未打算保留。

在 1.26 版本中,我們棄用了註解 batch.kubernetes.io/job-tracking,並且控制平面將在 Kubernetes 1.27 中停止新增它。隨著該變更,我們將移除舊版 Job 追蹤實作。因此,Job 控制器將使用 Finalizers 追蹤所有 Job,並且它將忽略沒有上述 Finalizer 的 Pod。

在您將叢集升級到 1.27 之前,我們建議您驗證是否沒有沒有註解的正在運行的 Job,或者您等待這些 Job 完成。否則,您可能會觀察到控制平面重新建立一些 Pod。我們預計這不應影響任何使用者,因為該功能自 Kubernetes 1.25 以來預設啟用,為舊版 Job 完成提供了足夠的緩衝時間。

新實作解決了什麼問題?

一般而言,Kubernetes 工作負載控制器(例如 ReplicaSet 或 StatefulSet)依賴 API 中 Pod 或其他物件的存在來確定工作負載的狀態以及是否需要替換。例如,如果屬於 ReplicaSet 的 Pod 終止或不再存在,則 ReplicaSet 控制器需要建立替換 Pod 以滿足所需的副本數 (.spec.replicas)。

自成立以來,Job 控制器也依賴 API 中 Pod 的存在來追蹤 Job 狀態。Job 具有 完成失敗處理 策略,需要已完成 Pod 的最終狀態來確定是否建立替換 Pod 或將 Job 標記為已完成或失敗。因此,Job 控制器依賴 Pod,即使是終止的 Pod,也必須保留在 API 中,以便追蹤狀態。

這種依賴性使得 Job 狀態的追蹤變得不可靠,因為 Pod 可能因多種原因從 API 中刪除,包括

  • 當 Node 關閉時,垃圾收集器移除孤立的 Pod。
  • 當終止的 Pod 達到閾值時,垃圾收集器移除它們。
  • Kubernetes 調度器搶佔 Pod 以容納更高優先順序的 Pod。
  • Taint 管理器驅逐不容忍 NoExecute Taint 的 Pod。
  • 未包含在 Kubernetes 中的外部控制器或人為刪除 Pod。

新的實作

當控制器需要在移除物件之前對物件執行操作時,它應該將 Finalizer 新增到它管理的物件。Finalizer 會阻止物件從 API 中刪除,直到移除 Finalizer。一旦控制器完成清理和已刪除物件的核算,它就可以從物件中移除 Finalizer,並且控制平面會從 API 中移除物件。

這就是新的 Job 控制器正在做的事情:在 Pod 建立期間新增 Finalizer,並在 Pod 終止並已在 Job 狀態中核算後移除 Finalizer。但是,這並非那麼簡單。

主要挑戰是至少涉及兩個物件:Pod 和 Job。雖然 Finalizer 存在於 Pod 物件中,但核算存在於 Job 物件中。沒有機制可以原子地移除 Pod 中的 Finalizer 並更新 Job 狀態中的計數器。此外,在給定時間可能有多個終止的 Pod。

為了解決這個問題,我們實作了三個階段的方法,每個階段都轉換為一個 API 呼叫。

  1. 對於每個終止的 Pod,將 Pod 的唯一 ID (UID) 新增到儲存在擁有 Job 的 .status 中的短期列表中 (.status.uncountedTerminatedPods)。
  2. 從 Pod 中移除 Finalizer。
  3. 原子地執行以下操作
    • 從短期列表中移除 UID
    • 增加 Job 的 status 中的整體 succeededfailed 計數器。

其他複雜情況來自於 Job 控制器可能會收到步驟 1 和 2 中 API 變更的結果順序錯誤。我們透過為已移除的 Finalizer 新增記憶體內快取來解決此問題。

儘管如此,我們在 beta 階段仍然遇到一些問題,在某些情況下留下一些 Pod 卡在 Finalizer 中 (#108645#109485#111646)。因此,我們決定將該功能閘道切換為在 1.23 和 1.24 版本中預設禁用。

一旦解決,我們在 1.25 版本中重新啟用了該功能。從那時起,我們收到了客戶的回報,他們透過 Job API 在其叢集中一次運行數萬個 Pod。看到這種成功,我們決定在 1.26 版本中將該功能升級為穩定版,作為我們長期致力於使 Job API 成為在 Kubernetes 叢集中運行大型批次 Job 的最佳方式的一部分。

若要了解有關該功能的更多資訊,您可以閱讀 KEP

致謝

與任何 Kubernetes 功能一樣,從測試和提交錯誤到審查程式碼,有多人為完成此功能做出了貢獻。

我謹代表 SIG Apps,特別感謝 Jordan Liggitt (Google) 幫助我除錯和集思廣益解決方案,以應對多個競爭條件,並感謝 Maciej Szulik (Red Hat) 的徹底審查。