作業

作業(Job)表示一次性任務,它們執行到完成然後停止。

一個 Job 會建立一個或多個 Pod,並會持續重試執行 Pod,直到指定數量的 Pod 成功終止。當 Pod 成功完成時,Job 會跟蹤這些成功完成。當達到指定數量的成功完成時,任務(即 Job)就完成了。刪除 Job 將會清理它所建立的 Pod。掛起 Job 將會刪除其活躍的 Pod,直到 Job 再次恢復。

一個簡單的例子是建立一個 Job 物件,以可靠地執行一個 Pod 直到完成。如果第一個 Pod 失敗或被刪除(例如由於節點硬體故障或節點重啟),Job 物件將啟動一個新的 Pod。

你也可以使用 Job 並行執行多個 Pod。

如果你想按計劃執行一個 Job(無論是單個任務,還是多個並行任務),請參閱 CronJob

執行一個示例 Job

這是一個示例 Job 配置。它計算圓周率到 2000 位並打印出來。它大約需要 10 秒才能完成。

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

你可以使用以下命令執行此示例

kubectl apply -f https://kubernetes.club.tw/examples/controllers/job.yaml

輸出類似於:

job.batch/pi created

使用 kubectl 檢查 Job 的狀態


Name:           pi
Namespace:      default
Selector:       batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
Labels:         batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
                batch.kubernetes.io/job-name=pi
                ...
Annotations:    batch.kubernetes.io/job-tracking: ""
Parallelism:    1
Completions:    1
Start Time:     Mon, 02 Dec 2019 15:20:11 +0200
Completed At:   Mon, 02 Dec 2019 15:21:16 +0200
Duration:       65s
Pods Statuses:  0 Running / 1 Succeeded / 0 Failed
Pod Template:
  Labels:  batch.kubernetes.io/controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c
           batch.kubernetes.io/job-name=pi
  Containers:
   pi:
    Image:      perl:5.34.0
    Port:       <none>
    Host Port:  <none>
    Command:
      perl
      -Mbignum=bpi
      -wle
      print bpi(2000)
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  21s   job-controller  Created pod: pi-xf9p4
  Normal  Completed         18s   job-controller  Job completed


apiVersion: batch/v1
kind: Job
metadata:
  annotations: batch.kubernetes.io/job-tracking: ""
             ...  
  creationTimestamp: "2022-11-10T17:53:53Z"
  generation: 1
  labels:
    batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
    batch.kubernetes.io/job-name: pi
  name: pi
  namespace: default
  resourceVersion: "4751"
  uid: 204fb678-040b-497f-9266-35ffa8716d14
spec:
  backoffLimit: 4
  completionMode: NonIndexed
  completions: 1
  parallelism: 1
  selector:
    matchLabels:
      batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
  suspend: false
  template:
    metadata:
      creationTimestamp: null
      labels:
        batch.kubernetes.io/controller-uid: 863452e6-270d-420e-9b94-53a54146c223
        batch.kubernetes.io/job-name: pi
    spec:
      containers:
      - command:
        - perl
        - -Mbignum=bpi
        - -wle
        - print bpi(2000)
        image: perl:5.34.0
        imagePullPolicy: IfNotPresent
        name: pi
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Never
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
status:
  active: 1
  ready: 0
  startTime: "2022-11-10T17:53:57Z"
  uncountedTerminatedPods: {}

要檢視 Job 已完成的 Pod,請使用 kubectl get pods

要以機器可讀的形式列出屬於一個 Job 的所有 Pod,你可以使用如下命令

pods=$(kubectl get pods --selector=batch.kubernetes.io/job-name=pi --output=jsonpath='{.items[*].metadata.name}')
echo $pods

輸出類似於:

pi-5rwd7

這裡,選擇器與 Job 的選擇器相同。 --output=jsonpath 選項指定了一個表示式,其中包含返回列表中每個 Pod 的名稱。

檢視其中一個 Pod 的標準輸出

kubectl logs $pods

檢視 Job 日誌的另一種方式

kubectl logs jobs/pi

輸出類似於:

3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275901

編寫 Job 規約

與其他所有 Kubernetes 配置一樣,Job 需要 apiVersionkindmetadata 欄位。

當控制平面為 Job 建立新的 Pod 時,Job 的 .metadata.name 是命名這些 Pod 的基礎部分。Job 的名稱必須是一個有效的 DNS 子域名值,但這可能會導致 Pod 主機名出現意外結果。為了獲得最佳相容性,名稱應遵循更嚴格的 DNS 標籤規則。即使名稱是 DNS 子域名,其長度也必須不超過 63 個字元。

Job 還需要一個 .spec 部分

Job 標籤

Job 標籤將帶有 batch.kubernetes.io/ 字首,用於 job-namecontroller-uid

Pod 模板

.spec.template.spec 中唯一必需的欄位。

.spec.template 是一個 Pod 模板。它與 Pod 具有完全相同的架構,只是它是巢狀的,並且沒有 apiVersionkind

除了 Pod 的必需欄位外,Job 中的 Pod 模板還必須指定適當的標籤(請參閱Pod 選擇器)和適當的重啟策略。

只允許 RestartPolicy 等於 NeverOnFailure

Pod 選擇器

.spec.selector 欄位是可選的。在幾乎所有情況下,你都不應該指定它。請參閱 指定你自己的 Pod 選擇器一節。

Job 的並行執行

