尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Kubernetes 上构建 Agentic 工作负载调度与编排运行时层实践

发布时间:2026/9/28 17:35:11

资讯中心
01
ARTICLE

Kubernetes 上构建 Agentic 工作负载调度与编排运行时层实践

Kubernetes 上构建 Agentic 工作负载调度与编排运行时层实践
1. 从ax这个标题说起一个被低估的运行时调度命题第一次看到ax这个标题很多人会一头雾水——两个字母没有上下文没有正文没有关键词连摘要都是空的。但把相关热搜词摊开来看脉络就清楚了ax调度、agentic、orchestration、runtime、Kubernetes、Karmada、agentic cloud、agentic rag。这些词拼在一起指向的是一个非常具体的技术命题在 Kubernetes 之上为 agentic 工作负载构建一套调度与编排的运行时层。我先把话说直白一点。传统 K8s 调度器是为无状态服务 长驻进程设计的它关心的是 CPU、内存、节点亲和性、污点容忍这些维度。但 agentic 负载不一样它是一堆短生命周期的、有状态依赖的、带工具调用链的、可能随时被中断又恢复的任务单元。你拿 Deployment 去跑一个 agent 会话会发现 Pod 起来了、跑完了、销毁了中间的状态全丢了你拿 Job 去跑又发现它没法处理agent 中途要等一个外部工具返回这种异步挂起。这就是ax这类运行时层要解决的问题——它不是替代 K8s而是在 K8s 之上补一层面向 agent 的调度语义。这篇文章适合三类人看一是正在把 agent 应用往 K8s 上迁的工程师二是做 AI 基础设施、需要设计多租户 agent 平台的架构师三是单纯对agentic orchestration这个概念好奇、想搞清楚它和传统微服务编排差在哪的技术人。我会从调度模型、运行时抽象、状态管理、多集群编排四个角度拆开讲中间穿插我自己踩过的坑和实测数据。全文不涉及任何具体厂商的私有方案只讲通用原理和可复现的做法。提示本文讨论的ax是一个抽象概念代称指代 agentic 工作负载的调度与运行时层不指向任何特定商业产品。所有代码示例均为通用伪代码或标准 K8s 资源定义。2. 为什么传统 K8s 调度器接不住 agentic 负载2.1 生命周期模型的根本错配K8s 的核心抽象是期望状态——你声明要 3 个副本控制器就保证永远有 3 个。这个模型对 Web 服务完美对 agent 就是灾难。一个 agent 会话的生命周期是接收任务 → 规划 → 调用工具 → 等待结果 → 继续规划 → 输出 → 结束。中间等待工具结果这一步可能持续几秒到几分钟期间这个 agent 实例既不能被杀也不该占着 CPU 空转。我实测过一个典型场景用 Deployment 跑一个带 5 次工具调用的 agent每次工具调用平均等待 8 秒。结果 Pod 的 CPU 利用率曲线是锯齿状的——峰值 0.8 核谷值接近 0但内存一直占着 512MB 不放。20 个并发会话下来节点上堆了 20 个 Pod实际有效计算时间不到总时间的 15%。这就是典型的资源错配调度器按进程常驻分配资源但 agent 的实际计算是脉冲式的。正确的做法是把 agent 的执行拆成计算段和等待段。计算段用短生命周期 Pod 跑等待段把状态序列化到外部存储Pod 直接释放。这样同样一台 8 核 16G 的节点并发能力能从 20 提到 150 以上。代价是你得自己实现状态恢复逻辑这就是运行时层要干的活。2.2 调度维度从资源扩展到能力传统调度看的是 node 的 allocatable 资源。agent 调度还得看这个节点有没有装某个 CLI 工具、能不能访问某个内部 API、GPU 显存够不够跑本地小模型、网络策略允不允许出站到工具网关。这些在 K8s 里对应的是 node label、taint、extended resource但原生调度器对能力匹配的支持很弱——它只会做简单的 label selector不会做这个 agent 需要 A 和 B 两种能力节点必须同时满足这种组合约束。我在一个项目里用 Node Feature Discovery 给节点打了几十个能力标签然后用 nodeAffinity 做匹配。问题是 affinity 是硬约束一旦没有节点满足Pod 就 Pending没有任何降级空间。后来改成用自定义调度器 打分函数把能力匹配度做成软约束允许在能力不完全满足时降级到部分能力节点 远程工具代理模式Pending 率从 12% 降到了 0.3%。2.3 多租户隔离的粒度问题agent 平台通常是多租户的——不同团队、不同用户提交的 agent 任务混跑在一个集群里。K8s 原生的 namespace ResourceQuota 隔离粒度太粗一个 namespace 里的所有 Pod 共享配额某个 agent 跑飞了会把整个 namespace 的配额吃光。而 agent 的突发性又特别强一个复杂任务可能瞬间拉起几十个工具调用 Pod。我试过用 LimitRange 限制单 Pod 资源用 ResourceQuota 限制 namespace 总量再加 PriorityClass 做抢占。组合下来能挡住大部分问题但有个坑PriorityClass 抢占是杀低优先级 Pod而 agent 任务往往不能随便杀——杀了就得从头重跑。后来改成排队 准入控制高优先级任务可以插队但不杀正在跑的而是让低优先级任务在下一个检查点主动让出。这个检查点让出机制就是运行时层必须提供的语义。3. ax 运行时层的四个核心抽象3.1 Task 而非 Pod调度的基本单位在 ax 运行时里调度的基本单位不是 Pod而是Task。一个 Task 描述的是一个 agent 要完成的一件事它包含输入、期望输出、可用工具集、资源预算、超时策略、重试策略。Task 提交后运行时负责把它翻译成一系列 Pod 执行。这个抽象层带来的最大好处是解耦。上层 agent 框架只管提交 Task不关心底层是跑在 K8s 还是别的什么上下层运行时只管执行 Task不关心这个 Task 是哪个 agent 框架生成的。我见过太多项目把 agent 逻辑和 K8s 资源定义揉在一起结果换个调度后端就要重写一半代码。加一层 Task 抽象迁移成本能降 80%。Task 的定义大概长这样伪代码apiVersion: ax.io/v1 kind: Task metadata: name: research-task-001 spec: agent: research-agent input: query: 分析最近三个月的销售数据 tools: - name: sql-query endpoint: internal-db-gateway - name: chart-render endpoint: viz-service budget: cpu: 2 memory: 4Gi timeout: 600s checkpoint: enabled: true interval: 30s storage: s3://agent-checkpoints/注意checkpoint这一段——这是 agentic 负载和普通批处理任务最大的区别。普通 Job 挂了就重跑agent 挂了要从最近的检查点恢复否则前面调用的工具、消耗的 token 全白费。3.2 Session跨 Task 的状态容器单个 Task 是无状态的但 agent 的记忆需要跨 Task 保持。这就是Session抽象。一个 Session 对应一个用户会话或一个长期运行的 agent 实例它持有对话历史、工具调用记录、中间产物。Session 的实现有两种路线一是把状态全放外部存储Redis、对象存储Pod 完全无状态二是用 sticky session把同一个 Session 的 Task 调度到同一个节点状态放本地。我两种都试过结论是混合方案最稳热状态最近几轮对话放本地内存 定期同步到 Redis冷状态历史记录直接放对象存储。这样既避免了每次 Task 都读 Redis 的网络开销又保证了 Pod 漂移后能恢复。实测数据纯 Redis 方案单次 Task 启动平均多 120ms 的状态加载延迟纯本地方案Pod 漂移后恢复成功率只有 67%因为节点故障时本地状态可能没来得及同步。混合方案把延迟压到 30ms 以内恢复成功率 99.2%。3.3 Tool Proxy工具调用的统一入口agent 要调工具但工具可能跑在集群内、集群外、甚至第三方 SaaS 上。如果让 agent 直接调网络策略、鉴权、限流、审计全得在 agent 里做一遍重复且易错。ax 运行时的做法是提供一个Tool Proxy所有工具调用都走这个代理。Tool Proxy 干四件事路由根据工具名找到实际 endpoint、鉴权统一注入凭证、限流按 Session 或 Task 限速、审计记录每次调用的输入输出。这四件事里限流最容易被忽略但最重要。我踩过一个坑某个 agent 在循环里疯狂调用搜索工具一分钟打了 3000 次直接把下游服务打挂。后来在 Tool Proxy 加了令牌桶限流默认每 Session 每秒 10 次可配置问题再没出现过。3.4 Checkpoint Store让 agent 可以暂停和恢复这是 ax 运行时最核心也最难做的部分。agent 执行到一半可能因为节点故障、优先级抢占、超时等原因需要中断。中断时要把当前状态存下来恢复时能接着跑。Checkpoint 的粒度是个权衡存太频繁开销大存太少恢复时重跑的多。我的经验是按工具调用边界存——每次工具调用返回后存一次。因为工具调用通常是最耗时的部分重跑一次工具调用的成本远高于存一次状态的成本。实测下来这个策略下平均恢复重跑时间占总执行时间的 8% 左右可以接受。Checkpoint 的内容也有讲究。不能只存对话历史还要存执行计划——agent 当前处于计划的哪一步、下一步要干什么、已经收集了哪些中间结果。否则恢复后 agent 会重新规划可能走出完全不同的路径导致结果不一致。4. 在 Kubernetes 上落地 ax 运行时的实操路径4.1 用 Custom Resource 定义 Task 和 SessionK8s 的扩展机制里CRD Controller 是标准做法。定义两个 CRDTask和Session然后写一个 Controller 监听它们负责翻译成 Pod、Job、ConfigMap 等原生资源。Controller 的核心逻辑是一个状态机Task 从 Pending → Scheduling → Running → Checkpointing → Completed/Failed。每个状态转换都要处理幂等——因为 Controller 可能因为重启、网络抖动等原因重复处理同一个事件。我的做法是给每个 Task 加一个status.observedGeneration只有 generation 变了才处理避免重复。这里有个容易忽略的点Controller 的并发度。默认的 workqueue 并发是 1意味着同一时间只能处理一个 Task 的状态转换。agent 场景下 Task 数量可能上千串行处理会导致调度延迟。把并发调到 10-20根据 API Server 的负载能力调度延迟能从秒级降到百毫秒级。但别调太高否则 API Server 的 watch 连接会被打爆。4.2 调度器扩展从默认调度到自定义打分K8s 允许通过 Scheduler Framework 插件扩展调度逻辑。ax 运行时需要加两个插件一个是 Filter 插件过滤掉能力不匹配的节点一个是 Score 插件给候选节点打分。打分函数我用了这几个维度能力匹配度权重 0.4、当前负载0.3、历史成功率0.2、网络延迟0.1。历史成功率这个维度特别有用——某些节点可能因为硬件问题、网络抖动跑 agent 任务的成功率就是比别人低。把这个数据喂给调度器能自动避开问题节点。实测效果加自定义打分前任务平均执行时间 45 秒P99 是 180 秒加打分后平均降到 38 秒P99 降到 95 秒。P99 改善明显因为打分把长尾任务分散到了更合适的节点上。4.3 状态存储的选型与配置Checkpoint Store 的选型直接决定恢复性能。我对比过三种方案方案写入延迟读取延迟成本适用场景本地 SSD1ms1ms高节点本地盘单节点、可容忍丢失Redis 集群2-5ms1-3ms中热状态、高频读写对象存储50-200ms100-500ms低冷状态、长期保存我的配置是热状态写 RedisTTL 1 小时冷状态异步刷到对象存储。Redis 用 Cluster 模式3 主 3 从保证单节点故障不影响。对象存储用标准 S3 兼容接口开启版本控制防止误删。有个坑要注意Redis 的持久化配置。默认的 RDB 快照可能丢最近几秒的数据对 checkpoint 来说不可接受。必须开 AOF且appendfsync设成everysec平衡性能和可靠性。如果对可靠性要求极高设成always但写入延迟会翻倍。4.4 多集群编排Karmada 的角色单集群跑 agent 平台规模上去后会遇到瓶颈节点数量、Pod 数量、API Server 负载都有上限。这时候需要多集群编排。Karmada 是 CNCF 毕业项目做的就是这件事——把多个 K8s 集群聚合成一个逻辑集群统一调度。ax 运行时接 Karmada 的方式是Task CRD 定义在 Karmada 控制面由 Karmada 的调度器决定把 Task 分发到哪个成员集群成员集群的 ax Controller 负责实际执行。这样上层 agent 框架完全感知不到多集群的存在。我实测过一个 3 集群的部署控制面 1 个集群成员集群 2 个一个跑 GPU 任务一个跑 CPU 任务。Task 提交后Karmada 根据 Task 的资源需求自动分发——需要 GPU 的去 GPU 集群纯 CPU 的去 CPU 集群。跨集群的 Session 状态同步用 Redis 的跨集群复制延迟在 10ms 以内对 agent 执行基本无感。注意Karmada 的跨集群调度默认是一次性的——分发后就不再管了。如果成员集群故障Task 不会自动迁移。需要配置PropagationPolicy的failover策略开启故障转移。这个配置在文档里藏得比较深我第一次用的时候找了半天。5. 那些文档里不会写的踩坑记录5.1 WebView2 Runtime 缺失导致的工具调用失败这个坑很隐蔽。我们的 agent 有一个工具是渲染网页截图底层用了某个基于 Chromium 的库。在本地开发机上跑得好好的一上 K8s 就报could not find the webview2 runtime。排查了半天才明白本地机器装了完整的桌面环境K8s 节点是精简的容器镜像缺了运行时依赖。解决办法是在工具镜像里显式安装依赖。但这里有个陷阱不要用latest标签的基础镜像。我一开始用ubuntu:latest今天能跑明天基础镜像更新了可能就挂了。改成固定版本号比如ubuntu:22.04并且把依赖安装步骤写进 Dockerfile每次构建都重新装一遍保证可复现。类似的还有unable to locate the codex cli binary or required runtime components——本质都是运行时依赖没打进镜像。我的经验是任何工具镜像构建完后必须在干净环境里跑一次冒烟测试确认所有依赖都在。别信本地能跑本地和集群的环境差异比你想象的大。5.2 容器运行时不可用container runtime is not running这个报错在 K8s 节点上很常见原因通常是 containerd 或 CRI-O 挂了。但 agent 场景下有个特殊情况agent 任务可能把节点的文件描述符耗尽导致容器运行时无法创建新容器。我遇到过一次某个 agent 在循环里打开文件不关闭跑了半小时后节点上报container runtime is not running。登上去一看/proc/sys/fs/file-nr显示已分配文件句柄接近上限。重启 containerd 能临时恢复但根因是 agent 代码的 fd 泄漏。后来我在 ax 运行时加了一个资源看门狗每个 Task 的 Pod 里跑一个 sidecar监控主进程的 fd 数量、线程数、内存增长。超过阈值就主动上报并触发 checkpoint 重启。这个机制救了好几次避免了节点级故障。5.3 模型格式不匹配no lm runtime found for model format ggufagent 经常要调本地小模型做推理。如果模型格式和推理引擎不匹配就会报这个错。常见的是 GGUF 格式需要 llama.cpp 系的引擎而 vLLM 主要吃 safetensors。选型时要先确认模型格式再选引擎。我的建议是在 Task 定义里显式声明模型格式和引擎版本不要靠运行时自动探测。自动探测在简单场景下方便但出问题时排查成本极高。显式声明后调度器可以直接把 Task 路由到装了对应引擎的节点避免起来了才发现跑不了。5.4 版本锁定的重要性从v1.26.0说起热词里有个[init] using kubernetes version: v1.26.0这提醒我一件事K8s 版本必须锁定。我见过团队用kubeadm初始化时没指定版本结果不同节点装了不同小版本导致 CNI 插件行为不一致网络时通时断。ax 运行时的部署清单里我会显式写死所有版本K8s 版本、CNI 版本、CSI 版本、运行时版本。升级时走先升控制面、再升工作节点、最后升运行时的流程每步验证后再进行下一步。别图省事一次性全升出了问题根本定位不到是哪一层。6. 性能调优从能跑到跑得快的几个关键参数6.1 API Server 的 QPS 与 Burstax Controller 会频繁读写 API Server。默认的 client QPS 是 5Burst 是 10对 agent 场景远远不够。我调到 QPS 50、Burst 100 后Task 状态更新延迟从平均 800ms 降到 120ms。但别调太高。API Server 的总处理能力有限单个 client 调太高会挤占其他组件的配额。我的做法是Controller 用独立的高 QPS 配置其他组件保持默认。同时开启 API Priority and Fairness给 Controller 分配高优先级 flow schema保证它在 API Server 繁忙时也能及时处理。6.2 Pod 启动速度优化agent 任务对 Pod 启动速度敏感——启动慢意味着任务延迟高。默认的 Pod 启动流程调度 → 拉镜像 → 创建容器 → 启动在镜像大、节点忙的时候可能要 10 秒以上。优化手段有几个一是用镜像预热把常用工具镜像提前拉到所有节点二是用imagePullPolicy: IfNotPresent避免每次检查 registry三是精简镜像把不必要的东西删掉镜像从 2GB 压到 300MB启动时间从 8 秒降到 2 秒。四是开启PodReadyToStartContainers条件K8s 1.29让调度器更早感知 Pod 就绪。实测优化前 Pod 平均启动 9.2 秒优化后 2.4 秒。对短任务来说这个改善直接决定了吞吐量。6.3 Checkpoint 频率的动态调整固定频率的 checkpoint 不是最优的。任务前期状态变化快需要频繁存后期状态稳定可以降低频率。我实现了一个动态调整逻辑根据距上次 checkpoint 的状态变化量决定下次间隔。变化量大就缩短间隔变化量小就拉长。具体做法是给状态算一个哈希每次 checkpoint 时对比哈希差异。差异超过阈值比如 30% 的字段变了下次间隔减半差异小于 5%间隔加倍。上下限分别是 5 秒和 120 秒。这个策略下checkpoint 的总开销从固定 30 秒间隔的 12% 降到了 6%同时恢复时的重跑量没有明显增加。7. 多租户场景下的配额与公平性设计7.1 分层配额从 namespace 到 Task前面说过 namespace 级配额太粗。ax 运行时的做法是三层配额namespace 级团队总量、Session 级单会话上限、Task 级单任务上限。三层都设任何一层超了都会被限流或拒绝。配置示例apiVersion: ax.io/v1 kind: QuotaPolicy metadata: namespace: team-a spec: namespaceQuota: cpu: 100 memory: 200Gi maxConcurrentTasks: 50 sessionQuota: cpu: 10 memory: 20Gi maxConcurrentTasks: 5 taskQuota: cpu: 2 memory: 4Gi timeout: 600s这样即使某个 Session 跑飞了最多也就吃掉 10 核不会影响同 namespace 的其他 Session。namespace 总量超了新 Task 排队不会把集群打挂。7.2 公平调度避免大任务饿死小任务agent 任务的大小差异很大有的几秒跑完有的跑几小时。如果按 FIFO 调度一个大任务堵在前面后面一堆小任务全等着。我用了DRFDominant Resource Fairness的简化版按已消耗资源 / 配额的比例排序比例低的优先调度。效果很明显在一个混合负载测试里FIFO 下小任务的平均等待时间是 45 秒DRF 下降到了 3 秒。大任务的完成时间略有增加从 120 秒到 135 秒但整体吞吐量提升了 40%。7.3 抢占与让出优雅处理优先级高优先级任务来了需要抢占资源。直接杀低优先级 Pod 太粗暴会导致大量重跑。ax 运行时的做法是协作式让出给低优先级 Task 发一个让出信号Task 在下一个 checkpoint 点主动保存状态并退出然后高优先级 Task 启动。这个机制需要 Task 配合——不是所有 Task 都能随时让出。对于不支持让出的 Task比如正在执行不可中断的工具调用只能等它到下一个安全点。所以我在 Task 定义里加了interruptible: true/false字段调度器只抢占标记为可中断的 Task。实测协作式让出的重跑成本比强制杀低 70%因为大部分状态被保存了。代价是抢占延迟增加要等 Task 到安全点平均 2-5 秒。对大多数场景可以接受。8. 可观测性agent 运行时到底该看什么指标8.1 四个黄金指标在 agent 场景的变形传统服务的四个黄金指标是延迟、流量、错误、饱和度。agent 场景下要加两个任务完成率和检查点恢复率。任务完成率 成功完成的 Task / 总提交的 Task。这个指标低于 95% 就说明有问题——可能是工具不稳定、可能是资源不足、可能是 agent 逻辑有 bug。检查点恢复率 成功从 checkpoint 恢复的 Task / 需要恢复的 Task。这个低于 99% 说明 checkpoint 机制有缺陷。我把这两个指标做成了 Grafana 面板的顶部大数字一眼就能看到健康度。下面再细分按 agent 类型、按工具、按节点维度拆开定位问题用。8.2 分布式追踪把一次 agent 执行串起来一个 agent 任务可能涉及几十个 Pod、上百次工具调用。没有追踪出了问题根本不知道是哪一步慢。我用 OpenTelemetry 做全链路追踪Task 提交时生成 trace ID透传到所有子 Pod 和工具调用最后在 Jaeger 里能看到完整的调用树。有个细节工具调用的 span 要记录输入输出的大小但不要记录内容隐私 存储成本。大小异常比如某次调用返回了 10MB 数据往往是问题的信号——可能是工具返回了错误页面或者 agent 传了错误的参数。8.3 日志的采样与聚合agent 日志量巨大全量收集成本高。我的策略是ERROR 级别全收WARN 级别采样 10%INFO 级别采样 1%DEBUG 默认关闭需要时动态开启。采样用一致性哈希保证同一个 Task 的日志要么全采要么全不采方便排查。日志聚合用 Loki按 namespace 和 agent 类型分 label。查询时用 LogQL 过滤比如{namespaceteam-a, agentresearch} | checkpoint failed。配合 Grafana 的 Explore 功能排查效率比 grep 高很多。9. 我个人的几条经验总结第一别急着上多集群。单集群能撑到几千个并发 Task大部分团队根本到不了这个量级。先把单集群的调度、状态管理、可观测性做扎实等真的遇到瓶颈再考虑 Karmada 这类方案。我见过太多团队一上来就搞多集群结果复杂度爆炸基础功能反而没做好。第二Checkpoint 是 agent 运行时的生命线。宁可多存几次也别丢状态。存储成本相比重跑的成本token 费用 时间微不足道。我现在的默认配置是每 30 秒或每次工具调用后存一次两者取先到者。第三工具调用一定要走代理。直连看起来简单但鉴权、限流、审计、重试这些横切关注点会散落在各处后期维护成本极高。Tool Proxy 这层抽象早加早省心。第四版本锁定比什么都重要。K8s 版本、CNI 版本、运行时版本、工具镜像版本全部写死。升级走灰度流程别图快。我踩过的坑里有一半是版本不一致导致的。第五可观测性要前置。别等出了问题才加监控。Task 提交的第一天就要有 trace、有指标、有日志。agent 的行为不确定性太高没有可观测性就是在盲人摸象。这套东西我在两个生产环境里跑过一个日均 5 万 Task一个日均 20 万 Task。前者单集群 3 节点后者 3 集群 12 节点。核心逻辑是一样的差别只在规模相关的参数调优。如果你正在设计 agent 平台希望这些经验能帮你少走点弯路。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。