Kubernetes 提供了 Job 和 CronJob 兩種資源,分別用於管理一次性任務和週期性任務。相較於裸 Pod、ReplicaSet 和 DaemonSet,Job 更適合處理需可靠執行且在完成後終止的任務。Job 的特性包含任務永續性、狀態追蹤、可控的完成次數和平行度,以及暫停、還原和容錯機制。理解 Job 的不同型別,如固定完成次數、工作佇列和索引 Job,能更好地根據實際需求選擇合適的執行方式。進一步地,CronJob 建立在 Job 基礎上,透過 cron 格式定義排程規則,實作週期性任務的自動化管理,並提供更精細的控制選項,例如錯過排程的處理策略和平行控制。

第7章:批次作業(Batch Job)

批次作業模式適用於管理獨立的原子工作單元,能夠在分散式環境中可靠地執行至完成。本章根據Kubernetes的Job資源進行探討。

問題描述

在Kubernetes中,Pod是管理與執行容器的基本單元。然而,不同型別的Pod具有不同的特性:

  • 裸Pod(Bare Pod):可手動建立Pod,但當執行Pod的節點故障時,Pod不會被重新啟動。這種方式主要用於開發或測試,不建議用於生產環境。
  • ReplicaSet:用於建立和管理預期持續執行的Pod,例如執行網頁伺服器容器。它保證特定數量的相同Pod在任何時候都處於執行狀態。詳細內容請參考第11章「無狀態服務(Stateless Service)」。
  • DaemonSet:在每個節點上執行一個Pod,用於管理平台功能,如監控、記錄彙總、儲存容器等。有關詳細資訊,請參閱第9章「守護程式服務(Daemon Service)」。

這些Pod的共同特點是代表長期執行的程式,不會在特定時間後停止。然而,在某些情況下,需要可靠地執行預定義的有限工作單元,然後關閉容器。為此,Kubernetes提供了Job資源。

解決方案

Kubernetes Job與ReplicaSet相似,都會建立一個或多個Pod並確保其成功執行。然而,Job與ReplicaSet的不同之處在於,當預期的Pod成功終止後,Job即視為完成,不會再啟動新的Pod。

Job定義範例

apiVersion: batch/v1
kind: Job
metadata:
  name: random-generator
spec:
  completions: 5
  parallelism: 2
  ttlSecondsAfterFinished: 300
  template:
    metadata:
      name: random-generator
    spec:
      restartPolicy: OnFailure
      containers:
      - image: k8spatterns/random-generator:1.0
        name: random-generator
        command: [ "java", "RandomRunner", "/numbers.txt", "10000" ]

#### 內容解密:

  • completions: 5:指定Job需要成功執行的Pod數量為5。
  • parallelism: 2:允許同時平行執行的Pod數量為2。
  • ttlSecondsAfterFinished: 300:Job完成後,保留Pod的時間為300秒(5分鐘),之後將被垃圾回收。
  • restartPolicy: OnFailure:指定當Pod失敗時的重啟策略為「失敗時重啟」。
  • containers部分定義了執行Job的容器細節,包括使用的映像檔、名稱和執行的命令。

相較於ReplicaSet,Job定義中的.spec.template.spec.restartPolicy至關重要。對於Job而言,唯一允許的值是OnFailureNever,而非預設的Always

為何使用Job?

使用Job而非裸Pod有許多可靠性和可擴充套件性的優勢:

  1. 永續性:Job是一種持久化的任務,能夠在叢集重啟後繼續存在。
  2. 跟蹤完成情況:Job完成後不會被刪除,以便跟蹤其狀態。所建立的Pod同樣保留,用於檢查容器日誌等。
  3. 多次執行:透過.spec.completions欄位,可以指定一個Pod需要成功完成的次數。
  4. 平行執行:透過.spec.parallelism欄位,可以指定同時執行的Pod數量,以實作平行處理。
  5. 暫停與還原:可以透過設定.spec.suspend欄位來暫停或還原Job。
  6. 容錯性:若節點故障或Pod被驅逐,Job會在新的健康節點上重新排程和執行Pod。

Job型別

根據.spec.completions.spec.parallelism的不同設定,Job可以分為以下幾種型別:

  • 單一Pod Job:當.spec.completions.spec.parallelism均未設定或設為1時,Job只會啟動一個Pod,並在該Pod成功終止後視為完成。

圖示說明

圖7-1展示了一個具有5個完成次數和2個平行度的Job執行過程。

此圖示清晰地展示了具有固定完成次數的平行批次作業的處理流程,能夠幫助讀者更好地理解Job的工作機制。

Kubernetes Job 資源管理與工作分配詳解

Kubernetes 中的 Job 資源是一種用於執行一次性任務的控制器,它能夠確保指定的 Pod 完成任務後終止。Job 可以用於多種場景,例如批次處理、資料處理、影片處理等。本文將詳細介紹 Kubernetes Job 的型別、工作分配方式以及相關的最佳實踐。