有三種主要型別的任務適合作為 Job 執行

  1. 非並行 Job
    • 通常只啟動一個 Pod,除非 Pod 失敗。
    • 一旦其 Pod 成功終止,Job 即完成。
  2. 具有**固定完成計數**的並行 Job
    • .spec.completions 指定一個非零正值。
    • Job 代表整體任務,當有 .spec.completions 個成功 Pod 時完成。
    • 當使用 .spec.completionMode="Indexed" 時,每個 Pod 會在 0 到 .spec.completions-1 範圍內獲得一個不同的索引。
  3. 具有**工作佇列**的並行 Job
    • 不要指定 .spec.completions,預設為 .spec.parallelism
    • Pod 之間必須相互協調或與外部服務協調,以確定每個 Pod 應處理什麼。例如,一個 Pod 可能會從工作佇列中獲取一批多達 N 個專案。
    • 每個 Pod 都能夠獨立地確定其所有對等體是否已完成,從而確定整個 Job 是否已完成。
    • 當 Job 中的**任何** Pod 成功終止時,不會建立新的 Pod。
    • 一旦至少一個 Pod 成功終止並且所有 Pod 都已終止,則 Job 成功完成。
    • 一旦任何 Pod 成功退出,其他 Pod 都不應再為該任務執行任何工作或寫入任何輸出。它們都應處於退出過程中。

對於**非並行** Job,你可以將 .spec.completions.spec.parallelism 都留空。當兩者都留空時,兩者都預設為 1。

對於**固定完成計數** Job,你應該將 .spec.completions 設定為所需的完成次數。你可以設定 .spec.parallelism,或將其留空,它將預設為 1。

對於**工作佇列** Job,你必須將 .spec.completions 留空,並將 .spec.parallelism 設定為非負整數。

有關如何利用不同型別 Job 的更多資訊,請參閱 Job 模式部分。

控制並行性

請求的並行度(.spec.parallelism)可以設定為任何非負值。如果未指定,則預設為 1。如果指定為 0,則 Job 將被有效暫停,直到其值增加。

由於各種原因,實際並行度(任何時刻執行的 Pod 數量)可能高於或低於請求並行度

  • 對於**固定完成計數**的 Job,實際並行執行的 Pod 數量不會超過剩餘的完成數量。較高的 .spec.parallelism 值實際上會被忽略。
  • 對於**工作佇列** Job,在任何 Pod 成功後都不會啟動新的 Pod——但是,允許剩餘的 Pod 完成。
  • 如果 Job 的 控制器 沒有足夠時間做出反應。
  • 如果 Job 控制器因任何原因(缺少 ResourceQuota、缺少許可權等)未能建立 Pod,那麼實際執行的 Pod 可能會少於請求數量。
  • 由於同一個 Job 中之前出現過多的 Pod 失敗,Job 控制器可能會限制新 Pod 的建立。
  • 當 Pod 優雅關閉時,需要時間停止。

完成模式

特性狀態: Kubernetes v1.24 [stable]

具有**固定完成計數**的 Job(即 .spec.completions 非空的 Job)可以具有在 .spec.completionMode 中指定的完成模式

  • NonIndexed(預設):當有 .spec.completions 個 Pod 成功完成時,Job 被認為是完成的。換句話說,每個 Pod 的完成都是同質的。請注意,.spec.completions 為空的 Job 隱式地是 NonIndexed

  • Indexed:Job 的 Pod 會獲得一個從 0 到 .spec.completions-1 的關聯完成索引。該索引透過四種機制提供

    • Pod 註解 batch.kubernetes.io/job-completion-index
    • Pod 標籤 batch.kubernetes.io/job-completion-index(適用於 v1.28 及更高版本)。請注意,必須啟用特性門控 PodIndexLabel 才能使用此標籤,並且預設情況下已啟用。
    • 作為 Pod 主機名的一部分,遵循模式 $(job-name)-$(index)。當你將 Indexed Job 與 Service 結合使用時,Job 內的 Pod 可以使用確定性主機名透過 DNS 相互通訊。有關如何配置此功能的更多資訊,請參閱 Pod 間通訊的 Job
    • 從容器化任務中,透過環境變數 JOB_COMPLETION_INDEX

    當每個索引有一個成功完成的 Pod 時,Job 被認為是完成的。有關如何使用此模式的更多資訊,請參閱 帶靜態工作分配的索引 Job 進行並行處理

處理 Pod 和容器故障

Pod 中的容器可能因多種原因而失敗,例如程序以非零退出程式碼退出,或容器因超出記憶體限制而被終止等。如果發生這種情況,並且 .spec.template.spec.restartPolicy = "OnFailure",則 Pod 會保留在節點上,但容器會重新執行。因此,你的程式需要處理其在本地重新啟動的情況,或者指定 .spec.template.spec.restartPolicy = "Never"。有關 restartPolicy 的更多資訊,請參閱 Pod 生命週期

整個 Pod 也可能因多種原因而失敗,例如當 Pod 被從節點上踢掉(節點升級、重啟、刪除等),或者如果 Pod 的容器失敗且 .spec.template.spec.restartPolicy = "Never"。當 Pod 失敗時,Job 控制器會啟動一個新的 Pod。這意味著你的應用程式需要處理其在新 Pod 中重新啟動的情況。特別是,它需要處理由於之前執行而導致的臨時檔案、鎖、不完整的輸出等。

預設情況下,每次 Pod 失敗都計入 .spec.backoffLimit 限制,請參閱 Pod 重試失敗策略。但是,你可以透過設定 Job 的 Pod 失敗策略來自定義 Pod 失敗的處理。

