1. 从“ax”这个标题说起:一个被低估的调度原语
第一次看到“ax”这个标题,很多人会以为是某个命令行工具的缩写,或者某个前端框架的代号。但把热搜词摊开来看——ax、agentic、orchestration、kubernetes、workspace——这几个词凑在一起,指向的其实是一个非常具体的技术命题:在 Kubernetes 之上,为 agentic 工作负载做一套调度与编排的抽象层。
我最早接触这类需求是在做多租户 AI 任务平台的时候。当时团队里有人提了一个很朴素的问题:我们有一堆 agent 要跑,每个 agent 有自己的 workspace、自己的依赖、自己的生命周期,为什么不能像调度 Pod 一样调度它们?这个问题听起来简单,但真正落地时会发现,Kubernetes 原生的调度语义是围绕“长期运行的服务”和“一次性任务”设计的,而 agentic 负载的特点是状态化、长会话、工具调用密集、workspace 需要持久化且隔离。直接用 Deployment 或 Job 去套,会撞上一堆边界问题。
“ax”在我的理解里,就是对这个问题的回应。它不是 Kubernetes 的替代品,而是架在 Kubernetes 之上的一层agent 调度抽象。你可以把它想象成 Kubernetes 的“agent 运行时接口”:底层还是 Pod、PVC、Service 那一套,但上层暴露的是 agent、workspace、session、tool 这些更贴近 agentic 场景的概念。热搜里出现的[init] using kubernetes version: v1.26.0、[preflight] running pre-flight chec这些片段,说明实际部署时确实是在跟 kubeadm 这类工具打交道,底层集群版本至少是 1.26。
这篇文章适合谁看?如果你正在做 agent 平台、AI 工作流编排、多租户 workspace 管理,或者你只是好奇“agentic orchestration 到底怎么落到 Kubernetes 上”,那这篇内容应该能给你一些可以直接抄的作业。我会从设计思路、核心概念、实操部署、问题排查四个层面拆开讲,尽量把每个“为什么”都说清楚。
2. 整体设计思路:为什么要在 Kubernetes 上再包一层
2.1 agentic 负载和普通微服务到底差在哪
普通微服务和 agentic 负载最大的区别,不在于计算量,而在于状态的生命周期。一个 HTTP 服务是无状态的,请求来了处理完就走,Pod 挂了重启就行。但一个 agent 不一样:它可能正在执行一个长达几十分钟的任务,中间调用了十几个工具,workspace 里写满了中间文件,会话上下文存在内存或本地磁盘里。这时候你把 Pod 重启了,任务就断了。
我踩过的第一个坑就在这里。早期我们直接用 Job 跑 agent 任务,结果发现 Job 的backoffLimit和activeDeadlineSeconds根本不够用——agent 任务不是“失败重试”那么简单,它需要的是断点续跑和workspace 保留。Job 失败后 Pod 被清理,PVC 如果没配好,中间产物全丢。后来我们改成 StatefulSet,又发现 StatefulSet 的扩缩容语义太重,一个 agent 一个 Pod 的模式在几百个 agent 并发时,etcd 压力直接拉满。
所以“ax”这类抽象层的第一个设计目标,就是把 agent 的生命周期和 Pod 的生命周期解耦。agent 是一个逻辑实体,Pod 只是它某一时刻的执行载体。agent 可以休眠、可以迁移、可以被唤醒,而 Pod 可以随时创建销毁。这个解耦是后面所有设计的基础。
2.2 为什么选 Kubernetes 作为底座而不是自研调度器
有人会问:既然 Kubernetes 原生语义不匹配,为什么不自己写一个调度器?我的答案是:不要重复造轮子,但要学会给轮子加适配器。
Kubernetes 已经解决了分布式系统里最难的那部分问题:资源调度、网络、存储编排、健康检查、滚动更新、RBAC、命名空间隔离。你自研一个调度器,这些全都要重写一遍,而且大概率写得不如社区好。agentic 场景真正特殊的地方,其实只在调度策略和生命周期管理这两层。底层的能力,Kubernetes 已经足够。
“ax”的做法是:底层继续用 Kubernetes 的 Pod、PVC、Service、ConfigMap,但在上面加一层 Controller 和 CRD(Custom Resource Definition)。比如定义一个AgentCRD,描述 agent 的镜像、workspace 大小、工具依赖、超时策略;再定义一个WorkspaceCRD,描述持久化存储的挂载点和配额。Controller 监听这些 CRD,把它们翻译成底层的 Pod 和 PVC。这样既复用了 Kubernetes 的成熟能力,又让上层语义贴近 agentic 场景。
热搜里提到的karmada正式毕业!华为云携手社区共建agentic cloud坚实底座,其实也印证了这个方向:多集群调度和 agentic cloud 的结合,正在成为社区共识。Karmada 解决的是跨集群编排,而“ax”这类层解决的是单集群内 agent 的精细调度,两者是互补的。
2.3 workspace 隔离:为什么不能共用一个 PVC
workspace 是 agentic 场景里最容易被低估的概念。热搜里有一条vscode的workspace是什么意思,虽然语境不同,但核心问题是一样的:workspace 是工作现场的边界。
在 agent 场景里,workspace 至少承担三个职责:第一,存放 agent 执行过程中的中间文件;第二,隔离不同 agent 的文件系统,防止互相污染;第三,作为会话恢复的依据。如果你让所有 agent 共用一个 PVC,会出现什么问题?A agent 写了一个config.json,B agent 读到了,直接跑出错误结果。更严重的是,如果 agent 会执行代码,共用 workspace 等于共用了一个可写目录,安全边界直接没了。
所以“ax”的设计里,workspace 必须是按 agent 实例隔离的。实现方式有两种:一种是每个 agent 一个 PVC,优点是隔离彻底,缺点是 PVC 数量多了之后,存储后端的元数据压力大;另一种是用一个共享 PVC,但每个 agent 挂载不同的子路径(subPath),优点是 PVC 数量少,缺点是隔离性依赖路径管理,一旦路径计算错误就会串。
我实测下来,中小规模用 subPath 方案,大规模用独立 PVC 方案。subPath 方案在 500 个 agent 以内表现很稳,超过之后 kubelet 的挂载操作会成为瓶颈。独立 PVC 方案在 2000 个 agent 时,只要存储后端支持动态供给,问题不大,但要注意 PVC 的回收策略,别让孤儿 PVC 把存储撑爆。
3. 核心概念拆解:Agent、Workspace、Session、Tool
3.1 Agent CRD 应该包含哪些字段
设计一个 Agent CRD,最忌讳的是字段太多太杂。我见过有人把 agent 的所有配置都塞进 CRD,结果 YAML 写了两百行,维护成本极高。我的经验是:CRD 只放调度相关的字段,业务配置放 ConfigMap。
一个够用的 Agent CRD 大概长这样:
apiVersion: ax.io/v1alpha1 kind: Agent metadata: name: research-agent spec: image: registry.example.com/agent:latest workspace: size: 10Gi storageClass: fast-ssd session: timeout: 3600 resumePolicy: OnFailure tools: - name: web-search endpoint: http://tool-websearch:8080 - name: code-exec endpoint: http://tool-codeexec:8080 resources: requests: cpu: "2" memory: "4Gi" limits: cpu: "4" memory: "8Gi"这里有几个字段值得展开说。session.timeout控制 agent 空闲多久后进入休眠,单位是秒。resumePolicy决定 Pod 挂了之后怎么恢复,OnFailure表示只有异常退出才恢复,Always表示无论如何都恢复。tools列表里的 endpoint 是工具服务的地址,agent 运行时通过这个地址调用工具。
注意:
resources.limits不要设得太紧。agent 任务经常有突发计算,CPU 限制太死会导致任务超时。我一般把 limits 设成 requests 的 2 倍,留出 burst 空间。
3.2 Workspace 的存储选型和配额管理
Workspace 的存储选型,核心看两个指标:IOPS 和延迟。agent 任务里常见的操作是读写小文件、解压代码包、写日志,这些对 IOPS 要求高,对吞吐要求一般。所以选 SSD 类存储比 HDD 类存储合适得多。
在 Kubernetes 里,这体现为 StorageClass 的选择。我一般会准备两个 StorageClass:一个fast-ssd给 workspace 用,一个standard给日志和备份用。fast-ssd 的volumeBindingMode设成WaitForFirstConsumer,避免 PVC 创建时 Pod 还没调度,导致卷绑到错误的节点。
配额管理是另一个容易翻车的地方。Kubernetes 的 ResourceQuota 可以限制 PVC 的总量,但不能限制单个 PVC 的大小。所以要在 Agent CRD 的 admission webhook 里做校验,比如单个 workspace 不超过 50Gi,单命名空间总 workspace 不超过 500Gi。这个校验不做,迟早有人申请一个 1Ti 的 workspace,把存储池打满。
3.3 Session 恢复机制:怎么做到断点续跑
Session 恢复是 agentic 编排里最复杂的部分。简单说,agent 执行到一半 Pod 挂了,重新拉起后要能接着跑,而不是从头开始。这要求 agent 本身支持 checkpoint,同时编排层要能把 workspace 和 session 状态重新挂载回去。
“ax”的做法是:agent 进程定期把执行状态写到 workspace 里的一个约定路径,比如/workspace/.ax/session.json。Pod 重启后,agent 启动时先读这个文件,如果存在且resumePolicy允许,就从上次的状态继续。编排层要保证的是:新 Pod 挂载的是同一个 workspace,且启动顺序在 workspace 就绪之后。
这里有个坑:如果 agent 写 session 文件写到一半 Pod 挂了,文件可能是损坏的。所以写 session 要用原子写:先写临时文件,再 rename。rename 在 POSIX 文件系统上是原子的,能保证要么读到旧版本,要么读到新版本,不会读到半截。
3.4 Tool 调用的网络模型
Tool 是 agent 调用的外部能力,比如搜索、代码执行、数据库查询。在 Kubernetes 里,tool 通常以 Service 的形式暴露。agent 通过环境变量或配置文件拿到 tool 的 endpoint,然后发起 HTTP 或 gRPC 调用。
网络模型上有两种选择:同命名空间直连和跨命名空间走 Service。同命名空间直连延迟低,但隔离性差;跨命名空间走 Service 隔离性好,但多一层网络跳转。我的建议是:tool 和 agent 放在同一个命名空间,但用 NetworkPolicy 限制只有特定 agent 能访问特定 tool。这样既保证延迟,又有安全边界。
提示:tool 的 endpoint 不要硬编码在 agent 镜像里。用 ConfigMap 注入,这样换环境时不用重新打镜像。
4. 实操部署:从零搭一个 ax 调度环境
4.1 集群准备和版本选择
热搜里出现了[init] using kubernetes version: v1.26.0,说明实际部署时用的是 1.26。我的建议是:至少用 1.26,最好用 1.28 以上。1.26 是很多特性的分水岭,比如PodSchedulingReadiness在 1.26 进入 beta,1.27 正式 GA。这个特性对 agent 调度很有用,因为它允许 Pod 在调度前等待外部条件就绪,正好匹配 workspace 挂载的场景。
集群初始化用 kubeadm 就行,命令不复杂:
kubeadm init --kubernetes-version=v1.28.0 \ --pod-network-cidr=10.244.0.0/16 \ --apiserver-advertise-address=192.168.1.100初始化完成后,装一个 CNI 插件,Calico 或 Cilium 都行。我倾向 Cilium,因为它的 NetworkPolicy 支持更细粒度,而且有 eBPF 加速,对 tool 调用的延迟更友好。
[preflight] running pre-flight chec这个片段说明 kubeadm 在跑预检。预检失败最常见的原因是:swap 没关、端口被占用、cgroup 驱动不匹配。swap 用swapoff -a关掉,端口检查用ss -tlnp看 6443、10250 这些端口有没有被占。cgroup 驱动要确保 kubelet 和容器运行时一致,都用 systemd。
4.2 安装 ax Controller 和 CRD
ax Controller 的安装方式取决于它的分发形式。如果是 Helm Chart,直接helm install ax ax/ax-controller -n ax-system --create-namespace。如果是源码,先 apply CRD,再部署 Controller:
kubectl apply -f config/crd/bases/ kubectl apply -f config/manager/CRD 安装后,用kubectl get crd | grep ax确认。应该能看到agents.ax.io、workspaces.ax.io、sessions.ax.io这几个。
Controller 部署后,检查它的日志:
kubectl logs -n ax-system deploy/ax-controller-manager -f正常启动会打印starting manager和Starting EventSource。如果卡在setting up workspace: loading packages...,大概率是镜像拉取慢或者 RBAC 没配好。RBAC 问题看日志里的forbidden关键字,镜像问题看 Pod 的ImagePullBackOff事件。
4.3 创建第一个 Agent 和 Workspace
先创建一个 Workspace:
apiVersion: ax.io/v1alpha1 kind: Workspace metadata: name: demo-workspace namespace: default spec: size: 5Gi storageClass: fast-ssd reclaimPolicy: RetainreclaimPolicy: Retain表示 Workspace 删除后 PVC 保留,方便排查问题。生产环境可以设成Delete,但要有备份机制。
再创建一个 Agent:
apiVersion: ax.io/v1alpha1 kind: Agent metadata: name: demo-agent namespace: default spec: image: busybox:latest command: ["sh", "-c", "while true; do echo agent running; sleep 60; done"] workspaceRef: demo-workspace session: timeout: 600 resumePolicy: OnFailureapply 之后,用kubectl get agents看状态。正常会经历Pending->Creating->Running。如果卡在Pending,用kubectl describe agent demo-agent看 Events,通常是 workspace 没就绪或者资源不足。
4.4 验证调度和隔离效果
验证调度效果,最直接的方法是看 Pod 落在哪个节点:
kubectl get pods -o wide | grep demo-agent然后进 Pod 看 workspace 挂载:
kubectl exec -it demo-agent-xxx -- df -h /workspace应该能看到 5Gi 的挂载点。再写个文件测试持久化:
kubectl exec -it demo-agent-xxx -- touch /workspace/test.txt kubectl delete pod demo-agent-xxx # 等 Controller 重建 Pod kubectl exec -it demo-agent-yyy -- ls /workspace如果test.txt还在,说明 workspace 持久化生效了。
隔离效果测试:创建两个 Agent,分别挂不同的 Workspace,在 A 里写文件,去 B 里看,应该看不到。如果能看到,说明 subPath 计算有问题,或者 PVC 绑错了。
5. 常见问题与排查技巧实录
5.1 Agent 一直 Pending 怎么办
Pending 是最常见的问题,原因通常有三类:资源不足、workspace 未就绪、调度约束不满足。
排查顺序:先kubectl describe agent <name>看 Events,再kubectl describe pod <pod>看更底层的 Events。如果 Events 里出现0/3 nodes are available: 3 Insufficient cpu,就是资源不足,要么加节点,要么调低 requests。如果出现waiting for workspace to be ready,就去查 Workspace 的状态,看 PVC 有没有 Bound。
PVC 不 Bound 的常见原因是 StorageClass 不存在,或者volumeBindingMode设成了Immediate但节点上没有对应存储。用kubectl get sc确认 StorageClass,用kubectl get pvc -n default看 PVC 状态。
5.2 Workspace 挂载失败:mount 报错怎么查
挂载失败的表现是 Pod 卡在ContainerCreating,kubectl describe pod里能看到MountVolume.SetUp failed。这时候要去节点上看 kubelet 日志:
journalctl -u kubelet -f | grep -i mount常见错误有no such file or directory(subPath 目录不存在)、permission denied(挂载点权限不对)、timeout(存储后端响应慢)。subPath 目录不存在的问题,可以在 Agent 启动前加一个 initContainer,先创建目录。权限问题通常是 PVC 的fsGroup没设,在 PodSecurityContext 里加fsGroup: 1000能解决大部分。
5.3 Session 恢复不生效的排查思路
Session 恢复不生效,先确认三件事:agent 有没有写 session 文件、resumePolicy 是不是允许恢复、新 Pod 挂的是不是同一个 workspace。
进 Pod 看/workspace/.ax/session.json存不存在。如果不存在,说明 agent 没写,要检查 agent 的 checkpoint 逻辑。如果存在但没恢复,看 Controller 日志里有没有resuming session关键字。没有的话,可能是 resumePolicy 设成了Never,或者 session 文件格式不对,Controller 解析失败。
提示:session 文件建议用 JSON 格式,字段名用 snake_case,避免不同语言解析时的兼容问题。
5.4 工具调用超时和网络策略冲突
Tool 调用超时,先kubectl exec进 agent Pod,用curl或nc测 tool 的 endpoint 通不通。不通的话,检查 NetworkPolicy:
kubectl get networkpolicy -n default如果 policy 里只允许了特定 label 的 Pod 访问,确认 agent Pod 的 label 匹配。另一个常见原因是 Service 的targetPort和容器实际监听端口不一致,用kubectl get svc <tool> -o yaml看targetPort,再进 tool Pod 用ss -tlnp看实际端口。
5.5 常见问题速查表
| 现象 | 可能原因 | 排查命令 | 解决方向 |
|---|---|---|---|
| Agent Pending | 资源不足 | kubectl describe agent | 调低 requests 或加节点 |
| Pod ContainerCreating | 挂载失败 | journalctl -u kubelet | 检查 subPath 和 fsGroup |
| Session 不恢复 | resumePolicy 不对 | kubectl logs ax-controller | 改 resumePolicy 为 OnFailure |
| Tool 调用超时 | NetworkPolicy 拦截 | kubectl get networkpolicy | 放行对应 label |
| Controller 启动卡住 | 镜像拉取慢 | kubectl describe pod | 配镜像加速或预拉取 |
| PVC 不 Bound | StorageClass 缺失 | kubectl get sc | 创建对应 StorageClass |
6. 性能调优和规模化经验
6.1 Controller 的并发调谐参数怎么调
ax Controller 默认的并发调谐数(concurrent reconciles)通常比较保守,比如 1 或 2。在 agent 数量少的时候没问题,但到了几百个 agent 时,调谐速度会跟不上。可以在 Controller 的启动参数里加--concurrent-agent-syncs=10,把并发数提上去。
但并发数不是越高越好。我实测下来,10 到 20 之间比较合适。超过 20 之后,API Server 的请求压力明显上升,而且 Controller 自身的 CPU 会成为瓶颈。调的时候要盯着 API Server 的apiserver_request_duration_seconds指标,如果 P99 超过 1 秒,就要往回调。
6.2 Workspace 存储的 IOPS 瓶颈怎么定位
Workspace 变慢,通常是 IOPS 打满了。定位方法是进节点用iostat -x 1看%util,如果接近 100%,就是存储瓶颈。另一个指标是await,超过 20ms 就说明延迟偏高。
解决方向有三个:换更高 IOPS 的存储类、把 workspace 拆到多个存储后端、减少不必要的文件操作。第三个最容易被忽略。我见过 agent 每执行一步就写一次全量日志到 workspace,IOPS 全耗在日志上。改成写 stdout,让容器运行时收集,workspace 的 IOPS 直接降了一半。
6.3 多租户场景下的资源配额设计
多租户场景下,配额要分三层:命名空间级、Agent 级、Workspace 级。命名空间级用 ResourceQuota 限制总 CPU、内存、PVC 数量;Agent 级用 LimitRange 限制单个 Pod 的资源范围;Workspace 级用 admission webhook 限制单个 workspace 的大小。
三层配额的关系是:命名空间级是硬上限,Agent 级是默认值和范围,Workspace 级是细粒度控制。设计的时候要注意,ResourceQuota 的 PVC 数量限制是按 PVC 个数算的,不是按容量。所以如果每个 agent 一个 PVC,配额要设得足够大,否则 agent 创建到一半就失败了。
7. 我对 agentic orchestration 的一点个人判断
做了几个 agent 平台之后,我越来越觉得,agentic orchestration 的核心难点不在调度算法,而在状态管理。Kubernetes 把无状态服务的编排做到了极致,但 agent 是有状态的,而且状态的生命周期比 Pod 长得多。谁能把状态管理做好,谁就能在 agentic 这波里站住脚。
“ax”这个方向是对的,但它不是终点。我预期后面会出现更细分的层:有的专门做 workspace 存储,有的专门做 session 恢复,有的专门做 tool 路由。就像当年 Kubernetes 生态分化出 Istio、Prometheus、Helm 一样,agentic 生态也会分化。现在入场,正好是卡位的时候。
最后分享一个我踩过的坑:不要用 ConfigMap 存 agent 的 session 状态。ConfigMap 有 1MiB 的大小限制,而且更新频率高了之后,etcd 的写入压力很大。session 状态老老实实写 workspace,用文件系统管,简单可靠。这个坑我花了两个星期才爬出来,希望你别再踩。