Agent 沙箱拆解:E2B 如何把 Firecracker 做成毫秒级执行环境

Claude Code、Codex、Gemini CLI 这类编码 Agent 的工作模式是「想一步、跑一步、看结果、再想」:一轮任务可能装依赖、跑测试、起服务,执行几十条命令。AI 从「写代码」走向「执行、验证、迭代代码」之后,执行环境就从脚手架变成了基础设施。这条赛道上,开源世界最火的 E2B 选了一条最重的路——Firecracker microVM(AWS Lambda 底层的虚拟化技术),却把沙箱启动做到了毫秒级。本文拆解它为什么这么做、架构怎么搭、快在哪一层,并对照 Anthropic 自己披露的分层沙箱方案,看沙箱选型的底层逻辑。

一、为什么 Agent 的代码必须关起来跑

传统工具调用——搜索、查库、发邮件——行为可枚举、可审计。代码执行不同,它是图灵完备的:Agent 一旦能执行任意代码,它能做的事和一个程序员坐在终端前完全一样,而你无法预测 LLM 会生成什么。一段看起来无害的「帮我分析这份 CSV」,生成的 Python 里可能就藏着把环境变量打包外传的语句。

威胁模型有三类:

  • 提示注入逃逸:恶意网页或文档内容诱使 Agent 执行攻击者代码,读取密钥或对外发起连接;
  • 资源滥用:无限循环、fork bomb、写满磁盘,拖垮整台宿主机;
  • 横向渗透:读取环境变量里的凭据、访问同机其他租户的数据。

模型层防不住这件事。Anthropic 在 2026 年 5 月的工程博客《How we contain Claude across products》里给过一个真实数字:内部红队用钓鱼让员工把恶意提示贴给 Claude,引导它读取 ~/.aws/credentials 并外传,25 次尝试成功了 24 次——提示词约束与分类器都靠不住,只能靠环境层兜底。这个结论是所有沙箱选型的起点。

由此,Agent 沙箱有三个硬指标:

  1. 隔离性:默认里面的代码是恶意的,要保证它伤不到宿主机、碰不到其他租户;
  2. 启动速度:毫秒到秒级——每条命令都等半分钟环境就绪,交互体验直接报废;
  3. 生命周期弹性:用户聊到一半去吃饭,沙箱要能随时冻结省钱,回来时现场原样恢复,连跑了一半的进程都在。

这三条放在一起,传统方案就被筛掉了大半:Docker 隔离偏弱,传统 VM 启动慢,而且两者都没有原生的「暂停 / 恢复」语义。

二、Docker、gVisor、Firecracker:syscall 走到哪,谁在检查

判断隔离强弱,最干净的视角是:应用的系统调用(syscall)走到哪里、由谁检查、谁接触宿主机内核。以一段 Python 代码 open("/tmp/test.txt", "w") 触发的 open/write 调用为例:

  • Docker / runc:容器里的进程本质是宿主机上的 Linux 进程,隔离由内核原语组合而成——namespace 造独立视图,cgroups 限资源,seccomp 过滤危险调用,capabilities 拆分 root 权限。问题在于所有容器共享同一个宿主机内核,内核有任何漏洞就可能容器逃逸。跑「可信的自家服务」可以接受,跑「AI 刚生成的陌生代码」不够。
  • gVisor:Google 开源(Cloud Run 在用),核心组件 Sentry 是一个用 Go 写在用户态的「应用内核」,拦截并翻译绝大部分 syscall,宿主机内核只暴露一小撮受限调用。隔离上了一个台阶,代价是兼容性与性能——Linux 语义不可能复刻完整,社区就有 Claude Code 在 gVisor ARM64 下卡死在 epoll_pwait 的真实 bug 报告。
  • Firecracker:AWS 开源的 microVM 运行时,走硬件虚拟化(CPU 的 VT-x / AMD-V → KVM → Firecracker 作为用户态 VMM)。每台 microVM 有自己独立的 guest 内核,设备模型裁剪到只剩 5 个模拟设备(virtio-net、virtio-block、virtio-vsock、串口控制台、精简键盘控制器),每 VM 内存开销 5 MiB 以内,官方口径启动 125 ms 以内——就算恶意代码在 guest 内核里提了权,面对的攻击面也比完整 Linux 内核小几个数量级。
