Kubernetes CronJob 定时任务

FreeGuideOnline 19阅读 2026-07-12

分 时 日 月 星期


每个字段的取值范围:

| 字段   | 允许值          | 特殊字符         |
|--------|----------------|------------------|
| 分     | 0-59           | `* / , -`       |
| 时     | 0-23           | `* / , -`       |
| 日     | 1-31           | `* / , - ?`     |
| 月     | 1-12 或 JAN-DEC | `* / , -`       |
| 星期   | 0-6 或 SUN-SAT  | `* / , - ?`     |

> **注意**:在 Kubernetes 中,`星期`字段的 `0` 和 `7` 都代表周日(与 Vixie cron 不同,后者 7 无效)。

### 特殊字符

- `*` 匹配所有值。
- `,` 分隔多个值,例如 `1,3,5`。
- `-` 表示范围,例如 `1-5`。
- `/` 表示步长,例如 `*/15` 表示每 15 分钟。
- `?` 在日或星期字段中表示不指定,避免冲突(通常用于其中一个字段为 `*` 时)。

### 常用示例

| 表达式                  | 含义                                                   |
|-------------------------|--------------------------------------------------------|
| `0 2 * * *`             | 每天凌晨 2:00 执行                                     |
| `*/10 * * * *`          | 每 10 分钟执行一次                                     |
| `0 9-17 * * 1-5`        | 工作日上午 9 点到下午 5 点之间每小时执行               |
| `30 8 1 * *`            | 每月 1 号上午 8:30 执行                                |
| `0 0 * * 0`             | 每周日午夜执行                                         |
| `@hourly`               | 每小时执行一次(同 `0 * * * *`)                       |
| `@daily` / `@midnight`  | 每天午夜执行(同 `0 0 * * *`)                         |
| `@weekly`               | 每周日午夜执行(同 `0 0 * * 0`)                       |
| `@monthly`              | 每月第一天午夜执行(同 `0 0 1 * *`)                   |
| `@yearly` / `@annually` | 每年 1 月 1 日午夜执行(同 `0 0 1 1 *`)               |

Kubernetes 还支持一个可选的 **秒** 字段(置于最前面),此时格式变为六字段:`秒 分 时 日 月 星期`,但这需要启用 `CronJobV2` 功能门控(较新版本默认支持)。建议尽量使用五字段,避免兼容性问题。

---

## 创建第一个 CronJob

下面是一个最简单的 CronJob 示例,它每分钟执行一次 `echo "Hello from CronJob"` 命令,并使用 `busybox` 镜像。

```yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello-cron
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox:latest
            command: ["/bin/sh", "-c", "echo Hello from CronJob; date"]
          restartPolicy: OnFailure

将上述内容保存为 cronjob.yaml,然后运行:

kubectl apply -f cronjob.yaml

查看 CronJob 状态:

kubectl get cronjob hello-cron

查看由该 CronJob 创建的 Job:

kubectl get jobs

查看 Pod 日志:

# 先获取 Pod 名称
kubectl get pods
# 查看日志
kubectl logs <pod-name>

你会看到每隔一分钟有一个新的 Job 被创建并执行。注意,根据 startingDeadlineSeconds 等配置,有时可能会跳过某个时间点。


CronJob 核心字段说明

CronJob 的 YAML 定义主要包含以下几个部分,了解它们有助于精确控制任务行为。

spec.schedule

必填字段,Cron 表达式字符串,定义任务执行的时间计划。

spec.jobTemplate

必填字段,指定要创建的 Job 模板。其结构与标准的 Job 对象的 spec 相同,你可以在此定义容器、命令、环境变量、挂载卷等。设置 restartPolicy 时,对于 Job 应为 NeverOnFailure,不能使用 Always

spec.concurrencyPolicy

并发策略,决定当上一个 Job 尚未完成,而新的调度时间到达时如何处理。可选值:

  • Allow(默认):允许并发执行,新旧 Job 同时运行。
  • Forbid:禁止并发,如果前一个 Job 未完成则跳过本次执行。
  • Replace:取消正在运行的旧 Job,并用新 Job 替代它。

示例:

spec:
  concurrencyPolicy: Forbid

spec.successfulJobsHistoryLimit

保留的成功完成 Job 的数量,默认为 3。设为 0 表示不保留任何成功的 Job。

spec.failedJobsHistoryLimit

保留的失败 Job 的数量,默认为 1。设为 0 表示不保留任何失败的 Job。

spec.startingDeadlineSeconds

可选字段,表示如果因为某种原因错过了调度时间(例如控制器重启),仍然允许启动 Job 的最大秒数。如果超过这个截止时间,则直接跳过该次调度。默认无限制。例如设置为 200 表示若错过调度,在 200 秒内仍可补启动,否则放弃。

spec:
  startingDeadlineSeconds: 200

spec.suspend

布尔值,如果设为 true,挂起后续的所有调度,但不影响已经运行的 Job。可用于临时停止定时任务。

spec:
  suspend: true

spec.timeZone

Kubernetes 1.24 起(默认启用 CronJobTimeZone 功能),允许为 CronJob 设置时区。若不指定则使用控制器所在时区(一般为 UTC)。要使用时区,格式为 IANA 时区数据库名称,如 "America/New_York"

spec:
  timeZone: "Asia/Shanghai"
  schedule: "0 9 * * 1-5"

这样上述 CronJob 会在北京时间工作日上午 9 点执行。


并发策略与历史记录限制

在实际应用中,合理设置并发策略和历史保留数量至关重要。

并发策略选择指南

  • 数据一致性要求高的任务:使用 Forbid,防止多个实例同时操作同一份数据。
  • 可重入或幂等的任务:使用 Allow,增加吞吐。
  • 希望总是执行最新请求的任务:使用 Replace,但需注意旧任务可能产生副作用(如未保存状态)。

历史 Job 清理

如果不限制保留的 Job 数量,集群中可能会堆积大量已完成的 Job 资源,影响 kubectl 操作性能并占用 etcd 存储。建议根据集群规模和任务频率设置合理的值。例如每小时执行一次的任务,保留 3 个成功 Job 通常足够;每天都执行的任务,可以保留 7 个便于查看一周记录。


启动截止时间与挂起

startingDeadlineSecondssuspend 是控制 CronJob 行为的两个高级特性。

启动截止时间的妙用

假设你有一个每 5 分钟执行一次的任务,但有时集群控制器因故障暂停了 10 分钟,当控制器恢复后,应该如何处理错过的两次调度?

  • 如果不设 startingDeadlineSeconds,控制器会补执行这些错过的 Job(可能会瞬间创建大量 Job)。
  • 如果设置 startingDeadlineSeconds: 100,控制器检查每个错过的调度,若从应该执行的时间点到现在的差值超过 100 秒,则直接丢弃,不再补创建。

这在需要保证任务的时效性而非完整性的场景中很有用。

挂起 CronJob

你可以动态调整 suspend 字段来暂停或恢复任务:

kubectl patch cronjob hello-cron -p '{"spec":{"suspend":true}}'

恢复:

kubectl patch cronjob hello-cron -p '{"spec":{"suspend":false}}'

挂起后,即使到了 schedule 时间,控制器也不会创建新的 Job。已经运行的 Job 不受影响。


常见使用场景与示例

数据库备份 CronJob

以下示例每晚 2:30 执行 PostgreSQL 备份,并将备份文件上传到 AWS S3(需要挂载密钥)。

apiVersion: batch/v1
kind: CronJob
metadata:
  name: db-backup
spec:
  schedule: "30 2 * * *"
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 2
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: postgres:14
            env:
            - name: PGHOST
              value: "postgres-service"
            - name: PGUSER
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: username
            - name: PGPASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: password
            command:
            - /bin/bash
            - -c
            - |
              pg_dump -Fc mydb > /backups/mydb_$(date +%Y%m%d_%H%M%S).dump
              aws s3 cp /backups/mydb_*.dump s3://my-bucket/backups/              
            volumeMounts:
            - name: backup-storage
              mountPath: /backups
          volumes:
          - name: backup-storage
            emptyDir: {}
          restartPolicy: OnFailure

定时调用 HTTP 接口

每 10 分钟向某个 webhook 发送请求:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: webhook-trigger
spec:
  schedule: "*/10 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: curl
            image: curlimages/curl:latest
            command:
            - /bin/sh
            - -c
            - 'curl -X POST -d "time=$(date)" https://hooks.example.com/trigger'
          restartPolicy: OnFailure

证书自动续期(配合 cert-manager)

若使用 cert-manager,可结合 CronJob 解决某些边缘情况,但通常 cert-manager 本身就有 Certificate 资源自动续期,此处仅作示例:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: cert-renew
spec:
  schedule: "0 0 * * 0"   # 每周日
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: renew
            image: certbot/certbot
            args:
            - renew
            - --quiet
            - --deploy-hook
            - "kubectl -n default create configmap renewed-certs --from-file=/etc/letsencrypt/live/"
          restartPolicy: Never

调试与故障排查

当 CronJob 没有按预期执行时,可以通过以下步骤定位问题。

检查 CronJob 状态

kubectl describe cronjob <名称>

输出中会包含 Last Schedule TimeActive Jobs 等信息。如果 Last Schedule Time 一直为 None,可能是 schedule 表达式有误或 suspend 为 true。

检查 Job 对象

kubectl get jobs --selector=job-name=<cronjob名称>-<时间戳>

通过 describe job 查看事件,判断是否因为资源不足、镜像拉取失败等原因导致 Job 无法启动。

查看 Pod 日志

kubectl logs job/<job-name>