此外,你可以選擇透過設定 Job 的 .spec.backoffLimitPerIndex 欄位,獨立計算 索引式 Job 的每個索引的 Pod 失敗次數(更多資訊請參閱 每個索引的重試限制)。

請注意,即使你指定 .spec.parallelism = 1.spec.completions = 1.spec.template.spec.restartPolicy = "Never",同一個程式有時也可能會啟動兩次。

如果你將 .spec.parallelism.spec.completions 都指定為大於 1,則可能會有多個 Pod 同時執行。因此,你的 Pod 也必須能夠容忍併發。

如果你指定 .spec.podFailurePolicy 欄位,則 Job 控制器不會將正在終止的 Pod(設定了 .metadata.deletionTimestamp 欄位的 Pod)視為失敗,直到該 Pod 達到終止狀態(其 .status.phaseFailedSucceeded)。但是,Job 控制器會在終止變得明顯時立即建立一個替換 Pod。一旦 Pod 終止,Job 控制器會評估相關 Job 的 .backoffLimit.podFailurePolicy,並將此已終止的 Pod 考慮在內。

如果這些要求中的任何一個未滿足,Job 控制器會將正在終止的 Pod 視為立即失敗,即使該 Pod 隨後以 phase: "Succeeded" 終止。

Pod 重試失敗策略

在某些情況下,你希望在由於配置中的邏輯錯誤等原因多次重試後使 Job 失敗。為此,請設定 .spec.backoffLimit 以指定在 Job 被視為失敗之前重試的次數。

預設情況下,.spec.backoffLimit 設定為 6,除非指定了 每個索引的退避限制(僅限索引 Job)。當指定 .spec.backoffLimitPerIndex 時,.spec.backoffLimit 預設為 2147483647(MaxInt32)。

Job 控制器會以指數退避延遲(10 秒、20 秒、40 秒…)重新建立與 Job 關聯的失敗 Pod,上限為 6 分鐘。

重試次數透過兩種方式計算

  • .status.phase = "Failed" 的 Pod 數量。
  • 當使用 restartPolicy = "OnFailure" 時,所有 .status.phase 等於 PendingRunning 的 Pod 中所有容器的重試次數。

如果任一計算達到 .spec.backoffLimit,則 Job 被視為失敗。

每個索引的重試限制

特性狀態: Kubernetes v1.33 [stable] (預設啟用:true)

當你執行索引式 Job 時,你可以選擇獨立處理每個索引的 Pod 故障重試。為此,請設定 .spec.backoffLimitPerIndex 以指定每個索引的最大 Pod 失敗次數。

當某個索引的每索引退避限制超出時,Kubernetes 會將該索引視為失敗,並將其新增到 .status.failedIndexes 欄位中。成功執行 Pod 的索引會記錄在 .status.completedIndexes 欄位中,無論你是否設定了 backoffLimitPerIndex 欄位。

請注意,一個失敗的索引不會中斷其他索引的執行。一旦所有索引都為指定了每索引退避限制的 Job 完成,如果其中至少有一個索引失敗,Job 控制器會透過在狀態中設定 Failed 條件來將整個 Job 標記為失敗。即使部分(可能幾乎所有)索引都已成功處理,Job 也會被標記為失敗。

你還可以透過設定 .spec.maxFailedIndexes 欄位來限制標記為失敗的索引的最大數量。當失敗索引的數量超過 maxFailedIndexes 欄位時,Job 控制器會觸發該 Job 所有剩餘執行中 Pod 的終止。一旦所有 Pod 終止,Job 控制器會將整個 Job 標記為失敗,方法是在 Job 狀態中設定 Failed 條件。

這是一個定義了 backoffLimitPerIndex 的 Job 示例清單

apiVersion: batch/v1
kind: Job
metadata:
  name: job-backoff-limit-per-index-example
spec:
  completions: 10
  parallelism: 3
  completionMode: Indexed  # required for the feature
  backoffLimitPerIndex: 1  # maximal number of failures per index
  maxFailedIndexes: 5      # maximal number of failed indexes before terminating the Job execution
  template:
    spec:
      restartPolicy: Never # required for the feature
      containers:
      - name: example
        image: python
        command:           # The jobs fails as there is at least one failed index
                           # (all even indexes fail in here), yet all indexes
                           # are executed as maxFailedIndexes is not exceeded.
        - python3
        - -c
        - |
          import os, sys
          print("Hello world")
          if int(os.environ.get("JOB_COMPLETION_INDEX")) % 2 == 0:
            sys.exit(1)          

在上面的示例中,Job 控制器允許每個索引重新啟動一次。當失敗索引的總數超過 5 時,整個 Job 將被終止。

Job 完成後,其狀態如下所示

kubectl get -o yaml job job-backoff-limit-per-index-example
  status:
    completedIndexes: 1,3,5,7,9
    failedIndexes: 0,2,4,6,8
    succeeded: 5          # 1 succeeded pod for each of 5 succeeded indexes
    failed: 10            # 2 failed pods (1 retry) for each of 5 failed indexes
    conditions:
    - message: Job has failed indexes
      reason: FailedIndexes
      status: "True"
      type: FailureTarget
    - message: Job has failed indexes
      reason: FailedIndexes
      status: "True"
      type: Failed

Job 控制器新增 FailureTarget Job 條件來觸發 Job 終止和清理。當所有 Job Pods 終止後,Job 控制器會新增 Failed 條件,其 reasonmessage 值與 FailureTarget Job 條件相同。有關詳細資訊,請參閱 Job Pod 的終止

