Skip to content

为什么 Agent 基础设施开始在 Kubernetes 之上再造一层控制面

Published on
为什么 Agent 基础设施开始在 Kubernetes 之上再造一层控制面

当我们去看以 substrate 为例的 Agent Infra 的架构,容易产生一个疑问:Kubernetes 已经有 API server、scheduler 和 kubelet,为什么上面又出现了 ateapi、Actor scheduler、atelet 和一套网络路由?Agent Infra 是在重新写一遍 Kubernetes 吗?

Kubernetes 与 Substrate 的组件职责对应:kube-apiserver 与 ateapi、kube-scheduler 与 Actor scheduler、kubelet 与 atelet。前者管理 Pod 到 Node,后者管理 Actor 到 Worker;虚线表示类比而非调用关系。

组件名称的相似,掩盖了调度对象的变化。Kubernetes 把 Pod 放到 Node 上;Substrate 把有身份、有状态的 Actor 放到已经运行的 Worker 上。这层设计首先把逻辑工作负载的生命周期与 Pod 分开,再把频繁的激活和休眠交给专门的控制面处理。

Actor 与 Worker 的生命周期

一个 Agent 可以存在几天,却只在收到消息时执行片刻。让它始终占据一个 Pod,内存、运行时和 Pod 槽位会随逻辑实例数增长;每次活动都重建 Pod,又要支付控制器协调、调度和启动成本,还必须回答进程状态从哪里继续。Warm Pool 能提前支付启动成本,但如果会话仍长期绑定实例,空闲占用的问题依然存在。

这里有两种不同的寿命:用户需要持续存在的身份和状态,机器只需要为当前活动提供执行位置。Substrate 将前者建模为 Actor,将后者建模为 Worker Pod。休眠的 Actor 保留记录和快照,恢复时再获得 Worker。池中 Pod 可以继续运行,里面承载的 Actor 则不断更替。

这种复用可以理解为不同 Actor 轮流使用 Worker:Actor A 空闲时保存状态、释放 Worker,Actor B 就可以接着使用;A 下次需要执行时,再分配一个空闲 Worker 恢复状态。因此,需要多少 Worker,主要取决于同时有多少 Actor 在工作。状态保存和恢复需要时间,突发请求也可能让更多 Actor 同时活跃,所以还要预留一些容量。Actor 等待的时间越长,这种复用通常越有价值。

两层控制面的分工

Substrate 的分层落实在资源和存储上:WorkerPool 是 Kubernetes CRD,由 atecontroller 协调成 Deployment;Actor、Worker 记录和 ActorTemplate 则由独立的 gRPC API 管理,持久化到 PostgreSQL。尤其值得注意,ActorTemplate 也不是 Kubernetes CRD。集群控制面看到的是容量池与 Pod,Actor 的高频状态转换留在 Substrate 内部。

但生命周期分离本身,并不必然要求独立 API:也可以用 Actor CRD 表达身份,再由控制器分配已有 Worker。Substrate 更进一步,将 Actor 记录和分配过程移出 Kubernetes API,使大量休眠实例不必逐一成为集群对象,频繁唤醒也无需逐次经过这些对象的写入与控制器协调。代价是自己维护状态存储、并发分配和故障恢复。是否值得这样做,取决于这些管理开销是否已成为瓶颈,以及请求能否接受异步协调的等待;增加一层 API 本身并不会让系统更快。

两级调度:Substrate 将 Actor 分配给预热 Worker,Kubernetes 将 Worker Pod 放置到 Node;Actor 的生命周期独立于 Pod。

一次请求沿着这条边界运行:atenet 识别目标 Actor,调用 ResumeActor;控制面为需要恢复的 Actor 选择 Worker,节点上的 atelet 协调 Worker 内的 ateom 恢复 sandbox,路由再把请求转发进去。已有 RUNNING Actor 则直接返回现有分配。这组 API 对应的是 Actor 的状态转换:

Actor 生命周期:创建后休眠,ResumeActor 分配 Worker 并恢复执行,SuspendActor 保存快照、释放 Worker 后回到休眠。省略中间状态与异常路径。

可以把 Worker 想成共享工位,Actor 的快照则是收起的工作现场。人离开后工位可以给别人用,工作现场则留待下次展开。

这解释了为什么网络层也是架构的一部分。目标 Actor 可能尚未占有执行位置,请求必须先触发生命周期操作,才能得到可用地址。atenet 的入口实现还包含容量耗尽时的请求等待逻辑;预热容量用完以后,延迟问题仍会回来。所谓快路径,是在已有容量上完成分配与恢复,并不意味着状态读取、快照传输和恢复成本消失了。

