Skip to content

Job 与 CronJob

Job 和 CronJob 处理的是“任务型”工作负载。Deployment 适合长期运行的服务,期望 Pod 一直存在;Job 适合跑完就退出的任务,比如数据库迁移、一次性数据修复、批量导入、离线计算;CronJob 则是在 Job 外面加了一层时间调度,适合定时备份、定时报表、定期清理。

任务型 Pod 的判断重点和在线服务不同。在线服务关注 Ready、副本数和请求延迟;Job 更关注是否按预期完成、失败后重试几次、并发多少个、旧任务保留多久,以及任务代码是否能被重复执行。

一、Job 的基本结构

一个最小 Job:

yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: db-migrate
  namespace: demo
spec:
  backoffLimit: 3
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: migrate
          image: registry.example.com/app/migrate:v1.0.0
          command: ["sh", "-c"]
          args:
            - |
              set -e
              # 迁移脚本需要支持重复执行,避免 Job 重试时造成脏数据
              ./migrate.sh

几个字段:

字段说明
backoffLimit失败后最多重试次数
restartPolicyJob 常用 NeverOnFailure,不能写 Always
templateJob 创建 Pod 使用的模板
completions需要成功完成几次
parallelism同时运行几个 Pod
activeDeadlineSeconds整个 Job 最长允许运行时间

restartPolicy: Never 时,容器失败后会新建 Pod 重试;OnFailure 时,同一个 Pod 内的容器可能被重启。排查任务日志时,这个差异会影响日志在哪个 Pod 里。

二、并行任务

批量处理可以设置完成次数和并发数:

yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: image-resize
  namespace: demo
spec:
  completions: 10
  parallelism: 3
  backoffLimit: 2
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: worker
          image: registry.example.com/tools/image-resize:v1.2.0
          env:
            - name: QUEUE_NAME
              value: resize

completions: 10 表示累计成功 10 个 Pod;parallelism: 3 表示最多同时跑 3 个。任务消费队列时比较常见,Pod 自己从队列领取任务,某个 Pod 失败后由 Job 重试补齐成功次数。

并发任务要考虑幂等。比如批量修数据、批量发消息、批量导出文件时,重试可能重复执行同一批数据。任务代码通常需要用唯一键、状态字段或任务表记录处理进度。

三、CronJob 的调度字段

CronJob 通过 crontab 表达式周期创建 Job:

yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: mysql-dump
  namespace: demo
spec:
  schedule: "10 2 * * *"
  timeZone: "Asia/Shanghai"
  concurrencyPolicy: Forbid
  startingDeadlineSeconds: 300
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 5
  jobTemplate:
    spec:
      backoffLimit: 2
      template:
        spec:
          restartPolicy: Never
          containers:
            - name: dump
              image: registry.example.com/ops/mysql-client:8.4
              command: ["sh", "-c"]
              args:
                - |
                  set -e
                  # 备份文件名带日期,避免覆盖上一轮产物
                  mysqldump -h mysql.demo.svc -uroot -p"$MYSQL_PWD" app \
                    > "/backup/app-$(date +%F).sql"
              env:
                - name: MYSQL_PWD
                  valueFrom:
                    secretKeyRef:
                      name: mysql-root
                      key: password
              volumeMounts:
                - name: backup
                  mountPath: /backup
          volumes:
            - name: backup
              persistentVolumeClaim:
                claimName: mysql-backup

concurrencyPolicy 很关键:

行为
Allow上一轮没结束,下一轮照常启动
Forbid上一轮没结束,本轮跳过
Replace新一轮启动时替换上一轮

备份、清理、报表这类任务通常用 Forbid。如果上一次备份跑了 2 小时还没结束,下一轮继续启动可能会抢锁、抢磁盘、打满数据库连接。

四、手动触发和查看状态

查看 CronJob 和它创建的 Job:

bash
kubectl get cronjob -n demo
kubectl get job -n demo
kubectl get pod -n demo -l job-name=mysql-dump-28600150

从 CronJob 手动创建一次 Job:

bash
# 手动触发一次,用于验证镜像、Secret、PVC 和命令是否正常
kubectl create job mysql-dump-manual \
  --from=cronjob/mysql-dump \
  -n demo

查看任务日志:

bash
kubectl logs job/mysql-dump-manual -n demo
kubectl describe job mysql-dump-manual -n demo
kubectl get events -n demo --sort-by=.lastTimestamp

Job 失败时,先看 kubectl describe job 里的 Pods StatusesBackoffLimitExceededDeadlineExceeded,再看具体 Pod 的日志和事件。镜像拉取失败、PVC 挂载失败、Secret 不存在,都可能导致任务还没执行到业务命令就失败。

五、清理和保留

CronJob 会按 successfulJobsHistoryLimitfailedJobsHistoryLimit 保留历史 Job。一次性 Job 可以设置 TTL:

yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: temp-check
  namespace: demo
spec:
  ttlSecondsAfterFinished: 3600
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: check
          image: busybox:1.36
          command: ["sh", "-c", "date && nslookup kubernetes.default.svc"]

TTL 到期后,Job 和它创建的 Pod 会被清理。日志如果只存在 Pod stdout/stderr,清理后就只能从日志平台查;没有日志采集链路的环境里,排查历史失败任务会很困难。

六、常见故障

现象常见原因
Job 一直 0/1Pod Pending、镜像拉取失败、调度失败、PVC 没绑定
Job 反复失败命令退出码非 0、Secret 错误、依赖服务不可达
CronJob 没按时跑suspend: true、时区不对、控制平面异常、错过 startingDeadlineSeconds
多个任务叠在一起concurrencyPolicy: Allow,上一轮执行时间超过调度周期
任务成功但数据不对任务逻辑不幂等,重试或并发导致重复处理

任务类资源不适合只看 kubectl get jobCOMPLETIONS。一次失败重试、一次跳过、一次超时,背后的处理方式完全不同,Events、日志和任务自身的业务状态都要对上。