CronJob

CronJob 按照重复的计划启动一次性 Job。
功能状态: Kubernetes v1.21 [稳定]

CronJob 按照重复的计划创建 Jobs

CronJob 用于执行定期的计划操作,例如备份、报告生成等。一个 CronJob 对象就像 Unix 系统上 crontab (cron 表) 文件中的一行。它按照给定的时间表(使用 Cron 格式编写)定期运行一个 Job。

CronJob 有局限性和特殊性。例如,在某些情况下,单个 CronJob 可以创建多个并发的 Job。请参阅下面的局限性

当控制平面为 CronJob 创建新的 Job 和(间接地)Pod 时,CronJob 的 .metadata.name 是命名这些 Pod 的基础。CronJob 的名称必须是有效的 DNS 子域名 值,但这可能会为 Pod 主机名产生意外的结果。为了获得最佳兼容性,该名称应遵循 DNS 标签的更严格规则。即使名称是 DNS 子域名,该名称的长度也不能超过 52 个字符。这是因为 CronJob 控制器会自动将 11 个字符附加到你提供的名称,并且 Job 名称的长度不能超过 63 个字符。

示例

此示例 CronJob 清单每分钟打印当前时间和一条 hello 消息

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”步长值。正如 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 之类的 Web 工具。

Job 模板

.spec.jobTemplate 定义了 CronJob 创建的 Job 的模板,并且是必需的。它与 Job 具有完全相同的模式,只是它是嵌套的并且没有 apiVersionkind。你可以为模板化的 Job 指定公共元数据,例如 标签注解。有关编写 Job .spec 的信息,请参阅编写 Job 规范

延迟 Job 启动的截止时间

.spec.startingDeadlineSeconds 字段是可选的。此字段定义了启动 Job 的截止时间(以整秒为单位),如果该 Job 由于任何原因错过了其计划时间。

在错过截止时间后,CronJob 将跳过该 Job 实例(将来的发生仍然会安排)。例如,如果你有一个每天运行两次的备份 Job,你可能会允许它最多延迟 8 小时启动,但不能更晚,因为任何更晚进行的备份都将无用:你宁愿等待下一次计划运行。

对于错过其配置截止时间的 Job,Kubernetes 将它们视为失败的 Job。如果你没有为 CronJob 指定 startingDeadlineSeconds,则 Job 实例没有截止时间。

如果设置了 .spec.startingDeadlineSeconds 字段(不为 null),则 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 [稳定]

对于未指定时区的 CronJob,kube-controller-manager 会根据其本地时区解释调度。

你可以通过将 .spec.timeZone 设置为有效的时区名称来为 CronJob 指定时区。 例如,设置 .spec.timeZone: "Etc/UTC" 会指示 Kubernetes 根据协调世界时解释调度。

Go 标准库中的时区数据库包含在二进制文件中,并用作系统上没有外部数据库时的后备。

CronJob 限制

不支持的时区规范

使用 .spec.schedule 中的 CRON_TZTZ 变量指定时区**未获得正式支持**(并且从未支持过)。

从 Kubernetes 1.29 开始,如果你尝试设置包含 TZCRON_TZ 时区规范的调度,Kubernetes 将会创建资源失败并返回验证错误。 对于已使用 TZCRON_TZ 的 CronJob 的更新,客户端将继续报告警告

修改 CronJob

按照设计,CronJob 包含一个*新* Job 的模板。 如果你修改现有的 CronJob,你所做的更改将应用于在你的修改完成后开始运行的新 Job。 已经启动的 Job(及其 Pod)会继续运行,而不会发生更改。 也就是说,即使 Job 仍在运行,CronJob 也**不会**更新现有的 Job。

Job 创建

CronJob 大约在其调度执行时间的每次执行中创建一个 Job 对象。 调度是近似的,因为在某些情况下可能会创建两个 Job,或者可能不创建 Job。 Kubernetes 尝试避免这些情况,但并不能完全防止它们。 因此,你定义的 Job 应该是**幂等的**。

从 Kubernetes v1.32 开始,CronJob 会将 batch.kubernetes.io/cronjob-scheduled-timestamp 注解应用于其创建的 Job。 此注解指示 Job 最初的计划创建时间,并且采用 RFC3339 格式。

如果将 startingDeadlineSeconds 设置为较大的值或不设置(默认值),并且将 concurrencyPolicy 设置为 Allow,则 Job 将始终至少运行一次。

对于每个 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,而不是从上次调度时间到现在为止。 例如,如果 startingDeadlineSeconds200,则控制器会计算在过去 200 秒内错过了多少个 Job。

如果 CronJob 未能在其预定时间创建,则被视为错过。 例如,如果 concurrencyPolicy 设置为 Forbid,并且当之前的调度仍在运行时尝试调度 CronJob,则该 CronJob 将被视为错过。

例如,假设一个 CronJob 设置为从 08:30:00 开始每分钟调度一个新 Job,并且未设置其 startingDeadlineSeconds 字段。 如果 CronJob 控制器碰巧从 08:29:0010:21:00 处于关闭状态,则 Job 将不会启动,因为错过其调度的错过 Job 数量大于 100。

为了进一步说明这个概念,假设一个 CronJob 设置为从 08:30:00 开始每分钟调度一个新 Job,并且其 startingDeadlineSeconds 设置为 200 秒。 如果 CronJob 控制器碰巧与前面的示例相同的时间段(08:29:0010:21:00)处于关闭状态,则 Job 仍将在 10:22:00 启动。 这是因为控制器现在检查了过去 200 秒内错过了多少个调度(即 3 个错过的调度),而不是从上次调度时间到现在为止。

CronJob 仅负责创建与其调度匹配的 Job,而 Job 反过来负责管理其代表的 Pod。

下一步

  • 了解 PodJob,这是 CronJob 所依赖的两个概念。
  • 阅读 CronJob .spec.schedule 字段的详细格式
  • 有关创建和使用 CronJob 的说明,以及 CronJob 清单的示例,请参阅使用 CronJob 运行自动化任务
  • CronJob 是 Kubernetes REST API 的一部分。 请阅读 CronJob API 参考了解更多详细信息。