Job 的型別

Kubernetes 中的 Job 主要分為三種型別:固定完成次數的 Job、工作佇列 Job 和索引 Job。

固定完成次數的 Job

固定完成次數的 Job 需要指定 .spec.completions 欄位,表示需要完成的任務數量。可以設定 .spec.parallelism 欄位來控制平行執行的 Pod 數量,如果未設定,則預設為 1。這種 Job 在 .spec.completions 數量的 Pod 成功完成後被視為完成。

程式碼範例:固定完成次數的 Job

apiVersion: batch/v1
kind: Job
metadata:
  name: fixed-completion-job
spec:
  completions: 5
  parallelism: 2
  template:
    spec:
      containers:
      - name: job-container
        image: busybox
        command: ["echo", "Hello Kubernetes Job!"]
      restartPolicy: OnFailure

內容解密:

  1. apiVersion 和 kind:定義了 Kubernetes 資源的 API 版本和型別,這裡使用的是 batch/v1 版本的 Job。
  2. metadata:包含了 Job 的後設資料,如名稱。
  3. spec:定義了 Job 的規格,包括完成次數(completions)、平行度(parallelism)和 Pod 範本(template)。
  4. template.spec:定義了 Pod 的規格,包括容器映像、命令和重啟策略。

工作佇列 Job

工作佇列 Job 不需要指定 .spec.completions,而是透過 .spec.parallelism 控制平行執行的 Pod 數量。這種 Job 在至少一個 Pod 成功終止且所有其他 Pod 終止後被視為完成。工作佇列 Job 需要 Pod 之間的協調,以確定每個 Pod 的工作內容。

程式碼範例:工作佇列 Job

apiVersion: batch/v1
kind: Job
metadata:
  name: work-queue-job
spec:
  parallelism: 3
  template:
    spec:
      containers:
      - name: job-container
        image: busybox
        command: ["sh", "-c", "echo Processing work item; sleep 5; echo Done"]
      restartPolicy: OnFailure

內容解密:

  1. 平行度設定:透過 .spec.parallelism 控制平行執行的 Pod 數量。
  2. 容器命令:模擬處理工作專案的過程,包括處理和等待。
  3. 重啟策略:設定為 OnFailure,表示在失敗時重啟 Pod。

索引 Job

索引 Job 是透過設定 .spec.completionModeIndexed 來實作的,每個 Pod 會獲得一個與之關聯的索引,範圍從 0 到 .spec.completions - 1。這個索引可以透過環境變數 JOB_COMPLETION_INDEX 取得,應用程式可以使用這個索引來確定自己的工作內容。

程式碼範例:索引 Job

apiVersion: batch/v1
kind: Job
metadata:
  name: indexed-job
spec:
  completionMode: Indexed
  completions: 5
  parallelism: 5
  template:
    spec:
      containers:
      - name: job-container
        image: alpine
        command: ["sh", "-c", "start=$(expr $JOB_COMPLETION_INDEX * 10000); end=$(expr $JOB_COMPLETION_INDEX * 10000 + 10000); awk \"NR>=$start && NR<$end\" /logs/random.log > /logs/random-$JOB_COMPLETION_INDEX.txt"]
        volumeMounts:
        - name: log-volume
          mountPath: /logs
      restartPolicy: OnFailure

內容解密:

  1. completionMode:設定為 Indexed,表示這是一個索引 Job。
  2. completions 和 parallelism:設定完成次數和平行度。
  3. 容器命令:使用 JOB_COMPLETION_INDEX 環境變數計算起始和結束行號,並使用 awk 命令處理檔案。

工作分配

在 Kubernetes 中,可以透過多種方式分配工作給不同的 Pod。對於工作佇列 Job,需要一個外部系統來提供工作項。而對於索引 Job,則可以透過索引來分配工作。

最佳實踐

  1. 選擇合適的 Job 型別:根據具體需求選擇固定完成次數的 Job、工作佇列 Job 或索引 Job。
  2. 合理設定平行度:根據叢集資源和任務需求,合理設定 .spec.parallelism
  3. 使用索引 Job 處理靜態工作分配:當工作可以被靜態分配時,使用索引 Job 可以簡化實作。

週期性任務(Periodic Job)模式詳解

問題背景

在分散式系統和微服務架構的世界裡,應用程式互動正逐漸朝向即時和事件驅動的方向發展,主要透過HTTP和輕量級訊息傳遞來實作。然而,儘管軟體開發趨勢不斷演進,任務排程的需求依然存在,且具有悠久的歷史。週期性任務(Periodic Job)被廣泛應用於自動化系統維護、行政任務,以及需要定期執行的業務應用中。典型的例子包括企業間透過檔案傳輸進行整合、透過資料函式庫輪詢進行應用程式整合、傳送電子報郵件,以及清理和歸檔舊檔案等任務。

