Kubernetes CronJob 完整指南:从 Cron 表达式到生产配置

8 阅读4分钟

Kubernetes CronJob 完整指南:从 Cron 表达式到生产配置

工具已上线,可直接使用:www.heycron.com

作为后端开发,定时任务是绕不开的需求。在 Kubernetes 环境下,CronJob 是官方推荐的定时任务方案。本文从 Cron 表达式基础讲起,覆盖 CronJob 的完整配置、常见坑和生产最佳实践。


一、Cron 表达式快速入门

Cron 表达式由 5 个字段组成:

┌───────────── 分钟 (0-59)
│ ┌───────────── 小时 (0-23)
│ │ ┌───────────── 日 (1-31)
│ │ │ ┌───────────── 月 (1-12)
│ │ │ │ ┌───────────── 星期 (0-60=周日)
│ │ │ │ │
* * * * *

常用示例:

表达式含义
0 9 * * 1每周一早上 9 点
0 0 * * *每天凌晨 0 点
*/5 * * * *每 5 分钟
0 9 * * 1-5工作日每天 9 点
0 0 1 * *每月 1 号凌晨
0 */6 * * *每 6 小时

💡 记不住语法?可以用 Hey Cron 直接输入中文描述生成表达式,比如输入"每周一早上9点"自动生成 0 9 * * 1


二、Kubernetes CronJob 基础配置

最简单的 CronJob

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
  namespace: default
spec:
  schedule: "0 9 * * 1"        # 每周一早上 9 点
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            command: ["/bin/sh", "-c", "echo Hello from CronJob"]
          restartPolicy: OnFailure

完整字段说明

apiVersion: batch/v1
kind: CronJob
metadata:
  name: my-cronjob
  namespace: production
spec:
  schedule: "0 9 * * 1"

  # 时区设置(K8s 1.25+ 支持)
  timeZone: "Asia/Shanghai"

  # 并发策略:Allow(默认)/ Forbid / Replace
  concurrencyPolicy: Forbid

  # 调度失败后保留的历史 Job 数量
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1

  # 错过调度时间后的最大补跑窗口(秒)
  startingDeadlineSeconds: 60

  # 是否暂停(不删除 CronJob,只停止调度)
  suspend: false

  jobTemplate:
    spec:
      # Job 超时时间(秒)
      activeDeadlineSeconds: 300

      # 失败重试次数
      backoffLimit: 3

      template:
        spec:
          containers:
          - name: worker
            image: my-image:latest
            resources:
              requests:
                memory: "64Mi"
                cpu: "100m"
              limits:
                memory: "128Mi"
                cpu: "200m"
          restartPolicy: OnFailure

三、时区问题(最常踩的坑)

Kubernetes CronJob 默认使用 UTC 时间。如果你的集群在中国,0 9 * * 1 实际执行的是北京时间 17:00,不是 9:00。

解决方案一:手动换算(简单粗暴)

北京时间 = UTC + 8,所以:

  • 想要北京时间 9:00 执行 → 填 0 1 * * 1(UTC 1:00)

解决方案二:使用 timeZone 字段(推荐,K8s 1.25+)

spec:
  schedule: "0 9 * * 1"
  timeZone: "Asia/Shanghai"   # 直接指定时区,填写北京时间即可

查看集群版本:

kubectl version --short

解决方案三:在容器内设置时区

containers:
- name: worker
  image: my-image:latest
  env:
  - name: TZ
    value: "Asia/Shanghai"

四、concurrencyPolicy 详解

这个字段控制当上一个 Job 还没跑完、下一个调度时间到了该怎么办:

行为
Allow(默认)允许并发,新 Job 照常启动
Forbid禁止并发,跳过本次调度
Replace取消正在运行的 Job,启动新 Job

生产建议: 大多数情况用 Forbid,避免任务堆积。数据处理类任务尤其要设置这个。

concurrencyPolicy: Forbid

五、常用操作命令

# 查看所有 CronJob
kubectl get cronjob -n <namespace>

# 查看 CronJob 详情(包括上次/下次执行时间)
kubectl describe cronjob my-cronjob -n <namespace>

# 查看由 CronJob 创建的 Job
kubectl get jobs -n <namespace> --selector=job-name

# 手动触发一次 CronJob(用于测试)
kubectl create job --from=cronjob/my-cronjob manual-trigger -n <namespace>

# 暂停 CronJob(不删除,只停止调度)
kubectl patch cronjob my-cronjob -p '{"spec":{"suspend":true}}' -n <namespace>

# 恢复 CronJob
kubectl patch cronjob my-cronjob -p '{"spec":{"suspend":false}}' -n <namespace>

# 删除 CronJob(同时删除其创建的所有 Job)
kubectl delete cronjob my-cronjob -n <namespace>

# 查看 Job 的 Pod 日志
kubectl logs -l job-name=my-cronjob-<timestamp> -n <namespace>

六、生产最佳实践

1. 永远设置资源限制

resources:
  requests:
    memory: "64Mi"
    cpu: "100m"
  limits:
    memory: "256Mi"
    cpu: "500m"

不设置资源限制的 CronJob 在高负载时会把节点打垮。

2. 设置超时时间

spec:
  activeDeadlineSeconds: 600   # Job 最多跑 10 分钟,超时强制终止

避免任务卡死后一直占用资源。

3. 控制历史记录数量

successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1

默认保留 3 个成功和 1 个失败的 Job 记录,调小一点节省 etcd 空间。

4. 幂等性设计

CronJob 可能因为各种原因被重复执行(startingDeadlineSeconds 内的补跑),你的任务代码必须是幂等的——执行一次和执行多次的结果要一致。

5. 用 ConfigMap 管理脚本

volumes:
- name: scripts
  configMap:
    name: cronjob-scripts
    defaultMode: 0755
containers:
- name: worker
  volumeMounts:
  - name: scripts
    mountPath: /scripts
  command: ["/scripts/run.sh"]

把脚本放进 ConfigMap,更新脚本不需要重新构建镜像。


七、监控和告警

检查 Job 是否成功

kubectl get jobs -n <namespace> \
  --sort-by=.status.startTime \
  | tail -5

推荐监控指标(Prometheus)

  • kube_cronjob_next_schedule_time:下次执行时间
  • kube_job_status_failed:失败的 Job 数量
  • kube_job_complete:成功完成的 Job 数量

建议在 Grafana 里配一个 CronJob 执行状态面板,失败时触发 AlertManager 告警。


总结

场景推荐配置
不允许并发执行concurrencyPolicy: Forbid
需要北京时间timeZone: "Asia/Shanghai"(K8s 1.25+)
任务有超时风险activeDeadlineSeconds: 300
生产环境必须设置 resources.limits

Kubernetes CronJob 配置虽然字段多,但核心就是这几个:scheduleconcurrencyPolicytimeZoneactiveDeadlineSeconds。把这几个配对了,大部分场景都能覆盖。


如果你经常需要在不同平台之间转换 Cron 表达式(K8s / GitHub Actions / Jenkins / Airflow),可以试试 Hey Cron,输入自然语言直接生成多平台配置,省去反复查文档的时间。