跳到主要内容

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

这个配置会:

  1. 同时运行 2 个 Pod(parallelism: 2)
  2. 直到有 5 个 Pod 成功完成(completions: 5)
  3. 每个失败的 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 支持预定义的宏:

等价于说明
@yearly0 0 1 1 *每年执行一次
@monthly0 0 1 * *每月执行一次
@weekly0 0 * * 0每周执行一次
@daily0 0 * * *每天执行一次
@hourly0 * * * *每小时执行一次
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 的区别

特性JobCronJob
触发方式手动创建定时触发
执行次数一次性周期性
适用场景批处理、初始化任务定时备份、定期报告
资源清理需要手动或配置 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}'

小结

本章我们学习了:

  1. Job:一次性批处理任务
  2. 并行模式:固定完成数和工作队列模式
  3. CronJob:定时任务管理
  4. Cron 表达式:定义执行时间表
  5. 高级配置:时区、并发策略、历史限制
  6. 最佳实践:资源限制、超时、幂等性设计

练习

  1. 创建一个计算圆周率后 100 位的 Job
  2. 创建一个每分钟执行的 CronJob,打印当前时间
  3. 配置 CronJob 的并发策略为 Forbid
  4. 设置 CronJob 保留 5 个成功的 Job 记录