Hotdry.

Article

用 Kubernetes Job 承载一次性 AI Agent 批任务:activeDeadlineSeconds 与 ttlSecondsAfterFinished 的配置要点

结合 Kubernetes 官方 Job 语义,说明如何把 LLM Agent 批处理映射为 Job/CronJob,并给出截止时间、重试、Pod 失败策略与收尾 TTL 的可落地参数与常见踩坑。

2026-06-17infra

把 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 必须为 NeverOnFailure。若使用 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 进入 Failedreason: 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 完成后(CompleteFailed),若未设置 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 的 startingDeadlineSecondsconcurrencyPolicy: Forbid 可防止上一次夜间任务未结束时叠加启动。子 Job 仍应自带 activeDeadlineSecondsttlSecondsAfterFinished

风险与边界

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 进程并发安全;官方文档指出当 parallelismcompletions 均大于 1 时,多个 Pod 可能同时运行,容器内逻辑须容忍并发(或通过分片隔离)。

ttlSecondsAfterFinished 过短会导致运维尚未拉取 kubectl logs Job 已被删除;过长的海量 Failed Job 仍会增加 apiserver 列表压力。应用应默认把运行摘要写入集中日志或对象存储。

成功语义:默认 Job 在 succeeded Pods == completions 时成功。Agent 若「跑完但结论为空」仍返回 0,Job 会显示成功而业务失败 —— 应在应用层做输出校验,或在 Indexed Job 上使用 successPolicy 并清楚记录部分成功。

可观测性与验收清单

  • 为 Job 设置 labelstask-idtenantagent-version,便于与 OpenTelemetry Trace 关联。
  • 监控 Job 条件:CompleteFailed、以及 FailureTarget(启用 podFailurePolicy 时)。
  • 告警:kube_job_status_failed 或等价指标在 1 小时内突增;DeadlineExceeded 占比超过基线。
  • 发布前检查:容器退出码约定已写入 runbook;ttlSecondsAfterFinished 已启用;结果写入与 Job 生命周期解耦。

参考来源

  • Kubernetes 文档:JobbackoffLimitparallelism/completionsactiveDeadlineSecondsttlSecondsAfterFinished、Job 终止与清理)
  • Kubernetes 文档:Pod failure policyFailJob / Ignore / DisruptionTarget,稳定于 v1.31)
  • Kubernetes 文档:TTL-after-finished controller
  • Kubernetes 文档:CronJobconcurrencyPolicystartingDeadlineSeconds

infra

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com