03 - E2B 与快照机制
前置:02 篇的 microVM 部分。
本篇回答:microVM 启动要 125 毫秒,为什么托管沙箱能做到"几乎瞬时"?暂停三小时后恢复,里面跑着的进程为什么还在?
本篇会用到的词:
| 词 | 意思 |
|---|---|
| 快照(snapshot) | 把一个正在运行的沙箱整体存下来,包括内存和磁盘。恢复出来的是一个「已经跑起来的」环境,不是刚开机的环境 |
| memfile | 内存快照文件。进程、打开的文件、网络连接状态全在里面 —— 这就是暂停三小时后进程还在的原因 |
| rootfs | 根文件系统快照,装好的依赖都在这里 |
| 按需分页 | 恢复时不把整份内存快照读进来,而是用到哪一页才加载哪一页。所以快照体积大不直接等于恢复慢 |
| envd | E2B 跑在沙箱内部的代理服务,宿主通过它执行命令、读写文件,而不是直接伸进沙箱操作 |
| 冷启动 | 从零创建一个可用沙箱所花的时间。它是这一层最核心的体验指标 —— 用户等不了 30 秒 |
一、服务端的组件划分
E2B 把 SDK(e2b-dev/E2B,★13,474)和服务端(e2b-dev/infra,★1,331)分成两个仓库。快照机制在服务端那个仓库里,这是本篇的主要拆解对象。

图片来源:e2b-dev/infra
readme-assets/,Apache-2.0
packages/ 下的组件:
| 组件 | 职责 |
|---|---|
api | 对外 API |
client-proxy | 客户端流量代理 |
dashboard-api | 控制台后端 |
orchestrator | 沙箱生命周期与快照,本篇重点 |
envd | 跑在沙箱内部的守护进程 |
nomad-nodepool-apm | 节点池弹性伸缩 |
1.1 envd:沙箱内部的那一半
官方对它的描述只有一句:
Daemon that runs inside a sandbox that allows interacting with the sandbox via calls from the SDK.
这个设计决定了沙箱的能力边界。 SDK 调用的不是宿主上的 Docker API,而是沙箱内部的一个进程。这意味着:
- 执行命令、读写文件、装依赖,都由沙箱内的 envd 完成 —— 宿主不需要有能力"伸进"沙箱
- envd 有独立的版本号,官方要求「任何影响行为的改动都必须升版本」,因为宿主侧与沙箱内的协议必须匹配
- 快照恢复时,envd 连同它管理的所有进程一起被恢复
二、冷启动的真正解法:不启动
orchestrator 的命令行工具暴露了核心机制。这几个子命令直接说明了实现路径:
| 子命令 | 作用 |
|---|---|
create-build | 从零构建一个环境模板 |
resume-build | 从已有快照恢复 |
copy-build | 在本地与 GCS 之间复制快照 |
mount-build-rootfs | 挂载快照的根文件系统 |
inspect-build | 检查快照头与数据块 |
diff-build | 比较两个快照的差异 |
关键在于 "build"在这里不是镜像,是一份完整的虚拟机状态快照,包含两部分:
memfile —— 内存快照:进程、打开的文件、网络连接状态,全都在里面
rootfs —— 根文件系统快照
2.1 为什么这比启动快
装依赖那一步被彻底跳过了 —— 它在制作快照的时候已经做完,结果直接固化在内存镜像里。
这就是托管沙箱与自建容器方案最大的体验差距:自建方案每次都要 pip install,而快照方案里那些包早就在内存中处于「已导入」状态。
2.2 按需分页与预取
resume-build 的两个标志暴露了内存加载策略 :
-cold 每次迭代前清空缓存,模拟冷启动
-no-prefetch 关闭内存预取
存在 -no-prefetch 说明默认是开启预取的。 内存快照可能有几百 MB 到几 GB,如果恢复时必须全量读入才能开始执行,就谈不上"瞬时"。
实际做法是按需分页:先映射,访问到哪一页再加载哪一页,同时后台预取可能用到的页。-cold 这个标志的存在则说明官方在认真测量冷热两种情况的差异 —— 这类基准工具通常只有真的在优化这条路径的项目才会写。
三、暂停与恢复:把运行中的进程冻起来
resume-build 支持四种触发快照的方式:
-pause 启动后立刻快照
-signal-pause <signal> 等待指定信号后快照(如 SIGUSR1)
-cmd-pause <cmd> 执行一条命令,成功后快照
-cmd-signal-pause <cmd> 通过 envd 启动命令,再等 SIGUSR1 后快照
3.1 最后一种是给长任务准备的
官方文档里的例子:
# 通过 envd 在沙箱里启动一个长跑任务,然后从宿主侧触发快照
sudo go run ./cmd/resume-build -from-build $BUILD3 -to-build $BUILD4 \
-storage .local-build -cmd-signal-pause "python3 /home/user/workspace/job.py"
# 在另一个终端,等任务跑到想要的位置时发信号
sudo kill -SIGUSR1 <resume-build-pid>
这实现的是「把一个正在运行的 Python 进程连同它的内存状态一起存下来,之后原地恢复」。
对 Agent 的意义:一个跑了二十分钟、加载了大模型权重或大量数据的分析任务,可以在用户离开时暂停、回来时恢复,不需要重新加载。这与 Agent 持久化执行专题里的检查点是同一类问题的不同层次 —— 那里存的是应用级状态,这里存的是整个虚拟机的物理内存。
3.2 构建链:环境的增量演进
# 每一步都基于上一步的快照,产出一个新快照
sudo go run ./cmd/resume-build -from-build $BUILD1 -to-build $BUILD2 \
-storage .local-build -cmd-pause "apt install curl"
sudo go run ./cmd/resume-build -from-build $BUILD2 -to-build $BUILD3 \
-storage .local-build -cmd-pause "pip install requests"
这是 Docker 分层构建的等价物,但层的内容是内存状态而非文件系统差异。
它带来一个 Docker 做不到的能力:可以把「已经 import 好、初始化完成的运行时」作为基础层。下游沙箱恢复后,解释器已经在内存里,连 import 耗时都省掉了。
四、存储与工具链
| 能力 | 说明 |
|---|---|
| 本地 / GCS 双后端 | -storage .local-build 或 -storage gs://bucket,同一套命令 |
inspect-build | 检查 memfile 或 rootfs 的头部、数据块,可只看前 N 块 |
diff-build | 比较两个快照的差异,支持可视化 |
| NBD 设备 | 用网络块设备暴露快照,需关闭 inotify 事件监听 |
diff-build 值得 单独说:它让"这一层构建到底改了什么"变成可检查的。自建快照方案时,缺的往往不是快照本身,而是这类调试工具 —— 没有它,一个几 GB 的内存镜像出了问题无从下手。
五、可迁移的三条结论
1. 冷启动优化的终局是不启动。 无论用什么隔离技术,只要还在走「启动 → 初始化 → 装依赖」这条路,就有一个下限。快照把这条路径整个绕过去了。
2. 沙箱内要有一个自己的守护进程。 envd 的存在让宿主不需要具备"伸进沙箱"的能力,这本身就是一层安全收益 —— 宿主与沙箱之间只有一个明确定义的协议接口。
3. 内存快照的代价是存储。 每个快照都是完整的内存镜像,几百 MB 起步。构建链的每一层都要存一份。这与 Agent 持久化执行 · DBOS里的写放大是同一类问题:性能的代价通常落在存储上,不是 CPU 上。
下一篇 → 04 - 开源产品怎么做
← 回到 专题索引 · Agent Infra 板块总览