一、概述
在 Kubernetes 中,Job 和 CronJob 是用于管理任务的两种控制器,它们分别用于处理一次性任务和周期性任务:
- Job:用于管理一次性任务,确保指定数量的 Pod 成功完成任务。当任务完成后,Job 会保持其状态,不会自动删除。
- CronJob:基于时间调度的 Job 控制器,允许用户按照指定的时间表达式(cron 表达式)定期创建 Job。
这两种控制器为 Kubernetes 集群中的任务管理提供了灵活的解决方案,满足了不同场景下的任务执行需求。
Job 的主要特点:
- 一次性任务:Job 管理的是短期运行的一次性任务
- 并行执行:支持设置并行度(parallelism)控制同时运行的 Pod 数量
- 完成计数:支持设置完成数(completions)控制需要成功完成的 Pod 数量
- 失败处理:支持设置失败重试次数(backoffLimit)控制任务失败后的重试策略
- 活跃期限:支持设置活跃期限(activeDeadlineSeconds)控制任务的最大运行时间
CronJob 的主要特点:
- 时间调度:使用标准的 cron 表达式定义任务执行时间
- 自动创建 Job:按照调度时间自动创建 Job 实例
- 并发控制:支持设置并发策略(Forbid、Replace、Allow)控制任务的并发执行
- 历史管理:支持设置成功和失败的 Job 历史记录数量
- 时区支持:支持设置时区,确保任务在正确的时区执行
二、工作原理
2.1 Job 工作原理

2.2 CronJob 工作原理

三、创建流程