此外,你可能希望將按索引退避與 Pod 失敗策略結合使用。使用按索引退避時,有一個新的 FailIndex 動作可用,允許你避免索引內不必要的重試。

Pod 失敗策略

特性狀態: Kubernetes v1.31 [stable] (預設啟用:true)

Pod 失敗策略由 .spec.podFailurePolicy 欄位定義,它使你的叢集能夠根據容器退出程式碼和 Pod 條件來處理 Pod 失敗。

在某些情況下,你可能希望在處理 Pod 故障時擁有比基於 Job 的 .spec.backoffLimitPod 重試失敗策略所提供的控制更精細的控制。以下是一些用例示例

  • 為了最佳化執行工作負載的成本,你可以透過在 Pod 因指示軟體錯誤的退出程式碼失敗時立即終止 Job,從而避免不必要的 Pod 重啟。
  • 為了保證你的 Job 即使在出現中斷時也能完成,你可以忽略由中斷引起的 Pod 失敗(例如 搶佔API 發起的逐出或基於汙點的逐出),這樣它們就不會計入 .spec.backoffLimit 的重試限制。

你可以在 .spec.podFailurePolicy 欄位中配置 Pod 失敗策略,以滿足上述用例。此策略可以根據容器退出程式碼和 Pod 條件處理 Pod 失敗。

這是一個定義了 podFailurePolicy 的 Job 清單

apiVersion: batch/v1
kind: Job
metadata:
  name: job-pod-failure-policy-example
spec:
  completions: 12
  parallelism: 3
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: main
        image: docker.io/library/bash:5
        command: ["bash"]        # example command simulating a bug which triggers the FailJob action
        args:
        - -c
        - echo "Hello world!" && sleep 5 && exit 42
  backoffLimit: 6
  podFailurePolicy:
    rules:
    - action: FailJob
      onExitCodes:
        containerName: main      # optional
        operator: In             # one of: In, NotIn
        values: [42]
    - action: Ignore             # one of: Ignore, FailJob, Count
      onPodConditions:
      - type: DisruptionTarget   # indicates Pod disruption

在上面的示例中,Pod 失敗策略的第一條規則指定如果 main 容器以 42 退出程式碼失敗,則 Job 應該被標記為失敗。以下是針對 main 容器的具體規則

  • 退出程式碼 0 意味著容器成功
  • 退出程式碼 42 意味著**整個 Job** 失敗
  • 任何其他退出程式碼表示容器失敗,因此整個 Pod 失敗。如果總的重啟次數低於 backoffLimit,Pod 將被重新建立。如果達到 backoffLimit,則**整個 Job** 失敗。

Pod 失敗策略的第二條規則,為條件為 DisruptionTarget 的失敗 Pod 指定 Ignore 動作,將 Pod 中斷排除在 .spec.backoffLimit 的重試限制之外。

以下是 API 的一些要求和語義

  • 如果想為 Job 使用 .spec.podFailurePolicy 欄位,則還必須定義該 Job 的 Pod 模板,並將 .spec.restartPolicy 設定為 Never
  • 你在 spec.podFailurePolicy.rules 下指定的 Pod 失敗策略規則按順序評估。一旦某個規則與 Pod 失敗匹配,其餘規則將被忽略。如果沒有規則與 Pod 失敗匹配,則應用預設處理。
  • 你可以透過在 spec.podFailurePolicy.rules[*].onExitCodes.containerName 中指定容器名稱來將規則限制到特定的容器。如果未指定,則該規則適用於所有容器。如果指定,它應與 Pod 模板中的某個容器或 initContainer 名稱匹配。
  • 你可以透過 spec.podFailurePolicy.rules[*].action 指定 Pod 失敗策略匹配時採取的操作。可能的值有
    • FailJob:用於指示 Pod 的作業應被標記為失敗,並且所有正在執行的 Pod 應被終止。
    • Ignore:用於指示不應增加 .spec.backoffLimit 的計數器,並且應建立替換 Pod。
    • Count:用於指示 Pod 應該以預設方式處理。.spec.backoffLimit 的計數器應該增加。
    • FailIndex:將此動作與每個索引的退避限制結合使用,以避免失敗 Pod 索引內不必要的重試。

當你使用 podFailurePolicy,並且 Job 由於 Pod 匹配帶有 FailJob 動作的規則而失敗時,Job 控制器會透過新增 FailureTarget 條件來觸發 Job 終止過程。有關更多詳細資訊,請參閱 Job 終止和清理

成功策略

建立 Indexed Job 時,你可以使用 .spec.successPolicy 根據成功的 Pods 來定義 Job 何時可以被宣告為成功。

預設情況下,當成功 Pod 的數量等於 .spec.completions 時,Job 成功。在以下一些情況下,你可能希望對宣告 Job 成功有額外的控制

  • 當使用不同引數執行模擬時,你可能不需要所有模擬都成功才能使整個 Job 成功。
  • 當遵循領導者-工作者模式時,只有領導者的成功才能決定 Job 的成功或失敗。例如 MPI 和 PyTorch 等框架。

