数据截至 (上游 commit d7bc4affdac9)
E2B Infra — 这是什么 · 全景图 · 阅读地图
30 秒导读: E2B 是给 AI 代码解释器 / agent 用的云沙箱——让不可信的、模型现写现跑的代码,在一个强隔离的环境里安全执行。本仓库(
e2b-dev/infra)是它的后端基础设施:用 Firecracker microVM 做隔离,能秒级创建、暂停、恢复沙箱,并把流量路由进具体那台 VM。本页只做导航与直觉:讲清"这是什么、大盘怎么转、该读哪一章",不进底层细节。
1. 这是什么(零基础也能懂)
一句话定义: E2B 给每个 AI agent 会话发一台云上的隔离小电脑,agent 让模型写的代码就在里面跑,跑坏了也伤不到别人。本仓库是造并管理这些小电脑的基础设施。
它解决谁的什么问题。 假设你在做一个 AI agent:模型会现场生成一段 Python,你得真的把它跑起来看结果。可是这段代码来自大模型、不可信——它可能删你的文件、偷你的密钥、或吃光机器资源。你需要一个用完即弃、强隔离、还要开得快的执行环境。E2B 就是把"造这种环境"这件事做成基础设施。
核心难点是:又要强隔离,又要快。 传统容器开得快但隔离弱(共享内核);传统虚拟机隔离强但开得慢(动辄几十秒)。E2B 选了 Firecracker microVM——AWS 给 Lambda 造的轻量虚拟机,有真正的内核级隔离,却能在几百毫秒到几秒内启动。
一句话直觉 / 类比。 把每个 agent 会话想成一台随手可开关机、状态还能冻结再解冻的独立小电脑:
- 开机 = 创建沙箱(从模板启动一台 microVM)。
- 冻结(暂停) = 把这台电脑的整个内存和磁盘状态拍成快照存起来,VM 停掉、不再占资源。
- 解冻(恢复) = 从快照秒级复活,内存里的变量、跑到一半的进程,原样接着来。
- 关机(销毁) = 用完即弃,连痕迹一起清掉。
E2B 最难、也最巧的部分,全在"冻结 / 解冻要快"上——这正是 03、04 两章要讲的精华。
它能做什么(能力清单):
- 从预构建的模板秒级拉起一台隔离沙箱。
- 暂停 / 恢复沙箱:冻结完整内存态,之后原样复活。
- 给每台沙箱独立网络栈,并对出站流量做管控(允许 / 拒绝、按域名改写)。
- 把外部 HTTP/gRPC 流量路由到正确的那台沙箱、正确的端口。
- 在 VM 内跑一个守护进程(
envd),对外提供执行命令、读写文件的 API。
它不是什么。 本仓库不是 SDK、也不是 CLI——面向用户的 SDK/CLI 在另一个仓库 e2b-dev/e2b(见 README.md:6)。本仓库是跑在云上的后端,用 Terraform + Nomad 部署,官方支持 GCP、AWS(Beta)(README.md:18-22)。
2. 顶层全景(它大概怎么转)
E2B 的架构分两条线,这是理解整个系统的钥匙:
- 控制面(control plane): 谁能开沙箱、开哪个模板、放到哪台机器——决策在这里。走 REST/gRPC,和 Postgres/Redis/ClickHouse 打交道。
- 数据面(data plane): 真正把 microVM 跑起来、承载流量——干活在这里。逐台节点(node)上跑
orchestrator,直接操作 Firecracker。
怎么读这张图
从左到右:客户端 → 边缘 → 控制面 → (gRPC 下发) → 数据面 → VM 内。上半区是控制面(决策 + 存储),下半区是数据面(执行)。
┌──────────────── 控制面(决策) ────────────────┐
│ │
客户端 ┌─────────┐ ┌──────────────┐ ⟷ Postgres(主数据)
(SDK/HTTP) ───▶ │ client- │ ───▶ │ API (REST) │ ⟷ Redis(状态/缓存)
│ proxy │ │ Gin 框架 │ ⟷ ClickHouse(分析)
│ 边缘路由 │ └──────┬───────┘
└────┬────┘ │ ① 选节点 + gRPC 下发
│ ▼
── 运行期流量 ───────┼────────── ┌──────────────────┐
直连到具体沙箱 │ │ orchestrator │ ← 每个数据面节点一个
│ │ (逐节点数据面) │
│ └────────┬─────────┘
│ │ ② 启动/恢复
│ ▼
│ ┌──────────────────┐
└──────────▶ │ Firecracker VM │
③ 路由进 │ ┌──────────┐ │
具体 VM │ │ envd │ │ ← VM 内守护进程
│ │ (49983) │ │ 跑命令 / 读写文件
│ └──────────┘ │
└──────────────────┘
部件一句话职责
| 部件 | 干什么 | 在哪个 package |
|---|---|---|
| client-proxy | 边缘路由层:把外部请求按沙箱 ID 找到承载它的节点并转发;通过 Redis 目录(catalog)做服务发现 | packages/client-proxy |
| API | REST 控制面(Gin):鉴权、解析请求、选模板、挑节点并 gRPC 下发创建请求 | packages/api |
| orchestrator | 逐节点数据面:接 gRPC,真正操作 Firecracker 启动/暂停/恢复/销毁 microVM | packages/orchestrator |
| envd | 跑在每台 VM 内部的守护进程(Connect RPC):对外暴露"执行进程、读写文件系统"的 API | packages/envd |
| shared | 跨服务公共件:gRPC/proto 定义、遥测、日志、DB、存储客户端(GCS/S3)、特性开关 | packages/shared |
| db | PostgreSQL 层:goose 迁移 + sqlc 生成的类型安全查询 | packages/db |
| Postgres / Redis / ClickHouse | 分别管:主数据 / 状态与缓存 / 分析事件 | (外部依赖) |
注:
orchestrator的源码在packages/orchestrator/pkg/…(不是仓库自带CLAUDE.md里写的internal/…;那份文档略有漂移,以真实目录为准)。
主线走一遍:一次"创建沙箱"的旅程(高层)
跟着一个 POST /sandboxes 请求走一遍,只看经过谁、干了啥,代码细节留给 01 章:
- 进边缘。 请求先到
client-proxy,转给API。 - 控制面决策。
API的PostSandboxes鉴权、解析 body、把模板别名解析成具体 build(packages/api/internal/handlers/sandbox_create.go:64PostSandboxes),然后交给startSandbox。 - 挑一台节点。
Orchestrator.CreateSandbox组装 gRPC 请求SandboxCreateRequest,用放置算法placement.PlaceSandbox从集群里选一台合适的数据面节点(packages/api/internal/orchestrator/create_instance.go:172CreateSandbox、:321PlaceSandbox)。 - gRPC 下发到数据面。 选中的
orchestrator节点收到Server.Create,取模板快照数据(packages/orchestrator/pkg/server/sandboxes.go:96Create)。 - microVM 跑起来。
orchestrator调Factory.CreateSandbox(全新启动)或ResumeSandbox(从快照恢复),启动 Firecracker 进程,VM 内envd就绪(packages/orchestrator/pkg/sandbox/sandbox.go:642CreateSandbox、:694ResumeSandbox)。 - 能收发流量了。 返回沙箱信息;之后运行期流量再经
client-proxy直接路由进这台 VM 的envd,agent 就能在里面跑命令、读写文件了。
一句话总结这条线:API 做"选谁、放哪"的决策,orchestrator 做"把 VM 跑起来"的执行,client-proxy 负责让请求找到那台 VM。
3. 阅读地图(各章讲什么、建议顺序)
建议顺序读;想直取精华可跳到 03、04。
| 顺序 | 章节 | 一句话讲什么 |
|---|---|---|
| 1 | 01-architecture | 把第 2 节的全景放大:控制面 vs 数据面的分工,一次创建沙箱端到端每一步的真实代码 |
| 2 | 02-sandbox-lifecycle | 一台 microVM 的一生:创建 / 暂停 / 恢复 / 销毁,以及 Firecracker 进程怎么被编排 |
| 3 | 03-fast-resume-uffd | 秒级恢复的核心:内存快照 + userfaultfd 惰性缺页——不预加载全部内存,用到哪页才拉哪页 |
| 4 | 04-cow-block-storage | 省空间的核心:分层去重的写时复制块存储,rootfs 与内存 diff 只存"改动的块" |
| 5 | 05-network-isolation | 每台沙箱的独立网络栈、IP 槽位分配,与出站流量的允许/拒绝/改写 |
| 6 | 06-edge-and-envd | 请求如何抵达具体沙箱:client-proxy 边缘路由 + VM 内 envd 守护进程 |
给 AI agent 的用法: 先读本页的 essence 与全景判断相关性;要"创建流程"看 01,要"暂停/恢复语义"看 02,要"为什么恢复这么快"看 03,要"存储怎么省"看 04,要"网络隔离"看 05,要"流量怎么进来"看 06。
4. 巧妙之处速览(精华在哪一章)
这一节把 E2B 最值得带走的三个设计各用一句话点破,细节在对应章:
-
① 内存快照 + UFFD 惰性缺页 → 秒级恢复。 恢复一台 VM 不预先把几个 GB 内存全读回来,而是先"空着起",Firecracker 通过
userfaultfd报告缺页,orchestrator 才按需从快照里拉那一页(packages/orchestrator/pkg/sandbox/uffd/uffd.go:40Uffd、:72Start)。→ 详见 03。 -
② 分层去重的写时复制块存储 → 省空间又快。 沙箱磁盘和内存都建在一个只读基础层之上;读时先查本地缓存、没有再落到只读设备,写只落到缓存层(即"diff"),相同块跨沙箱去重(
packages/orchestrator/pkg/sandbox/block/overlay.go:15Overlay、ReadAt/WriteAt)。→ 详见 04。 -
③ 按需恢复被暂停的沙箱 → 冷的不占资源、来流量才复活。 沙箱可被暂停成快照、完全不占节点;当有请求打到一个已暂停的沙箱,边缘层触发按需恢复再转发(
packages/client-proxy/internal/proxy/paused_resumer.go、proxy.go:97handlePausedSandbox)。→ 详见 02 与 06。
5. 顶层代码地图(导航索引)
想直接跳进源码时,用符号名 grep 比行号更抗漂移(上游更新后行号会变、符号名通常还在)。
| 主题 | 文件 | 符号 |
|---|---|---|
| 控制面入口:接收创建沙箱请求 | packages/api/internal/handlers/sandbox_create.go | PostSandboxes |
| 控制面:组装 gRPC 请求 + 选节点下发 | packages/api/internal/orchestrator/create_instance.go | CreateSandbox、PlaceSandbox |
| 数据面 gRPC 服务:创建/恢复沙箱 | packages/orchestrator/pkg/server/sandboxes.go | Server.Create |
| 数据面:microVM 工厂(启动/恢复/重启) | packages/orchestrator/pkg/sandbox/sandbox.go | Factory、CreateSandbox、ResumeSandbox |
| Firecracker 进程封装 | packages/orchestrator/pkg/sandbox/fc/process.go | Process、NewProcess |
| 秒级恢复:惰性缺页 | packages/orchestrator/pkg/sandbox/uffd/uffd.go | Uffd、Uffd.Start |
| 写时复制块存储 | packages/orchestrator/pkg/sandbox/block/overlay.go | Overlay、ReadAt、WriteAt |
| 每台沙箱的网络栈 | packages/orchestrator/pkg/sandbox/network/ | network.go、pool.go、slot.go |
| 边缘路由 + 按需恢复 | packages/client-proxy/internal/proxy/proxy.go | NewClientProxy、handlePausedSandbox |
| VM 内守护进程入口 | packages/envd/main.go | main(端口 49983) |
| 服务通信全景(参考) | packages/orchestrator/orchestrator.proto | SandboxCreateRequest |
想继续: 直接进 01-architecture 看这条创建之旅的真实代码走读;或跳到 03-fast-resume-uffd 直取"为什么恢复能做到秒级"的核心机制。