方案 隔离机制 syscall 路径 原生启动 主要代价
Docker / runc namespace + cgroup,共享宿主内核 应用 → 宿主内核 约 10–50ms 内核漏洞即逃逸口
gVisor 用户态内核 Sentry 拦截翻译 应用 → Sentry → 少量受限调用 约 50–100ms 兼容性与性能损耗
Firecracker KVM 硬件虚拟化,独立 guest 内核 应用 → guest 内核 → VM exit VMM 125ms 内,端到端约 1s 需要 KVM 与 root,运维最重

规律很直白:隔离越强,原生启动越慢——端到端冷启动(含 guest 内核 boot 与服务就绪)实测约 1 秒,这正是 E2B 要用快照优化掉的部分。

Anthropic 的选择恰好印证了「场景决定方案」:claude.ai 网页端的代码执行跑在 gVisor 临时容器里(多租户、短会话、与容器体系无缝集成);本地 Claude Code 用操作系统自带沙箱——macOS Seatbelt / Linux Bubblewrap,策略是读全放开、工作区可写、默认断网,权限弹窗因此减少了 84%,运行时还以 sandbox-runtime 开了源;Cowork 这种让 AI 长时间无人值守干活的产品则直接上完整虚拟机。威胁模型越接近「恶意租户」,隔离边界越往硬件层走;越接近「手滑的助手」,越留在操作系统层。

三、E2B 架构:控制面与数据面分离

E2B 把整套后端开源(仓库现名 e2b-dev/runtime,原 infra,Go monorepo)。官方架构文档的核心设计是两条:沙箱即恢复的快照——模板是预启动 VM 的内存、磁盘、VM 状态三件套存于对象存储,「创建」沙箱就是恢复快照;控制面与数据面分离——API 决定沙箱在哪运行,各节点的 orchestrator 决定怎么运行,沙箱流量不经过 API。

组件 跑在哪 职责
API API 节点 公开 REST:沙箱生命周期、鉴权、配额、调度选点
client-proxy API 节点 无状态边缘路由:解析 3000-i7fa3.域名 这类地址并转发
orchestrator 每台沙箱节点(root) 真正驱动 Firecracker:创建 / 暂停 / 恢复 / 销毁,管网络与存储
envd 每台 VM 内部 沙箱内守护进程:进程执行与文件系统 API,SDK 一切操作的最终执行者
template-manager 构建节点 同一二进制的另一角色:把 Docker 镜像烘焙成 VM 快照模板

状态存储分工:PostgreSQL 存持久实体(团队、模板、构建、快照记录),Redis 是运行中沙箱的事实来源兼路由表,对象存储按构建 ID 存模板与快照工件,ClickHouse 存事件与指标。

flowchart LR
    SDK["SDK / 客户端"] -- 管理流量 --> API["API 控制面"]
    API -- 调度选点 --> ORCH["Orchestrator 节点代理"]
    ORCH -- 驱动 --> FC["Firecracker microVM"]
    FC --> ENVD["envd VM 内守护进程"]
    SDK -- 执行流量 --> PROXY["client-proxy 边缘路由"]
    PROXY -- 查路由表 --> REDIS[("Redis")]
    PROXY -- 直连节点 --> ORCH
    API -- 写路由表 --> REDIS

