K8s Job 與 CronJob:批次工作、排程與 YAML 層級
K8s Job 與 CronJob:批次工作、排程與 YAML 層級的重點整理。
K8s Job 與 CronJob:批次工作、排程與 YAML 層級
TL;DR
Job 確保工作成功完成;CronJob 按時間建立 Job;Job 再建立 Pod 執行。 適合批次處理、報表與資料庫 migration 等工作,不是只能用在少數特殊情境。
| 資源 | 目的 | 例子 |
|---|---|---|
| Deployment | 維持應用持續運行 | 網站、API |
| Job | 達到指定成功完成條件後結束 | 資料處理、migration |
| CronJob | 按排程建立新的 Job | 每天晚上產生報表 |
flowchart TD
A[CronJob:按時間觸發]
A --> B[建立 Job]
B --> C[建立 Pod 執行工作]
C --> D{工作結果}
D -->|成功| E[累積成功數,滿足條件則 Job 完成]
D -->|失敗| F[依重試設定處理,超過限制則失敗]
Job 不保證程式只執行一次;任務需考慮重試、重複執行與並行,避免重複扣款或重複寫入等副作用。
Job:完成數、並行與失敗重試
以下先看一般固定完成數、非 Indexed 的 Job:
| 欄位(Job spec 下) | 意義 | 例子 |
|---|---|---|
| completions | 需要成功完成的 Pod 數量;失敗不算成功 | 5:累積 5 個成功 |
| parallelism | 希望同時執行的 Pod 數量,實際數量受剩餘工作與叢集狀態影響 | 2:通常同時跑 2 個 |
| backoffLimit | 判定 Job 失敗的重試限制,不是成功目標,也不是總 Pod 數 | 3:有限次重試 |
| activeDeadlineSeconds | Job 執行時間上限;不是 CronJob 的排程遲到期限 | 超時可使 Job 失敗 |
| ttlSecondsAfterFinished | Job 成功或失敗結束後多久可自動清理 | 60:結束後等待 60 秒清理 Job 及其 Pod |
- completions 與 parallelism 都不指定的一般 Job,兩者預設為 1。
- 一般 backoffLimit 預設為 6;進階 per-index 設定有不同預設與計數方式。
- backoffLimit 是 Job 的整體重試限制,不是 completions 中每個成功名額各有一份額度。
- 重試計數會看失敗 Pod;使用 OnFailure 時,執行中 Pod 的容器重啟次數也參與判斷。不要把
backoffLimit: 3直接換算成一定建立 3 個 Pod。 - 失敗後會退避等待再重試,不是沒有間隔地一直重跑;更進階的 podFailurePolicy 可改變失敗處理。
| Pod template 的 restartPolicy | 行為 |
|---|---|
| Never | kubelet 不在原 Pod 重啟失敗容器;Job 可在限制內建立替代 Pod |
| OnFailure | kubelet 可在原 Pod 重啟失敗容器;重啟次數也可能觸及 Job 失敗限制 |
| Always | Job 不允許使用 |
Job YAML:把 Pod spec 放進 template.spec
apiVersion: batch/v1
kind: Job
metadata:
name: hello
spec:
completions: 5
parallelism: 2
backoffLimit: 3
template:
spec:
containers:
- name: worker
image: busybox:1.36
command: ["sh", "-c", "echo done"]
restartPolicy: Never
這個範例會重複執行相同命令以累積 5 個成功;不會自動把輸入資料拆成 5 份不同任務。
| 原 Pod 位置 | Job 位置 |
|---|---|
| spec.containers | spec.template.spec.containers |
| spec.volumes | spec.template.spec.volumes |
| spec.restartPolicy | spec.template.spec.restartPolicy,須用 Never/OnFailure |
| metadata.labels | 可放到 spec.template.metadata.labels,作為建立出來的 Pod labels |
不是只改 kind:還要改成 apiVersion: batch/v1,並增加 template 層。
CronJob YAML:CronJob 包 Job,Job 包 Pod
apiVersion: batch/v1
kind: CronJob
metadata:
name: throw-dice-cron-job
spec:
schedule: "30 21 * * *"
timeZone: Asia/Taipei
concurrencyPolicy: Forbid
jobTemplate:
spec:
completions: 1
backoffLimit: 3
template:
spec:
containers:
- name: worker
image: busybox:1.36
command: ["sh", "-c", "echo done"]
restartPolicy: Never
此範例每天台北時間 21:30 執行;name 只是物件名稱,命令是簡單示意。要改成擲骰任務,替換實際 image/command。
| 層級 | 放什麼 |
|---|---|
| spec | CronJob 的 schedule、timeZone、concurrencyPolicy 等 |
| spec.jobTemplate.spec | 原本 Job 的 spec,例如 completions、parallelism、backoffLimit、ttlSecondsAfterFinished |
| spec.jobTemplate.spec.template.spec | 原本 Pod 的 spec,例如 containers、volumes、restartPolicy |
沿用 Job 的 spec 時放進 jobTemplate.spec;不要把 Job 的 apiVersion、kind 一起塞進去。
排程:分鐘、小時、日期、月份、星期
| 欄位 | 範圍 |
|---|---|
| 分鐘 | 0~59 |
| 小時 | 0~23 |
| 日期(每月幾號) | 1~31 |
| 月份 | 1~12 |
| 星期 | 0~6:星期日~星期六 |
| schedule | 意義 |
|---|---|
"* * * * *" |
每分鐘 |
"*/5 * * * *" |
每 5 分鐘 |
"30 21 * * *" |
每天 21:30 |
"30 21 * * 0" |
每週日 21:30 |
"30 21 1 * *" |
每月 1 日 21:30 |
時間依 spec.timeZone 解讀,例如 Asia/Taipei;未指定則依 kube-controller-manager 的本機時區,不會自動沿用你操作電腦的時區。名稱是 cron/CronJob,不是 corn。
| CronJob 欄位 | 意義 |
|---|---|
| concurrencyPolicy: Allow | 預設;同一 CronJob 的前後 Job 可同時執行 |
| concurrencyPolicy: Forbid | 上次尚未結束時,不啟動重疊的新一輪;是否可稍後補啟動還受 deadline 等規則影響 |
| concurrencyPolicy: Replace | 到下一輪時,替換尚未結束的舊 Job |
| suspend: true | 暫停後續排程,不終止已開始的 Job |
| successfulJobsHistoryLimit/failedJobsHistoryLimit | 保留成功/失敗歷史 Job 的數量,預設為 3/1 |
完成不等於刪除
| 情況 | 結果 |
|---|---|
| 一般 Job 完成、沒設自動清理 | 容器停止;Job 與已完成 Pod 通常保留供查詢與看 logs |
| 設 ttlSecondsAfterFinished | 成功或失敗結束後,等待 TTL 再清理 Job 與其 Pod |
| 刪除 Job | 一般連帶刪除管理的 Pod;特殊刪除傳播設定另論 |
| CronJob 的歷史保留上限 | 清理超過保留數量的歷史 Jobs,與單一 Job 的 TTL 是不同設定 |
常用指令與排錯
| 用途 | 指令 |
|---|---|
| 建立 Job | kubectl create job hello --image=busybox:1.36 -- echo Hello |
| 產生 Job YAML | kubectl create job hello --image=busybox:1.36 --dry-run=client -o yaml -- echo Hello |
| 產生 CronJob YAML | kubectl create cronjob hello --image=busybox:1.36 --schedule='30 21 * * *' --dry-run=client -o yaml -- echo Hello |
| 查狀態 | kubectl get jobs,cronjobs,pods |
| 找 Job 的 Pod | kubectl get pods -l batch.kubernetes.io/job-name=hello |
| 看輸出/事件 | kubectl logs job/hello/kubectl describe job hello |
| 等待成功 | kubectl wait --for=condition=complete job/hello --timeout=120s;失敗不會符合 Complete |
| 手動觸發某個 CronJob | kubectl create job manual-run --from=cronjob/throw-dice-cron-job |
| 刪除 Job | kubectl delete job hello |
| 驗證/套用檔案 | kubectl apply --dry-run=server -f file.yaml → kubectl apply -f file.yaml |
上表是不同情境,擇一操作;例如同名 hello 不能同時是建立後又再次 create 的新 Job。平行多 Pod 時 logs 查詢只取某個 Pod,需完整查看時逐一指定 Pod/容器。
| 錯誤 | 修正方式 |
|---|---|
| no matches for kind cronjobs | kind 要寫 CronJob,不是命令列資源複數;不需要安裝 CRD |
| unknown field spec.jobtemplate | 改為 jobTemplate,T 大寫 |
| unknown field …template.containers | template 下少了 spec;containers 放在 template.spec 下 |
| unknown field …template.restartPolicy | 同樣放回 template.spec.restartPolicy |
當場查欄位:kubectl explain job.spec、kubectl explain cronjob.spec.jobTemplate.spec.template.spec。忘記層級時查 explain,不要只改 kind 或靠猜縮排。
官方文件與關鍵詞
- Jobs:
completions、parallelism、pod backoff failure policy、ttlSecondsAfterFinished。 - CronJob:
jobTemplate、schedule syntax、timeZone、concurrencyPolicy。 - 相關:K8s 中的 Pod 是什麼、K8s kubectl 常用指令。