Job 与 CronJob
Job 和 CronJob 是 Kubernetes 中用于运行批处理任务的控制器。与 Deployment 不同,Job 用于执行一次性任务,而 CronJob 则用于定时执行任务。
Job 概述
Job 创建一个或多个 Pod,并确保指定数量的 Pod 成功终止。当 Pod 成功完成后,Job 会跟踪成功次数。当达到指定的成功次数时,Job 就完成了。
Job 的特点
Job 适用于以下场景:
- 批处理任务:数据处理、文件转换、批量导入
- 计算任务:科学计算、机器学习训练
- 备份任务:数据库备份、文件备份
- 初始化任务:数据库迁移、缓存预热
与 Deployment 不同,Job 管理的 Pod 在完成任务后不会持续运行,而是正常退出。
Job 的执行模式
Job 支持三种执行模式:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| 非并行 Job | 创建一个 Pod,Pod 成功则 Job 完成 | 单次执行任务 |
| 并行 Job(固定完成数) | 并行运行多个 Pod,指定成功数 | 并行处理任务 |
| 并行 Job(工作队列) | 并行运行多个 Pod,逐个处理工作队列项 | 消息队列消费 |
创建 Job
基础 Job 示例
apiVersion: batch/v1
kind: Job
metadata:
name: pi-calculation
spec:
template:
spec:
containers:
- name: pi
image: perl:5.34.0
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never # Job 必须指定 restartPolicy
backoffLimit: 4 # 失败重试次数
activeDeadlineSeconds: 600 # 最大运行时间(秒)
# 创建 Job
kubectl apply -f job.yaml
# 查看 Job 状态
kubectl get jobs
# 查看 Job 详情
kubectl describe job pi-calculation
# 查看 Job 创建的 Pod
kubectl get pods -l job-name=pi-calculation
# 查看 Pod 日志
kubectl logs <pod-name>
理解 restartPolicy
Job 的 Pod 模板必须指定 restartPolicy,支持的值有:
- Never:Pod 失败时不重启,而是创建新的 Pod。适用于需要保留失败日志的场景。
- OnFailure:Pod 失败时在同一个 Pod 内重启容器。适用于可以重复执行的任务。
# 使用 OnFailure 重启策略
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: task
image: busybox:1.36
command: ["sh", "-c", "echo 'Processing...' && exit 0"]
控制失败重试
apiVersion: batch/v1
kind: Job
metadata:
name: retry-job
spec:
template:
spec:
containers:
- name: task
image: busybox:1.36
command: ["sh", "-c", "echo 'Attempting task...'"]
restartPolicy: Never
backoffLimit: 6 # 最大重试次数,默认为 6
activeDeadlineSeconds: 300 # Job 最大运行时间
当 Pod 失败次数达到 backoffLimit 时,Job 被标记为失败。每个失败 Pod 的重试间隔会指数增加(10s、20s、40s...),最大为 6 分钟。
并行 Job
固定完成数的并行 Job
指定 completions(完成数)和 parallelism(并行数):
apiVersion: batch/v1
kind: Job
metadata:
name: parallel-job
spec:
completions: 5 # 需要成功完成的 Pod 数量
parallelism: 2 # 同时运行的 Pod 数量
template:
spec:
containers:
- name: worker
image: busybox:1.36
command: ["sh", "-c", "echo 'Processing item $ITEM' && sleep 5"]
env:
- name: ITEM
valueFrom:
fieldRef:
fieldPath: metadata.name
restartPolicy: OnFailure
这个配置会:
- 同时运行 2 个 Pod(parallelism: 2)
- 直到有 5 个 Pod 成功完成(completions: 5)
- 每个失败的 Pod 会被重新创建
工作队列模式的并行 Job
只指定 parallelism,不指定 completions:
apiVersion: batch/v1
kind: Job
metadata:
name: work-queue-job
spec:
parallelism: 3 # 同时运行 3 个 Pod
# 不指定 completions
template:
spec:
containers:
- name: worker
image: my-worker:latest
command: ["python", "worker.py"]
env:
- name: QUEUE_URL
value: "redis://queue-service:6379"
restartPolicy: OnFailure
工作队列模式的特点:
- Job 创建
parallelism数量的 Pod - 每个 Pod 独立从工作队列获取任务
- 任一 Pod 成功退出即认为成功
- 适用于消息队列消费场景
Job 清理策略
自动清理完成的 Job
使用 ttlSecondsAfterFinished 自动清理:
apiVersion: batch/v1
kind: Job
metadata:
name: auto-cleanup-job
spec:
ttlSecondsAfterFinished: 100 # 完成后 100 秒自动删除
template:
spec:
containers:
- name: task
image: busybox:1.36
command: ["echo", "Hello"]
restartPolicy: Never
手动清理
# 删除 Job(同时删除关联的 Pod)
kubectl delete job <job-name>
# 删除所有完成的 Job
kubectl delete jobs --field-selector status.successful=1
# 查看已完成的 Job
kubectl get jobs --field-selector status.successful=1
CronJob 概述
CronJob 按照 Cron 表达式定义的时间表创建 Job。适用于需要定期执行的任务。
CronJob 的典型应用
- 定时备份:数据库备份、文件同步
- 定期报告:日报、周报生成
- 清理任务:日志清理、临时文件删除
- 定期检查:健康检查、安全扫描
创建 CronJob
基础 CronJob 示例
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello-cronjob
spec:
schedule: "* * * * *" # 每分钟执行一次
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox:1.36
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- date; echo "Hello from Kubernetes cluster"
restartPolicy: OnFailure
# 创建 CronJob
kubectl apply -f cronjob.yaml
# 查看 CronJob
kubectl get cronjobs
# 查看 CronJob 创建的 Job
kubectl get jobs -l cronjob-name=hello-cronjob
# 查看 CronJob 详情
kubectl describe cronjob hello-cronjob
Cron 表达式详解
Cron 表达式由 5 个字段组成:
# ┌───────────── 分钟 (0 - 59)
# │ ┌───────────── 小时 (0 - 23)
# │ │ ┌───────────── 日期 (1 - 31)
# │ │ │ ┌───────────── 月份 (1 - 12)
# │ │ │ │ ┌───────────── 星期 (0 - 6, 0=周日)
# │ │ │ │ │
# * * * * *
字段说明:
| 字段 | 允许值 | 特殊字符 |
|---|---|---|
| 分钟 | 0-59 | * , - / |
| 小时 | 0-23 | * , - / |
| 日期 | 1-31 | * , - / ? |
| 月份 | 1-12 | * , - / |
| 星期 | 0-6(0=周日)或 sun,mon,tue,wed,thu,fri,sat | * , - / ? |
特殊字符说明:
| 字符 | 说明 | 示例 |
|---|---|---|
* | 任意值 | * * * * * 每分钟 |
, | 列举多个值 | 0,30 * * * * 每小时的第0和30分钟 |
- | 范围 | 0 9-17 * * * 9点到17点每小时 |
/ | 间隔 | */15 * * * * 每15分钟 |
? | 任意值(同 *) | 用于日期或星期字段 |
CronJob 名称长度限制:
CronJob 的名称有特殊限制,这是由于 Kubernetes 在创建 Job 和 Pod 时会在名称后追加字符:
- CronJob 名称不能超过 52 个字符
- Job 名称格式:
<cronjob-name>-<timestamp>,最长 63 字符 - Pod 名称格式:
<job-name>-<random-string>
# 错误示例:名称过长
# metadata.name: "this-is-a-very-long-cronjob-name-that-exceeds-the-limit"
# 正确示例:简短且有意义的名称
metadata:
name: mysql-backup # 合适的长度
常见示例:
| 表达式 | 说明 |
|---|---|
0 * * * * | 每小时执行 |
0 0 * * * | 每天午夜执行 |
0 0 * * 0 | 每周日午夜执行 |
0 0 1 * * | 每月 1 号午夜执行 |
*/15 * * * * | 每 15 分钟执行 |
0 9-17 * * 1-5 | 工作日 9 点到 17 点每小时执行 |
预定义的时间表
Kubernetes 支持预定义的宏:
| 宏 | 等价于 | 说明 |
|---|---|---|
@yearly | 0 0 1 1 * | 每年执行一次 |
@monthly | 0 0 1 * * | 每月执行一次 |
@weekly | 0 0 * * 0 | 每周执行一次 |
@daily | 0 0 * * * | 每天执行一次 |
@hourly | 0 * * * * | 每小时执行一次 |
spec:
schedule: "@daily" # 每天午夜执行
CronJob 高级配置
时区设置
Kubernetes 1.27+ 支持时区配置:
apiVersion: batch/v1
kind: CronJob
metadata:
name: timezone-cronjob
spec:
schedule: "0 9 * * *" # 上午 9 点
timeZone: "Asia/Shanghai" # 使用北京时间
jobTemplate:
spec:
template:
spec:
containers:
- name: task
image: busybox:1.36
command: ["echo", "Good morning!"]
restartPolicy: OnFailure
如果不指定时区,默认使用 kube-controller-manager 的本地时区(通常是 UTC)。
并发策略
CronJob 支持三种并发策略:
spec:
concurrencyPolicy: Forbid # 并发策略
| 策略 | 说明 |
|---|---|
Allow(默认) | 允许并发执行多个 Job |
Forbid | 禁止并发,如果上一个 Job 还在运行则跳过 |
Replace | 如果上一个 Job 还在运行,则替换它 |
apiVersion: batch/v1
kind: CronJob
metadata:
name: forbid-concurrent
spec:
schedule: "*/5 * * * *"
concurrencyPolicy: Forbid # 禁止并发执行
jobTemplate:
spec:
template:
spec:
containers:
- name: task
image: busybox:1.36
command: ["sh", "-c", "sleep 600"] # 模拟长时间任务
restartPolicy: OnFailure
历史记录限制
控制保留的已完成 Job 数量:
spec:
successfulJobsHistoryLimit: 3 # 保留 3 个成功的 Job
failedJobsHistoryLimit: 1 # 保留 1 个失败的 Job
启动截止时间
设置 Job 启动的最后期限:
spec:
startingDeadlineSeconds: 300 # 如果错过执行时间 5 分钟,则跳过
如果 CronJob 在预定时间无法启动(例如控制器宕机),在 startingDeadlineSeconds 内仍会尝试启动,超过则跳过。
暂停和恢复
spec:
suspend: true # 暂停 CronJob
# 暂停 CronJob
kubectl patch cronjob <name> -p '{"spec":{"suspend":true}}'
# 恢复 CronJob
kubectl patch cronjob <name> -p '{"spec":{"suspend":false}}'
完整示例:数据库备份
apiVersion: batch/v1
kind: CronJob
metadata:
name: mysql-backup
namespace: database
spec:
schedule: "0 2 * * *" # 每天凌晨 2 点
timeZone: "Asia/Shanghai"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 7
failedJobsHistoryLimit: 3
startingDeadlineSeconds: 600
jobTemplate:
spec:
backoffLimit: 3
activeDeadlineSeconds: 3600
template:
spec:
containers:
- name: backup
image: mysql:8.0
command:
- /bin/sh
- -c
- |
mysqldump -h $MYSQL_HOST -u $MYSQL_USER -p$MYSQL_PASSWORD \
--all-databases --single-transaction \
| gzip > /backup/mysql-$(date +%Y%m%d-%H%M%S).sql.gz
# 清理 30 天前的备份
find /backup -name "mysql-*.sql.gz" -mtime +30 -delete
echo "Backup completed successfully"
env:
- name: MYSQL_HOST
value: "mysql-service"
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: mysql-credentials
key: username
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-credentials
key: password
volumeMounts:
- name: backup-storage
mountPath: /backup
volumes:
- name: backup-storage
persistentVolumeClaim:
claimName: backup-pvc
restartPolicy: OnFailure
Job 和 CronJob 的区别
| 特性 | Job | CronJob |
|---|---|---|
| 触发方式 | 手动创建 | 定时触发 |
| 执行次数 | 一次性 | 周期性 |
| 适用场景 | 批处理、初始化任务 | 定时备份、定期报告 |
| 资源清理 | 需要手动或配置 TTL | 可配置历史记录限制 |
| 并发控制 | 支持 | 支持并发策略 |
最佳实践
1. 设置合理的资源限制
spec:
template:
spec:
containers:
- name: task
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
2. 配置超时和重试
spec:
backoffLimit: 3 # 重试次数
activeDeadlineSeconds: 3600 # 超时时间
3. 使用 TTL 清理完成的 Job
spec:
ttlSecondsAfterFinished: 3600 # 完成后 1 小时清理
4. 幂等性设计
确保 Job 可以安全地多次执行,产生相同的结果:
# 使用唯一标识符避免重复处理
command:
- /bin/sh
- -c
- |
TASK_ID=$(date +%Y%m%d%H%M%S)
if [ ! -f /data/processed-$TASK_ID ]; then
# 处理逻辑
touch /data/processed-$TASK_ID
fi
5. 监控和告警
# 添加标签便于监控
metadata:
labels:
app: backup
type: cronjob
常见问题排查
Job 一直不完成
# 查看 Job 状态
kubectl describe job <job-name>
# 查看 Pod 日志
kubectl logs <pod-name>
# 检查 Pod 事件
kubectl describe pod <pod-name>
CronJob 没有按时执行
# 检查 CronJob 是否暂停
kubectl get cronjob <name> -o jsonpath='{.spec.suspend}'
# 检查上一次执行时间
kubectl describe cronjob <name>
# 检查控制器日志
kubectl logs -n kube-system -l component=kube-controller-manager
Job 失败次数过多
# 查看失败原因
kubectl describe job <job-name>
# 检查 backoffLimit 设置
kubectl get job <job-name> -o jsonpath='{.spec.backoffLimit}'
小结
本章我们学习了:
- Job:一次性批处理任务
- 并行模式:固定完成数和工作队列模式
- CronJob:定时任务管理
- Cron 表达式:定义执行时间表
- 高级配置:时区、并发策略、历史限制
- 最佳实践:资源限制、超时、幂等性设计
练习
- 创建一个计算圆周率后 100 位的 Job
- 创建一个每分钟执行的 CronJob,打印当前时间
- 配置 CronJob 的并发策略为 Forbid
- 设置 CronJob 保留 5 个成功的 Job 记录