两条流量路径不相交带来的好处很实际:API 挂了不影响正在跑的沙箱,执行流量再大也压不到控制面。路由的关键在 Redis——沙箱创建时 API 挑一台节点并写入「沙箱 ID → 节点 IP」映射,之后每个执行请求到达 client-proxy 都查这张表转发。用 Redis 而不是 Postgres,是因为每个请求都查、QPS 高、数据小,而且允许丢失——节点会定期重新上报自己承载的沙箱来自愈。如果沙箱已被暂停(路由表查不到),client-proxy 会回调 ResumeSandbox 接口先唤醒再重试,暂停对用户完全透明。

VM 内侧同样有一层设计:envd 监听 49983 端口,提供进程服务(启动、stdout/stderr 流、PTY)与文件系统服务(stat、move、watch),SDK 的每行代码最终都落在它身上。它的鉴权令牌由 orchestrator 通过 /init 握手下发,源头是经 Firecracker MMDS(元数据服务)注入 VM 的哈希——沙箱里的代码自己拿不到这个令牌,外面的客户端没有令牌也连不进来。

四、「创建沙箱」其实是「恢复快照」

读源码最有意思的发现是:启动一台 microVM 没有任何黑盒。orchestrator 先用 unshare -m 给独立挂载视图、ip netns exec 给独立网络栈,再 exec 一个 Firecracker 进程放进专属 cgroup;Firecracker 起来后在 Unix socket 上监听官方 REST API,编排器按固定顺序发 9 个 HTTP 请求完成配置并启动:

# 请求 作用
1 PUT /metrics 指标输出到 FIFO
2 PUT /boot-source 内核路径与内核启动参数
3 PUT /drives 挂 ext4 根文件系统(经 NBD 的写时复制层)
4 PUT /network-interfaces tap 网卡、MAC 与限速
5 PUT /machine-config vCPU 与内存规格
6 PUT /entropy 硬件随机数设备
7 PUT /balloon 内存气球,回收空闲页
8 PUT /mmds 元数据:沙箱 ID、访问令牌哈希
9 PUT /actions InstanceStart,正式启动

几十行 shell 加 9 个 HTTP 请求,就是「启动一台沙箱」的全部秘密。这套 API 是 Firecracker 官方公开的,任何人用 curl --unix-socket 都能复现(需要 Linux、KVM 与 root)。

那毫秒级启动从哪来?答案是两段式生命周期:

  • 模板构建(离线,慢,只做一次):执行 e2b template build 时,构建机上真正冷启动一台 VM,按 Dockerfile 装依赖、启动服务、等就绪探针通过——然后立刻拍快照,把 memfile(内存)、rootfs.ext4(磁盘)、snapfile(VM 状态)三件套存进对象存储。这个阶段要几十秒到几分钟,但发生在任何用户请求之前。
  • 创建沙箱(在线,快,做无数次):调用 Sandbox.create() 时编排器从快照恢复 VM——guest 内核从被冻结的那条指令继续执行,内核 boot、init、装依赖、起服务全部跳过,因为那些事在拍快照之前就做完了。恢复路径甚至比冷启动更短:rootfs 先指向 /dev/null 拉起 FC 进程,并行等 FC socket、UFFD socket、rootfs 就位,PUT /snapshot/load 只登记内存不加载,最后 PATCH /vm 恢复执行。

类比游戏存档:BOSS 战前存个档,之后每次「开始游戏」都是读档直接站在 BOSS 房门口,没人重新过一遍新手村。E2B 没有让冷启动变快,而是让冷启动只发生一次——毫秒级启动的代价被转移到了离线的模板构建阶段,而构建是可以慢的。官方宣传口径是 150ms 级启动一个全新沙箱,社区实测几十到一两百毫秒不等,取决于模板大小与本地缓存命中。

五、毫秒级启动的三个关键技术

快照是总开关,但「恢复快照」这条路径上还剩两块大头:内存和磁盘。E2B 各用一招解决。