调度与快照位置

Kubernetes 决定 Worker Pod 落在哪个 Node;Substrate 则从已运行的 Worker 中,为 Actor 选择满足运行时类型、标签、节点位置和剩余容量要求的执行位置。底层解决 Pod 的资源放置,上层还要保证 Actor 的状态能够在选中的 Worker 上恢复。

状态位置让这层调度有了自己的语义。Substrate 区分 Pause 与 Suspend:前者把快照留在节点本地,恢复调度通过 RequiredNodes 限制到持有快照的节点;后者将快照提交到外部存储,解除这种本地绑定。少搬运状态可以缩短路径,却缩小了可选容量范围。Actor placement 同时是计算放置和状态放置。

Pause 将快照留在节点本地,恢复受节点位置约束;Suspend 将快照提交到对象存储,允许在满足其他约束的节点上恢复。

例如,一个 Actor 的快照只在 Node A,而 A 上的 Worker 都忙着。即使 Node B 有空闲 Worker,这次恢复仍可能等待;把新 Pod 扩到 B 也没有解决问题。此时需要的是 A 上可用的执行位置,或先将快照转成可跨节点恢复的持久状态,并支付传输成本。由此可以推导,集群的空闲容量,不等于某个 Actor 可用的容量。

这也给两层控制面的协作提出了要求:上层的等待信号需要带上运行时类型、状态位置等约束,才能帮助底层补充真正可用的 Worker。只看总体 CPU 利用率或 Worker 总数,可能出现一边闲置、一边排队。预热容量吸收的是短时突发,扩容补充的是后续供给;两者服务于不同时间尺度。

与 Agent Sandbox 的区别

Kubernetes Agent Sandbox 选择在 Kubernetes API 内表达隔离实例。其 Suspended 语义是终止 backing Pod,保留 Sandbox 身份和持久存储;这并不自动包含进程内存的 checkpoint。Substrate 则把 Actor 状态从 Pod 生命周期中拆出来,利用运行时快照恢复到 Worker。两者解决的问题有交集,但持久目录还在,与进程能从原处继续,是不同的承诺。

Substrate 本身也没有只有一种恢复方式:Full 快照包含进程内存、rootfs 增量及相关持久目录;Data 只保存支持快照的卷数据,恢复可以重新启动进程。不能把所有 suspend/resume 都理解成完整执行现场的延续,更不能据此推导外部 API 调用具备 exactly-once 语义。

对于能从文件或应用状态重建上下文、活动持续时间较长的 Agent,Sandbox CRD 加上会话路由可能已经足够。只有当大量逻辑实例、高比例空闲和频繁恢复共同成为主要成本时,独立管理面才更有理由承担额外复杂度。这是一条需要由负载特征证明的架构边界。

独立控制面的成本

第二层控制面把工作从 Kubernetes 移走,也把责任接了过来。假设 Worker 已经恢复了 Actor,但控制面在记录 RUNNING 之前中断,数据库里还停留在 RESUMING。下一次请求不能因为没看到 RUNNING,就另找一个 Worker 再启动一份;也不能直接认定上一次恢复已经成功。它需要核对原来的分配是否仍有效,并判断执行端究竟完成到了哪一步。

Substrate 的恢复流程先持久化 Worker 分配,再调用节点恢复,最后提交 RUNNING;重新进入 RESUMING 时,会校验并复用有效的原分配。这个顺序给重试留下了依据。但数据库记录与运行时进程并不处于同一个事务中,执行端还需要能识别重复请求,避免重复恢复或覆盖已运行的状态。第二层控制面的难点,是让记录中的状态与实际执行状态在失败和重试后重新一致。 Actor 租约、分配校验和持久化中间状态,都是围绕这个问题展开的。

权限管理也需要跟上这层分工。Kubernetes RBAC 可以约束谁能管理 WorkerPool 等集群资源,但谁能创建、恢复或删除某个 Actor,需要由上层 API 自己判断。把 Actor 移出 Kubernetes API,也意味着要为它建立相应的权限与审计机制。

当大量逻辑实例只间歇执行,而频繁激活与休眠又不适合反复创建、销毁 Pod 时,将逻辑生命周期与物理容量分开管理,就有了实际价值。 Substrate 展示了这条边界如何落到代码里:Kubernetes 维持可用容量,上层管理身份、状态与激活。判断这层设计是否值得,最终要看节省的空闲成本和恢复延迟,能否覆盖快照、容量余量与第二套控制面的代价。

参考:Agent Substrate · Kubernetes Agent Sandbox。