Kubernetes 上构建 Agentic 运行时:从调度到可观测性的工程实践
1. 从ax这个标题说起一个被低估的运行时调度命题第一次看到ax这个标题很多人会以为是某个命令行工具的缩写或者某个项目的代号。但把关键词摊开来看——agentic、orchestration、runtime、Kubernetes——这四个词拼在一起指向的其实是一个非常具体的技术命题在 Kubernetes 之上如何为 agentic 工作负载构建一套可编排、可调度、可观测的运行时底座。这不是一个空泛的概念。过去两年Kubernetes 生态里最明显的变化之一就是工作负载形态从无状态服务 定时任务向长时运行、有状态、多步骤协作的智能体任务迁移。传统的 Deployment、Job、CronJob 三件套面对一个需要反复调用工具、维护中间状态、按依赖关系分阶段执行的 agent 流程时会显得力不从心。你会遇到几个很现实的问题一个 agent 任务跑到一半需要等待外部事件Pod 该不该退出多个 agent 之间有数据依赖调度器怎么感知任务执行到第 7 步失败了能不能从第 7 步重试而不是从头再来ax这个标题背后我理解的核心就是回答这些问题的一套运行时编排思路。它不是一个具体的开源项目名而更像是一类问题的统称——agentic runtime orchestration on Kubernetes。热词里出现的 Karmada 正式毕业、华为云共建 agentic cloud 底座、agentic rag 这些词都在指向同一个方向基础设施层正在为智能体这种新工作负载做适配。这篇文章适合谁看如果你正在把 LLM 应用从单次 API 调用推进到多步骤、多工具、有状态的 agent 形态并且打算把它跑在 Kubernetes 上那这篇内容就是给你写的。如果你只是听说过 agentic 这个词但还没动手也能从里面拿到一套可落地的判断框架。我会尽量把为什么这么设计讲透而不是只丢一堆 YAML。需要先说明一点由于原始项目正文和关键词为空下面的内容是基于标题ax和热词方向结合我在 Kubernetes 上跑 agentic 工作负载的常见实践做的合理补全。凡是涉及具体参数和步骤的地方我都会说明推导逻辑你可以按自己的环境调整。2. 为什么传统 K8s 工作负载模型撑不住 agentic 场景2.1 Deployment 和 Job 的语义边界在哪里失效先把问题说清楚。Kubernetes 原生的 workload 资源设计初衷是围绕进程而不是任务流程的。Deployment 管的是长期存活的副本它假设你的容器是一个持续提供服务的进程挂了就重启副本数不够就扩。这个模型对 web 服务、API 网关非常合适。但一个 agent 执行流程不是这样的——它更像一个有向无环图DAG每个节点是一个步骤步骤之间有数据传递和条件分支。你没法用一个 Deployment 表达先检索、再推理、再调用工具、最后汇总这种顺序依赖。Job 管的是跑完就结束的批处理任务它假设任务是一个整体成功就 Complete失败就按 backoffLimit 重试。问题在于agent 任务的失败往往是部分失败前 6 步都成功了第 7 步调用某个工具超时。Job 的重试语义是整任务重来这意味着前 6 步的算力和 token 成本全部浪费。对于动辄消耗大量推理资源的 agent 流程这个浪费是不可接受的。CronJob 更不用说它只解决定时触发不解决流程编排。所以第一个结论很明确agentic 工作负载需要的是流程级的状态管理而不是进程级的副本管理。这就是为什么你会看到 Argo Workflows、Tekton、Kueue 这类项目在 agent 场景里被频繁提及——它们补的正是 K8s 原生 workload 缺失的那一层。2.2 状态、依赖、事件等待三个绕不开的硬需求把 agent 流程拆开看有三个需求是传统模型处理不了的。第一是中间状态持久化。一个 agent 在第 3 步产出的检索结果第 5 步还要用。如果这个状态只存在容器内存里Pod 一重启就没了。你需要把它落到外部存储——可以是对象存储、可以是 Redis、也可以是数据库。这里的关键设计决策是状态存哪里、以什么粒度存、失败了怎么恢复。我见过最常见的做法是把每一步的输入输出都序列化后写入一个持久化层key 用workflow_id step_id组织这样重试时可以直接读取上一步的产物。第二是步骤间依赖表达。步骤 B 依赖步骤 A 的输出步骤 C 和 D 可以并行但都依赖 B。这种依赖关系需要一个编排引擎来解析和调度。Kubernetes 本身不提供这个能力你得靠上层框架。Argo Workflows 用 DAG 模板表达Tekton 用 Task 和 Pipeline 表达各有取舍。第三是外部事件等待。agent 经常需要暂停并等待——等一个 webhook 回调、等一个人工审批、等一个外部系统就绪。这时候 Pod 不应该一直占着资源空转。理想的做法是把流程状态挂起释放计算资源等事件到达再恢复。这个模式在 K8s 里没有原生支持需要编排层自己实现挂起-恢复语义。2.3 一个具体的失败案例整任务重试的代价我拿一个真实场景说明整任务重试有多痛。假设一个 RAG agent 流程是这样的文档解析 → 分块 → 向量化 → 检索 → 重排 → 生成答案。前四步是 CPU 和 IO 密集型第五步要调用一个外部重排服务第六步是 LLM 推理。如果第五步因为外部服务抖动失败了Job 的默认行为是把整个 Pod 重跑一遍。文档解析和向量化可能要花几分钟这几分钟纯属浪费。更糟的是如果向量化本身有副作用比如写入了向量库重跑还可能产生重复数据。正确的做法是步骤级重试只重跑第五步前四步的产物从持久化层读取。这就是编排引擎存在的意义。你在设计 agentic runtime 时第一个要问自己的问题就是我的重试粒度是什么如果答案是整个任务那基本可以判定这套设计还没到位。3. ax 运行时的分层设计从调度到执行的拆解3.1 控制面与数据面的职责划分一套能跑 agentic 负载的运行时我倾向于把它拆成控制面和数据面两层来理解。控制面负责决定做什么解析流程定义、维护执行状态机、决定下一步调度哪个步骤、处理重试和超时、对外暴露查询接口。这一层通常是一个常驻服务跑在 K8s 里就是一个 Deployment副本数不用多2 到 3 个做高可用即可。它的状态必须持久化因为它要记录每个 workflow 执行到哪一步了。数据面负责实际执行每个步骤对应一个 Pod或一个容器跑完就退出。这一层是弹性的可以按需扩缩。数据面的 Pod 是无状态的——所有需要跨步骤传递的状态都通过控制面或外部存储中转。这个划分的好处是职责清晰。控制面是大脑数据面是手脚。大脑不需要很强但要稳定可靠手脚可以很多但要能快速拉起和回收。我见过一些实现把状态机塞进每个执行 Pod 里结果就是状态分散、难以查询、恢复逻辑复杂最后都改回了集中式控制面。3.2 调度器如何感知 agent 步骤的资源画像agent 步骤的资源需求差异极大。向量化步骤可能吃满 CPULLM 推理步骤可能要吃 GPU而一个等待审批的步骤几乎不消耗资源。如果调度器把它们一视同仁就会出现资源错配。这里要引入一个概念步骤级资源画像。在流程定义里每个步骤应该声明自己的资源需求类别而不是一个笼统的 request/limit。比如步骤类型CPU内存GPU典型耗时文档解析2 核4Gi无30s-2min向量化4 核8Gi可选1-5minLLM 推理2 核16Gi需要10s-2min工具调用0.5 核1Gi无1-30s事件等待0.1 核256Mi无不定有了这张表调度器就能做两件事一是把 GPU 步骤调度到有 GPU 的节点二是把轻量步骤打包到同一节点提高利用率。Kubernetes 的 nodeSelector、affinity、taints/tolerations 都能用上但前提是你在流程定义里把这些信息声明清楚了。提示不要给所有步骤都设成一样的资源规格。我见过一个团队为了省事所有步骤都申请 4 核 8Gi结果 GPU 节点被大量轻量步骤占着真正需要 GPU 的步骤反而排不上队。3.3 状态存储的选型对象存储、Redis 还是数据库状态存哪里是 ax 运行时设计里最容易纠结的地方。三种主流选择各有适用场景。对象存储如 S3 兼容存储适合存大块的中间产物比如解析后的文档、向量化结果、生成的中间文件。它的优势是便宜、容量大、天然持久。缺点是读写延迟比内存高不适合存高频访问的小状态。Redis适合存流程的运行时状态——当前执行到哪一步、每步的状态标记、轻量的上下文数据。它的优势是快适合控制面频繁读写。缺点是要考虑持久化和高可用否则 Redis 挂了状态就丢了。关系型数据库适合存需要查询和审计的元数据——workflow 列表、执行历史、每步的耗时和结果摘要。它的优势是支持复杂查询方便做可观测和排障。缺点是写入吞吐不如 Redis。我的实践建议是组合使用Redis 存热状态对象存储存冷产物数据库存元数据和审计日志。控制面读状态走 Redis读产物走对象存储查历史走数据库。这样各取所长不会互相拖累。3.4 事件驱动与轮询等待语义的两种实现agent 流程里的等待有两种实现方式选错了会很痛苦。轮询是控制面定期检查某个条件是否满足满足就推进流程。实现简单但有两个问题一是延迟轮询间隔决定了响应速度二是空转即使什么都没发生控制面也在不停地查。对于等待时间长的场景比如等人工审批几小时轮询非常浪费。事件驱动是外部系统在条件满足时主动通知控制面控制面收到事件后推进流程。响应快、无空转但需要外部系统支持回调实现复杂度高一些。我的建议是按等待时长分流短等待秒级到分钟级用轮询间隔设短一点长等待分钟级以上用事件驱动通过 webhook 或消息队列通知。这样兼顾了实现简单和资源效率。4. 在 Kubernetes 上落地 ax 的关键配置与踩坑点4.1 用 CRD 定义 agent 流程的实践要让 Kubernetes 认识agent 流程这种新资源最自然的做法是定义 CRD。你可以定义一个AgentWorkflow资源spec 里描述步骤和依赖status 里记录执行状态。apiVersion: ax.example.com/v1 kind: AgentWorkflow metadata: name: rag-pipeline spec: steps: - name: parse image: parser:latest resources: requests: {cpu: 2, memory: 4Gi} - name: embed image: embedder:latest dependsOn: [parse] resources: requests: {cpu: 4, memory: 8Gi} - name: retrieve image: retriever:latest dependsOn: [embed] - name: generate image: llm-server:latest dependsOn: [retrieve] resources: requests: {cpu: 2, memory: 16Gi, nvidia.com/gpu: 1} status: phase: Running currentStep: retrieve stepStatuses: parse: Succeeded embed: Succeeded retrieve: Running这个 CRD 的好处是把流程定义变成了 K8s 原生资源可以用 kubectl 直接查看和管理也能复用 RBAC、namespace 隔离这些现成机制。控制面就是一个监听这个 CRD 的 controller它 watch 到新资源后开始调度步骤。注意CRD 的 status 子资源要开启否则 controller 更新状态时可能和用户更新 spec 产生冲突。这个坑我在早期实现里踩过表现为状态更新偶尔丢失。4.2 步骤 Pod 的生命周期管理细节每个步骤对应一个 Pod但 Pod 的生命周期管理有几个细节容易出错。第一是 Pod 完成后不要立即删除。保留一段时间比如 1 小时方便排障看日志、看退出码。可以用 TTL controller 或者自己实现清理逻辑。我见过有人为了干净让 Pod 跑完就删结果出问题时什么线索都没有。第二是优雅终止。agent 步骤可能正在写状态收到 SIGTERM 后需要时间收尾。terminationGracePeriodSeconds 要设够默认 30 秒对某些步骤不够。同时容器里要正确处理 SIGTERM别直接忽略。第三是日志采集。步骤 Pod 是短命的日志如果不及时采集就丢了。建议用 sidecar 或者节点级日志代理把日志实时推到集中存储。对于 agent 场景日志里往往包含推理的中间结果排障时非常关键。4.3 资源配额与优先级避免 agent 任务互相饿死多个 agent 流程同时跑的时候资源竞争是必然的。如果没有配额和优先级可能出现一个大批量任务把所有资源占满导致其他任务全部排队。ResourceQuota用来限制 namespace 级别的总资源防止某个团队或某个流程无限占用。LimitRange用来设默认的 request/limit避免有人不写资源声明导致调度器无法决策。PriorityClass用来区分任务优先级。交互式的 agent 任务用户等着结果应该比批处理任务优先级高。当资源紧张时调度器会优先满足高优先级任务必要时驱逐低优先级任务。apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: interactive-agent value: 1000000 globalDefault: false description: 交互式 agent 任务用户等待中这里有个经验优先级不要设得太细三到四档就够了交互式、常规、批处理、可中断。档位太多反而难以管理而且容易因为优先级反转出问题。4.4 网络与存储的常见配置陷阱agent 步骤之间要传递数据网络和存储配置不当会直接拖垮性能。网络方面如果步骤间通过服务调用传递数据要注意 Service 的负载均衡和连接复用。频繁建立短连接会消耗大量资源。建议在步骤容器里复用 HTTP 连接或者干脆通过共享存储传递数据而不是走网络。存储方面如果多个步骤要读写同一份数据用 ReadWriteMany 的 PVC 或者对象存储。ReadWriteOnce 的 PVC 只能挂到一个节点跨节点步骤访问不了。这个坑很常见——本地测试时所有 Pod 在一个节点上没问题一上多节点集群就挂载失败。另外临时数据尽量用 emptyDir别用 PVC。emptyDir 随 Pod 生命周期创建销毁不占持久存储配额适合存放步骤内的临时文件。5. 可观测性让 agent 流程的每一步都看得见5.1 指标埋点从流程级到步骤级agent 流程的可观测性比普通服务更重要因为它的执行路径长、分支多、失败点分散。指标埋点要覆盖三个层次。流程级指标总执行数、成功数、失败数、平均耗时、P95 耗时。这些指标回答整体健康度如何。步骤级指标每个步骤的执行次数、成功率、耗时分布、重试次数。这些指标回答哪个步骤是瓶颈。资源级指标每个步骤的 CPU、内存、GPU 使用率。这些指标回答资源用得值不值。埋点方式上我推荐用 OpenTelemetry 统一采集它同时支持指标、日志、追踪三种信号而且和 K8s 生态集成好。控制面在调度每个步骤时打点步骤容器在执行时也打点两边通过 trace_id 关联起来。5.2 分布式追踪在跨步骤流程中的应用一个 agent 流程跨越多个 Pod传统的单机日志排查方式完全不够用。分布式追踪能把整个流程串起来让你看到每一步的耗时和调用关系。实现上控制面在启动流程时生成一个 trace_id每个步骤容器通过环境变量拿到这个 trace_id在自己的调用里带上。这样所有步骤的 span 都挂在同一个 trace 下在追踪系统里能看到完整的调用链。这里有个细节步骤之间的等待时间要不要算进 span我的做法是分开算。步骤自身的执行时间算一个 span步骤之间的排队和等待算另一个 span。这样能清楚区分是执行慢还是调度慢。5.3 日志聚合与排障的实战路径排障时最常问的三个问题是流程卡在哪一步那一步的日志是什么为什么失败要快速回答这三个问题日志必须满足两个条件带流程标识和可检索。每条日志都要带上 workflow_id、step_id、trace_id这样你能按流程维度聚合所有相关日志。日志要进集中存储比如 Loki、Elasticsearch支持按字段检索。我常用的排障路径是这样的先看流程状态确定卡在哪一步再按 step_id 拉那一步的所有日志然后看退出码和错误信息。如果日志不够再看那一步 Pod 的 events 和资源使用情况。这套路径走下来大部分问题都能定位。提示给日志加结构化字段比加自由文本有用得多。把 workflow_id、step_id 作为独立字段而不是拼在消息字符串里检索效率天差地别。6. 从单机脚本到集群运行时迁移中的经验与取舍6.1 什么时候该上 K8s什么时候不该不是所有 agent 流程都需要 Kubernetes。如果你的流程是单机跑的、步骤少、没有并发需求那用一个 Python 脚本加个任务队列就够了上 K8s 是过度设计。判断标准我总结成三条是否需要弹性扩缩、是否需要多步骤隔离、是否需要多租户。三条里满足两条以上上 K8s 才划算。如果只是偶尔跑跑、步骤之间没有强隔离需求那用单机方案维护成本低得多。我见过一些团队明明只有几个定时任务非要搞一套 K8s 编排结果运维复杂度上去了收益却没多少。技术选型要匹配实际规模别为了先进而先进。6.2 冷启动延迟的优化手段agent 步骤 Pod 的冷启动延迟是个实际问题。镜像拉取、容器启动、依赖初始化加起来可能几十秒。对于交互式任务这个延迟用户能感知到。优化手段有几个。镜像预拉取把常用镜像提前拉到节点上用 DaemonSet 或者镜像缓存。镜像瘦身用多阶段构建把镜像从几个 G 压到几百 M。依赖预热把耗时的初始化比如加载模型做成常驻服务步骤容器只做轻量调用。Pod 池维护一批预热好的 Pod需要时直接分配。这几个手段里镜像瘦身性价比最高几乎无成本。Pod 池效果最好但实现复杂适合对延迟极敏感的场景。6.3 成本控制GPU 步骤的调度策略GPU 是 agent 流程里最贵的资源。一个 GPU 节点每小时的成本可能是 CPU 节点的几十倍。控制成本的关键是提高 GPU 利用率。策略上一是按需分配只有真正需要 GPU 的步骤才申请 GPU其他步骤跑在 CPU 节点。二是批处理合并把多个小推理请求合并成一批一次 GPU 调用处理多个请求。三是抢占式实例对延迟不敏感的批处理任务用可中断的实例成本能降不少。四是及时释放步骤跑完立即释放 GPU别让 Pod 空占着。这里有个反直觉的点有时候多花钱反而更省。比如给 GPU 步骤配更快的 CPU 和更大的内存虽然单价高了但步骤耗时短了GPU 占用时间少了总成本反而低。要算总账不能只看单价。7. 我在实际项目里踩过的几个坑7.1 状态不一致控制面和数据面各说各话最坑的一次是控制面认为某步骤还在运行但数据面 Pod 早就退出了。原因是 Pod 退出后控制面没及时收到通知状态更新延迟。结果流程卡住用户以为还在跑实际早就死了。根因是控制面依赖 watch Pod 状态来更新流程状态但 watch 有延迟和丢失的可能。修复方式是双保险既 watch Pod 事件也定期对账reconcile。对账逻辑定期扫描所有运行中的流程检查对应 Pod 的实际状态发现不一致就纠正。这个模式借鉴了 K8s controller 的 reconcile 思路非常有效。7.2 重试风暴失败步骤把集群打挂有一次某个外部服务挂了导致大量 agent 流程的同一步骤同时失败。这些步骤按重试策略疯狂重试瞬间把集群的 Pod 数量打满连正常服务都受影响。教训是重试必须有限流和退避。重试次数要有上限重试间隔要指数退避还要加抖动避免同时重试。更重要的是要有熔断机制当某步骤的失败率超过阈值时暂停调度新任务等外部依赖恢复。这个在 K8s 层面可以用 PDBPodDisruptionBudget配合自定义 controller 实现。7.3 镜像版本漂移导致的诡异问题同一个流程昨天跑得好好的今天突然失败。排查半天发现是步骤镜像用了latest标签镜像被更新了行为变了。这个坑的解法很简单但必须坚持生产环境永远不用 latest 标签用明确的版本号或 digest。流程定义里引用的镜像要固定版本升级镜像要走显式的流程。这样保证同样的流程定义在任何时候跑出来的行为一致。8. 关于 ax 这类运行时我的一些判断agentic 工作负载在 Kubernetes 上的编排现在还处在快速演化的阶段。Karmada 这类多集群调度项目毕业、云厂商开始提 agentic cloud 底座说明基础设施层已经意识到这个方向的重要性。但具体到一套标准的 agent runtime 该长什么样业界还没有共识。我的判断是短期内会出现几种并存的方案一种是基于现有工作流引擎Argo、Tekton扩展一种是专门为 agent 设计的新运行时还有一种是云厂商的托管服务。选哪种取决于你的团队规模和技术栈。小团队用托管服务省心大团队自建可控性高。不管选哪种有几个原则是不变的状态要持久化、重试要步骤级、资源要按需分配、可观测性要到位。这四条做到了不管底层用什么流程都能跑得稳。做不到再花哨的框架也救不了。最后分享一个我自己的习惯每次设计一个新的 agent 流程我都会先问自己如果第 N 步失败了会发生什么。把这个问题的答案想清楚流程的健壮性就有了基本保障。这个习惯帮我避开了很多后期才暴露的坑。