本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
Kubernetes 1.28:改進了 Job 的故障處理
這篇部落格討論了 Kubernetes 1.28 中為改進批處理使用者的 Job 而引入的兩個新特性:Pod 替換策略和逐索引的回退限制。
這些特性延續了Pod 故障策略所開啟的努力,旨在改進 Job 中 Pod 故障的處理方式。
Pod 替換策略
預設情況下,當一個 Pod 進入終止(terminating)狀態(例如,由於搶佔或驅逐)時,Kubernetes 會立即建立一個替換 Pod。因此,兩個 Pod 會同時執行。在 API 術語中,當 Pod 具有 deletionTimestamp
並且其階段(phase)為 Pending
或 Running
時,該 Pod 就被視為正在終止。
在給定時間點有兩個 Pod 正在執行的場景對於一些流行的機器學習框架來說是有問題的,例如 TensorFlow 和 JAX,它們要求對於給定的索引,在同一時間最多隻有一個 Pod 在執行。如果某個索引有兩個 Pod 在執行,TensorFlow 會給出以下錯誤。
/job:worker/task:4: Duplicate task registration with task_name=/job:worker/replica:0/task:4
更多詳情請參見 (issue)。
在前一個 Pod 完全終止之前建立替換 Pod,也可能在資源稀缺或預算緊張的叢集中引起問題,例如:
- 對於等待排程的 Pod 來說,叢集資源可能很難獲取,因為在現有 Pod 完全終止之前,Kubernetes 可能需要很長時間才能找到可用的節點。
- 如果啟用了叢集自動擴縮器,替換的 Pod 可能會產生不希望的擴容。
如何使用它?
這是一個 Alpha 特性,你可以透過在叢集中啟用 JobPodReplacementPolicy
特性門控來開啟它。
一旦在你的叢集中啟用了該特性,你就可以透過建立一個新的 Job 來使用它,其中指定了 podReplacementPolicy
欄位,如下所示:
kind: Job
metadata:
name: new
...
spec:
podReplacementPolicy: Failed
...
在該 Job 中,Pod 只有在達到 Failed
階段時才會被替換,而不是在它們正在終止時。
此外,你可以檢查 Job 的 .status.terminating
欄位。該欄位的值是 Job 擁有的當前正在終止的 Pod 的數量。
kubectl get jobs/myjob -o=jsonpath='{.items[*].status.terminating}'
3 # three Pods are terminating and have not yet reached the Failed phase
這對於外部排隊控制器(例如 Kueue)特別有用,它可以跟蹤一個 Job 正在執行的 Pod 的配額,直到從當前正在終止的 Job 中回收資源為止。
請注意,當使用自定義的Pod 故障策略時,podReplacementPolicy: Failed
是預設設定。
逐索引的回退限制
預設情況下,Indexed Job 的 Pod 故障會計入全域性重試限制,由 .spec.backoffLimit
表示。這意味著,如果有一個持續失敗的索引,它會反覆重啟,直到耗盡限制。一旦達到限制,整個 Job 就會被標記為失敗,並且某些索引可能從未啟動過。
這對於希望獨立處理每個索引的 Pod 故障的用例是有問題的。例如,如果你使用 Indexed Job 來執行整合測試,其中每個索引對應一個測試套件。在這種情況下,你可能希望考慮到可能的不穩定測試,允許每個套件重試 1 或 2 次。可能會有一些有問題的套件,導致相應的索引持續失敗。在這種情況下,你可能更傾向於限制有問題的套件的重試次數,同時允許其他套件完成。
該特性允許你:
- 儘管某些索引失敗,但仍能完成所有索引的執行。
- 透過避免對持續失敗的索引進行不必要的重試,更好地利用計算資源。
如何使用它?
這是一個 Alpha 特性,你可以透過在叢集中啟用 JobBackoffLimitPerIndex
特性門控來開啟它。
一旦在你的叢集中啟用了該特性,你就可以建立一個指定了 .spec.backoffLimitPerIndex
欄位的 Indexed Job。
示例
以下示例演示瞭如何使用此特性來確保 Job 執行所有索引(前提是沒有其他原因導致 Job 提前終止,例如達到 activeDeadlineSeconds
超時或被使用者手動刪除),並且失敗次數是按每個索引控制的。
apiVersion: batch/v1
kind: Job
metadata:
name: job-backoff-limit-per-index-execute-all
spec:
completions: 8
parallelism: 2
completionMode: Indexed
backoffLimitPerIndex: 1
template:
spec:
restartPolicy: Never
containers:
- name: example # this example container returns an error, and fails,
# when it is run as the second or third index in any Job
# (even after a retry)
image: python
command:
- python3
- -c
- |
import os, sys, time
id = int(os.environ.get("JOB_COMPLETION_INDEX"))
if id == 1 or id == 2:
sys.exit(1)
time.sleep(1)
現在,在 Job 完成後檢查 Pod:
kubectl get pods -l job-name=job-backoff-limit-per-index-execute-all
返回類似以下的輸出:
NAME READY STATUS RESTARTS AGE
job-backoff-limit-per-index-execute-all-0-b26vc 0/1 Completed 0 49s
job-backoff-limit-per-index-execute-all-1-6j5gd 0/1 Error 0 49s
job-backoff-limit-per-index-execute-all-1-6wd82 0/1 Error 0 37s
job-backoff-limit-per-index-execute-all-2-c66hg 0/1 Error 0 32s
job-backoff-limit-per-index-execute-all-2-nf982 0/1 Error 0 43s
job-backoff-limit-per-index-execute-all-3-cxmhf 0/1 Completed 0 33s
job-backoff-limit-per-index-execute-all-4-9q6kq 0/1 Completed 0 28s
job-backoff-limit-per-index-execute-all-5-z9hqf 0/1 Completed 0 28s
job-backoff-limit-per-index-execute-all-6-tbkr8 0/1 Completed 0 23s
job-backoff-limit-per-index-execute-all-7-hxjsq 0/1 Completed 0 22s
此外,你可以檢視該 Job 的狀態:
kubectl get jobs job-backoff-limit-per-index-fail-index -o yaml
輸出以類似以下的 status
結尾:
status:
completedIndexes: 0,3-7
failedIndexes: 1,2
succeeded: 6
failed: 4
conditions:
- message: Job has failed indexes
reason: FailedIndexes
status: "True"
type: Failed
在這裡,索引 1
和 2
都各自重試了一次。在它們各自第二次失敗後,指定的 .spec.backoffLimitPerIndex
被超過,因此重試被停止。作為比較,如果停用了逐索引的回退,那麼有問題的索引會一直重試,直到超過全域性的 backoffLimit
,然後整個 Job 會被標記為失敗,而一些更高編號的索引可能還未啟動。
如何瞭解更多?
- 閱讀Pod 替換策略、逐索引的回退限制和Pod 故障策略的使用者文件。
- 閱讀關於Pod 替換策略、逐索引的回退限制和Pod 故障策略的 KEP(Kubernetes Enhancement Proposal)。
參與其中
這些特性由 SIG Apps 贊助。在批處理工作組中,我們正積極為 Kubernetes 使用者改進批處理用例。工作組是專注於特定目標的相對短期的倡議。WG Batch 的目標是改善批處理工作負載使用者的體驗,為批處理用例提供支援,並針對常見用例增強 Job API。如果你對此感興趣,請透過訂閱我們的郵件列表或在 Slack 上加入我們的工作組。
致謝
與任何 Kubernetes 特性一樣,許多人都為完成這項工作做出了貢獻,從測試、提交錯誤到程式碼審查。
如果沒有 Aldo Culquicondor (Google) 在整個 Kubernetes 生態系統中提供卓越的領域知識和專業技術,我們無法實現這兩項特性。