把 AI Agent 从交互式会话搬到集群里做批处理(批量改工单、夜间报表生成、分片爬取后汇总)时,常见误区是用 Deployment 常驻一个「永远在线」的 Agent 进程,或在 CronJob 里套一个无上限的 Shell 循环。Agent 的执行时长受模型延迟、工具链 I/O 与重试次数影响,很难像普通 Cron 脚本那样用固定 wall-clock 估算;一旦任务挂起或反复拉起 Pod,既会拖垮节点资源,也会在 etcd 里堆积大量已结束却未清理的 Job 对象。
Kubernetes 的 Job 控制器为「跑完即停」的工作负载提供了明确的完成语义、重试上限与自动清理钩子。本文只讨论如何把 Agent 批任务对齐到这些官方机制,不涉及某个商业 Agent 产品的内部实现。
问题背景:Agent 批任务与控制器语义不匹配
与 HTTP 请求驱动的在线推理不同,批处理 Agent 通常具备以下特征:
- 执行时间长尾:单次 LLM 调用可能数秒到数分钟,多轮工具调用叠加后总时长波动大。
- 失败类型混合:网络超时、429 限流适合重试;配置错误、权限拒绝应快速失败,不应耗尽
backoffLimit。 - 天然一次性:任务完成后不需要副本保持;遗留 Pod 会占用 IP、cgroups 与日志存储。
- 可分片:大任务可拆成 N 份由多个 Pod 并行处理(固定完成数或工作队列模式)。
Deployment 关注的是长期运行的副本健康;Job 关注的是达到 completions 或判定失败。对「触发一次、跑完退出」的 Agent,Job(或由 CronJob 创建的 Job)是更贴近语义的控制器。
可落地实现:Job 规格与 Agent 退出约定
最小 Job 骨架
Agent 容器应以进程退出码向 Job 控制器汇报结果。建议约定:
| 退出码 | 含义 | Job 侧处理 |
|---|---|---|
0 |
任务成功,输出已落盘或已上报 | 计入 succeeded |
1 |
可重试的瞬态错误(超时、限流) | 计入 backoff,由 Job 新建 Pod |
2 |
不可恢复错误(坏配置、鉴权失败) | 宜通过 podFailurePolicy 直接 FailJob |
3 |
部分成功(仅当业务接受) | 需配合 Indexed Job 的 successPolicy,见下文边界 |
Pod 模板中 restartPolicy 必须为 Never 或 OnFailure。若使用 podFailurePolicy(Kubernetes 1.31 起稳定),官方要求 Pod 模板为 Never。
apiVersion: batch/v1
kind: Job
metadata:
name: agent-batch-20260617-001
labels:
app: agent-batch
task-id: "ticket-export-42"
spec:
completions: 1
parallelism: 1
backoffLimit: 3
activeDeadlineSeconds: 3600
ttlSecondsAfterFinished: 86400
template:
spec:
restartPolicy: Never
activeDeadlineSeconds: 3300
containers:
- name: agent
image: your-registry/agent-runner:1.2.0
env:
- name: TASK_ID
value: "ticket-export-42"
- name: OTEL_SERVICE_NAME
value: agent-batch
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2"
memory: "4Gi"
activeDeadlineSeconds:两层都要想清楚
官方文档说明:Job 的 .spec.activeDeadlineSeconds 限制整个 Job 的生命周期(含多次 Pod 创建),到期后所有运行中 Pod 会被终止,Job 进入 Failed,reason: DeadlineExceeded。且 Job 级 deadline 优先于 backoffLimit—— 时间耗尽后不会再创建新 Pod,即使重试次数未用完。
同时,Pod 模板里也可以设置 activeDeadlineSeconds,其作用于单个 Pod。实践上建议:
- Job 级:设为业务 SLA 上限(例如 1 小时),防止 Agent 无限重试。
- Pod 级:略小于 Job 级(例如少 5–10 分钟),让单次 Pod 先失败,留给 Job 控制器在总预算内再调度重试 Pod。
估算 Job 级 deadline 的粗算公式(需按实测校准):
activeDeadlineSeconds ≈ (预期轮次 × 单轮 p95 耗时) × 1.3 + 冷启动开销
单轮 p95 应包含:最长工具调用超时、LLM 流式读满、写结果存储。开发环境可先取 1800,生产按 Trace 中 span 汇总后下调。
backoffLimit 与 podFailurePolicy
默认 backoffLimit 为 6。对 Agent 批任务,若每次失败都会重新加载模型客户端、预热连接,6 次重试可能拉长总时长并放大云 API 费用。建议:
- 开发:
backoffLimit: 2 - 生产(强依赖外部 API):
backoffLimit: 3–5,并配合应用内指数退避,避免 kubelet 立刻拉起新 Pod 造成 API 风暴
对「退出码 2 = 永久失败」的场景,可配置 podFailurePolicy,避免无意义重试:
podFailurePolicy:
rules:
- action: FailJob
onExitCodes:
containerName: agent
operator: In
values: [2]
- action: Ignore
onPodConditions:
- type: DisruptionTarget
第二条规则的含义是:因抢占、API 驱逐等导致的 Pod 中断可不计入 backoffLimit(官方 Pod failure policy 文档中的 Ignore 语义),适合在可抢占节点上跑非关键批任务。
ttlSecondsAfterFinished:避免孤儿 Pod 与 etcd 膨胀
Job 完成后(Complete 或 Failed),若未设置 TTL,直接创建的 unmanaged Job 在删除时默认 orphanDependents 策略可能留下仍在运行的 Pod;官方文档明确建议设置 ttlSecondsAfterFinished,由 TTL 控制器在指定秒数后删除 Job 及其级联对象。
推荐参数:
| 环境 | ttlSecondsAfterFinished | 说明 |
|---|---|---|
| 开发 | 600(10 分钟) |
便于快速复现后清理 |
| 生产 | 86400(24 小时) |
留足时间拉取日志、对照 Trace |
| 合规审计 | 604800(7 天) |
若日志已集中采集,可缩短 |
注意:TTL 删除的是 Job API 对象;应用侧持久化结果应写在 Job 之外(对象存储、数据库),不要依赖 Pod 文件系统在 TTL 之后仍可访问。
并行分片:completions 与 parallelism
当 Agent 按分片处理队列(例如每个 index 处理 100 条记录)时,可使用 固定完成数 Job:
spec:
completions: 8
parallelism: 4
parallelism 为同时运行的 Pod 上限;completions 为成功 Pod 总数。Agent 进程需通过 JOB_COMPLETION_INDEX(Indexed Job)或外部租约(数据库 SKIP LOCKED)避免重复处理同一分片。Indexed Job 还可配合 successPolicy,在部分分片成功时即宣告 Job 成功 —— 仅适用于业务明确接受部分结果的场景(见风险节)。
与 CronJob 组合
定时 Agent 应使用 CronJob,让其每次触发创建新的 Job,而不是在旧 Job 上重复执行。CronJob 的 startingDeadlineSeconds、concurrencyPolicy: Forbid 可防止上一次夜间任务未结束时叠加启动。子 Job 仍应自带 activeDeadlineSeconds 与 ttlSecondsAfterFinished。
风险与边界
DeadlineExceeded 不等于可重跑的安全态。 Job 因 activeDeadlineSeconds 失败后不会自动重启;需要人工或上层工作流(Argo、Temporal 等)根据 task-id 重新提交。若 Agent 在截止前已部分写入数据库,必须依赖幂等键或事务边界,避免重提 Job 时重复副作用。
Job 级与 Pod 级 deadline 配置错误是常见故障:只设 Pod 级而未设 Job 级时,Job 可能在多轮 Pod 上总时长失控;只设 Job 级而 Pod 级过大时,单次 hung 工具调用会占满整轮预算。
podFailurePolicy 与 restartPolicy 必须满足官方约束;混用 restartPolicy: Always 会导致策略被拒绝或行为与预期不符。
并行 Job 要求 Agent 进程并发安全;官方文档指出当 parallelism 与 completions 均大于 1 时,多个 Pod 可能同时运行,容器内逻辑须容忍并发(或通过分片隔离)。
ttlSecondsAfterFinished 过短会导致运维尚未拉取 kubectl logs Job 已被删除;过长的海量 Failed Job 仍会增加 apiserver 列表压力。应用应默认把运行摘要写入集中日志或对象存储。
成功语义:默认 Job 在 succeeded Pods == completions 时成功。Agent 若「跑完但结论为空」仍返回 0,Job 会显示成功而业务失败 —— 应在应用层做输出校验,或在 Indexed Job 上使用 successPolicy 并清楚记录部分成功。
可观测性与验收清单
- 为 Job 设置
labels:task-id、tenant、agent-version,便于与 OpenTelemetry Trace 关联。 - 监控 Job 条件:
Complete、Failed、以及FailureTarget(启用 podFailurePolicy 时)。 - 告警:
kube_job_status_failed或等价指标在 1 小时内突增;DeadlineExceeded占比超过基线。 - 发布前检查:容器退出码约定已写入 runbook;
ttlSecondsAfterFinished已启用;结果写入与 Job 生命周期解耦。
参考来源
- Kubernetes 文档:Job(
backoffLimit、parallelism/completions、activeDeadlineSeconds、ttlSecondsAfterFinished、Job 终止与清理) - Kubernetes 文档:Pod failure policy(
FailJob/Ignore/DisruptionTarget,稳定于 v1.31) - Kubernetes 文档:TTL-after-finished controller
- Kubernetes 文档:CronJob(
concurrencyPolicy、startingDeadlineSeconds)
内容声明:本文无广告投放、无付费植入。
如有事实性问题,欢迎发送勘误至 i@hotdrydog.com。