一、 概念模型:Event Loop 与 Agent Substrate 的异同
在理解 Substrate 的设计哲学时,拿 Ja vaScript 的 Event Loop 机制来类比,会特别直观。两者本质上都在解决同一个问题:如何用有限的物理资源,去承载远超数量的并发单位。

| 维度 | Ja vaScript Event Loop | Agent Substrate |
|---|---|---|
| 调度单位 | 回调函数 / Promise | Actor(沙箱实例) |
| 执行资源 | 单线程 | Worker Pod 池 |
yield(让出控制权) | await 切换栈 | 生成 VM 快照并保存至对象存储 |
resume(恢复执行) | 回调函数入队 | 将快照恢复(Restore)至指定 Worker |
| 唤醒触发器 | I/O 等事件完成 | 入站请求(由 atenet 拦截) |
| 挂起状态存储 | 堆内存(RAM) | 对象存储(低成本冷存储) |
Ja vaScript 靠单线程的 Event Loop 扛住高并发连接,Substrate 则借助 Pod 池来调度海量 Actor。两者共通的底层逻辑都是把 N 个逻辑并发单位映射到 M 个物理资源(),利用大多数单位处于等待状态的特点,实现资源的超卖(Oversubscribe)。
不过,Substrate 在系统设计上做了更激进的取舍:
- 上下文切换开销差异:代码级切栈是纳秒级,而 VM 级的快照与恢复则达到毫秒级甚至百毫秒级。所以 Substrate 不会在每次系统调用时都做快照,只在 Actor 进入长时间空闲态时才执行挂起。
- 状态边界扩展:挂起时需要保存完整的内存状态、文件系统以及内核态信息。由于恢复过程可能跨节点,系统必须重点解决数据局部性和分层存储这两大难题。
- 沙箱级强隔离:底层基于 gVisor/Kata 实现强隔离,因此具备运行不可信代码的能力。
- 存储架构变更:传统协程挂起受限于内存容量上限;Substrate 把挂起状态转移到廉价的对象存储,这就让可容纳的 Actor 规模主要受限于存储容量和恢复带宽——这也是其规模目标定在 10 亿级的底层逻辑。
二、 场景痛点:Agent 负载的资源利用率瓶颈
Substrate 的服务对象覆盖所有 Agent-like 工作负载,包括 AI Agent、沙箱代码执行器、MCP Server 等。这类负载有三个显著特征:
- 高度突发性:连续请求之间存在长周期的空闲状态(无入站流量);处理单次请求耗时从毫秒到数天不等,但请求间的空闲时间远大于实际执行时间。
- 强隔离需求:经常需要运行不可信代码,必须依托沙箱环境进行单租户隔离,导致实例数量爆发式增长。
- 有状态依赖:会话上下文、文件系统状态等不可丢弃。
如果直接跑在标准 Kubernetes 上,会面临以下瓶颈:
| 架构痛点 | Kubernetes 的局限性原因 |
|---|---|
| 空闲 Pod 占用资源 | 采用 1:1 的 Agent 与 Pod 绑定模式,CPU、内存及节点 Pod 配额被闲置实例长期占用。 |
| 控制面规模瓶颈 | etcd 无法支撑百万级 K8s 对象的并发管理与高频更新。 |
| 调度延迟过高 | 控制器收敛、网络路由建立及镜像拉取带来的秒级延迟,对实时响应任务不可接受。 |
| 卷管理开销 | PersistentVolume 无法支持百万级存储卷的高频挂载与卸载。 |
一句话总结:Kubernetes 的设计初衷是管理长周期运行的工作负载,难以高效应对海量、短周期、突发且有状态的 Agent 负载。
三、 核心架构:Actor 在 Worker 池上的多路复用
Agent Substrate 的核心机制可以概括为:Actor 在 Worker 池上的多路复用。
3.1 Actor 生命周期示例
gantt
title Actor 生命周期:长期挂起,瞬时激活
dateFormat X-axis Format
section Worker (CPU/RAM)
突发处理 1 (入站请求) :active, 0, 1
突发处理 2 (入站请求) :active, 6, 7
突发处理 3 (入站请求) :active, 14, 15
section State (廉价存储持久化)
身份与快照持久化保存 :done, 0, 20
Worker 资源只在入站请求触发的突发处理(Burst)期间被占用,其余时间释放给其他 Actor;Actor 的身份标识与快照状态则以极低成本持续存在存储层。
通过将多个 Actor 的突发处理在时间维度上交错分布,少量的 Pod 就能实现高密度复用。
Actor 的生命周期如下:
stateDiagram-v2
[*] --> RUNNING : CreateActor
RUNNING --> SUSPENDING : 空闲 / 主动挂起
SUSPENDING --> SUSPENDED : 快照完成,释放 worker
SUSPENDED --> RESUMING : 入站请求到达
RESUMING --> RUNNING : 从快照恢复
SUSPENDED --> [*] : DeleteActor
3.2 三大关键设计
- 挂起机制(VM 快照):利用 gVisor(
runsc)或 Kata Micro-VM 的 Checkpoint/Restore 技术,将内存状态、文件系统及内核态冻结并写入对象存储。挂起后,Actor 不再消耗 CPU 与 Worker 内存。 - 恢复机制(当前是流量驱动):轻量级网络袋里
atenet负责拦截发往 Actor 的请求(通过Host请求头识别目标实体),并触发快照恢复流程。恢复的目标节点是动态的,可以落到不同物理机上的 Worker。 - 旁路 K8s 控制面:Actor 的生命周期不通过 K8s API Server 及原生调度器,而是由独立的高 QPS 控制面(基于 Redis/Valkey 的
ate-api-server)统一管理,从而剥离关键路径上的 K8s 调度开销,降低激活延迟。
3.3 架构设计目标(指标待验证)
| 性能指标 | 目标设定 | 架构意图 |
|---|---|---|
| 单集群 Actor 容量 | 10 亿 | 系统扩展性的极限设计指标 |
| 唤醒吞吐量 | 1000 次/秒 | 支持大规模并发从 SUSPENDED 切换至 RUNNING |
| P95 激活延迟 | 100ms | 降低从入站请求到达至 Actor 恢复运行的整体时延 |
四、 技术演进与架构先例
Substrate 的各项核心技术在学术界与工业界都有成熟先例。本质上是对已知技术方案的工程整合与下沉,而不是基础理论的全新突破。
| 技术范畴 | 学术 / 工业先例 | Substrate 的工程差异 |
|---|---|---|
| 快照冷启动优化 | Catalyzer (ASPLOS'20)、SEUSS (EuroSys'20)、vHive/REAP (ASPLOS'21)、FaaSnap (EuroSys'22)、AWS Lambda SnapStart | 下沉至 gVisor 沙箱粒度 |
| 预热池与微型 VM | Firecracker (NSDI'20)、SOCK (ATC'18) | 叠加有状态 Checkpoint/Restore 恢复 |
| 沙箱级 Checkpoint-Restore | CRIU、gVisor runsc C/R、userfaultfd 按需分页 | 针对高密度 Agent 复用场景优化 |
| 虚拟 Actor 模型 | Microsoft Orleans (MSR-TR'14)、Erlang、Cloudflare Durable Objects | 下沉至沙箱实例级,而非进程/线程级 |
| Scale-to-Zero 流量唤醒 | Knative Activator | 引入有状态快照恢复,优化延迟表现 |
| 空闲资源回收 | Harvest VMs (OSDI'20)、Memory-Harvesting VMs (ASPLOS'22) | 结合对象存储实现跨节点无状态化迁移 |
系统的核心工程创新点在于:
- 构建独立的低延迟控制面,剥离 K8s 调度依赖;
- 将 gVisor 的 Checkpoint/Restore 机制应用于高密度 Agent 场景;
- 采用 Redis/Valkey 替代 etcd 作为 Actor 运行时状态的存储媒介;
- 基于数据局部性进行快照路由优化。
4.1 当前已知局限
- 项目处于早期阶段,不保证 API 的向后兼容性;
- 缺乏公开的 Benchmark 验证数据;
- gVisor 的 Checkpoint/Restore 机制对长链接网络协议的支持仍存在已知缺陷;
- 现阶段深度绑定 GCP/GKE 生态(快照强依赖 GCS),通用环境支持尚处于规划阶段。
五、 存储选型:为什么选择 Redis 而不直接用 etcd?
存储选型反映了系统在一致性与吞吐量之间的核心取舍。
5.1 etcd 的设计定位:强一致性配置数据库
Kubernetes 将状态统一持久化于 etcd。etcd 基于 Raft 共识算法提供多副本强一致性,其定位是存储小容量、高价值、读多写少的集群配置数据。
这一定位决定了其架构瓶颈:
- 写入开销大:每次写操作均需经过 Raft 协议落盘并达成多数派共识。写吞吐量存在物理上限(受限于单 Leader 串行提交),无法通过增加节点实现水平扩展。
- 容量限制:官方推荐数据库容量上限约为 8GB。
- Watch 事件风暴:海量对象与高频变更会导致 API Server 的 Watch 监听机制承载过量事件,进而拖垮控制面。
5.2 Agent 负载的存储挑战
| etcd 优化的场景 | Agent 负载的实际特征 |
|---|---|
| 万级对象规模 | 百万至十亿级 Actor 规模 |
| 读多写少 | 单 Actor 高频变更(状态、物理位置、快照指针) |
| 长生命周期、低频更新 | 高频突发式的挂起与恢复 |
| 强一致性优先 | 100ms 级激活,延迟敏感 |
如果强制用 K8s 原生 API Server/etcd 架构来承载海量 Agent,就必须对 K8s 存储层进行重构(比如放弃强一致性假设、重写事件分发机制等),这跟 K8s 保证集群稳定性的设计哲学是相违背的。
5.3 Substrate 的解耦方案:数据冷热分层
Substrate 采用了数据分层存储架构:
K8s + etcd → 仅管理低频、高价值控制面数据:WorkerPool、ActorTemplate 等 CRD(整体数量少,变更频率极低)
Redis/Valkey → 承载高频、海量运行时数据:各 Actor 的实时状态、物理位置、快照指针(支持十万级单机写 QPS,可基于 Hash Slot 实现横向扩展)
Redis/Valkey 能满足高并发需求,是因为它作出了不同的 CAP 权衡:
- 弱化强一致性:基于内存读写与异步复制机制,避免了每次写入都进行跨节点共识与磁盘
fsync的开销。 - 内存级读写:Actor 元数据体量小,内存操作天然适配高频读写。
- 水平分片能力:可基于 key 进行分片扩展,写吞吐量随节点扩容线性增加。
相应的架构代价是:系统放弃了存储层的强一致性保障,一致性控制、故障恢复以及状态校准逻辑都需要 Substrate 在应用层自行实现。
六、 Kubernetes 原生演进与 Substrate 的定位
随着 K8s 生态的演进,部分底层原语已经逐步原生化,但核心控制逻辑仍然会保持分层隔离。
6.1 K8s 社区已有的原语补充
| 原语能力 | 演进现状 | 与 Substrate 的关系 |
|---|---|---|
| 容器 Checkpoint/Restore | KEP-2008(v1.25 Alpha,基于 CRIU) | 当前主要用于故障取证;Restore 能力停留在运行时层(containerd/CRI-O),未进入 K8s 核心 API |
| 资源原地动态调整 | KEP-1287(v1.27 Beta) | 简化了 Worker 资源动态调整的实现复杂度 |
| 任务挂起/恢复 | Job 的 .spec.suspend 已支持 | 提供声明式挂起概念,但未覆盖沙箱级快照 |
| 沙箱运行时支持 | gVisor/Kata 通过 RuntimeClass 接入 | 基础设施层已完全成熟 |
6.2 K8s 核心层不易吸收的技术路径
- etcd 协议栈限制:API Server/etcd 无法直接承载百万级高频变更对象。
- 控制器异步收敛时延:K8s 基于状态对比与最终一致性的“控制器收敛”模型,换取了极高的通用性与自愈能力,但无法满足亚秒级的实时激活需求。
Substrate 的架构核心在于将 Agent 调度的关键路径从 K8s 控制面中剥离,这一设计与 K8s 核心架构的演进路线存在本质差异,因此更适合作为上层扩展(Add-on)存在。
6.3 系统架构分工预判
未来更合理的架构分工趋势是:K8s 负责管理基础设施和长周期资源,而 Substrate 这类组件则专注于领域特定的调度能力。这一定位与 Knative(Scale-to-Zero)及 KEDA(事件驱动伸缩)的演进路径一致:不改变 K8s 核心,而是作为上层生态组件提供差异化能力。
