Serverless 冷启动 + Orleans 虚拟 Actor:Agent Substrate 的架构血统考
【免费下载链接】substrateAgent Substrate: the core system项目地址: https://gitcode.com/GitHub_Trending/substrate7/substrate
一个看似矛盾的事实正在改写云原生的资源模型:Kubernetes 统治了容器时代,但它最引以为傲的 Pod 抽象,在 AI Agent 面前却暴露了结构性低效——Agent 大部分时间在等待(等待 LLM 响应、等待工具调用、等待用户输入),真正干活的时间以毫秒计,而一个常驻 Pod 的 CPU 与内存配额却在等待中白白燃烧。Google 开源的 Agent Substrate 给出的答案是:把 Serverless 的快照冷启动、Orleans 的虚拟 Actor 模型、Knative 的 Scale-to-Zero 思路三脉汇流,再把这些能力整体下沉到 gVisor 沙箱运行时里。本文不评价宣传话术,只从 docs/architecture.md 与源码出发,考证这套架构的血统到底从哪来、改了什么、又留下了什么。
一、血统溯源:Serverless 快照冷启动与 Knative Scale-to-Zero
1.1 同一个问题陈述:间歇性负载
内部架构文档 对目标负载的描述几乎就是一份 Serverless 负载画像:
Agents and "agent-like" workloads are generally very bursty, spending most of their time waiting for input or events, then handling those events, then going back to waiting.
等待时间无界、执行时间极短、不可信逻辑必须进沙箱、因此天然是单租户实例——这正是十年来 Serverless 平台反复优化的同一组约束。Knative 的 Scale-to-Zero 用"请求驱动扩容、空闲缩到零副本"解决闲置成本,代价是冷启动:镜像拉取、容器引导、探针就绪,秒级起步。Agent Substrate 保留了"空闲即零"的精神,但把"零"的位置从 Pod 换到了 Actor:
- Knative 缩到零的是 Revision 副本,冷启动意味着重建整个执行环境;
- Substrate 缩到零的是沙箱内的 Actor,底层保留着一批"温"的 Worker Pod 作为蓄水池。
这并非文字游戏,而是对成本结构的重新分配——Kubernetes 调度 Pod 需要多个异步过程收敛、多次网络跳转加镜像拉取(architecture.md 中专门论证了为什么不能走 K8s 调度路径),对"运行几毫秒"的负载不可接受。于是 WorkerPool 作为 CRD 预启动一批沙箱 Pod 待命(workerpool_types.go 的 spec.replicas 与 sandboxClasses 定义),激活路径完全绕开 Kubelet。
1.2 快照冷启动:黄金快照与末次快照
Serverless 第二代解决冷启动的关键技术是"快照启动"——用 Firecracker 的 microVM 快照、或用 CRIU 对容器进程做 checkpoint/restore,把启动成本从秒级压到百毫秒级。Substrate 把这套语义原封不动地搬进了 Actor 生命周期:
- 黄金快照(Golden Snapshot):创建 ActorTemplate 时,系统临时冷启动一次工作负载,捕获一份共享的
MEMORY级初始快照。首次激活的 Actor 直接从这份快照恢复,而非从 OCI 镜像冷启动(docs/glossary.md 的 Snapshots 一节); - 末次快照(Last Snapshot):每次 Suspend 写入 Actor 专属快照,下次 Resume 恢复它——"热"恢复而非冷启动;
- Phase 1 创建即挂起:CreateActor 在数据库登记状态为
ACTOR_STATE_SUSPENDED,并携带黄金快照引用,"确保 Actor 在首次请求时能瞬时水合进温 Worker"(architecture.md)。
这套"预热池 + 黄金快照 + 按需恢复"的组合,就是 Knative 与 Firecracker 血统的显性继承。项目给自己立的北极星指标也完全对标 Serverless 的冷启动军备竞赛:激活延迟 p95 100ms、单集群支撑 10 亿 Actor、每秒处理 1000 次唤醒(architecture.md 的 North Star Metrics)。
1.3 代价清单也一脉相承
文档毫不避讳地承认"天下没有免费的午餐"(architecture.md 的 New Problems 一节):百万级 Actor 的状态存储与频繁更新带来海量数据管理问题;数据局部性成为一等公民——事件到达时必须知道 Actor 最新状态在哪,要么路由过去、要么把状态搬过来;Actor 在 Worker 间频繁迁移让可观测性变难。这几条与 Serverless 平台在快照冷启动落地时踩过的坑(快照体积、存储带宽、调度局部性)高度同构,说明作者确实是从第一性原理重新推导了一遍,而不是抄了个概念外壳。
二、Orleans 虚拟 Actor 在沙箱里的投影
如果说快照冷启动是 Serverless 的遗产,那么 Actor 生命周期模型则带着鲜明的 Orleans 印记。
2.1 虚拟 Actor 的三个核心语义
Orleans 的虚拟 Actor(Grain)模型有三个关键设计:逻辑实体常驻(Grain 在逻辑上永远存在,无需显式创建/销毁)、物理按需激活(消息到达时才在 Silo 上激活,空闲超时自动失活)、状态持久化与迁移(失活后状态落盘,下次激活可能落在任意 Silo)。对照 architecture.md 的 Actor 生命周期,几乎是一一映射:
- CreateActor 注册即挂起——Actor 记录永久存在于控制面状态库,状态字段在
RUNNING与SUSPENDED间切换,物理资源完全解耦,这就是"逻辑存在、物理不存在"; - ResumeActor 流量驱动激活——事件到达才指派 Worker 恢复快照,对应 Grain 的"消息触发激活";
- SuspendActor 空闲回落——checkpoint 到对象存储、清空 Worker,对应 Grain 失活;
- 迁移自由——"下次请求可能在不同的 Worker 上恢复"(architecture.md 的 High-Level Design),对应 Orleans 的 Placement。
2.2 状态库取代 Grain Directory
Orleans 用 Grain Directory 记录 Grain 当前的 Silo 位置。Substrate 的对应物是控制面的 PostgreSQL 状态库:API 资源模型 明确把资源切成两层——WorkerPool 这类低频基础设施走 Kubernetes CRD,而 Actor/Worker 这类高频瞬态记录放进专门的高性能状态库,记录 Actor 的全局唯一 ID、物理位置(Worker IP)、当前状态与快照元数据。理由写得非常直白:Kubernetes API Server 不是为百万级离散资源和高频写流量设计的,PostgreSQL 才是(architecture.md 的 Architectural Rationale)。这正是"虚拟 Actor"模型对底层注册表的要求——目录查询必须低延迟、原子化,才能支撑 100ms 级激活。
2.3 流量驱动的唤醒:路由器的单飞去重
虚拟 Actor 模型的关键体验是"调用方无需关心激活",Substrate 把这一层做进了网络栈:客户端只需带一个ate-target-actor: <atespace>/<actor>头(Quickstart、demos/counter/README.md),atenet-router的 Envoyext_proc处理器负责查控制面、触发恢复、再把请求隧道到目标 Worker。
更精彩的是并发控制细节:cmd/atenet/internal/router/ingress/resumer.go 实现了按 Actor 维度的 singleflight——同一 Actor 的并发恢复请求共享一个 flight,首调用者成为 leader,后续调用者 join;当 WorkerPool 瞬时饱和时,请求不直接返回 503,而是进入 parking 模式,在预算时间内按退避重试恢复(wait.ExponentialBackoffWithContext+ 每 flight 独立预算)。这与 Orleans 激活风暴下的排队策略是同一思想:把"激活"当作系统级操作去合并、去排队,而不是让每个调用方各自为战。demostration 层面,README.md 记录的 demo 是 8 个物理 Pod 上复用约 250 个有状态 Actor(30x 超配),这正是"大量虚拟 Actor 映射到少量 Silo"的极端形态。
2.4 投影之上的关键差异:沙箱边界
如果仅止于此,Substrate 不过是用 CRIU 换 Grain。真正的差异在于:Orleans 的 Grain 共享 Silo 进程,而 Substrate 的每个 Actor 独占一个沙箱。词汇表说得清楚:一个 Worker "hosts several Actors at once, each in its own sandbox"(docs/glossary.md)。于是虚拟 Actor 模型的每一次激活/失活,都被叠加了一层零信任隔离边界——调度密度来自"闲置即回收",安全性来自"每个 Actor 都是独立内核边界"。这也是为什么威胁模型、mTLS 全链路、快照加密会成为一等课题:虚拟 Actor 的迁移语义天然把"谁能在哪运行什么"变成了必须在线决策的安全问题。
三、gVisor 能力下沉:为什么改造沙箱而不是另起炉灶
3.1 选型逻辑:原生支持 suspend/resume
架构文档给出了清晰的选型理由:gVisor 与 microVM(Kata Containers)碰巧都原生支持挂起与恢复,而这正是 Agent Substrate 的核心功能(architecture.md)。换句话说,项目不打算发明新的沙箱运行时,而是选择那些已经把"检查点"作为一等公民的运行时,再把编排能力下沉进去。gVisor 作为默认沙箱类(sandboxClasses[].name: gvisor,见 workerpool_types.go),microVM 作为更强隔离的高配选项(需要 KVM/vhost 设备,见 sandboxconfig_types.go)。
3.2 runsc checkpoint/restore 的工程细节
gVisor 的检查点能力不在内核而在用户态:runsc 序列化的是 Sentry(用户态内核)的完整状态,无需 CRIU 这类依赖内核特性的工具,因此快、且可移植——这是把"Serverless 快照"语义下沉到沙箱的物理前提。cmd/ateom-gvisor/runsc.go 把这一能力包装成一组原子操作:
runsc checkpoint -image-path <path> <container>:冻结沙箱进程树并落盘检查点镜像;runsc restore -bundle <bundle> -image-path <path> -background -detach <container>:恢复沙箱,-background -detach保证恢复后进程立即接管;- 两个不起眼但重要的全局 flag:
--allow-connected-on-save(绕过 gVisor 检查点期间网络连接恢复的已知缺陷,architecture.md 也专门提过)与--cpu-num-from-quota(让恢复后的 Sentry 按 cgroup CPU 配额而非宿主机 CPU 数来定 vCPU,保证沙箱按 Pod 限额缩放)。
3.3 双保真快照:MEMORY 与 VOLUMES
cmd/ateom-gvisor/main.go 的CheckpointWorkload揭示了快照的两种保真度,对应虚拟 Actor 状态的不同持久化需求:
- MEMORY 保真:先对 pause 容器(沙箱根容器,只负责持有命名空间、运行
/pause,不承载工作负载代码)执行runsc checkpoint,再对持久目录打 tar 包——进程内存 + 根文件系统变更 + 持久卷一起捕获; - VOLUMES 保真:不冻结进程,而是
runsc pause→ 仅对持久目录打 tar →runsc resume——恢复时容器从 OCI 镜像冷启动、只回灌持久卷数据。这是 roadmap 里"Disk-Only Resume"的先行形态(docs/roadmap.md)。
恢复路径(RestoreWorkload)同样精细:先 create + restore pause 容器,再对每个应用容器以同一份检查点镜像逐一遍历 restore(runsc.go 注释明确说明"只对沙箱根容器做 checkpoint,但要用同一份镜像 restore 每个容器"),全部就绪后还要通过 wakeup probe 确认容器已返回 200,才激活隧道放行流量(wakeupprobe.WaitAll)。恢复过程中的每一阶段都有ateomphaselog计时——prep、egress、netSetup、pauseCreate、pauseRestore、appRestore、wakeupProbe、activate 全链路可观测,这是为 p95 100ms 目标服务的工程肌肉。
3.4 改造而非另起炉灶:三层胶水
Substrate 对 gVisor 的"改造"体现在三层管理胶水上,全部在沙箱外部:
- atelet(节点级 DaemonSet):监督物理 Worker Pod,负责拉镜像、组装 OCI bundle、驱动 ateom、把快照流式上传/下载到对象存储(GCS/S3);
- ateom(Pod 内沙箱牧羊人):每个沙箱类一个镜像(
ateom-gvisor、ateom-microvm),向 atelet 暴露RunWorkload/CheckpointWorkload/RestoreWorkloadgRPC 接口,"确保物理 Pod 生命周期与沙箱化 Agent 进程解耦"(architecture.md); - SandboxConfig(集群级 CRD):用 sha256 内容寻址钉住 runsc 二进制、pause 镜像等运行时资产(sandboxconfig_types.go),并把运行时版本钉进快照清单,保证"恢复永远复现快照拍摄时的运行时"——一处升级、全集群生效,同时阻断"快照在旧 runsc 上拍、在新 runsc 上恢复"的漂移风险。
这个架构姿态本身就回答了"为什么改造沙箱而不是另起炉灶":标准 OCI 容器 + 现成沙箱运行时 + 外部编排胶水,换来的是框架无关性——README 明确说因为是在内核层管理标准 OCI 容器(经 gVisor),所以能承载 ADK、LangChain、Claude Code 等任何栈构建的 Agent(README.md)。它不发明执行环境,而是发明执行环境的"调度与生命周期"。
结论:三脉汇流,与两条未走完的路
把三条血统线并置,Agent Substrate 的设计可以这样概括:
- Serverless 血统提供"成本模型"——间歇负载按需付费,快照冷启动压缩激活延迟(北极星 p95 100ms 是 Knative/Firecracker 冷启动军备竞赛的直接继承者);
- Orleans 血统提供"逻辑模型"——常驻虚拟 Actor、流量驱动激活、状态持久化与自由迁移,PostgreSQL 状态库扮演 Grain Directory;
- gVisor 血统提供"物理模型"——用户态 checkpoint/restore 让"每个 Actor 一个沙箱"的密度梦想成为可能,而 SandboxConfig 的版本钉住把可复现性焊死在快照清单里。
正如 roadmap 所坦承的(docs/roadmap.md),这套模型面前还有两条未走完的路:一是存储层的增量快照与分层(zswap/本地 SSD/对等节点/blob 的存储分级)——全量快照撑得起 demo,未必撑得起 10 亿 Actor;二是 Actor Fork/Clone——从既有检查点分支出新 Actor,把"状态根"变成推理路径的复制源。这两项一旦落地,虚拟 Actor 的"虚拟"二字才算真正闭环:不仅物理可迁移,逻辑也可繁衍。在那之前,Substrate 已经用一份相当诚实的架构文档和可复现的 demo,把 Serverless 与 Actor 模型在沙箱时代的结合部测绘了出来。
【免费下载链接】substrateAgent Substrate: the core system项目地址: https://gitcode.com/GitHub_Trending/substrate7/substrate
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考