你可以在 .spec.successPolicy 欄位中配置成功策略,以滿足上述用例。此策略可以根據成功的 Pod 處理 Job 的成功。當 Job 滿足成功策略後,Job 控制器會終止其餘的 Pod。成功策略由規則定義。每條規則可以採用以下形式之一

  • 當只指定 succeededIndexes 時,一旦 succeededIndexes 中指定的所有索引都成功,Job 控制器將 Job 標記為成功。succeededIndexes 必須是 0 到 .spec.completions-1 之間的區間列表。
  • 當僅指定 succeededCount 時,一旦成功索引的數量達到 succeededCount,Job 控制器就會將 Job 標記為成功。
  • 當你同時指定 succeededIndexessucceededCount 時,一旦 succeededIndexes 中指定的索引子集中成功索引的數量達到 succeededCount,Job 控制器就會將 Job 標記為成功。

請注意,當你在 .spec.successPolicy.rules 中指定多條規則時,Job 控制器會按順序評估這些規則。一旦 Job 滿足某條規則,Job 控制器就會忽略其餘規則。

這是一個帶有 successPolicy 的 Job 清單

apiVersion: batch/v1
kind: Job
metadata:
  name: job-success
spec:
  parallelism: 10
  completions: 10
  completionMode: Indexed # Required for the success policy
  successPolicy:
    rules:
      - succeededIndexes: 0,2-3
        succeededCount: 1
  template:
    spec:
      containers:
      - name: main
        image: python
        command:          # Provided that at least one of the Pods with 0, 2, and 3 indexes has succeeded,
                          # the overall Job is a success.
          - python3
          - -c
          - |
            import os, sys
            if os.environ.get("JOB_COMPLETION_INDEX") == "2":
              sys.exit(0)
            else:
              sys.exit(1)            
      restartPolicy: Never

在上面的示例中,succeededIndexessucceededCount 都已指定。因此,當指定的索引 0、2 或 3 中的任何一個成功時,Job 控制器將把 Job 標記為成功並終止其餘的 Pod。滿足成功策略的 Job 將獲得 SuccessCriteriaMet 條件,其 SuccessPolicy 原因是 SuccessPolicy。發出移除剩餘 Pod 的命令後,Job 將獲得 Complete 條件。

請注意,succeededIndexes 表示為由連字元分隔的區間。數字由序列的第一個和最後一個元素表示,並用連字元分隔。

Job 終止與清理

當 Job 完成後,不再建立 Pod,但 Pod 通常也不會被刪除。保留它們允許你仍然檢視已完成 Pod 的日誌,以檢查錯誤、警告或其他診斷輸出。Job 物件在完成後也仍然存在,以便你可以檢視其狀態。使用者有責任在記錄其狀態後刪除舊的 Job。使用 kubectl 刪除 Job(例如 kubectl delete jobs/pikubectl delete -f ./job.yaml)。當你使用 kubectl 刪除 Job 時,它建立的所有 Pod 也將被刪除。

預設情況下,Job 將不間斷執行,除非 Pod 失敗 (restartPolicy=Never) 或容器退出出錯 (restartPolicy=OnFailure),此時 Job 將遵循上述的 .spec.backoffLimit。一旦達到 .spec.backoffLimit,Job 將被標記為失敗,並且任何正在執行的 Pod 將被終止。

終止 Job 的另一種方法是設定活躍截止時間。透過將 Job 的 .spec.activeDeadlineSeconds 欄位設定為一個秒數來完成。activeDeadlineSeconds 適用於 Job 的持續時間,無論建立了多少 Pod。一旦 Job 達到 activeDeadlineSeconds,其所有正在執行的 Pod 都將被終止,並且 Job 狀態將變為 type: Failed,原因:reason: DeadlineExceeded

請注意,Job 的 .spec.activeDeadlineSeconds 優先於其 .spec.backoffLimit。因此,正在重試一個或多個失敗 Pod 的 Job 在達到 activeDeadlineSeconds 指定的時間限制後將不再部署額外的 Pod,即使 backoffLimit 尚未達到。

示例

apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-timeout
spec:
  backoffLimit: 5
  activeDeadlineSeconds: 100
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

請注意,Job 規約和 Job 內的 Pod 模板規約都具有 activeDeadlineSeconds 欄位。請確保在適當的級別設定此欄位。

請記住,restartPolicy 適用於 Pod,而不是 Job 本身:一旦 Job 狀態為 type: Failed,Job 就不會自動重啟。也就是說,透過 .spec.activeDeadlineSeconds.spec.backoffLimit 啟用的 Job 終止機制會導致永久性 Job 失敗,需要手動干預才能解決。

終端 Job 條件

一個 Job 有兩種可能的終止狀態,每種狀態都有一個對應的 Job 條件

  • 成功:Job 條件 Complete
  • 失敗:Job 條件 Failed

Job 失敗的原因如下

  • Pod 失敗次數超過了 Job 規約中指定的 .spec.backoffLimit。有關詳細資訊,請參閱 Pod 重試失敗策略
  • Job 執行時超過了指定的 .spec.activeDeadlineSeconds
  • 使用 .spec.backoffLimitPerIndex 的索引 Job 存在失敗索引。有關詳細資訊,請參閱 每個索引的退避限制
  • Job 中失敗索引的數量超過了指定的 spec.maxFailedIndexes。有關詳細資訊,請參閱 每個索引的退避限制
  • 失敗的 Pod 與 .spec.podFailurePolicy 中具有 FailJob 動作的規則匹配。有關 Pod 失敗策略規則如何影響失敗評估的詳細資訊,請參閱 Pod 失敗策略

Job 成功的原因如下

  • 成功 Pod 的數量達到了指定的 .spec.completions
  • 滿足 .spec.successPolicy 中指定的條件。有關詳細資訊,請參閱 成功策略