① UFFD:内存按需取页。 恢复时 Firecracker 根本不读内存文件,只把内存区域注册给 Linux 的 userfaultfd 机制;guest 碰到哪一页触发缺页,编排器里的 Go handler 就现场从快照文件里填哪一页,另有可选的预取器按模板构建期记录的「新沙箱必然碰哪些页」清单预热。社区分析估算,512MB 的沙箱启动实际要读的内存可能只有几十 MB。打个比方:冷启动像搬家公司把整个家一次性运来,UFFD 像人先住进去、要用什么让管家送什么——大多数时候你根本用不到全部家当。

② COW rootfs:磁盘写时复制。 模板磁盘只读、被节点上所有沙箱共享;每个沙箱的写入落进私有 COW 缓存,由一个进程内用户态 NBD 服务器拼成完整块设备暴露给 Firecracker。一千个沙箱共享一份 10GB 模板,磁盘成本约等于一份。

③ 暂停是恢复的逆过程,也是弹性的来源。 暂停时 cgroup 冻结全部进程,内存做脏页 diff、磁盘做 COW diff,先落本地缓存再异步上传对象存储(API 与节点共享 80 秒预算)。由此沙箱获得了「无服务器」属性:空闲自动冻结停止计费,流量来了经 client-proxy 自动唤醒;恢复还优先调度回原节点,本地缓存命中可以跳过对象存储读取。用户聊到一半去吃饭,沙箱不烧钱,回来现场原样。

六、隔离的五层叠加与选型边界

回到「默认代码是恶意的」,E2B 的隔离从硬件到应用叠了五层:

层 机制 防什么
虚拟化 KVM microVM,独立 guest 内核,设备模型裁剪到 5 个 逃逸到宿主机或其他租户
网络 每沙箱独立 netns + veth + tap,nftables 出站防火墙(支持按域名黑白名单) 沙箱互访、扫内网、未授权外联
资源 每沙箱独立 cgroup 限额,网卡与磁盘限速,vCPU 可超卖 吵闹邻居、资源耗尽
认证 团队 API key 管控制面;每沙箱独立 access token 经 MMDS 注入 跨团队操作、他人连入你的沙箱
流量 节点代理转发前校验 per-sandbox 令牌,文件端点用签名 URL 猜中沙箱域名就能访问

网络那层值得多说一句:出站防火墙不是简单封 80/443,而是按域名黑白名单放行——nftables 规则里做 SNI/Host 检查。这正是 Anthropic 踩坑后的教训:他们最大的一次泄露是恶意文件带着攻击者的 API key,让 Claude 把文件传到攻击者自己的 Anthropic 账户——出口代理看到 api.anthropic.com 就放行了。修法是在 VM 内加防御性中间人代理,只放行携带 VM 自身会话令牌的请求。出站控制比入口拦截更重要,因为数据总是经由「看起来合法」的路径离开。

这条赛道正在快速铺开:Kubernetes SIG 立项了 agent-sandbox 项目(文档直接引用 envd 作数据面守护进程的范式),阿里云 Agent Sandbox 推出了基于 MicroVM 的二代运行时,Modal、Cloudflare 都在为 Claude Managed Agents 提供托管沙箱,Vercel Sandbox 同样采用 Firecracker。社区分析普遍提到 Manus、Perplexity 等产品用 E2B 承载过代码执行——一个 Agent 任务一台完整的虚拟电脑,内含浏览器、终端、文件系统。

选型没有银弹。gVisor 和 Firecracker 各有清晰的适用边界,Anthropic 按产品线混用三种方案就是最好的例证;E2B 的价值在于把 Firecracker 这条最重的路,做成了一套任何人都能读、能部署、能复现的开源实现——编排逻辑全部可读,连它自己定制的 Firecracker 工件都在公共对象存储上开放下载,self-host 不需要自己编译。当执行环境成为 Agent 的标配基础设施,理解这套「控制面 / 数据面分离 + 快照时间挪移 + 写时复制」的组合拳,比记住任何一个产品名词都重要。

参考资料

← 返回资讯列表

读者留言

COMMENTS 暂无
仅本站原创文章开放留言 · 请勿留下手机号、邮箱等个人信息

还没有留言,来说第一句?