3.1 Job 创建流程
- 用户请求:用户通过 kubectl 客户端发起创建 Job 资源请求
- API Server 处理:API Server 对请求进行鉴权、准入控制,然后将请求写入 etcd
- Job 控制器监听:Job 控制器通过 Informer 机制监听 Job 资源变化
- Pod 创建:控制器根据 Job 的配置创建 Pod
- Kubelet 处理:节点上的 Kubelet 组件监听到 Pod 创建事件,在本地运行 Pod
- 任务执行:Pod 执行指定的任务
- 状态更新:控制器根据 Pod 的执行结果更新 Job 的状态
3.2 CronJob 创建流程
- 用户请求:用户通过 kubectl 客户端发起创建 CronJob 资源请求
- API Server 处理:API Server 对请求进行鉴权、准入控制,然后将请求写入 etcd
- CronJob 控制器监听:CronJob 控制器通过 Informer 机制监听 CronJob 资源变化
- 时间计算:控制器计算下一次应该执行任务的时间
- Job 创建:在调度时间到达时,控制器创建 Job 实例
- Job 执行:Job 控制器接管并执行具体的任务
- 历史管理:控制器管理 Job 的历史记录,清理超过限制的历史 Job
- 状态更新:更新 CronJob 的状态信息
四、配置
4.1 Job Setting
4.1.1 Parallel Pods
Job 支持通过 spec.parallelism 设置并行执行的 Pod 数量:
- 未设置:默认值为 1,即一次只运行一个 Pod
- 设置为 0:Job 会被暂停,直到 parallelism 被设置为大于 0 的值
- 设置为大于 0:控制器会同时创建指定数量的 Pod 运行任务
代码实现:
在 manageJob 函数中,控制器根据 spec.parallelism 和 spec.completions 计算需要创建的 Pod 数量:
1 | # pkg/controller/job/job_controller.go |
关键逻辑:
- 对于未指定
completions的 Job,并行度直接等于parallelism - 对于指定了
completions的 Job,并行度不能超过剩余需要完成的 Pod 数量 - 控制器会批量创建 Pod,每次最多创建
MaxPodCreateDeletePerSync个
4.1.2. Completions Mode
Job 支持两种完成策略:
- 非索引 Job (Non-indexed):当
spec.completions个 Pod 成功完成时,Job 完成 - 索引 Job (Indexed):当
spec.completions个索引 Pod 成功完成时,Job 完成,每个索引只需要成功完成一次
代码实现:
在 syncJob 函数中,控制器判断 Job 是否完成:
1 | # pkg/controller/job/job_controller.go |
索引 Job 的处理:
在 trackJobStatusAndRemoveFinalizers 函数中,控制器处理索引 Job 的完成状态:
1 | # pkg/controller/job/job_controller.go |
关键逻辑:
- 非索引 Job:通过统计成功完成的 Pod 数量来判断是否完成
- 索引 Job:通过记录成功完成的索引来判断是否完成,每个索引只需要成功一次
- 索引 Job 使用
status.completedIndexes字段记录已完成的索引
4.1.3 backoffLimit
Job 支持通过 spec.backoffLimit 设置失败重试次数:
- 未设置:默认值为 6,即任务失败后会重试 6 次
- 设置为 0:任务失败后不会重试
- 设置为大于 0:任务失败后会重试指定的次数
代码实现:
在 syncJob 函数中,控制器检查失败次数是否超过限制:
1 | # pkg/controller/job/job_controller.go |
pastBackoffLimitOnFailure 函数:
1 | # pkg/controller/job/job_controller.go |
关键逻辑:
- 控制器会检查失败的 Pod 数量是否超过
backoffLimit - 对于
restartPolicy == OnFailure的情况,控制器会累加容器重启次数 - 当超过失败重试次数时,Job 会被标记为失败状态
4.1.4. activeDeadlineSeconds
Job 支持通过 spec.activeDeadlineSeconds 设置任务的最大运行时间:
- 未设置(null):任务可以一直运行,直到完成或失败重试次数耗尽
- 设置为大于 0:任务运行超过指定时间后,控制器会终止所有活跃的 Pod,并将 Job 标记为失败
代码实现:
pastActiveDeadline 函数:
1 | # pkg/controller/job/job_controller.go |
在 syncJob 函数中的使用:
1 | func (jm *Controller) syncJob(ctx context.Context, key string) (forget bool, rErr error) { |
关键逻辑:
- 控制器会计算 Job 从开始到现在的运行时间
- 如果超过
activeDeadlineSeconds指定的时间,控制器会终止所有活跃的 Pod - 控制器会将 Job 标记为失败,并设置
DeadlineExceeded条件 - 对于设置了活跃期限的 Job,控制器会提前安排下次同步,以确保及时检查是否超过期限
4.2 CronJob Setting
4.2.1 Cron 表达式解析
CronJob 使用标准的 cron 表达式定义任务执行时间,格式为:
1 | ┌───────────── 分钟 (0-59) |
控制器使用 cron.ParseStandard 函数解析 cron 表达式,并计算下一次执行时间。
4.2.2 timeZone
CronJob 支持通过 spec.timeZone 设置时区,确保任务在正确的时区执行:
- 未设置:使用控制器所在环境的时区
- 设置为有效时区:使用指定的时区计算执行时间
- 设置为无效时区:控制器会记录警告事件,任务不会执行
代码实现:
在 syncCronJob 函数中:
1 | # pkg/controller/cronjob/cronjob_controllerv2.go |
formatSchedule 函数:
1 | # pkg/controller/cronjob/cronjob_controllerv2.go |
关键逻辑:
- 控制器会检查
spec.timeZone是否有效 - 如果时区无效,控制器会记录警告事件并停止执行任务
- 有效的时区会被添加到 cron 表达式中,格式为
TZ=时区 cron表达式 - 解析后的调度表达式会用于计算下次执行时间
4.2.3. concurrencyPolicy
CronJob 支持三种并发策略:
"Allow"(默认):允许 CronJob 并发运行;"Forbid":禁止并发运行,如果上一次运行尚未完成则跳过下一次运行;"Replace":取消当前正在运行的作业并将其替换为新作业。
关键逻辑:
- Allow 策略:直接创建新的 Job,不做任何特殊处理
- Forbid 策略:如果有活跃的 Job,直接返回,不创建新的
- Replace 策略:删除所有活跃的 Job,然后创建新的
代码实现:
在 syncCronJob 函数中:
1 | # pkg/controller/cronjob/cronjob_controllerv2.go |
deleteJob 函数:
1 | # pkg/controller/cronjob/cronjob_controllerv2.go |
4.2.4. startingDeadlineSeconds
CronJob 支持通过 spec.startingDeadlineSeconds 设置作业的启动截止时间:
- 未设置:任务可以在任何时间启动
- 设置为大于 0:如果任务错过调度时间超过指定秒数,任务不会启动,视为失败的作业
代码实现:
在 syncCronJob 函数中:
1 | # pkg/controller/cronjob/cronjob_controllerv2.go |
关键逻辑:
- 控制器计算下次调度时间
scheduledTime - 如果设置了
startingDeadlineSeconds,控制器检查当前时间是否超过了调度时间加上启动期限 - 如果超过了启动期限,控制器记录警告事件并跳过本次执行
- 控制器会计算下次调度时间并重新入队,等待下一次执行
五、状态管理
5.1 Job 状态管理
5.1.1 状态字段
Job 的状态包含以下关键字段:
- Active:当前活跃的 Pod 数量
- Succeeded:成功完成的 Pod 数量
- Failed:失败的 Pod 数量
- Ready:就绪的 Pod 数量
- CompletionTime:Job 完成的时间
- StartTime:Job 开始的时间
- Conditions:Job 的状态条件,包括 Complete、Failed、Suspended 等
5.1.2 状态更新
控制器通过 updateJobStatus 函数更新 Job 的状态,确保状态信息准确反映当前任务的执行状态。
5.2 CronJob 状态管理
5.2.1. 状态字段
CronJob 的状态包含以下关键字段:
- Active:当前活跃的 Job 列表
- LastScheduleTime:上次调度的时间
- LastSuccessfulTime:上次成功执行的时间
5.2.2. 状态更新
控制器在以下情况下更新 CronJob 的状态:
- 创建 Job:当创建新的 Job 时,将其添加到 Active 列表,并更新 LastScheduleTime
- Job 完成:当 Job 完成时,从 Active 列表中移除,并更新 LastSuccessfulTime
- Job 失败:当 Job 失败时,从 Active 列表中移除
六、Job 与 CronJob 的对比
| 特性 | Job | CronJob |
|---|---|---|
| 任务类型 | 一次性任务 | 周期性任务 |
| 调度方式 | 手动创建 | 时间调度 |
| 并行执行 | 支持(通过 parallelism) | 支持(通过并发策略) |
| 失败处理 | 支持(通过 backoffLimit) | 间接支持(通过 Job 的失败处理) |
| 活跃期限 | 支持(通过 activeDeadlineSeconds) | 间接支持(通过 Job 的活跃期限) |
| 历史管理 | 不支持 | 支持(通过 successfulJobsHistoryLimit 和 failedJobsHistoryLimit) |
| 时区支持 | 不支持 | 支持(通过 timeZone) |
| 启动期限 | 不支持 | 支持(通过 startingDeadlineSeconds) |
| 适用场景 | 批处理任务、数据分析、一次性操作 | 定期备份、报表生成、清理任务、周期性检查 |
旧文档链接:Job、CronJob 逻辑结构分析