在 Kubernetes v1.31 及更高版本中,Job 控制器會延遲新增終止條件 FailedComplete,直到所有 Job Pods 終止。

在 Kubernetes v1.30 及更早版本中,Job 控制器會在 Job 終止過程觸發且所有 Pod 終結器被移除後立即新增 CompleteFailed Job 終止條件。但是,在新增終止條件時,某些 Pod 仍可能正在執行或正在終止。

在 Kubernetes v1.31 及更高版本中,控制器**只有在**所有 Pod 終止後才新增 Job 終止條件。你可以透過使用 JobManagedByJobPodReplacementPolicy(兩者預設啟用)特性門控來控制此行為。

Job Pod 的終止

當 Job 滿足成功或失敗條件後,Job 控制器會向 Job 新增 FailureTarget 條件或 SuccessCriteriaMet 條件,以觸發 Pod 終止。

諸如 terminationGracePeriodSeconds 之類的因素可能會增加從 Job 控制器新增 FailureTarget 條件或 SuccessCriteriaMet 條件到所有 Job Pod 終止且 Job 控制器新增 終止條件 (FailedComplete) 的時間量。

你可以使用 FailureTargetSuccessCriteriaMet 條件來評估 Job 是否失敗或成功,而無需等待控制器新增終止條件。

例如,你可能想決定何時建立替換失敗 Job 的 Job。如果你在 FailureTarget 條件出現時替換失敗 Job,你的替換 Job 會執行得更快,但這可能導致失敗 Job 和替換 Job 的 Pod 同時執行,從而使用額外的計算資源。

或者,如果你的叢集資源容量有限,你可以選擇等待 Failed 條件出現在 Job 上,這會延遲你的替換 Job,但會透過等待所有失敗的 Pod 都被移除來確保你節省資源。

自動清理已完成的 Job

系統中通常不再需要已完成的 Job。將其保留在系統中會給 API 伺服器帶來壓力。如果 Job 由更高級別的控制器(例如 CronJobs)直接管理,則 CronJobs 可以根據指定的基於容量的清理策略清理 Job。

已完成 Job 的 TTL 機制

特性狀態: Kubernetes v1.23 [穩定]

另一種自動清理已完成 Job (無論是 Complete 還是 Failed) 的方法是使用由 TTL 控制器提供的 TTL 機制,透過指定 Job 的 .spec.ttlSecondsAfterFinished 欄位。

當 TTL 控制器清理 Job 時,它將級聯刪除 Job,即連同 Job 一起刪除其依賴物件,例如 Pod。請注意,當 Job 被刪除時,其生命週期保證(例如 finalizers)將被遵守。

例如

apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-ttl
spec:
  ttlSecondsAfterFinished: 100
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

Job pi-with-ttl 在完成後 100 秒將被自動刪除。

如果此欄位設定為 0,則 Job 在完成後將立即被自動刪除。如果此欄位未設定,則此 Job 在完成後不會被 TTL 控制器清理。

Job 模式

Job 物件可用於處理一組獨立但相關的**工作項**。這些工作項可能是要傳送的電子郵件、要渲染的幀、要轉碼的檔案、要掃描的 NoSQL 資料庫中的鍵範圍等。

在一個複雜的系統中,可能存在多個不同的工作項集。這裡我們只考慮使用者希望一起管理的一個工作項集——一個**批處理作業**。

平行計算有幾種不同的模式,每種模式都有其優點和缺點。權衡是

  • 每個工作項一個 Job 物件,與所有工作項一個 Job 物件。每個工作項一個 Job 會給使用者和系統帶來一些管理大量 Job 物件的開銷。對於大量專案,所有工作項一個 Job 更好。
  • 建立的 Pod 數量等於工作項數量,與每個 Pod 可以處理多個工作項。當 Pod 數量等於工作項數量時,Pod 通常需要較少修改現有程式碼和容器。對於大量專案,每個 Pod 處理多個工作項更好。
  • 有幾種方法使用工作佇列。這需要執行一個佇列服務,並修改現有程式或容器以使其使用工作佇列。其他方法更容易適應現有的容器化應用程式。
  • 當 Job 與 無頭服務關聯時,你可以使 Job 中的 Pod 相互通訊以協作進行計算。

這裡總結了這些權衡,其中第 2 到 4 列對應於上述權衡。模式名稱也是示例和更詳細描述的連結。

模式單個 Job 物件Pod 數量少於工作項?使用未修改的應用程式?
佇列模式(每個工作項一個 Pod)有時
佇列模式(可變 Pod 數量)
帶靜態工作分配的索引 Job
帶 Pod-to-Pod 通訊的作業有時有時
Job 模板擴充套件

當你使用 .spec.completions 指定完成次數時,Job 控制器建立的每個 Pod 都具有相同的 spec。這意味著任務的所有 Pod 都將具有相同的命令列和相同的映象、相同的卷以及(幾乎)相同的環境變數。這些模式是安排 Pod 完成不同工作的不同方式。

此表顯示了每種模式的 .spec.parallelism.spec.completions 所需設定。其中,W 是工作項的數量。

模式.spec.completions.spec.parallelism
佇列模式(每個工作項一個 Pod)W任何
佇列模式(可變 Pod 數量)任何
帶靜態工作分配的索引 JobW任何
帶 Pod-to-Pod 通訊的作業WW
Job 模板擴充套件1應該為 1

高階用法

掛起 Job

特性狀態: Kubernetes v1.24 [stable]

