“跑一次就结束的容器"在 K8s 里和长期服务完全是两套玩法:Deployment 追求"永远在跑”,而批处理追求"跑完、跑对、失败能重试、别占着资源不还"。K8s 用 Job 保证"至少要跑完 N 次",用 CronJob 在指定时间触发,再配合工作队列让成百上千的任务被一小撮 Worker 消费。本指南把 Job/CronJob 的语义、退避与清理、工作队列模式、批处理平台(Kueue/Volcano)与监控告警一次讲透。
目录
- 1. Job 核心语义:completions 与 parallelism
- 2. Job 生命周期与重试:backoffLimit
- 3. 指数退避、TTL 与清理
- 4. CronJob:调度、时区与并发策略
- 5. 工作队列模式:多 Pod 消费
- 6. 批处理平台对比:Kueue 与 Volcano
- 7. 批处理资源管理与排队
- 8. 批处理监控与告警
- 9. 生产最佳实践
1. Job 核心语义:completions 与 parallelism
1.1 Job 在解决什么问题
Deployment 期望 N 个副本一直运行,而 Job 期望完整执行一次/多次、执行完就结束。Job 有三个核心字段:completions(总共要成功多少次)、parallelism(同时并行跑多少个 Pod)、backoffLimit(失败后最多重试几次)。它支持两种模型:非并行(completions: 1, parallelism: 1,一件事跑一次)与并行(completions: N, parallelism: M,M 个 Pod 一起处理,凑够 N 个成功即完成)。
1.2 基础 Job 示例
apiVersion: batch/v1
kind: Job
metadata:
name: pi-compute
spec:
completions: 4
parallelism: 2
template:
spec:
restartPolicy: Never
containers:
- name: pi
image: perl:5.34
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
上面这个 Job 会同时起 2 个 Pod,每个成功后总完成数 +1,直到 4 个都成功才标记 Complete。注意 Job 的 Pod 的 restartPolicy 必须设为 Never 或 OnFailure,不能是 Always,否则"失败"对 Job 来说永远不结束。
2. Job 生命周期与重试:backoffLimit
2.1 失败与重试
restartPolicy: Never 时,Pod 失败后由 Job 控制器新建一个 Pod,这算一次"尝试";restartPolicy: OnFailure 则在同一 Pod 内由 kubelet 重启容器。重试上限由 spec.backoffLimit 控制(默认 6),达到上限后 Job 被标记为 Failed,不再重建 Pod。要注意区分:completions 数的是成功次数,backoffLimit 数的是失败尝试次数。
2.2 精确控制重试
apiVersion: batch/v1
kind: Job
metadata:
name: etl-retry
spec:
backoffLimit: 3
activeDeadlineSeconds: 3600
template:
spec:
restartPolicy: Never
containers:
- name: etl
image: my-etl:1.4
三种终止条件别搞混:activeDeadlineSeconds 是整体超时(到点标记失败)、backoffLimit 是失败次数上限、completions 是成功的量。日常用 kubectl get job etl-retry -w 观察状态,kubectl describe job 看失败原因。
3. 指数退避、TTL 与清理
3.1 指数退避与 TTL
Job 失败后不会立即重建,而是指数退避:首次失败后等约 10 秒,之后 20s → 40s → … 封顶 6 分钟。这是 Job 控制器的内建策略,作用是防止"同一批 Pod 同时崩溃又同时重启"打爆依赖的下游(数据库连接池、外部 API 配额)。
已完成(Complete 或 Failed)的 Job 建议用 spec.ttlSecondsAfterFinished 让控制器自动回收,避免 Job 对象无限堆积。TTL 由 ttl-controller 负责,需要在 kube-controller-manager 上启用。
3.2 TTL 示例
apiVersion: batch/v1
kind: Job
metadata:
name: cleanup-me
spec:
ttlSecondsAfterFinished: 3600
ttlSecondsAfterFinished: 3600 表示完成后 1 小时自动删除 Job 与 Pod(模板仍是普通 Job 模板);Failed 的 Job 也建议设 TTL,避免诊断完忘记清理。
4. CronJob:调度、时区与并发策略
4.1 基础 CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-report
spec:
schedule: "30 2 * * *"
timeZone: "Asia/Shanghai"
concurrencyPolicy: Forbid
startingDeadlineSeconds: 600
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
restartPolicy: Never
containers:
- name: report
image: my-report:2.1
4.2 并发策略与时区
concurrencyPolicy 三选一:Allow(默认,可堆叠,慎用)、Forbid(上一次还在跑就跳过本次)、Replace(把上一次的 Job 删掉重跑)。
最常踩的坑是时区:K8s 默认按 UTC 计算 cron,服务器是 UTC 而业务是北京时间时,定时任务会差 8 小时,务必用 timeZone: "Asia/Shanghai"(v1.27+ 支持)。历史记录用 successfulJobsHistoryLimit / failedJobsHistoryLimit 控制保留数量。
另外要记住 CronJob 只保证"至少一次"调度,不保证"恰好一次":配合 startingDeadlineSeconds 与幂等的任务逻辑(用任务 ID 去重),才能避免重复执行。
5. 工作队列模式:多 Pod 消费
5.1 为什么需要工作队列
场景是"一天要处理 10 万条消息":一个 Pod 串行跑太慢,每条消息都开一个独立 Job 又会打爆控制面。工作队列模式用一个队列(Redis/RabbitMQ/Kafka/SQS)加少量常驻 Worker:Job 只负责"拉起一批 Worker",Worker 从队列拉任务处理。这样解耦了任务量与 Pod 数量,队列还天然提供重试与积压度量。
5.2 队列式 Job 示例
apiVersion: batch/v1
kind: Job
metadata:
name: queue-worker
spec:
completions: 1
parallelism: 10
template:
spec:
restartPolicy: Never
containers:
- name: worker
image: my-worker:3.2
容器里用环境变量 REDIS_URL=redis://redis-svc:6379/0 指到队列,脚本循环:while [ $(redis-cli -h redis-svc llen tasks) -gt 0 ]; do t=$(redis-cli -h redis-svc rpoplpush tasks processing); [ -n "$t" ] && python process.py $t; redis-cli -h redis-svc lrem processing 1 $t; done。并行度先小后大,观察队列吞吐再上调;Worker 把队列清空后主动退出,Job 才真正 Complete。
6. 批处理平台对比:Kueue 与 Volcano
6.1 对比
| 维度 | Kueue | Volcano |
|---|---|---|
| 定位 | 多租户作业排队/配额 | 高性能调度器 |
| 核心对象 | LocalQueue/ClusterQueue | Queue/PodGroup |
| 调度方式 | 仍用默认调度器 | 自研 Volcano Scheduler |
| 特性 | 配额/优先级/弹性联动 | Gang/公平/拓扑感知 |
| 适用 | 多团队批处理资源治理 | GPU 训练、MPI、大数据 |
6.2 选择
原生 Job 缺的是:排队(多团队没有配额/优先级概念)、整组调度(Gang)、公平性(大 Job 吃光资源)、弹性联动。如果只是"按团队配额排队",选 Kueue 接入成本最低;如果需要"多卡训练整组调度 + 拓扑感知",选 Volcano。两者也可叠加:Kueue 做准入排队 + Volcano 做调度,这是生产的常见组合。
7. 批处理资源管理与排队
7.1 用配额约束批处理
用 ResourceQuota 约束命名空间的批处理规模,防止某个团队无限开 Job 挤垮集群:例如 jobs.batch/active: "20"(最多 20 个活跃 Job)、pods: "200"、requests.cpu: "40"、requests.memory: 80Gi。查看用 kubectl get resourcequota batch-quota -n etl。
7.2 Kueue 排队示例
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
name: default-cq
spec:
resourceGroups:
- coveredResources: ["cpu", "memory"]
flavors:
- name: default-flavor
resources:
- name: "cpu"
nominalQuota: 60
- name: "memory"
nominalQuota: 120Gi
创建 ClusterQueue 后,给命名空间打标签 kubectl label ns etl kueue.x-k8s.io/queue-name=user-queue,Job 就会被 Kueue 挂起排队,直到配额允许才真正调度。注意配了队列后"作业为什么没跑"从调度失败变成"在排队",一定要把排队时长接进告警,否则批处理会"静默变慢"。
8. 批处理监控与告警
8.1 该看哪些指标
Job 级用 kubectl get jobs -w 看 Active/Succeeded/Failed;CronJob 级用 kube_cronjob_status_last_schedule_time 对比上次调度时间,若比 2×schedule 周期还旧,说明可能漏跑;积压级要看队列:Redis 的 LLEN tasks(任务积压)、Kafka 的 consumer lag(消费延迟)。
8.2 告警规则示例
groups:
- name: batch-alerts
rules:
- alert: JobFailed
expr: kube_job_status_failed > 0
for: 10m
labels: { severity: warning }
- alert: CronJobNotScheduled
expr: time() - kube_cronjob_status_last_schedule_time > 3600
for: 15m
labels: { severity: critical }
批处理告警别只看"Job Failed",要盯积压趋势(如 redis_llen_tasks > 500 告警);关键批处理(如日结)要有"未按时完成"告警,而不是只等失败。
9. 生产最佳实践
9.1 Checklist
□ 明确完成语义:completions/parallelism 与任务模型对齐
□ restartPolicy 一律 Never/OnFailure(Job 不允许 Always)
□ 设置 backoffLimit + activeDeadlineSeconds,防无限重试烧钱
□ 已完成 Job 设 ttlSecondsAfterFinished 自动清理
□ CronJob 显式写 timeZone,concurrencyPolicy 默认 Forbid
□ 大批量任务用"工作队列 + 常驻 Worker",而非海量 Pod
□ 多团队批处理用 Kueue/Volcano 排队,任务逻辑保持幂等
9.2 常见坑与对策
| 坑 | 现象 | 对策 |
|---|---|---|
| restartPolicy=Always | Job 永不结束 | 改 Never/OnFailure |
| backoffLimit 太小 | 偶发失败就整体失败 | 按失败率放大重试上限 |
| 时区未配 | 定时任务差 8 小时 | timeZone 显式指定 |
| 并发策略 Allow | 任务堆叠重复跑 | Forbid + 幂等逻辑 |
| 大批量每条一个 Job | 控制面压力大 | 工作队列模式 |
| 无 TTL | Job 堆积占资源 | ttlSecondsAfterFinished |
小结
K8s 批处理 = Job(保证完成次数)+ CronJob(定时触发)+ 工作队列(大规模任务消费)+ 排队平台(Kueue/Volcano) 的组合。核心心法就一句话:想清楚"怎样算完成",再把"失败怎么重试、堆积怎么清理、谁来排队"配好。生产落地记住五件事:restartPolicy 用 Never、backoffLimit 与 activeDeadlineSeconds 一起设、CronJob 显式配时区与 Forbid、大批量任务走工作队列、批处理监控盯积压与漏跑。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。