傳統上,處理週期性任務的方式是使用專門的排程軟體或cron。然而,專門的軟體對於簡單的應用場景可能過於昂貴,而執行在單一伺服器上的cron任務則難以維護,並且存在單點故障的問題。因此,開發者往往傾向於自行實作解決方案,以同時處理排程和業務邏輯的需求。例如,在Java生態系中,開發者可能會使用Quartz、Spring Batch等函式庫,或是利用ScheduledThreadPoolExecutor類別來執行定時任務。然而,這種做法的主要挑戰在於如何使排程功能具備彈性和高用性,這通常需要耗費大量的資源。此外,這種方法還要求整個應用程式具備高用性,通常需要執行多個應使用案例項,並確保只有一個例項處於活躍狀態並執行排程任務,這涉及到長官者選舉(leader election)和其他分散式系統的挑戰。

最終,一個簡單的服務,如每天複製幾個檔案的任務,可能會需要多個節點、分散式長官者選舉機制等複雜的架構。Kubernetes的CronJob實作有效地解決了這些問題,它允許使用熟悉的cron格式來排程Job資源,讓開發者能夠專注於實作需要執行的任務,而無需關心定時排程的實作細節。

解決方案

在第7章「批次任務(Batch Job)」中,我們探討了Kubernetes Job的使用案例和功能。這些內容同樣適用於本章,因為CronJob是建立在Job之上的。CronJob例項類別似於Unix crontab(cron表格)中的一行,用於管理Job的時間相關方面。它允許在指定的時間點週期性地執行Job。範例8-1展示了一個CronJob資源的定義。

範例8-1:CronJob資源定義

apiVersion: batch/v1
kind: CronJob
metadata:
  name: random-generator
spec:
  schedule: "*/3 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - image: k8spatterns/random-generator:1.0
            name: random-generator
            command: [ "java", "RandomRunner", "/numbers.txt", "10000" ]
          restartPolicy: OnFailure

內容解密:

  1. apiVersionkind 定義了 Kubernetes 資源的版本和型別,這裡是 CronJob
  2. metadata.name 指定了 CronJob 的名稱。
  3. spec.schedule 使用 cron 格式定義了 Job 的執行排程,這裡設定為每3分鐘執行一次。
  4. jobTemplate.spec 定義了要執行的 Job 的範本,包括容器映像檔、命令等,與普通的 Job 定義相同。
  5. restartPolicy 設定為 OnFailure,表示當容器失敗時會重啟。

除了Job規格之外,CronJob還有額外的欄位來定義其時間相關的屬性:

  • .spec.schedule:用於指定Job的排程規則,例如每小時執行一次的0 * * * *。也支援像@daily@hourly這樣的簡寫。
  • .spec.startingDeadlineSeconds:用於指定Job錯過排程時間後開始執行的截止時間(以秒為單位)。如果因資源不足或其他依賴缺失導致Job無法在預定時間執行,可以設定此欄位來決定是否跳過該次執行。
  • .spec.concurrencyPolicy:定義瞭如何管理由同一個CronJob建立的多個Job之間的平行執行。預設行為是允許平行執行,但也可以設定為禁止平行執行或取消當前正在執行的Job並啟動新的Job。
  • .spec.suspend:用於暫停後續的Job執行,但不會影響已經開始執行的Job。
  • .spec.successfulJobsHistoryLimit.spec.failedJobsHistoryLimit:用於指定保留多少成功和失敗的Job記錄,以便於稽核。

討論

CronJob是一個非常專門化的Kubernetes資源,它適用於具有時間維度的任務。儘管它不是一個通用型的資源,但它很好地展示了Kubernetes如何將不同的功能組合起來,以支援非雲原生(cloud-native)的應用場景。當CronJob與其他Kubernetes資源(如Pod、容器資源隔離等)結合使用時,它能夠成為一個非常強大的任務排程系統,使開發者能夠專注於業務邏輯的實作,而無需擔心排程本身的實作。

此圖示說明瞭 CronJob 與其他 Kubernetes 元件之間的關係:

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Kubernetes 批次與週期任務管理

package "Kubernetes Cluster" {
    package "Control Plane" {
        component [API Server] as api
        component [Controller Manager] as cm
        component [Scheduler] as sched
        database [etcd] as etcd
    }

    package "Worker Nodes" {
        component [Kubelet] as kubelet
        component [Kube-proxy] as proxy
        package "Pods" {
            component [Container 1] as c1
            component [Container 2] as c2
        }
    }
}

api --> etcd : 儲存狀態
api --> cm : 控制迴圈
api --> sched : 調度決策
api --> kubelet : 指令下達
kubelet --> c1
kubelet --> c2
proxy --> c1 : 網路代理
proxy --> c2

note right of api
  核心 API 入口
  所有操作經由此處
end note

@enduml

內容解密:

  1. Kubernetes CronJob 管理 Job 的排程。
  2. Job 定義了要執行的任務,並由 Kubernetes 建立 Pod 來執行這些任務。
  3. Pod 中包含了容器(Container),實際執行業務邏輯。

更多資訊