建立 Job 時,Job 控制器會立即開始建立 Pod 以滿足 Job 的要求,並會持續這樣做,直到 Job 完成。但是,你可能希望暫時掛起 Job 的執行並在以後恢復它,或者以掛起狀態啟動 Job,並讓自定義控制器稍後決定何時啟動它們。

要掛起 Job,可以將 Job 的 .spec.suspend 欄位更新為 true;稍後,當你想要再次恢復它時,將其更新為 false。將 .spec.suspend 設定為 true 建立 Job 會使其處於掛起狀態。

當 Job 從掛起狀態恢復時,其 .status.startTime 欄位將被重置為當前時間。這意味著當 Job 掛起和恢復時,.spec.activeDeadlineSeconds 計時器將停止並重置。

當你掛起一個 Job 時,所有沒有 Completed 狀態的正在執行的 Pod 都將以 SIGTERM 訊號終止。Pod 的優雅終止期將被遵守,你的 Pod 必須在此期間處理此訊號。這可能涉及儲存進度以供以後使用或撤消更改。以這種方式終止的 Pod 將不計入 Job 的 completions 計數。

處於掛起狀態的 Job 定義示例可以是這樣

kubectl get job myjob -o yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: myjob
spec:
  suspend: true
  parallelism: 1
  completions: 5
  template:
    spec:
      ...

你還可以透過使用命令列修補 Job 來切換 Job 的掛起狀態。

掛起活躍 Job

kubectl patch job/myjob --type=strategic --patch '{"spec":{"suspend":true}}'

恢復掛起的 Job

kubectl patch job/myjob --type=strategic --patch '{"spec":{"suspend":false}}'

Job 的狀態可用於確定 Job 是否已掛起或過去是否曾掛起

kubectl get jobs/myjob -o yaml
apiVersion: batch/v1
kind: Job
# .metadata and .spec omitted
status:
  conditions:
  - lastProbeTime: "2021-02-05T13:14:33Z"
    lastTransitionTime: "2021-02-05T13:14:33Z"
    status: "True"
    type: Suspended
  startTime: "2021-02-05T13:13:48Z"

型別為 "Suspended" 且狀態為 "True" 的 Job 條件表示 Job 已掛起;lastTransitionTime 欄位可用於確定 Job 已掛起多長時間。如果該條件的狀態為 "False",則表示 Job 之前已掛起,現在正在執行。如果 Job 的狀態中不存在此類條件,則 Job 從未停止過。

當 Job 掛起和恢復時,也會建立事件

kubectl describe jobs/myjob
Name:           myjob
...
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  12m   job-controller  Created pod: myjob-hlrpl
  Normal  SuccessfulDelete  11m   job-controller  Deleted pod: myjob-hlrpl
  Normal  Suspended         11m   job-controller  Job suspended
  Normal  SuccessfulCreate  3s    job-controller  Created pod: myjob-jvb44
  Normal  Resumed           3s    job-controller  Job resumed

最後四個事件,特別是 "Suspended" 和 "Resumed" 事件,直接是切換 .spec.suspend 欄位的結果。在這兩個事件之間,我們看到沒有 Pod 被建立,但一旦 Job 恢復,Pod 建立立即重新開始。

可變排程指令

特性狀態: Kubernetes v1.27 [穩定]

在大多數情況下,並行作業會希望 Pod 在受到限制的情況下執行,例如都在同一個區域,或都只在 GPU 模型 x 或 y 上,而不是兩者混合。

掛起欄位是實現這些語義的第一步。掛起允許自定義佇列控制器決定何時啟動作業;然而,一旦作業被取消掛起,自定義佇列控制器對作業的 Pod 將實際落在何處沒有影響。

此功能允許在 Job 啟動之前更新其排程指令,這使得自定義佇列控制器能夠影響 Pod 的放置,同時將實際的 Pod 到節點分配解除安裝給 kube-scheduler。這隻允許用於以前從未取消掛起過的掛起 Job。

Job 的 Pod 模板中可以更新的欄位包括節點親和性、節點選擇器、容忍度、標籤、註解和排程門

指定你自己的 Pod 選擇器

通常,當你建立 Job 物件時,你不會指定 .spec.selector。系統預設邏輯會在建立 Job 時新增此欄位。它會選擇一個不會與其他任何 Job 重疊的選擇器值。

但是,在某些情況下,你可能需要覆蓋此自動設定的選擇器。為此,你可以指定 Job 的 .spec.selector

請務必小心。如果你指定的標籤選擇器不是該 Job 的 Pod 唯一的,並且匹配了不相關的 Pod,那麼不相關 Job 的 Pod 可能會被刪除,或者該 Job 可能會將其他 Pod 計為完成,或者一個或兩個 Job 可能拒絕建立 Pod 或執行到完成。如果選擇了非唯一的選擇器,那麼其他控制器(例如 ReplicationController)及其 Pod 也可能以不可預測的方式執行。Kubernetes 不會在你指定 .spec.selector 時阻止你犯錯。

以下是一個你可能希望使用此功能的示例。

假設 Job old 已經在執行。你希望現有 Pod 繼續執行,但你希望它建立的其餘 Pod 使用不同的 Pod 模板,並且 Job 具有新的名稱。你無法更新 Job,因為這些欄位不可更新。因此,你刪除 Job old,但**使其 Pod 繼續執行**,使用 kubectl delete jobs/old --cascade=orphan。在刪除它之前,你記錄它使用的選擇器

kubectl get job old -o yaml

輸出類似於:

