定时作业

CronJob 按照重复的日程启动一次性 Job。
特性状态: 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 标签 规则。即使名称是 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”步进值。如 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 具有完全相同的模式,只是它是嵌套的,没有 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,它允许 Job 在实际调度后最多 200 秒内创建。

并发策略

.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 变量指定时区**不被官方支持**(也从未被支持)。如果你尝试设置包含 TZCRON_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 至少会运行一次。

对于每个 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 设置为从 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。

下一步

  • 了解 CronJob 所依赖的两个概念:PodJob
  • 阅读有关 CronJob .spec.schedule 字段的详细格式
  • 有关创建和使用 CronJob 的说明,以及 CronJob 清单示例,请参阅使用 CronJob 运行自动化任务
  • CronJob 是 Kubernetes REST API 的一部分。有关更多详细信息,请参阅 CronJob API 参考。
上次修改时间:2025 年 1 月 7 日 下午 5:38 PST:更新 CronJob 不支持的时区字段信息 (d0fa9da4b2)