摘要:理解 CronJob 与 Job 的关系,掌握 Cron 表达式、时区、并发策略、错过调度的处理、历史清理以及暂停和手动触发操作。
本节学习 Kubernetes 中用于管理周期性任务的工作负载控制器——CronJob。
Job 负责执行一次性任务,例如:
- 数据库初始化。
- 数据迁移。
- 批量数据处理。
- 文件转换。
但是实际生产环境中,很多任务需要定期执行。
例如:
- 每天凌晨 2 点备份数据库。
- 每隔 5 分钟检查服务状态。
- 每周日清理历史日志。
- 每月 1 日生成统计报表。
如果每次都手动创建 Job,显然不现实。
因此 Kubernetes 提供了 CronJob。
CronJob 的核心作用:按照预设的时间规则,周期性创建 Job 来执行任务。
一、CronJob 基本概念
1. 什么是 CronJob?
CronJob 是 Kubernetes 中用于管理定时任务的工作负载控制器。
它基于 Cron 时间表达式,在指定时间创建 Job。
架构关系:
flowchart TB
N0["CronJob"]
N1["Job"]
N2["Pod"]
N3["Container"]
N4["执行任务"]
N0 -->|"定时触发"| N1
N1 --> N2
N2 --> N3
N3 --> N4
例如:
flowchart TB
N0["CronJob:每天凌晨 2 点执行"]
N1["创建 Job-001"]
N2["执行数据库备份"]
N3["完成任务"]
N4["第二天凌晨 2 点"]
N5["创建 Job-002"]
N6["执行数据库备份"]
N0 --> N1
N1 --> N2
N2 --> N3
N4 --> N5
N5 --> N6
注意:
CronJob 不直接运行容器,而是定时创建 Job,由 Job 创建 Pod 执行任务。
2. CronJob 与 Job 的区别
| 对比 | Job | CronJob |
|---|---|---|
| 主要用途 | 一次性任务 | 周期性任务 |
| 创建方式 | 手动或其他控制器创建 | 按时间规则自动创建 |
| 执行次数 | 达到目标后结束 | 周期性重复创建 Job |
| 时间表达式 | 不需要 | 需要 Cron 表达式 |
| 管理对象 | Pod | Job |
简单理解:
- Job:执行一次任务。
- CronJob:定时创建 Job。
二、Cron 表达式的字段、取值与语法
这是本节最重要的知识点,也是 CKA 常见考点。
1. 什么是 Cron 表达式?
Cron 表达式是一种描述定时执行规则的字符串。
Kubernetes CronJob 使用五个字段:
分 时 日 月 星期格式:
flowchart LR
N0["Cron 表达式:* * * * *"]
N1["第 1 个 *:分钟(0-59)"]
N2["第 2 个 *:小时(0-23)"]
N3["第 3 个 *:日期(1-31)"]
N4["第 4 个 *:月份(1-12)"]
N5["第 5 个 *:星期(0-6,星期日通常为 0)"]
N0 --> N1
N0 --> N2
N0 --> N3
N0 --> N4
N0 --> N5
例如:
0 2 * * *含义:
每天凌晨 2 点执行。
2. 五个字段的取值范围
| 字段 | 允许范围 | 含义 |
|---|---|---|
| 分钟 | 0-59 | 第几分钟 |
| 小时 | 0-23 | 第几个小时 |
| 日期 | 1-31 | 每月第几天 |
| 月份 | 1-12 | 哪个月 |
| 星期 | 0-6 | 星期几 |
星期通常:
| 数字 | 星期 |
|---|---|
| 0 | 星期日 |
| 1 | 星期一 |
| 2 | 星期二 |
| 3 | 星期三 |
| 4 | 星期四 |
| 5 | 星期五 |
| 6 | 星期六 |
3. 常用特殊字符
Cron 表达式主要使用以下字符:
| 字符 | 含义 | 示例 |
|---|---|---|
* | 任意值 | * * * * * |
, | 枚举多个值 | 0 2,4,6 * * * |
- | 连续范围 | 0 9-17 * * * |
/ | 间隔执行 | */5 * * * * |
例如:
每分钟执行
* * * * *每 5 分钟执行
*/5 * * * *每小时执行
0 * * * *每天凌晨 2 点执行
0 2 * * *每周一凌晨 3 点执行
0 3 * * 1每月 1 日凌晨 0 点执行
0 0 1 * *每天上午 9 点和下午 6 点执行
0 9,18 * * *每周一至周五上午 9 点执行
0 9 * * 1-5三、CronJob 完整 YAML 结构
1. 创建一个简单的 CronJob
需求:
每天凌晨 2 点执行一次任务,输出当前时间。
创建文件:
vim cronjob.yaml编写:
apiVersion: batch/v1kind: CronJob
metadata: name: hello-cronjob
spec: schedule: "0 2 * * *"
jobTemplate: spec: template: spec: containers: - name: hello image: busybox:1.36 command: - sh - -c - "date; echo Hello Kubernetes CronJob"
restartPolicy: OnFailure这里有一个非常重要的字段:
jobTemplate:它代表 CronJob 创建 Job 时所使用的模板。
可以理解为:
flowchart TB
N0["CronJob"]
N1["到达指定时间"]
N2["根据 jobTemplate 创建 Job"]
N3["Job 根据 template 创建 Pod"]
N4["Container 执行任务"]
N0 -->|"schedule"| N1
N1 --> N2
N2 --> N3
N3 --> N4
注意 YAML 的层级关系:
flowchart TB
N0["CronJob"]
N1["spec"]
N2["schedule"]
N3["jobTemplate"]
N4["spec"]
N5["template"]
N6["spec"]
N7["containers"]
N0 --> N1
N1 --> N2
N1 --> N3
N3 --> N4
N4 --> N5
N5 --> N6
N6 --> N7
这是 CKA 编写 CronJob YAML 时很容易出错的地方。
2. 创建 CronJob
kubectl apply -f cronjob.yaml查看:
kubectl get cronjobs输出示例:
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULEhello-cronjob 0 2 * * * False 0 <none>字段含义:
| 字段 | 含义 |
|---|---|
| SCHEDULE | 定时执行规则 |
| SUSPEND | 是否暂停 |
| ACTIVE | 当前活动 Job 数量 |
| LAST SCHEDULE | 最近一次调度时间 |
3. 查看 CronJob 创建的 Job
kubectl get jobs例如:
NAME COMPLETIONShello-cronjob-28200000 1/1查看 Job 创建的 Pod:
kubectl get pods查看执行日志:
kubectl logs <pod-name>四、CronJob 时区管理(timeZone)
这是一个非常重要的生产环境知识点。
假设:
你希望每天北京时间凌晨 2 点执行数据库备份。
配置:
schedule: "0 2 * * *"是否就代表北京时间凌晨 2 点?
不一定。
Kubernetes CronJob 默认使用 kube-controller-manager 所采用的时区,通常为 UTC。
如果控制平面使用 UTC,那么:
UTC 02:00对应北京时间:
北京时间 10:00这可能导致备份任务执行时间错误。
1. 使用 timeZone 指定时区
Kubernetes 从 v1.27 开始稳定支持 spec.timeZone。
例如:
apiVersion: batch/v1kind: CronJob
metadata: name: backup-cronjob
spec: schedule: "0 2 * * *"
timeZone: "Asia/Shanghai"
jobTemplate: spec: template: spec: containers: - name: backup image: busybox:1.36 command: - sh - -c - "echo backup started"
restartPolicy: OnFailure现在:
timeZone: "Asia/Shanghai"表示使用上海时区解释 Cron 表达式。
因此每天北京时间凌晨 2 点执行。
常见时区:
| 时区 | 配置 |
|---|---|
| 中国标准时间 | Asia/Shanghai |
| 美国东部时间 | America/New_York |
| 美国太平洋时间 | America/Los_Angeles |
| UTC | Etc/UTC |
| 日本标准时间 | Asia/Tokyo |
建议生产环境显式配置 timeZone,避免不同集群时区不一致造成任务执行错误。
五、CronJob 并发执行策略(concurrencyPolicy)
这是本节最重要的考点之一。
考虑一个问题:
假设我们设置:
schedule: "*/5 * * * *"表示每 5 分钟执行一次。
但任务需要 8 分钟才能完成。
那么:
00:00 创建 Job-100:05 创建 Job-200:08 Job-1 完成00:10 创建 Job-300:13 Job-2 完成这意味着多个 Job 可能同时运行。
对于某些业务来说,这是不允许的。
例如:
数据库备份任务,如果多个备份任务同时执行,可能占用大量磁盘和网络资源。
Kubernetes 提供了 concurrencyPolicy。
主要有三种策略:
- Allow
- Forbid
- Replace
1. Allow:允许并发
concurrencyPolicy: Allow这是默认策略。
含义:
如果前一个 Job 还没有完成,下一个调度时间到达时,仍然可以创建新的 Job。
执行示意:
00:00 Job-1 开始00:05 Job-2 开始00:10 Job-3 开始
Job-1、Job-2、Job-3 可以同时运行适用场景:
- 不同任务之间相互独立。
- 允许并行处理。
- 不需要严格串行执行。
2. Forbid:禁止并发
concurrencyPolicy: Forbid含义:
如果前一个 Job 还没有完成,那么新的调度时间到达时,不会创建新的 Job。
例如:
flowchart TB
N0["00:00 Job-1 开始"]
N1["00:05 应该创建 Job-2"]
N2["发现 Job-1 仍在运行"]
N3["跳过本次调度"]
N4["00:08 Job-1 完成"]
N5["00:10 到达下一个调度时间"]
N6["创建 Job-3"]
N0 --> N1
N1 --> N2
N2 --> N3
N3 --> N4
N4 --> N5
N5 --> N6
注意:被跳过的调度不会在前一个 Job 完成后自动补执行。
适用场景:
- 数据库备份。
- 数据清理。
- 不允许任务重叠的批处理。
3. Replace:替换旧任务
concurrencyPolicy: Replace含义:
如果前一个 Job 还没有完成,那么新的调度时间到达时,会尝试终止之前的活动 Job,并创建新的 Job。
例如:
flowchart TB
N0["00:00 Job-1 开始"]
N1["00:05 Job-2 到达调度时间"]
N2["终止 Job-1"]
N3["创建 Job-2"]
N0 --> N1
N1 --> N2
N2 --> N3
适用场景:
- 只关心最新一次执行结果。
- 旧任务已经没有继续执行的价值。
- 新任务需要优先执行。
注意:终止旧 Job 是异步过程,不意味着旧容器中的业务操作一定瞬间停止。需要考虑任务清理、数据一致性和优雅终止。
4. 三种策略对比
| 策略 | 前一个 Job 运行中,新调度到来时 |
|---|---|
| Allow | 创建新 Job,允许并发 |
| Forbid | 跳过本次调度 |
| Replace | 尝试终止旧 Job,创建新 Job |
CKA 高频考点
记忆:
- Allow:允许并发。
- Forbid:禁止并发。
- Replace:替换旧任务。
六、错过调度时间的处理(startingDeadlineSeconds)
再考虑一个生产问题:
假设 CronJob 每天凌晨 2 点执行。
但是凌晨 2 点时,Kubernetes 控制平面出现故障,控制器没有及时创建 Job。
等到凌晨 3 点集群恢复,应该怎么办?
是否应该立即补执行?
Kubernetes 提供了:
startingDeadlineSeconds
1. 什么是 startingDeadlineSeconds?
它表示 Job 可以开始执行的最晚时间窗口。
例如:
startingDeadlineSeconds: 300表示:
允许错过原定调度时间最多 300 秒。
例如:
flowchart TB
N0["计划执行:02:00"]
N1["02:03 控制器恢复"]
N2["距离计划时间 180 秒"]
N3["仍在 300 秒窗口内"]
N4["可以补创建 Job"]
N0 --> N1
N1 --> N2
N2 --> N3
N3 --> N4
如果:
flowchart TB
N0["计划执行:02:00"]
N1["02:10 控制器恢复"]
N2["距离计划时间 600 秒"]
N3["超过 300 秒"]
N4["不再补执行这次任务"]
N0 --> N1
N1 --> N2
N2 --> N3
N3 --> N4
2. YAML 示例
spec: schedule: "0 2 * * *"
startingDeadlineSeconds: 300表示每天凌晨 2 点执行,允许延迟 5 分钟开始。
3. 不设置 startingDeadlineSeconds 会怎样?
如果没有设置该字段,CronJob 会尝试补偿错过的调度。
但需要注意:
Kubernetes 对错过调度次数有保护机制。
如果控制器发现超过 100 次错过的调度,会停止此次补偿并报告相关错误。
因此:
startingDeadlineSeconds 不只是控制延迟,还可以限制任务补执行的时间范围。
适合:
- 定时备份。
- 定时数据同步。
- 周期性报表。
- 不能无限补执行的业务任务。
七、CronJob 历史任务管理
CronJob 每次执行都会创建一个 Job。
如果每天执行一次:
backup-001backup-002backup-003backup-004...随着时间推移,历史 Job 会不断增加。
Kubernetes 提供两个参数管理历史任务数量。
1. successfulJobsHistoryLimit
控制保留多少个成功的 Job。
例如:
successfulJobsHistoryLimit: 3表示最多保留 3 个成功完成的历史 Job。
默认值是 3。
2. failedJobsHistoryLimit
控制保留多少个失败的 Job。
例如:
failedJobsHistoryLimit: 1表示最多保留 1 个失败的历史 Job。
默认值是 1。
3. 完整配置
spec: schedule: "0 2 * * *"
successfulJobsHistoryLimit: 3 failedJobsHistoryLimit: 1含义:
成功任务:最多保留 3 个失败任务:最多保留 1 个注意:
这两个参数管理的是 CronJob 的历史 Job,不是 Job 的 Pod 数量,也不是 TTL。
另外,历史数量清理是控制器异步协调的,不应将其理解为精确的实时数量限制。
八、CronJob 暂停与恢复(suspend)
生产环境中,有时需要暂时停止定时任务。
例如:
- 数据库正在维护。
- 备份系统正在升级。
- 当前不允许执行数据清理任务。
- 需要暂停某个批处理任务。
这时可以使用:
spec: suspend: true1. 暂停 CronJob
命令:
kubectl patch cronjob backup-cronjob \ -p '{"spec":{"suspend":true}}'查看:
kubectl get cronjob输出:
NAME SCHEDULE SUSPENDbackup-cronjob 0 2 * * * True2. 恢复 CronJob
kubectl patch cronjob backup-cronjob \ -p '{"spec":{"suspend":false}}'3. 暂停和恢复的区别
需要特别注意:
暂停 CronJob 不会自动停止已经创建并正在运行的 Job。
例如:
flowchart TB
N0["CronJob"]
N1["Job-1(正在运行)"]
N2["Job-2(已经完成)"]
N0 --> N1
N0 --> N2
此时设置:
suspend: true只会阻止后续调度创建新 Job。
Job-1 仍然可以继续运行。
如果还需要停止 Job-1,需要单独处理。
另外,恢复 CronJob 后,暂停期间错过的调度可能会被补执行,具体取决于 startingDeadlineSeconds 和错过调度次数等规则。
九、CronJob 的重要运行机制与注意事项
前面学习了 CronJob 的各种配置,但生产环境还需要理解几个底层问题。
1. CronJob 不保证任务绝对只执行一次
这是一个很容易忽略的知识点。
Kubernetes CronJob 的调度不是严格的分布式事务系统。
在某些异常情况下,可能出现:
- 某次调度没有创建 Job。
- 某次调度创建了重复 Job。
- 控制器故障后补偿调度。
因此,不能假设每次 CronJob 调度都严格只执行一次。
例如:
flowchart TB
N0["02:00 创建 Job-1"]
N1["任务执行数据库备份"]
N2["控制器异常"]
N3["可能再次创建对应调度任务"]
N0 --> N1
N1 --> N2
N2 --> N3
如果任务不具备重复执行安全性,就可能造成数据问题。
如何解决?
使用幂等性设计(Idempotency)。
例如:
数据库备份时,可以根据日期生成唯一备份文件:
backup_file="backup-$(date +%Y%m%d).sql"执行前检查文件是否已经存在:
if [ -f "$backup_file" ]; then echo "Backup already exists" exit 0fi这只是一个简单示例,生产环境还应考虑备份完整性校验、临时文件和原子发布。
对于支付、订单、数据迁移等任务,应该通过业务唯一标识、数据库唯一约束或分布式锁保证重复执行安全。
核心原则:CronJob 负责调度,业务程序负责保证任务的幂等性。
2. CronJob 的调度并不是精确实时调度
例如:
schedule: "0 2 * * *"表示计划每天凌晨 2 点执行。
但不代表容器一定在 02:00:00 精确启动。
原因包括:
- 控制器调度周期。
- 控制平面负载。
- Job 创建延迟。
- Pod 调度延迟。
- 镜像拉取时间。
- 容器启动时间。
因此:
CronJob 更适合周期性批处理任务,不适合要求毫秒级精确执行的实时任务。
3. CronJob 不会自动管理业务执行结果
CronJob 只负责按照时间规则创建 Job。
真正的业务执行逻辑由 Job 中的容器负责。
例如:
flowchart TB
N0["CronJob:每天凌晨 2 点触发"]
N1["Job:负责执行备份任务"]
N2["Pod:运行备份脚本"]
N3["Container:执行数据库备份"]
N4["生成备份文件"]
N0 --> N1
N1 --> N2
N2 --> N3
N3 --> N4
如果备份脚本虽然返回成功,但生成的文件实际上已经损坏,Kubernetes 并不能自动发现。
所以生产环境还需要业务层的校验机制。
十、如何手动触发 CronJob?
生产环境中,我们经常需要立即执行一次 CronJob,而不是等待下一次定时调度。
例如:
每天凌晨 2 点执行备份,但现在下午 3 点需要立即进行一次备份。
可以直接从 CronJob 创建一个 Job。
kubectl create job manual-backup \ --from=cronjob/mysql-backup这里:
- manual-backup:新 Job 的名称。
- cronjob/mysql-backup:指定 CronJob 作为模板来源。
查看:
kubectl get jobs查看日志:
kubectl logs job/manual-backup这种方式非常实用。
例如:
| 场景 | 操作 |
|---|---|
| 立即备份数据库 | 手动创建 Job |
| 测试定时任务配置 | 手动创建 Job |
| 临时补执行一次任务 | 手动创建 Job |
| 验证脚本是否正常 | 手动创建 Job |
注意:手动创建的 Job 不受原 CronJob 的定时调度和并发策略控制。如果业务不允许并发,手动触发前也要检查是否有其他备份任务正在运行。
十一、CronJob 故障排查
1. CronJob 没有创建 Job
查看:
kubectl get cronjob重点关注:
SCHEDULESUSPENDACTIVELAST SCHEDULE常见原因:
- Cron 表达式错误。
- CronJob 被暂停。
- 时区配置不正确。
- startingDeadlineSeconds 超出限制。
- 控制器没有正常工作。
- 达到错过调度次数限制。
进一步排查:
kubectl describe cronjob mysql-backup查看集群事件:
kubectl get events --sort-by=.metadata.creationTimestamp2. CronJob 创建了 Job,但 Job 没有完成
查看:
kubectl get jobs查看 Pod:
kubectl get pods查看日志:
kubectl logs <pod-name>查看详细信息:
kubectl describe pod <pod-name>常见原因:
- 容器启动失败。
- 镜像拉取失败。
- 脚本执行错误。
- 外部数据库不可用。
- Job 达到超时时间。
3. CronJob 重复执行
重点检查:
- concurrencyPolicy 是否为 Allow。
- 上一次任务是否执行时间过长。
- 是否存在控制器故障后的重复调度。
- 是否存在人工触发的 Job。
- 业务任务是否缺少幂等控制。
4. CronJob 常见问题总结
| 问题 | 排查方向 |
|---|---|
| 没有触发 | schedule、timeZone、suspend |
| 延迟执行 | 控制器状态、startingDeadlineSeconds |
| 多个 Job 并发 | concurrencyPolicy |
| Job 执行失败 | backoffLimit、Pod 日志 |
| 历史记录太多 | 历史保留策略、TTL |
| 时间不正确 | timeZone |
| 重复执行 | 幂等性、调度状态 |
十二、CronJob 必须掌握的命令
| 操作 | 命令 |
|---|---|
| 查看 CronJob | kubectl get cronjobs |
| 查看所有命名空间 | kubectl get cronjobs -A |
| 查看详细信息 | kubectl describe cronjob mysql-backup |
| 创建 CronJob | kubectl apply -f cronjob.yaml |
| 手动触发 | kubectl create job manual --from=cronjob/mysql-backup |
| 暂停 CronJob | kubectl patch cronjob mysql-backup -p '{"spec":{"suspend":true}}' |
| 恢复 CronJob | kubectl patch cronjob mysql-backup -p '{"spec":{"suspend":false}}' |
| 删除 CronJob | kubectl delete cronjob mysql-backup |
| 查看 Job | kubectl get jobs |
| 查看 Pod | kubectl get pods |
十三、本节知识总结
CronJob 核心知识体系
flowchart TB
N0["CronJob Controller<br/>按照 Cron 表达式定时创建 Job"]
N1["Job<br/>执行一次性任务"]
N2["Pod<br/>运行容器"]
N3["Container<br/>执行实际业务逻辑"]
N0 --> N1
N1 --> N2
N2 --> N3
必须掌握的参数
| 参数 | 作用 |
|---|---|
| schedule | 定时表达式 |
| timeZone | 时区 |
| concurrencyPolicy | 并发策略 |
| startingDeadlineSeconds | 错过调度的补偿时间 |
| successfulJobsHistoryLimit | 成功历史保留数量 |
| failedJobsHistoryLimit | 失败历史保留数量 |
| suspend | 暂停调度 |
| jobTemplate | Job 模板 |