kind: Job
metadata:
  name: old
  ...
spec:
  selector:
    matchLabels:
      batch.kubernetes.io/controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
  ...

然後你建立一個名為 new 的新 Job,並顯式指定相同的選擇器。由於現有 Pod 具有標籤 batch.kubernetes.io/controller-uid=a8f3d00d-c6d2-11e5-9f87-42010af00002,它們也由 Job new 控制。

你需要在新 Job 中指定 manualSelector: true,因為你沒有使用系統通常自動為你生成的選擇器。

kind: Job
metadata:
  name: new
  ...
spec:
  manualSelector: true
  selector:
    matchLabels:
      batch.kubernetes.io/controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
  ...

新 Job 本身將擁有與 a8f3d00d-c6d2-11e5-9f87-42010af00002 不同的 UID。設定 manualSelector: true 會告訴系統你知道自己在做什麼,並允許這種不匹配。

使用終結器跟蹤 Job

特性狀態: Kubernetes v1.26 [穩定]

控制平面會跟蹤屬於任何 Job 的 Pod,並注意是否有任何此類 Pod 從 API 伺服器中移除。為此,Job 控制器會建立帶有終結器 batch.kubernetes.io/job-tracking 的 Pod。控制器僅在 Pod 在 Job 狀態中被記錄後才移除終結器,從而允許其他控制器或使用者移除 Pod。

彈性索引 Job

特性狀態: Kubernetes v1.31 [stable] (預設啟用:true)

你可以透過同時修改 .spec.parallelism.spec.completions 來擴充套件或收縮 Indexed Job,使得 .spec.parallelism == .spec.completions。在縮容時,Kubernetes 會移除索引較高的 Pod。

彈性索引 Job 的用例包括需要擴縮索引 Job 的批處理工作負載,例如 MPI、Horovod、Ray 和 PyTorch 訓練作業。

延遲建立替換 Pod

特性狀態: Kubernetes v1.34 [穩定] (預設啟用:true)

預設情況下,Job 控制器會在 Pod 失敗或終止(有刪除時間戳)後立即重新建立 Pod。這意味著,在給定時間,當某些 Pod 正在終止時,Job 的執行中 Pod 數量可能大於 parallelism 或大於每個索引一個 Pod(如果你使用的是索引 Job)。

你可以選擇僅在終止中的 Pod 完全終止(具有 status.phase: Failed)時才建立替換 Pod。為此,請設定 .spec.podReplacementPolicy: Failed。預設替換策略取決於 Job 是否設定了 podFailurePolicy。如果 Job 沒有定義 Pod 失敗策略,則省略 podReplacementPolicy 欄位會選擇 TerminatingOrFailed 替換策略:控制平面會在 Pod 刪除後立即建立替換 Pod(一旦控制平面看到此 Job 的 Pod 設定了 deletionTimestamp)。對於設定了 Pod 失敗策略的 Job,預設的 podReplacementPolicyFailed,不允許其他值。請參閱 Pod 失敗策略以瞭解更多關於 Job 的 Pod 失敗策略的資訊。

kind: Job
metadata:
  name: new
  ...
spec:
  podReplacementPolicy: Failed
  ...

如果你的叢集啟用了特性門控,你可以檢查 Job 的 .status.terminating 欄位。該欄位的值是 Job 當前正在終止的 Pod 數量。

kubectl get jobs/myjob -o yaml
apiVersion: batch/v1
kind: Job
# .metadata and .spec omitted
status:
  terminating: 3 # three Pods are terminating and have not yet reached the Failed phase

將 Job 物件的管理委託給外部控制器

特性狀態: Kubernetes v1.32 [beta] (預設啟用:true)

此功能允許你針對特定 Job 停用內建 Job 控制器,並將 Job 的協調委託給外部控制器。

你可以透過為 spec.managedBy 欄位設定自定義值來指示協調 Job 的控制器——任何值,除了 kubernetes.io/job-controller。該欄位的值是不可變的。

替代方案

裸 Pod

當 Pod 執行的節點重啟或發生故障時,該 Pod 將被終止且不會重新啟動。但是,Job 將建立新的 Pod 來替換已終止的 Pod。因此,我們建議您使用 Job 而不是裸 Pod,即使您的應用程式只需要一個 Pod。

副本控制器

Job 與副本控制器互補。副本控制器管理不期望終止的 Pod(例如 Web 伺服器),而 Job 管理期望終止的 Pod(例如批處理任務)。

Pod 生命週期中所述,Job 適用於 RestartPolicy 等於 OnFailureNever 的 Pod。(注意:如果未設定 RestartPolicy,預設值為 Always。)

單個 Job 啟動控制器 Pod

另一種模式是單個 Job 建立一個 Pod,然後該 Pod 建立其他 Pod,充當這些 Pod 的一種自定義控制器。這提供了最大的靈活性,但可能在入門時有些複雜,並且與 Kubernetes 的整合度較低。

此模式的一個示例是,一個 Job 啟動一個 Pod,該 Pod 執行一個指令碼,該指令碼又啟動一個 Spark 主控制器(參見Spark 示例),執行一個 Spark 驅動程式,然後進行清理。

這種方法的優點是整個過程獲得了 Job 物件的完成保證,同時保持對建立哪些 Pod 以及如何將工作分配給它們的完全控制。

下一步

上次修改時間:2025 年 6 月 29 日上午 8:26 PST:[KEP-3939] - 將 JobPodReplacementPolicy 提升為 GA (bf232870d9)