Job 与 CronJob 任务管理
长期服务使用 Deployment,完成后退出的工作使用 Job,按计划反复执行的工作使用 CronJob。选错对象会造成任务重复执行、永不重试或不必要的常驻资源。
本篇目标:能写出幂等的 Job、配置合理的重试与清理策略,掌握 CronJob 的并发控制和时区设置,并理解孤儿 Pod 的产生与回收。
1. Job:一次性 / 批处理任务与重试策略
Job 创建 Pod 执行任务,直到达到成功次数或超过失败重试上限:
apiVersion: batch/v1
kind: Job
metadata:
name: schema-migrate
namespace: learning
spec:
backoffLimit: 3
ttlSecondsAfterFinished: 86400
template:
spec:
restartPolicy: OnFailure
containers:
- name: migrate
image: registry.example.com/app:1.4.2
command: ["./bin/migrate"]
1.1 Job 的关键参数
| 参数 | 作用 | 建议 |
|---|---|---|
backoffLimit | 失败重试次数上限(默认 6) | 迁移类设 3,幂等重试设更高 |
completions | 需要成功完成的 Pod 次数(默认 1) | 批量任务设 >1 |
parallelism | 同时运行的最大 Pod 数(默认 1) | 独立子任务可调大加速 |
activeDeadlineSeconds | Job 的总超时,超时后强制终止 | 防止失控任务一直跑 |
ttlSecondsAfterFinished | 完成后多少秒自动清理 Job 和 Pod | 生产必须设,否则历史 Job 堆积 |
1.2 并行 Job 与 Indexed Job
spec:
completions: 10 # 总共需要 10 次成功
parallelism: 3 # 同时跑 3 个 Pod
backoffLimit: 2
template:
spec:
restartPolicy: OnFailure
containers:
- name: worker
image: registry.example.com/worker:2.1.0
设置 completionMode: Indexed 后,Job 会给每个 Pod 注入环境变量 JOB_COMPLETION_INDEX(从 0 开始),Pod 据此知道自己负责的分片编号,适合把一批固定数量的数据项切分后并行处理:
spec:
completions: 4 # 数据共分成 4 片
parallelism: 2 # 同时运行 2 个 Pod
completionMode: Indexed
template:
spec:
restartPolicy: OnFailure
containers:
- name: shard
image: registry.example.com/shard-worker:1.0.0
# 容器内读取环境变量 JOB_COMPLETION_INDEX,判断自己处理第几片数据
1.3 幂等性:任务可能被执行多次
数据库迁移、数据导入这类任务必须具备幂等性:
- 迁移脚本用
IF NOT EXISTS/CREATE TABLE IF NOT EXISTS; - 备份任务按时间戳写入,避免覆盖已有备份;
- 不假设"上次成功、这次不需要跑"——Job 重试或人工重新创建时,任务逻辑可能再次执行。
2. CronJob:定时任务
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-backup
namespace: learning
spec:
schedule: "0 2 * * *"
timeZone: "Asia/Shanghai" # 1.24 GA,指定时区
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
startingDeadlineSeconds: 300 # 若调度错过 5 分钟以上则跳过
suspend: false # 设为 true 可临时暂停
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: registry.example.com/backup:2.1.0
2.1 并发控制
concurrencyPolicy | 行为 |
|---|---|
Forbid | 上一次没跑完就跳过本次(适合备份、报表,不能并发) |
Replace | 终止旧任务再启动新任务(适合"只要最新一次结果"的场景) |
Allow | 允许重叠执行(适合完全独立的短任务) |
2.2 错过执行策略
- CronJob 调度存在控制器延迟,不能用于毫秒级精确调度;
startingDeadlineSeconds:若因控制面不可用错过了调度窗口,超过该秒数就跳过本次(而不是补跑),避免积压任务潮涌;- 需要"集群中只有一个实例执行"的定时任务,考察 Leader Election 或外部调度(Airflow、Argo Workflows)。
2.3 常见陷阱
- 历史 Job 会堆积,必须设
successfulJobsHistoryLimit和failedJobsHistoryLimit(Kubernetes 1.25 起默认分别为 3 和 1,之前默认不清理); - 夏令时/闰秒:如果
schedule的触发时间在夏令时切换中不存在或出现两次,CronJob 行为取决于实现版本。用timeZone明确指定时区; - 临时停用:
kubectl patch cronjob daily-backup -n learning -p '{"spec":{"suspend":true}}'。
3. 观察与人工触发
kubectl get jobs,cronjobs -n learning
kubectl describe job schema-migrate -n learning
kubectl logs job/schema-migrate -n learning
# 从现有 CronJob 创建一次临时 Job(用于补跑或测试)
kubectl create job --from=cronjob/daily-backup daily-backup-manual -n learning
失败时保留 Job 和 Pod,先导出日志、事件和退出码,再决定重试或删除:
kubectl get pod -n learning -l job-name=schema-migrate \
-o jsonpath='{.items[*].status.containerStatuses[*].state.terminated.exitCode}'
kubectl logs job/schema-migrate -n learning > /tmp/migrate-failure.log
4. 孤儿 Pod 清理与垃圾回收
4.1 孤儿 Pod 从哪来
Job 或 CronJob 生成的历史 Pod 在任务结束后默认保留(用于查看日志、退出码和排查),如果不清理会越积越多。来源包括:
- Job 完成后未设置
ttlSecondsAfterFinished; - CronJob 每次触发产生的 Job 与 Pod;
- 手动创建的裸 Pod 不属于任何控制器。
4.2 自动清理机制
spec:
# Job 层:完成后 24 小时自动删除 Job 及其 Pod
ttlSecondsAfterFinished: 86400
spec:
# CronJob 层:保留最近 N 个成功/失败的 Job
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
| 机制 | 清理对象 | 配置位置 |
|---|---|---|
ttlSecondsAfterFinished | 已完成 Job 及其 Pod | Job spec |
successfulJobsHistoryLimit | 成功的历史 Job | CronJob spec |
failedJobsHistoryLimit | 失败的历史 Job | CronJob spec |
4.3 手动清理
# 删除所有已完成的 Job(及其 Pod)
kubectl delete jobs --field-selector status.successful=1 -n learning
# 或按时间
kubectl delete job $(kubectl get jobs -n learning -o jsonpath='{.items[?(@.status.completionTime)].metadata.name}')
5. 练习
练习 A
- 创建一个 Job,运行
busybox执行echo "done" && exit 0,观察状态变为Completed - 再创建一个 Job,运行
exit 1,观察backoffLimit达到上限后状态变为Failed - 在 YAML 中添加
ttlSecondsAfterFinished: 60,等待 1 分钟后观察 Job 是否被自动清理
练习 B
- 创建一个 CronJob(每分钟一次),用
busybox打印当前时间 - 运行 3 分钟后暂停该 CronJob(
suspend: true),确认不再产生新的 Job - 用
kubectl create job --from=cronjob/...手动触发一次,验证任务逻辑 - 不设置
successfulJobsHistoryLimit和failedJobsHistoryLimit,观察历史 Job 堆积,再补上清理配置验证生效
下一篇:Service 服务发现。