Appearance
Job 与 CronJob
Job 和 CronJob 处理的是“任务型”工作负载。Deployment 适合长期运行的服务,期望 Pod 一直存在;Job 适合跑完就退出的任务,比如数据库迁移、一次性数据修复、批量导入、离线计算;CronJob 则是在 Job 外面加了一层时间调度,适合定时备份、定时报表、定期清理。
任务型 Pod 的判断重点和在线服务不同。在线服务关注 Ready、副本数和请求延迟;Job 更关注是否按预期完成、失败后重试几次、并发多少个、旧任务保留多久,以及任务代码是否能被重复执行。
一、Job 的基本结构
一个最小 Job:
yaml
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
namespace: demo
spec:
backoffLimit: 3
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: registry.example.com/app/migrate:v1.0.0
command: ["sh", "-c"]
args:
- |
set -e
# 迁移脚本需要支持重复执行,避免 Job 重试时造成脏数据
./migrate.sh几个字段:
| 字段 | 说明 |
|---|---|
backoffLimit | 失败后最多重试次数 |
restartPolicy | Job 常用 Never 或 OnFailure,不能写 Always |
template | Job 创建 Pod 使用的模板 |
completions | 需要成功完成几次 |
parallelism | 同时运行几个 Pod |
activeDeadlineSeconds | 整个 Job 最长允许运行时间 |
restartPolicy: Never 时,容器失败后会新建 Pod 重试;OnFailure 时,同一个 Pod 内的容器可能被重启。排查任务日志时,这个差异会影响日志在哪个 Pod 里。
二、并行任务
批量处理可以设置完成次数和并发数:
yaml
apiVersion: batch/v1
kind: Job
metadata:
name: image-resize
namespace: demo
spec:
completions: 10
parallelism: 3
backoffLimit: 2
template:
spec:
restartPolicy: Never
containers:
- name: worker
image: registry.example.com/tools/image-resize:v1.2.0
env:
- name: QUEUE_NAME
value: resizecompletions: 10 表示累计成功 10 个 Pod;parallelism: 3 表示最多同时跑 3 个。任务消费队列时比较常见,Pod 自己从队列领取任务,某个 Pod 失败后由 Job 重试补齐成功次数。
并发任务要考虑幂等。比如批量修数据、批量发消息、批量导出文件时,重试可能重复执行同一批数据。任务代码通常需要用唯一键、状态字段或任务表记录处理进度。
三、CronJob 的调度字段
CronJob 通过 crontab 表达式周期创建 Job:
yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: mysql-dump
namespace: demo
spec:
schedule: "10 2 * * *"
timeZone: "Asia/Shanghai"
concurrencyPolicy: Forbid
startingDeadlineSeconds: 300
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
restartPolicy: Never
containers:
- name: dump
image: registry.example.com/ops/mysql-client:8.4
command: ["sh", "-c"]
args:
- |
set -e
# 备份文件名带日期,避免覆盖上一轮产物
mysqldump -h mysql.demo.svc -uroot -p"$MYSQL_PWD" app \
> "/backup/app-$(date +%F).sql"
env:
- name: MYSQL_PWD
valueFrom:
secretKeyRef:
name: mysql-root
key: password
volumeMounts:
- name: backup
mountPath: /backup
volumes:
- name: backup
persistentVolumeClaim:
claimName: mysql-backupconcurrencyPolicy 很关键:
| 值 | 行为 |
|---|---|
Allow | 上一轮没结束,下一轮照常启动 |
Forbid | 上一轮没结束,本轮跳过 |
Replace | 新一轮启动时替换上一轮 |
备份、清理、报表这类任务通常用 Forbid。如果上一次备份跑了 2 小时还没结束,下一轮继续启动可能会抢锁、抢磁盘、打满数据库连接。
四、手动触发和查看状态
查看 CronJob 和它创建的 Job:
bash
kubectl get cronjob -n demo
kubectl get job -n demo
kubectl get pod -n demo -l job-name=mysql-dump-28600150从 CronJob 手动创建一次 Job:
bash
# 手动触发一次,用于验证镜像、Secret、PVC 和命令是否正常
kubectl create job mysql-dump-manual \
--from=cronjob/mysql-dump \
-n demo查看任务日志:
bash
kubectl logs job/mysql-dump-manual -n demo
kubectl describe job mysql-dump-manual -n demo
kubectl get events -n demo --sort-by=.lastTimestampJob 失败时,先看 kubectl describe job 里的 Pods Statuses、BackoffLimitExceeded、DeadlineExceeded,再看具体 Pod 的日志和事件。镜像拉取失败、PVC 挂载失败、Secret 不存在,都可能导致任务还没执行到业务命令就失败。
五、清理和保留
CronJob 会按 successfulJobsHistoryLimit 和 failedJobsHistoryLimit 保留历史 Job。一次性 Job 可以设置 TTL:
yaml
apiVersion: batch/v1
kind: Job
metadata:
name: temp-check
namespace: demo
spec:
ttlSecondsAfterFinished: 3600
template:
spec:
restartPolicy: Never
containers:
- name: check
image: busybox:1.36
command: ["sh", "-c", "date && nslookup kubernetes.default.svc"]TTL 到期后,Job 和它创建的 Pod 会被清理。日志如果只存在 Pod stdout/stderr,清理后就只能从日志平台查;没有日志采集链路的环境里,排查历史失败任务会很困难。
六、常见故障
| 现象 | 常见原因 |
|---|---|
Job 一直 0/1 | Pod Pending、镜像拉取失败、调度失败、PVC 没绑定 |
| Job 反复失败 | 命令退出码非 0、Secret 错误、依赖服务不可达 |
| CronJob 没按时跑 | suspend: true、时区不对、控制平面异常、错过 startingDeadlineSeconds |
| 多个任务叠在一起 | concurrencyPolicy: Allow,上一轮执行时间超过调度周期 |
| 任务成功但数据不对 | 任务逻辑不幂等,重试或并发导致重复处理 |
任务类资源不适合只看 kubectl get job 的 COMPLETIONS。一次失败重试、一次跳过、一次超时,背后的处理方式完全不同,Events、日志和任务自身的业务状态都要对上。