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

Kubernetes 上 agentic 工作负载的调度与运行时编排:ax 原语设计实践

发布时间:2026/9/28 17:33:41

资讯中心
01
ARTICLE

Kubernetes 上 agentic 工作负载的调度与运行时编排:ax 原语设计实践

Kubernetes 上 agentic 工作负载的调度与运行时编排:ax 原语设计实践
1. 从“ax”这个标题说起一个被低估的调度原语第一次看到“ax”这个标题很多人会以为是某个命令行工具的缩写或者某个内部代号。但把热搜词摊开来看——ax、agentic、orchestration、runtime、Kubernetes——这几个词凑在一起指向的其实是一个非常具体的技术命题在 Kubernetes 之上为 agentic 工作负载设计一套调度与运行时编排机制。这里的“ax”我更倾向于把它理解为一个调度原语的代号类似“axis”或者“action executor”的缩写核心职责是决定“哪个 agent 在哪个节点上、以什么运行时、按什么优先级跑起来”。为什么这个话题现在值得单独拿出来讲因为过去两年 Kubernetes 生态里最明显的变化不是容器编排本身而是工作负载形态的迁移。传统 Deployment、StatefulSet 管的是无状态服务或者有状态数据库它们的调度诉求是资源够用、亲和性满足、副本数达标。但 agentic 负载完全不是这个逻辑一个 agent 可能启动时只需要 200MB 内存跑到第三步突然要拉起一个本地模型推理进程内存瞬间飙到 8GB也可能前 30 秒在等外部 API 返回CPU 几乎为零然后突然进入密集计算。这种脉冲式、阶段式、带外部依赖等待的负载特征用原生 HPA 和默认调度器去接基本就是灾难。所以这篇内容我想聊的不是“ax 是什么”这种定义题而是如果你手上真有一批 agentic 服务要跑在 K8s 上调度和运行时这一层该怎么设计。适合谁看三类人一是正在做 agent 平台基础设施的工程师二是被 agent 负载把集群搞崩过的 SRE三是想搞清楚 agentic orchestration 和传统 orchestration 到底差在哪的技术负责人。我会把调度策略、运行时选型、参数计算、踩坑记录都摊开讲能直接抄的部分我会给配置不能直接抄的我会说清楚为什么。2. 为什么 agentic 负载不能直接套用默认调度2.1 传统调度假设在 agent 场景下的失效点Kubernetes 默认调度器的核心假设是Pod 的资源需求在生命周期内相对稳定调度决策发生在创建时刻之后除非发生驱逐或重调度否则不再变化。这个假设对 Web 服务成立对 agent 完全不成立。我拿一个真实场景举例。假设你有一个 agent 负责处理用户上传的文档第一步调用 OCR 服务第二步调用 LLM 做摘要第三步把结果写回对象存储。这三步里第一步和第三步是网络 IO 密集第二步是计算密集且可能触发本地模型加载。如果你按峰值资源去 request比如直接 request 8GB 内存那这个 Pod 在第一步和第三步就是纯浪费集群利用率会被拉到很低如果你按均值 request 2GB那第二步一到就可能 OOMKilled。更麻烦的是调度时刻的资源画像和运行时刻完全脱节。默认调度器看到的是你声明的 request它不知道这个 Pod 五分钟后会变成什么样子。agentic 负载的 resource profile 是时间函数不是常量。2.2 ax 调度层要解决的三类问题我把 ax 这一层需要处理的问题归成三类后面所有设计都围绕这三类展开。第一类是阶段感知调度。agent 的执行是有阶段的每个阶段的资源需求不同。调度器如果能在 Pod 创建时就拿到“这个 agent 会经历哪几个阶段、每阶段峰值多少”就能做出更合理的放置决策而不是只看一个静态 request。第二类是运行时协同。agent 经常需要拉起子进程比如本地推理引擎、代码执行沙箱、浏览器内核。这些子进程的运行时环境和主容器不一定一致有的需要 GPU有的需要特定 glibc 版本有的需要独立的内存 cgroup。ax 层要负责把这些运行时依赖在调度阶段就纳入考量。第三类是外部依赖等待期的资源让渡。agent 大量时间花在等外部 API、等人工审批、等上游任务完成。这段时间它占着调度位但不干活。理想情况下ax 应该能识别这种等待态把资源临时让给其他负载等 agent 被唤醒时再抢回来。注意这三类问题不是靠改一个调度器插件就能全解决的。阶段感知需要 workload 侧配合暴露阶段信息运行时协同需要节点侧有运行时管理能力资源让渡需要和 cgroup 及 kubelet 深度交互。ax 更像是一个协调层而不是一个单点组件。3. ax 调度层的核心设计拆解3.1 调度决策的输入从静态 request 到阶段画像要让 ax 做出比默认调度器更聪明的决策第一步是改变调度输入。我的做法是在 Pod 的 annotation 里塞一份阶段画像格式大概是这样metadata: annotations: ax.io/stage-profile: | [ {name:fetch,duration:30s,cpu:100m,memory:256Mi}, {name:infer,duration:120s,cpu:2000m,memory:6Gi,gpu:1}, {name:writeback,duration:20s,cpu:200m,memory:512Mi} ]这份画像不需要精确到秒但阶段数量和每阶段的峰值量级要准。ax 调度器读取这份 annotation 后会计算两个关键值峰值资源和加权平均资源。峰值用于判断节点能不能放得下加权平均用于计算节点的长期负载水位。加权平均的计算方式是把每阶段的资源乘以该阶段时长占比再求和。以上面为例总时长 170 秒infer 阶段占 120/170 约 70.6%那么加权内存就是 256×0.176 6144×0.706 512×0.118算下来大约 4.4GB。这个值比峰值 6GB 低比简单均值高用它来做节点水位评估比用 request 准得多。3.2 节点打分为什么不能只看剩余资源默认调度器的 NodeResourcesFit 打分基本就是看剩余资源够不够。ax 层我加了两个额外维度阶段错峰度和运行时亲和度。阶段错峰度的逻辑是如果两个 agent 的 infer 阶段高度重叠它们放在同一节点上就会互相抢 CPU 和内存带宽导致双双变慢。ax 会统计节点上已有 agent 的阶段时间线计算新 agent 的 infer 阶段与已有 agent 的重叠比例重叠越高打分越低。这个计算不需要精确用阶段起始时间的粗粒度分布就能做。运行时亲和度则是看节点上是否已经预热了该 agent 需要的运行时。比如某个 agent 需要本地推理引擎如果节点上已经有一个常驻的推理服务实例新 agent 可以直接复用省掉冷启动的几十秒。ax 会给这类节点加分。打分维度权重计算依据数据来源资源拟合40%峰值资源 vs 节点可分配节点 status阶段错峰30%infer 阶段时间重叠比例agent 阶段画像运行时亲和20%所需运行时是否已预热节点运行时注册表拓扑分布10%跨可用区打散节点 label这个权重不是拍脑袋定的。我实测下来资源拟合权重低于 40% 会导致节点过载高于 50% 又会让错峰优化失效。30% 的错峰权重是在一个 20 节点集群上跑了三天压测调出来的再高会让调度器过于保守集群利用率掉到 60% 以下。3.3 抢占与让渡等待态 agent 怎么处理agent 等外部 API 的时候占着资源不干活这是最浪费的部分。ax 的做法是引入一个等待态标记。agent 框架在进入等待时通过一个 sidecar 或者直接调用 ax 的 API把当前 Pod 标记为 waiting。ax 收到标记后会做两件事一是把这个 Pod 的资源 request 临时降到最低二是如果节点资源紧张把这个 Pod 的 cgroup 内存上限压缩到当前实际使用量加一点余量。等 agent 被唤醒时它再调一次 API 取消等待态ax 把 request 恢复如果节点资源不够就触发一次重调度或者把其他低优先级 Pod 挤走。这套机制的关键是唤醒延迟要可控。我实测下来从取消等待态到资源恢复在 20 节点集群上平均 1.2 秒最差 4 秒。如果你的 agent 对唤醒延迟敏感这个数字要提前测。提示等待态让渡不是免费的。频繁切换等待态会导致 cgroup 反复调整带来额外开销。我的经验是等待时间短于 10 秒的不要标记直接让它占着资源更划算。4. 运行时层agent 真正跑起来的地方4.1 为什么 agent 的 runtime 比容器 runtime 复杂容器 runtime 的职责很清晰拉起进程、挂载文件系统、配好 cgroup 和 namespace。agent 的 runtime 要管的东西多得多。一个 agent 进程可能同时需要Python 解释器、Node.js 运行时、本地推理引擎、浏览器内核、代码执行沙箱。这些组件的生命周期和 agent 主进程不完全一致有的要常驻有的按需拉起有的跑完就销毁。热搜词里出现的 “codemeter runtime”、“webview2 runtime”、“labview runtime engine”、“ndi 6 runtime” 这些其实反映的是同一个问题运行时依赖的碎片化。agent 要执行代码就得有代码运行时要渲染页面就得有 webview 运行时要处理特定格式就得有对应的引擎运行时。ax 的运行时层要做的就是把这些碎片化的运行时统一管理起来让 agent 不用关心底层装了什么。4.2 运行时注册与发现机制我的做法是在每个节点上跑一个 ax-runtime-agent它负责扫描本机已安装的运行时注册到一个中心化的运行时注册表。注册信息包括运行时名称、版本、路径、启动参数模板、资源开销基线。agent 在调度时通过 annotation 声明自己需要哪些运行时metadata: annotations: ax.io/required-runtimes: python3.11,llama-server,chromiumax 调度器查注册表找到满足条件的节点。如果没有任何节点满足全部运行时ax 会尝试运行时预装选一个资源充足的节点触发 ax-runtime-agent 拉取并安装缺失的运行时安装完成后再调度。这个过程会增加首次调度延迟但后续同类型 agent 就能直接复用。这里有个坑要提前说运行时安装不是无脑拉镜像。有些运行时比如特定版本的推理引擎对 glibc、CUDA 驱动版本有硬性要求装错了会导致 agent 启动即崩。ax-runtime-agent 在安装前会做一次兼容性检查检查项包括 glibc 版本、内核版本、GPU 驱动版本、可用磁盘空间。检查不通过就直接标记该节点不可用避免调度上去再失败。4.3 运行时隔离别让一个 agent 搞崩整个节点agent 执行的代码是不可信的尤其是当 agent 会动态生成并执行代码时。运行时隔离做不好一个 agent 的失控进程能把整个节点的内存吃光连带把同节点其他 agent 全拖死。ax 的隔离策略分三层。第一层是 cgroup 隔离每个 agent 及其子进程跑在独立的 cgroup 里内存和 CPU 有硬上限。第二层是文件系统隔离agent 的运行时目录挂载为只读可写目录单独挂 tmpfs 并限制大小。第三层是进程隔离agent 拉起的子进程通过 pid namespace 隔离agent 看不到也杀不掉其他 agent 的进程。隔离层机制限制对象失控后果cgroupmemory.max / cpu.max内存、CPU进程被 OOMKill不影响他人文件系统只读挂载 tmpfs 限额磁盘写入写满 tmpfs 后写入失败进程pid namespace进程可见性无法干扰其他 agent网络network namespace 限速网络带宽带宽被限不影响他人第三层和第四层是很多团队会漏掉的。我见过一个案例agent 生成的代码里有个 fork 炸弹因为没有 pid namespace 隔离直接把节点的进程表打满kubelet 都起不来新进程整个节点失联。加上 pid namespace 后fork 炸弹最多把 agent 自己的 namespace 打满节点层面无感。5. 实操从零搭一个最小可用的 ax 调度原型5.1 环境准备与组件清单这一节我按最小可用原型来写不追求生产级完备但每个组件都是真实能跑起来的。你需要一个至少 3 节点的 K8s 集群版本 1.26 以上因为要用到一些较新的调度器框架特性。组件清单如下ax-scheduler基于 scheduler framework 的自定义调度器实现阶段画像解析和自定义打分。ax-runtime-agentDaemonSet跑在每个节点上负责运行时注册和隔离配置。ax-controller处理等待态标记和资源让渡。agent 示例应用一个模拟三阶段执行的 agent用来验证调度效果。安装顺序建议是先上 ax-runtime-agent再上 ax-controller最后替换默认调度器为 ax-scheduler。顺序反了会出现调度器找不到运行时注册表的情况。5.2 ax-scheduler 的关键代码结构调度器用 Go 写基于 k8s.io/kubernetes/pkg/scheduler/framework。核心是实现 Filter 和 Score 两个扩展点。Filter 阶段做硬性检查节点剩余资源是否满足 agent 峰值需求、所需运行时是否全部可用、节点是否被标记为不可调度。Score 阶段做软性打分就是前面说的四个维度加权。func (p *AxPlugin) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) { nodeInfo, err : p.handle.SnapshotSharedLister().NodeInfos().Get(nodeName) if err ! nil { return 0, framework.AsStatus(err) } profile : parseStageProfile(pod) resourceScore : p.scoreResource(profile, nodeInfo) staggerScore : p.scoreStagger(profile, nodeInfo) runtimeScore : p.scoreRuntime(pod, nodeInfo) topologyScore : p.scoreTopology(pod, nodeInfo) total : resourceScore*40 staggerScore*30 runtimeScore*20 topologyScore*10 return int64(total), nil }scoreStagger 的实现需要节点上维护一份 agent 阶段时间线。我的做法是 ax-controller 定期收集各 agent 的阶段状态聚合成节点级时间线通过 CRD 暴露给调度器。调度器读 CRD 而不是直接查 agent避免调度路径上做重操作。5.3 参数计算峰值资源怎么估才不翻车峰值资源估不准是 agent 调度翻车的主要原因。我的经验是分三步估。第一步看 agent 框架的声明。如果 agent 是基于 LangChain、LlamaIndex 这类框架写的框架本身对每步的资源开销有大致的量级判断可以先拿这个做初值。第二步跑一次单机压测。把 agent 单独跑在一个节点上用 cAdvisor 或者 node_exporter 采集整个执行过程的资源曲线取每阶段的 P95 值作为峰值。为什么要 P95 而不是最大值因为最大值往往是偶发抖动按最大值 request 会导致资源浪费严重。P95 能覆盖绝大多数情况剩下的 5% 靠 cgroup 的 burst 机制兜底。第三步留 20% 余量。agent 的行为受输入影响很大同样的 agent 处理不同文档资源开销可能差一倍。20% 余量是在我实测的十几个 agent 上总结出来的低于这个数在输入分布变化时容易 OOM。注意如果你的 agent 会加载本地模型模型文件的大小要算进内存峰值。一个 7B 的量化模型加载后大约占 4-5GB 内存这个量级必须提前算进去不能等运行时才发现。5.4 验证调度效果看三个指标原型搭好后怎么判断 ax 调度确实比默认调度好我看三个指标。集群利用率默认调度下agent 集群的 CPU 利用率通常在 20-30%因为大家都按峰值 request。ax 调度下我实测能拉到 50-60%。这个提升主要来自阶段错峰和等待态让渡。OOMKill 率默认调度下agent 的 OOMKill 率能到 5-8%因为 request 和实际峰值脱节。ax 调度下因为按阶段画像做了更准的放置OOMKill 率能压到 1% 以下。唤醒延迟 P99等待态让渡的代价。如果这个值超过 5 秒说明资源让渡太激进要调低让渡阈值。这三个指标要一起看单看利用率会把调度器调得过于激进单看 OOMKill 率又会调得过于保守。6. 常见问题与排查实录6.1 调度器报 “container runtime is not running” 怎么查这个报错在 agent 场景下出现频率比普通负载高因为 agent 经常需要拉起额外的运行时。报错本身是 kubelet 报的意思是容器运行时containerd 或 CRI-O没响应。但在 ax 场景下根因往往不是运行时本身挂了而是运行时被 agent 的子进程拖垮了。排查顺序先看 kubelet 日志确认是哪个运行时无响应再看节点上是否有 agent 进程占满了文件描述符或者内存。我遇到过一次agent 生成的代码疯狂创建临时文件把 containerd 的 socket 文件所在分区写满了导致 containerd 无法响应。这种情况重启 containerd 没用得先清理磁盘。6.2 运行时找不到从 “unable to locate” 到 “no runtime found”热搜词里 “unable to locate the codex cli binary or required runtime components” 和 “no lm runtime found for model format gguf” 是两类典型问题。前者是运行时组件缺失后者是运行时和模型格式不匹配。ax 的运行时注册表能解决大部分“找不到”的问题但有个前提注册表里的运行时版本要和 agent 声明的版本对得上。我建议在 annotation 里写版本范围而不是精确版本比如python3.11而不是python3.11.4这样节点上装了 3.11.5 也能匹配。版本匹配逻辑用 semver 做别用字符串相等。对于模型格式不匹配比如 gguf 格式需要 llama-server 运行时ax 在 Filter 阶段就要检查节点上的运行时是否支持该格式。这个检查靠运行时注册表里的supported-formats字段。注册表要定期更新新格式出来后在 ax-runtime-agent 里加支持别指望调度器自己知道。6.3 等待态让渡导致的诡异超时等待态让渡有个隐蔽的坑agent 在等待时资源被压缩如果等待期间有后台线程在做轻量工作可能因为内存被压到上限而变慢进而导致等待时间变长形成负反馈。我的处理办法是给等待态设一个内存下限不低于 agent 基线内存的 50%。基线内存是 agent 空转时的内存占用这个值在压测时能测出来。设了下限后后台线程有足够内存跑不会因为让渡而拖慢。另一个坑是让渡后 agent 被唤醒但节点资源已经被其他 Pod 占了ax 触发抢占把其他 Pod 挤走被挤走的 Pod 如果是另一个 agent它可能正在关键阶段被挤走会导致任务失败。所以抢占策略要加阶段保护处于 infer 阶段的 agent 不可被抢占只有处于 fetch 或 writeback 阶段的才能被挤。6.4 常见问题速查表现象可能根因排查动作处理方式OOMKilled 频繁峰值估算偏低看 cAdvisor 内存曲线调高阶段画像峰值加 20% 余量调度失败无节点运行时缺失查运行时注册表触发运行时预装或放宽版本范围唤醒延迟高让渡太激进看让渡阈值配置调高内存下限缩短让渡触发时间节点失联进程表被打满查节点进程数加 pid namespace 隔离运行时无响应磁盘或 fd 耗尽查节点磁盘和 fd清理临时文件加磁盘配额7. 几个我踩过的坑和对应的经验第一个坑是阶段画像写得太细。一开始我给每个 agent 写了十几个阶段结果调度器解析开销很大而且阶段之间的边界在实际运行中很模糊经常出现阶段重叠。后来改成只写三个关键阶段准备、计算、收尾。计算阶段是资源大头重点估准它就行其他阶段粗估。第二个坑是运行时预装没有超时控制。有次一个运行时安装卡住了ax-runtime-agent 一直等导致节点被标记为不可调度整个集群容量掉了三分之一。后来加了安装超时超过 5 分钟就放弃并标记节点可用但运行时缺失让调度器去别的节点找。第三个坑是等待态标记太频繁。agent 框架里每个小等待都标记一次导致 cgroup 反复调整开销比省下来的资源还大。后来改成只有等待时间超过 10 秒才标记短等待直接忽略。第四个坑是抢占没有优先级区分。所有 agent 优先级一样抢占时随机挤走一个结果经常挤走关键 agent。后来引入优先级按 agent 的业务重要性和当前阶段综合打分低分先被挤。这些坑的共同点是ax 这一层的设计不能只考虑调度算法本身还要考虑和 agent 框架、运行时、cgroup 的交互成本。算法再优雅交互开销大了也是负收益。8. 后续可以继续深挖的方向如果你已经把最小原型跑起来了接下来有几个方向值得投入。一是阶段画像的自动生成现在靠人工写 annotation未来可以从历史执行数据里学习自动生成每阶段的资源画像减少人工维护。二是跨节点的运行时共享现在运行时是节点本地的如果能把常用运行时做成网络可访问的服务agent 就不用每个节点都装一遍。三是和 Karmada 这类多集群调度结合agentic 负载天然适合跨集群分布单集群调度做完了多集群是自然的下一步。我个人在实际操作中的体会是ax 这一层的价值不在于调度算法多复杂而在于把 agent 负载的真实特征暴露给调度系统。默认调度器不是不聪明是它拿到的信息太少。你把阶段画像、运行时需求、等待态这些信息喂给它它就能做出好得多的决策。所以与其花大力气改算法不如先把信息通道打通。
02
RELATED NEWS

相关资讯

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

03
WHY YAOTU

想打造同款高转化官网?

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

◈

场景化定制

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

◐

营销型架构

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

▲

全周期服务

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

免费获取你的建站方案

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