CronJob
Kubernetes v1.21 [stable]
CronJob 根据重复的调度创建Job。
CronJob 用于执行定期的计划操作,例如备份、生成报告等。一个 CronJob 对象类似于 Unix 系统上 crontab(cron 表)文件中的一行。它根据给定调度(使用 Cron 格式编写)定期运行一个 Job。
CronJob 有限制和特殊性。例如,在某些情况下,单个 CronJob 可以创建多个并发 Job。请参见下面的限制。
当控制平面为 CronJob 创建新的 Job 并(间接)创建 Pod 时,CronJob 的 .metadata.name
是这些 Pod 命名基础的一部分。CronJob 的名称必须是有效的DNS 子域名值,但这可能导致 Pod 主机名出现意外结果。为了获得最佳兼容性,名称应遵循更严格的DNS Label 规则。即使名称是 DNS 子域名,其长度也不能超过 52 个字符。这是因为 CronJob 控制器会自动在你提供的名称后附加 11 个字符,并且 Job 名称的长度不能超过 63 个字符。
示例
这个 CronJob 示例清单每分钟打印当前时间和问候消息
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "* * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox:1.28
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
(使用 CronJob 运行自动化任务 更详细地介绍了此示例)。
编写 CronJob 规范
调度语法
.spec.schedule
字段是必需的。该字段的值遵循 Cron 语法
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12)
# │ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday)
# │ │ │ │ │ OR sun, mon, tue, wed, thu, fri, sat
# │ │ │ │ │
# │ │ │ │ │
# * * * * *
例如,0 3 * * 1
表示此任务计划在每周一凌晨 3 点运行。
该格式还包含扩展的 "Vixie cron" 步进值(step values)。如 FreeBSD 手册中所解释:
步进值可以与范围结合使用。在范围后加上
/<number>
指定在该范围内跳过该数值。例如,0-23/2
可用于小时字段,表示每隔一小时执行一次命令(V7 标准中的替代方法是0,2,4,6,8,10,12,14,16,18,20,22
)。步进值也允许跟在星号后面,所以如果你想表示“每两小时”,只需使用*/2
。
注意
调度中的问号 (?
) 与星号 *
含义相同,即它代表给定字段的任何可用值。除了标准语法外,还可以使用一些宏,例如 @monthly
条目 | 描述 | 等同于 |
---|---|---|
@yearly (或 @annually) | 每年 1 月 1 日午夜运行一次 | 0 0 1 1 * |
@monthly | 每月第一天午夜运行一次 | 0 0 1 * * |
@weekly | 每周日凌晨午夜运行一次 | 0 0 * * 0 |
@daily (或 @midnight) | 每天午夜运行一次 | 0 0 * * * |
@hourly | 每小时开始时运行一次 | 0 * * * * |
要生成 CronJob 调度表达式,你还可以使用诸如 crontab.guru 这样的网络工具。
Job 模板
.spec.jobTemplate
定义了 CronJob 创建的 Job 的模板,它是必需的。它与 Job 的 Schema 完全相同,只是它被嵌套,并且没有 apiVersion
或 kind
。你可以为模板 Job 指定一些公共的元数据,例如标签或注解。有关如何编写 Job .spec
的信息,请参阅编写 Job 规范。
延迟 Job 启动的截止时间
.spec.startingDeadlineSeconds
字段是可选的。该字段定义了一个(以整秒计的)启动 Job 的截止时间,如果该 Job 因任何原因错过了其计划时间。
错过截止时间后,CronJob 会跳过该 Job 实例(未来的 Job 仍会按计划调度)。例如,如果你有一个每天运行两次的备份 Job,你可能允许它延迟最多 8 小时开始,但不能再晚,因为再晚进行的备份就没有用了:在这种情况下你宁愿等待下一个计划运行时间。
对于错过其配置截止时间的 Job,Kubernetes 将它们视为失败的 Job。如果你未为 CronJob 指定 startingDeadlineSeconds
,则 Job 的执行没有截止时间。
如果设置了 .spec.startingDeadlineSeconds
字段(非空),CronJob 控制器会测量 Job 计划创建时间与当前时间之间的时间差。如果差值高于该限制,它将跳过此次执行。
例如,如果设置为 200
,则允许在实际调度时间后最多 200 秒内创建 Job。
并发策略
.spec.concurrencyPolicy
字段也是可选的。它指定如何处理由此 CronJob 创建的 Job 的并发执行。规范只能指定以下并发策略之一:
Allow
(默认): CronJob 允许并发运行 Job。Forbid
:CronJob 不允许并发运行;如果到时间要启动一个新的 Job 运行,而上一个 Job 运行尚未完成,则 CronJob 会跳过新的 Job 运行。另请注意,当上一个 Job 运行完成时,仍然会考虑.spec.startingDeadlineSeconds
,并可能导致启动一个新的 Job 运行。Replace
:如果到时间要启动一个新的 Job 运行,而上一个 Job 运行尚未完成,则 CronJob 会用新的 Job 运行替换当前正在运行的 Job 运行。
请注意,并发策略仅适用于由同一个 CronJob 创建的 Job。如果有多个 CronJob,它们各自的 Job 始终允许并发运行。
暂停调度
你可以通过将可选的 .spec.suspend
字段设置为 true 来暂停 CronJob 的 Job 执行。该字段默认为 false。
此设置**不**影响 CronJob 已启动的 Job。
如果你确实将该字段设置为 true,则所有后续执行都将被暂停(它们仍然被调度,但 CronJob 控制器不会启动 Job 来运行任务),直到你取消暂停 CronJob。
Job 历史记录限制
.spec.successfulJobsHistoryLimit
和 .spec.failedJobsHistoryLimit
字段指定应保留多少个已完成和失败的 Job。这两个字段都是可选的。
.spec.successfulJobsHistoryLimit
:此字段指定要保留的成功完成的 Job 的数量。默认值为3
。将此字段设置为0
将不保留任何成功 Job。.spec.failedJobsHistoryLimit
:此字段指定要保留的失败完成的 Job 的数量。默认值为1
。将此字段设置为0
将不保留任何失败 Job。
另一种自动清理 Job 的方法,请参阅自动清理已完成的 Job。
时区
Kubernetes v1.27 [stable]
对于未指定时区的 CronJob,kube-controller-manager 会根据其本地时区解释调度。
你可以通过将 .spec.timeZone
设置为有效时区的名称来为 CronJob 指定时区。例如,设置 .spec.timeZone: "Etc/UTC"
会指示 Kubernetes 根据协调世界时解释调度。
Go 标准库中的时区数据库包含在二进制文件中,并在系统上没有外部数据库可用时用作回退。
CronJob 的限制
不受支持的时区规范
在 .spec.schedule
中使用 CRON_TZ
或 TZ
变量指定时区是**非官方支持**的(并且从未支持过)。如果你尝试设置一个包含 TZ
或 CRON_TZ
时区规范的调度,Kubernetes 将无法通过验证并创建或更新资源。你应该改用时区字段来指定时区。
修改 CronJob
根据设计,CronJob 包含**新** Job 的模板。如果你修改现有的 CronJob,你所做的更改将应用于修改完成后开始运行的新 Job。已经开始运行的 Job(及其 Pod)将继续运行而不会改变。也就是说,CronJob **不会**更新现有 Job,即使它们仍在运行。
Job 创建
CronJob 大约在其调度的每次执行时间创建一次 Job 对象。调度是近似的,因为在某些情况下可能会创建两个 Job,或者可能不创建任何 Job。Kubernetes 尽量避免这些情况,但不能完全阻止它们。因此,你定义的 Job 应该是**幂等**的。
从 Kubernetes v1.32 开始,CronJob 会为其创建的 Job 应用一个注解 batch.kubernetes.io/cronjob-scheduled-timestamp
。此注解指示 Job 最初的计划创建时间,格式为 RFC3339。
如果 startingDeadlineSeconds
设置为较大的值或未设置(默认),并且 concurrencyPolicy
设置为 Allow
,则 Job 至少会运行一次。
注意
如果startingDeadlineSeconds
设置的值小于 10 秒,CronJob 可能不会被调度。这是因为 CronJob 控制器每 10 秒检查一次。对于每个 CronJob,CronJob 控制器会检查从上次计划时间到现在期间错过了多少次调度。如果错过调度次数超过 100,则它不会启动 Job 并记录错误。
Cannot determine if job needs to be started. Too many missed start time (> 100). Set or decrease .spec.startingDeadlineSeconds or check clock skew.
值得注意的是,如果设置了 startingDeadlineSeconds
字段(非 nil
),控制器会计算从 startingDeadlineSeconds
的值到现在期间错过了多少个 Job,而不是从上次计划时间到现在。例如,如果 startingDeadlineSeconds
是 200
,则控制器会计算最近 200 秒内错过了多少个 Job。
如果 CronJob 在其计划时间未能创建,则被计为错过。例如,如果 concurrencyPolicy
设置为 Forbid
,并且当先前的调度仍在运行时尝试调度 CronJob,则会被计为错过。
例如,假设一个 CronJob 设置为从 08:30:00
开始每分钟调度一个新的 Job,并且其 startingDeadlineSeconds
字段未设置。如果 CronJob 控制器恰好从 08:29:00
到 10:21:00
处于宕机状态,则 Job 不会启动,因为错过其调度的 Job 数量大于 100。
为了进一步说明这个概念,假设一个 CronJob 设置为从 08:30:00
开始每分钟调度一个新的 Job,并且其 startingDeadlineSeconds
设置为 200 秒。如果 CronJob 控制器恰好在与上一个示例相同的时段(08:29:00
到 10:21:00
)处于宕机状态,Job 仍然会在 10:22:00 启动。这是因为控制器现在检查最近 200 秒内错过了多少次调度(即 3 次错过调度),而不是从上次计划时间到现在。
CronJob 仅负责创建符合其调度的 Job,而 Job 又负责管理其代表的 Pod。
接下来
- 了解 Pod 和 Job,这两个 CronJob 依赖的概念。
- 阅读关于 CronJob 的
.spec.schedule
字段详细的格式。 - 有关创建和使用 CronJob 的说明,以及 CronJob 清单的示例,请参阅使用 CronJob 运行自动化任务。
CronJob
是 Kubernetes REST API 的一部分。有关更多详细信息,请阅读 CronJob API 参考。