E2B Infra 项目分析:AI Code Interpreter 的开源基础设施
· 1859 字 · 约 9 分钟
分析基线:本文基于你本地仓库 content/blog/211-E2B Infra 项目分析:AI Code Interpreter 的开源基础设施/files/infra 在 2026-03-01 的代码状态分析。
先说结论:
- 这是一个“控制面 + 数据面 + 调度层 + 云资源编排”都在一个仓库的完整基础设施项目,不只是单个 API 服务。
- 核心执行链路是:
API -> Template Manager / Orchestrator(gRPC) -> Client Proxy -> Sandbox(Envd)。 - 官方自托管主路径是 GCP + Terraform + Nomad;AWS 目录存在,但目前仍是建设中状态。
- 如果你的目标是“私有化 AI Code Interpreter”,这个仓库基本覆盖了可运行闭环。
阅读导航
为了减少“信息很全但阅读跳跃”的问题,本文按三层结构组织:
- 第一部分(1-23):先讲项目边界、目录、部署入口、API 与关键机制,适合第一次通读。
- 第二部分(24-33):按模块深挖控制面、调度、构建、安全、网络、存储、观测、升级、多云与计费,适合带着问题查细节。
- 第三部分(34 + 参考链接):给出阅读边界和后续扩展方向。
第一部分:项目总览与落地路径
A. 项目边界与代码地图
1. 这个项目能做什么
从代码和 OpenAPI 看,它支持的核心能力是:
- Sandbox 生命周期管理:创建、连接、暂停、恢复、删除、超时续期。
- Template 生命周期管理:创建构建请求、触发构建、查询构建状态和日志、标签与别名管理。
- 持久卷能力:卷创建、挂载、删除(受 feature flag 控制)。
- 观测与治理:团队指标、sandbox 指标、构建日志、admin 节点与运维接口。
- 多组件自托管:API、orchestrator、template-manager、client-proxy、ingress、redis、loki、otel、clickhouse。
2. 根目录逐项说明
按你关心的“挨个文件/目录作用”,先从仓库根目录开始:
.env.template:自托管变量模板,定义 GCP/域名/Postgres/集群规格/可选组件等关键参数。README.md:项目定位与云厂商支持状态概览。self-host.md:官方自托管流程,含前置依赖、make部署顺序、Secrets 配置、集群初始化。DEV-LOCAL.md:本地开发模式,如何起本地依赖和本地服务。DEV.md:远程开发(SSH/VSCode)指引。Makefile:全仓统一入口,封装init/plan/apply/build-and-upload/migrate/test/local-infra等操作。go.work:Go workspace,把多包工程拼成一个工作区。VERSION:版本号文件。iac/:Terraform 基础设施层(云资源 + Nomad job 发布)。packages/:业务与基础服务实现。spec/:四套 API 定义(主 API、edge、dashboard、hyperloop)。tests/:集成测试和周期性测试。scripts/:环境、发布、生成、检查相关脚本。
3. packages 目录逐项说明
这是核心代码层,每个子目录职责如下:
packages/api:主控制面 API(Gin + OpenAPI 校验),负责鉴权、业务编排、调 orchestrator/template-manager。packages/orchestrator:节点侧核心调度执行服务,管理 sandbox、网络、存储、模板缓存、事件、gRPC 服务。packages/client-proxy:流量代理层,把外部会话请求路由到具体 sandbox 节点,并处理健康/排空。packages/dashboard-api:Dashboard 侧 API,提供构建与记录查询等能力。packages/envd:运行在 sandbox 内/边界处的执行代理组件。packages/db:数据库访问层与迁移逻辑。packages/clickhouse:事件与指标存储相关逻辑。packages/auth:认证鉴权通用模块(API key / access token / Supabase token)。packages/shared:跨服务共享组件(logger、telemetry、grpc proto、feature flags 等)。packages/docker-reverse-proxy:镜像相关反向代理服务。packages/local-dev:本地依赖栈拉起与种子数据工具。packages/nomad-nodepool-apm:Nomad autoscaler 的自定义 APM 插件。packages/otel-collector:OTel collector 相关配置与构建。
4. iac 目录逐项说明
iac/provider-gcp:主部署实现(Terraform backend/provider/module 调用)。iac/provider-gcp/init/*:先创建 bucket、secret、service account 等基础资源。iac/provider-gcp/nomad-cluster/*:GCP 上 Nomad 集群与 node pool(api/build/clickhouse/loki/…)。iac/provider-gcp/nomad/*:把服务渲染为 Nomad job 并发布(api/template-manager/redis/autoscaler/…)。iac/provider-gcp/nomad/jobs/*.hcl:各服务运行时作业模板和环境变量注入入口。iac/modules/job-*:可复用 Nomad job Terraform module(orchestrator/client-proxy/ingress/…)。iac/provider-aws:AWS provider 骨架,目前内容明显少于 GCP 路径。
B. 部署与自托管入口
5. 部署依赖与组件清单
从 self-host.md、.env.template、Terraform 与 Nomad 模板汇总,依赖分为:
-
基础工具
PackerTerraform 1.5.xgcloud CLIGoDockernpm -
云与外部服务
GCP projectCloudflare account + domainPostgreSQL (文档注明 Supabase DB 目前受支持)对象存储 bucket (模板、构建产物、FC 组件) -
运行时基础设施组件
NomadConsulRedis (managed 或 job)ClickHouseLokiOpenTelemetry CollectorGrafana/Posthog (可选但推荐)
6. 官方自托管流程(GCP)
主流程可归纳为:
- 配置
.env.{dev|staging|prod}并执行make set-env ENV=dev。 make provider-login完成云登录。make init初始化 Terraform 和基础资源。make build-and-upload构建并上传服务产物。make copy-public-builds准备 Firecracker/kernel 公共构建件。- 在 Secret Manager 填入关键密钥(Cloudflare token、Postgres、JWT、Posthog 等)。
make plan-without-jobs && make apply先建云底座。make plan && make apply再部署 Nomad jobs。- 初始化集群数据(用户、团队、基础模板)。
C. API 与执行链路
7. API 分组与职责
按 spec 文件可分 4 套 API:
spec/openapi.yml:主 API(团队、sandbox、template、volume、admin、access token、api key)。spec/openapi-edge.yml:edge 侧 API(service discovery、sandbox logs/metrics 聚合)。spec/openapi-dashboard.yml:dashboard API(build 列表、build 状态、sandbox record)。spec/openapi-hyperloop.yml:hyperloop API(/me、/logs,用于 sandbox 内身份与日志上报链路)。
8. 核心 API 流程
下面是最重要的几个流程(按代码调用链):
-
创建 sandbox:
POST /sandboxesAPI ParseBodytemplateCache.ResolveAlias + Get校验 team limit / network / volumeMountorchestrator.CreateInstance (gRPC)返回 sandbox metadata -
连接已存在 sandbox:
POST /sandboxes/{id}/connect先 KeepAliveFor若运行中则直接返回若不存在则查询最后 snapshot基于 snapshot 调 startSandbox 恢复 -
暂停/恢复 sandbox:
POST /sandboxes/{id}/pause|resumepause: orchestrator.RemoveSandbox(带状态机检查)resume: 检查当前状态 -> 拉取 snapshot -> startSandbox -
触发模板构建:
POST /v3/templates+POST /v2/templates/{templateID}/builds/{buildID}v3 负责注册 build 请求和元数据v2 start 负责选择 builder node 并调用 template-manager.CreateTemplate后续通过 build status / logs API 轮询进度 -
卷创建与删除:
POST /volumes、DELETE /volumes/{id}写 DB 记录调用 orchestrator Volume gRPC 创建或删除底层目录/卷删除采用异步清理策略
9. 服务启动与流量路径
从 packages/orchestrator/main.go 看,节点内同时跑多类服务:
- sandbox proxy(数据流量代理)。
- tcp egress firewall(出网限制)。
- network/nbd/template cache 等底层资源管理。
- hyperloop HTTP server。
- gRPC server(sandbox/volume/template/info 服务)。
- cmux 在同端口复用 HTTP 健康检查与 gRPC。
- 可选 nfs proxy + portmapper(有持久卷挂载时)。
外部典型链路:
SDK/CLI -> API -> Orchestrator gRPC -> Sandbox
Sandbox 会话流量 -> Client Proxy -> 对应节点 sandbox
日志/指标 -> OTel/Vector -> Loki/ClickHouse/Grafana
D. 运行限制与机制要点
10. 使用方限制与兼容性要求
这部分是“真正会踩坑”的地方,尤其是你提到的旧机器兼容问题。
- 仅 Linux 作为有效运行基座(本地开发文档也明确 Linux 必需)。
- 强依赖 NBD 与 hugepages。
本地开发和 orchestrator 文档都要求先
modprobe nbd、配置vm.nr_hugepages,没有这些能力会直接卡住。 - GCP 是主路径,AWS 还不是同等成熟路径。
代码结构上
provider-gcp明显完整,provider-aws目前只是骨架。 - 并发、时长、资源都有硬限制,不是无限开箱即用。 包括团队并发、单团队资源上限、每次查询上限、上传体积上限等。
11. 你提到的 6.x 内核新特性问题
这个仓库里确实有“新内核特性依赖”。
- overlayfs 使用了 fsconfig/fsmount 新挂载接口,代码注释明确要求内核 6.8+。 这意味着在更老内核上,模板构建或相关挂载流程可能失败。
- 默认 sandbox guest kernel 是
vmlinux-6.1.158。 这是 guest 内核版本,不等于宿主机内核版本要求。 - UFFD(userfaultfd)路径使用了
UFFD_FEATURE_WP_ASYNC测试和相关逻辑。 这属于较新的内核能力域,旧内核或裁剪过的内核配置可能出现功能降级或不可用。 - cgroup 代码里有
kernel 6.12+的 TODO(per-FD peak reset)。 说明团队已经在对接更高版本内核特性,但该项当前还不是硬依赖。
结合起来可以给一个实用判断:
- 如果机器内核 < 6.8,先假设“模板构建挂载链路不可用”。
- 即便内核足够新,也要确认内核模块与系统参数(nbd、hugepages、namespace、nftables/iptables)齐全。
- 对旧机器最稳妥的路径是先跑最小化本地 smoke,再进云上完整部署。
12. 网络机制是怎么实现的
网络不是“简单放行”,而是多层控制:
- 每个 sandbox 分配独立 network namespace + veth/tap。
- host 侧通过 iptables/nat 建立转发和地址转换。
- nftables 做 slot 级规则集,内置默认 deny 私网段(10/8、172.16/12、192.168/16 等)并允许必要控制面地址。
- TCP 流量按端口分流到三类防火墙代理口: 80 走 HTTP Host 检查 443 走 TLS SNI 检查 其他端口走 CIDR-only 路径(避免 SSH 这类 server-first 协议被阻塞)
- 对域名 allowlist 场景,代理会二次校验解析 IP,避免 DNS 伪造把合法域名导向内网地址。
默认行为上:
- 若未设置 egress 规则,默认允许访问。
- 若显式
allowInternetAccess=false,会注入0.0.0.0/0deny(全网禁出)。 - 若设置了
allowOut域名,会自动补 DNS 服务器地址以保证域名可解析。
13. allowOut 域名的代码级实现细节
你前面问到“infra 怎么知道要访问哪个域名”,核心是运行时从连接里提取,不是预先声明。
-
端口分路 在 tcp firewall 里,80/443/其他端口分成三条监听路径。 80 用 HTTP Host,443 用 TLS SNI,其他端口只做 CIDR 检查。
-
443 的 SNI 解析来源 SNI 解析不是 infra 自己写 TLS 解析器,而是调用
github.com/inetaf/tcpproxy。 代码通过AddSNIMatchRoute注册 TLS 路由,再从tcpproxy.Conn.HostName读取解析后的主机名。 -
allowOut 的前置校验 当
allowOut里出现域名时,API 层强制要求denyOut含0.0.0.0/0(ALL_TRAFFIC)。 原因是默认策略是 allow,如果不先 block-all,域名 allowlist 没有实际约束力。 -
地址与域名拆分
allowOut会被拆成AllowedCidrs和AllowedDomains。 若有域名,系统会自动把默认 DNS (8.8.8.8) 加入允许地址,避免“配置了域名却无法解析”。 -
域名匹配规则 支持三种: 精确匹配(
api.openai.com) 全通配(*) 后缀通配(*.example.com) -
防 DNS 绕过 命中域名规则后,代理会按域名重新拨号,并在连接前校验解析出的 IP 是否落入内网/保留网段。 如果解析到内网地址会直接阻断,避免沙箱内
/etc/hosts或 DNS 污染把白名单域名导向内网。 -
非 HTTP/TLS 的边界 非 80/443 流量没有 Host/SNI 可读,域名规则无法发挥作用,只能按 IP/CIDR 控制。 这也是为何数据库、私有协议等出站策略通常要配 CIDR。
14. 其他容易忽略的实现细节
-
allowInternetAccess=false和网络规则会叠加 如果显式禁网,会覆盖成 deny all;不是简单与 allowOut 并列。 -
maskRequestHost会做 ASCII 校验 非 ASCII 主机名会在 API 层被拒绝,避免混淆域名输入。 -
查询接口有明确批量上限 如 sandbox metrics 一次最多 100 个,避免观测接口把后端打挂。
-
并发限制在多层生效 团队级并发、节点级 sandbox 上限、节点级“启动中 sandbox 上限”同时存在。
-
构建与恢复高度依赖 feature flags 包括 chunker 行为、缓存策略、限流参数、firecracker 版本映射等,线上行为不只由代码版本决定。
15. 存储、缓存与快照机制
这个仓库的存储是“对象存储 + 本地/NFS 缓存 + 差分块”组合。
- 存储后端抽象为
StorageProvider,支持 GCP bucket、AWS S3、本地文件系统。 - 默认模板内核和构建产物路径是对象存储(GCS/S3),通过
OpenSeekable/OpenBlob抽象访问。 - 有 NFS/本地缓存包装层
WrapInNFSCache,减少重复远端读取。 - 差分文件(memfile/rootfs)通过 chunker 按块读取,支持流式和缓存。
- 快照上传不是整盘覆盖,而是: snapfile + metadata + memfile diff + rootfs diff 组合上传。
这套设计的直接收益:
- 冷启动与恢复速度更可控。
- 网络和对象存储流量成本可压缩。
- 快照链条支持增量构建和恢复。
16. 计费与用量统计是怎么做的
从代码看,这个仓库更像“计量与事件采集层”,不是完整结算系统。
- sandbox 创建/关闭会发
InstanceStarted/InstanceStopped事件,携带 CPU/RAM/Disk/Duration 等字段。 - 团队指标查询走 ClickHouse(如
concurrent_sandboxes、sandbox_start_rate)。 - API 会基于团队 limits 在入口处做配额拦截(并发数、CPU、内存、超时时长)。
- 当团队并发超限时,接口直接返回上限错误,并给出 billing 文档链接。
实务上可理解为:
- 平台内核已有“可计费数据采集能力”。
- 真正出账、套餐、财务对账逻辑很可能在这个仓库之外的产品层实现。
17. 关键限制参数清单
按代码里可见的硬限制整理如下:
- API 请求体限制: 主 API 约 16 MiB hyperloop API 约 256 MiB
- 列表与批量查询限制:
/v2/sandboxes分页上限 100/sandboxes/metrics每次最多 100 个 sandbox dashboard build 列表上限 100 - 资源配额限制:
团队并发由
SandboxConcurrency控制 CPU、内存上限受 team limits 约束 运行时长受MaxLengthHours限制 - 节点侧限制: 每节点最大 sandbox 数受 feature flag 控制(默认 200) 启动中 sandbox 也有限流(避免节点瞬时抖动)
18. 部署风险与建议
如果你准备真正自托管,建议按下面优先级排查:
- 内核与模块能力先行
先确认
nbd、hugepages、namespace、nftables/iptables、overlayfs 新挂载接口能力。 - 云资源配额预申请 文档里提到 GCP 配额可能不足,实际部署前先扩容 CPU/SSD 等关键配额。
- Secrets 与域名链路 Cloudflare、Postgres、JWT、可观测性 token 缺一项都可能半成功半失败。
- 先最小化上线再扩容 先跑 API + orchestrator + client-proxy + postgres + redis 的最小闭环,再加 clickhouse/loki/otel。
- 把 feature flags 当“运行时开关系统” 这个项目大量行为受 feature flags 控制,生产环境要先定义旗标基线。
E. 上手资料与索引
19. 分层架构图
下面是按控制面、数据面、日志面、存储面分层的一张简化图。
flowchart TB
subgraph Clients["使用侧"]
SDK["SDK / CLI / Dashboard"]
end
subgraph Control["控制面"]
API["packages/api"]
TM["template-manager (gRPC)"]
ORCH["orchestrator (gRPC)"]
DB["PostgreSQL"]
REDIS["Redis (catalog/state)"]
FF["LaunchDarkly feature flags"]
end
subgraph DataPlane["数据面"]
CP["client-proxy"]
SBX["Sandbox (Firecracker + envd)"]
NET["tcp firewall + network namespace"]
end
subgraph Storage["存储面"]
OBJ["GCS/S3 buckets"]
CACHE["NFS/local cache"]
SNAP["snapshot diffs (memfile/rootfs)"]
end
subgraph Observability["日志与观测面"]
OTEL["OTel collector / vector"]
CH["ClickHouse"]
LOKI["Loki"]
GRAF["Grafana / metrics API"]
PH["Posthog / analytics collector"]
end
SDK --> API
API --> DB
API --> REDIS
API --> TM
API --> ORCH
API --> FF
SDK --> CP
CP --> SBX
ORCH --> SBX
ORCH --> NET
TM --> OBJ
ORCH --> OBJ
ORCH --> CACHE
ORCH --> SNAP
API --> PH
ORCH --> OTEL
OTEL --> CH
OTEL --> LOKI
CH --> GRAF
20. 最小可运行自托管步骤(dev)
这里是“只保留必填项”的最小路径,目标是先把核心闭环跑起来。
- 准备
.env.dev
|
|
- 先填这 5 个必填变量(来自
.env.templateRequired block)
|
|
- 选择环境并登录云
|
|
- 初始化基础资源
|
|
- 构建并上传服务与运行时产物
|
|
-
在 GCP Secret Manager 填关键密钥 必须至少补齐:
e2b-cloudflare-api-tokene2b-postgres-connection-string -
先部署基础设施,再部署 jobs
|
|
- 初始化集群基础数据(用户/团队/基础模板)
|
|
- 快速健康检查
|
|
21. 真实 API 示例(curl)
下面三条就是你提到的最常见链路:创建 sandbox、连接 sandbox、查看模板构建日志。
先准备环境变量:
|
|
- 创建 sandbox
|
|
- 连接(续期)sandbox
先把上一步返回里的
sandboxID记成变量:
|
|
|
|
- 查看模板构建日志
|
|
22. 关键源码定位索引
下面这些路径可以直接对应本文关键结论:
- 443 SNI 分流入口:
packages/orchestrator/internal/tcpfirewall/proxy.go(AddSNIMatchRoute) - Host/SNI 提取与 egress 判定:
packages/orchestrator/internal/tcpfirewall/handlers.go - allowOut 校验(域名要求
denyOut=0.0.0.0/0):packages/api/internal/handlers/sandbox_create.go(validateNetworkConfig) - 地址/域名拆分与默认 DNS:
packages/api/internal/orchestrator/create_instance.go+packages/shared/pkg/sandbox-network/firewall.go - network namespace/veth/tap 与 NAT 规则:
packages/orchestrator/internal/sandbox/network/network.go - nftables 规则与私网 deny:
packages/orchestrator/internal/sandbox/network/firewall.go - 存储后端抽象(GCP/AWS/Local):
packages/shared/pkg/storage/storage.go+storage_google.go+storage_aws.go - 快照上传(snap + metadata + diffs):
packages/orchestrator/internal/sandbox/snapshot.go - 团队并发配额拦截:
packages/api/internal/orchestrator/create_instance.go - 团队指标查询(ClickHouse):
packages/api/internal/handlers/team_metrics.go
23. 我对这个仓库的工程判断
- 这是偏“平台工程”的仓库,不是单体应用仓库,理解成本主要在跨层调用而不是单个函数复杂度。
- OpenAPI + 代码生成 + middleware 校验让接口契约比较明确,便于二次开发。
- 运行稳定性主要依赖 Nomad job 参数、feature flags 和集群容量配置,业务代码只是其中一环。
- 真正的部署门槛在云资源配额、Secrets、网络与观测栈联通,而不是
go build。
第二部分:核心机制深挖(按模块)
A. 控制面与调度
24. 控制面内部状态机与失败回滚
这一层最关键的是“创建/删除/暂停/恢复”不是直接打点执行,而是先做状态占位、再调节点、最后统一收敛。
- 创建链路
packages/api/internal/orchestrator/create_instance.go先sandboxStore.Reserve(...)占位并做并发协同,再进入 placement 与 gRPC 创建。 如果中途失败,会走 deferred cleanup,调用 remove 逻辑做回收,避免“DB 有记录但节点上没有实例”。 - 删除链路
packages/api/internal/orchestrator/delete_instance.go通过StartRemoving做去重,区分StateActionKill与StateActionPause。 对无效状态转换(如已经不在可暂停状态)有InvalidStateTransitionError分支,避免重复操作把状态机打乱。 - 暂停链路
packages/api/internal/orchestrator/pause_instance.go先触发 orchestrator pause/snapshot,再写 paused sandbox 元数据(含 auto resume config)。 有PauseQueueExhaustedError明确错误类型,避免把“队列满”与“系统故障”混为一类。 - 超时淘汰链路
packages/api/internal/orchestrator/evictor/evict.go到期后按策略选择pause或kill,并在优雅退出时等待 in-flight eviction 完成。
这意味着控制面的重点不是“一个接口对应一个动作”,而是“动作 + 幂等 + 回滚 + 统计事件”四件套一起执行。
25. 调度与容量管理细节
调度由两层组成:API 侧 placement 决策 + Nomad node pool 资源供给。
- placement 算法层
packages/api/internal/orchestrator/placement/*可见 best-of-k、cpu 兼容性、placement benchmark 等实现与测试,说明调度不是纯随机,而是有资源拟合与性能对比机制。 - 节点状态层
packages/api/internal/orchestrator/nodemanager/*维护节点状态映射(healthy/draining/unhealthy)和 in-progress placement 指标,避免瞬时过量启动压垮单节点。 - Nomad node pool 层
iac/provider-gcp/variables.tf+iac/provider-gcp/nomad/main.tfapi/build/clickhouse/loki/orchestrator 拆分 node pool,便于按角色扩容。 - 自动扩缩容层
iac/provider-gcp/nomad/jobs/nomad-autoscaler.hcl+packages/nomad-nodepool-apm/plugin/plugin.go自定义 APM 插件按 node pool 统计 ready+eligible 节点数,给 autoscaler 提供扩缩容信号。
推断: 从代码结构看,它更偏“节点池容量驱动”的粗粒度扩缩,而不是“按租户精细弹性”。
B. 构建系统与产物发布
26. 模板构建完整链路与缓存策略
你现在文档已有 API 名称,但“构建内部如何走”还可以更细。
- 构建请求入口
spec/openapi.yml的/v3/templates、/v2/templates/{templateID}/builds/{buildID}、.../status、.../logs。 - 构建执行核心
packages/orchestrator/internal/template/build/*按 phase 执行,失败会在 phase 上报状态,日志走 build logs API 可查询。 - 增量构建能力
packages/orchestrator/README.md里的create-build、resume-build、show-build-diff、inspect-build。 从命令与参数可见支持 from-build/to-build 链式增量,非每次全量重建。 - 缓存与块层
packages/orchestrator/internal/sandbox/block/*+SNAPSHOT_CACHE_DIR差分块、快照缓存、chunk cache 共存,核心目标是降低恢复与构建 I/O。 - 构建取消与治理
spec/openapi.yml存在 admin cancel builds 接口,支持团队级停止正在进行构建。
推断: 它把“构建系统”做成了可独立运维子系统,不只是模板字段存库。
26.1 后台如何自己完成镜像制作(代码链路)
如果从“平台后台怎么自己做出镜像”看,完整链路是:
- 客户端先上传
COPY/ADD需要的文件包 调用GET /templates/{templateID}/files/{hash}请求上传地址(返回 signed URL 和present)。 对应代码:packages/api/internal/handlers/template_layer_files_upload.gopackages/orchestrator/internal/template/server/upload_layer_files_template.gopackages/orchestrator/internal/template/build/storage/paths/paths.go对象存储路径是cacheScope/files/{hash}.tar。 - 触发构建
调用
POST /v2/templates/{templateID}/builds/{buildID},提交fromImage/fromTemplate/steps/startCmd/readyCmd/force/fromImageRegistry。 对应代码:packages/api/internal/handlers/template_start_build_v2.go - API 侧落库并选择 builder 节点
API 会校验团队权限、写入 build 元信息(包括 builder node、CPU 信息、dockerfile/steps JSON),然后调用 template-manager。
对应代码:
packages/api/internal/handlers/template_start_build_v2.go - API 把请求转成 gRPC
TemplateCreateCreateTemplate会组装TemplateConfig,把 steps 转成TemplateStep(含filesHash),并异步启动状态同步。 对应代码:packages/api/internal/template-manager/create_template.gopackages/orchestrator/template-manager.proto - template-manager 收到请求后异步启动 build goroutine
先创建 build cache 与内存日志缓冲,再调用
builder.Build(...);失败会回填 reason。 对应代码:packages/orchestrator/internal/template/server/create_template.gopackages/orchestrator/internal/template/cache/build_cache.go - 实际镜像构建由 phase 管线完成
构建器注释里给出了主流程:拉基础镜像 -> 注入基础层 -> 提取 rootfs -> 启动 FC VM 做 provisioning -> 执行用户 steps -> finalize(
startCmd/readyCmd) -> snapshot -> 上传模板与缺失层。 对应代码:packages/orchestrator/internal/template/build/builder.gopackages/orchestrator/internal/template/build/phases/base/*packages/orchestrator/internal/template/build/phases/user/*packages/orchestrator/internal/template/build/phases/steps/*packages/orchestrator/internal/template/build/phases/finalize/*packages/orchestrator/internal/template/build/phases/optimize/* COPY/ADD在后台如何落地 执行 step 时,commands/copy.go会按filesHash从对象存储下载 tar,传入 sandbox 的/tmp解包后搬运到目标路径,并处理 owner/permissions。 对应代码:packages/orchestrator/internal/template/build/commands/copy.go- 缓存与上传策略
构建会使用 hash index + layer upload tracker,避免重复上传;可选 NFS cache 降低对象存储读压。
对应代码:
packages/orchestrator/internal/template/build/builder.gopackages/orchestrator/internal/template/build/storage/cache/*packages/orchestrator/internal/template/build/layer/* - 状态与日志如何被上层看到
template-manager 的
TemplateBuildStatus从 build cache 返回状态与日志;API 再聚合为/status与/logs。 对应代码:packages/orchestrator/internal/template/server/template_status.gopackages/api/internal/handlers/template_build_status.gopackages/api/internal/handlers/template_build_logs.go - 失败处理与回收
构建失败会把错误转成用户可读 reason(phase + message);并删除失败 build 的对象存储前缀,减少脏产物。
对应代码:
packages/orchestrator/internal/template/build/builderrors/errors.gopackages/orchestrator/internal/template/build/builder.go
一句话总结: 这个项目的“镜像制作”不是外部 CI 做完再导入,而是平台内部通过 template-manager + orchestrator phase pipeline 在运行时完成构建、快照和发布。
C. 安全、网络与数据面
27. 安全模型与权限边界
认证在 API 层是组合认证器,不是单一 token。
- 认证链
packages/api/main.goApiKeyAuthenticator、AccessTokenAuthenticator、SupabaseTokenAuthenticator、SupabaseTeamAuthenticator、AdminTokenAuthenticator组合执行。 - 契约层
spec/openapi.yml定义了ApiKeyAuth、AccessTokenAuth、Supabase1TokenAuth、Supabase2TeamAuth、AdminTokenAuth。 - 团队边界 大量接口显式携带 teamID 或从 auth context 解出 team,日志也在鉴权后附带 team 信息。
- 数据面访问边界 sandbox URL 可配置认证访问(spec 字段描述已体现),并配 access token 机制给 envd 通信。
还缺少的文档化项:
- 密钥轮换 SOP(API key、admin token、supabase secrets)。
- 最小权限策略(谁能调 admin 接口,谁能取消团队构建)。
- 审计日志归档与保留策略。
28. 网络策略边界与协议差异
你前面已写 80/443/其他端口三路分流,这里补“边界行为”:
- 域名规则只对可提取 Host/SNI 的流量有效。 非 80/443 的 server-first 协议,无法可靠取域名,只能走 CIDR 规则。
allowOut域名白名单并不等于 DNS 放行所有地址。 实现里会对解析结果做私网拦截校验,防止域名绕过到内网。- 默认策略与 deny all 的关系。
如果不先
denyOut=0.0.0.0/0,域名 allowlist 约束会被默认放行语义弱化。 - 规则层次。 API 参数校验 -> 控制面下发 -> 节点 nftables/iptables 执行 -> tcp firewall 运行时判定。
这也是为什么同一个 allowOut 配置在 HTTP 请求和数据库连接上表现可能不同。
29. 存储、卷、快照与一致性语义
- 对象存储抽象
packages/shared/pkg/storage/*支持 GCP/AWS/Local,统一 OpenBlob/OpenSeekable 能力。 - 快照内容
packages/orchestrator/internal/sandbox/snapshot.go由 snapfile + metadata + memfile diff + rootfs diff 组成,不是整盘镜像覆盖。 - 卷服务
packages/orchestrator/internal/volumes/service.go有 teamID/volumeID 校验,路径拼接有安全检查,防止 path traversal。 - 缓存层 NFS/local cache + chunk cache + snapshot cache 三层组合,目的是降对象存储读压。
一致性语义建议在文档中明确:
- pause 快照是“近实时恢复点”,不是跨版本强一致事务快照。
- volume 删除是资源删除语义,不保证应用级事务回滚。
- 多节点缓存下,读到旧构建/旧快照的窗口期取决于缓存刷新策略。
D. 运维、演进与商业化
30. 可观测性、告警与排障手册
项目里“采集能力”已经有,但“怎么运维”文档还不够。
- 采集路径 服务 -> OTel -> ClickHouse/Loki -> Grafana(部分场景还接 Posthog)。
- 关键观测源
packages/orchestrator/main.go初始化 sandbox observer、host stats、clickhouse event delivery。 - 指标数据模型
ClickHouse migrations 已有
sandbox_metrics_gauge、team_metrics_gauge/sum、sandbox_events。 - 健康状态模型
服务状态至少有
Healthy/Draining/Unhealthy,并且 health handler 会映射输出。
建议在文档新增默认告警基线:
- API 5xx 比例、P95 延迟、create sandbox 失败率。
- 节点 ready 数与 draining 比例。
- pause queue exhausted 计数。
- build 卡在 building 状态超时。
- ClickHouse 写入失败与积压。
31. 升级、回滚与兼容矩阵
当前代码与 IaC 已具备升级基础,但文档没形成完整操作手册。
- Nomad job 升级策略 多个 job 配了 canary、stagger、auto_promote、auto_revert。
- 服务优雅下线 orchestrator/client-proxy 有显式 draining 流程,并等待传播后再关服务。
- 杀进程时间窗口
Nomad client
max_kill_timeout可到 24h,兼容长连接和长任务。 - 兼容风险 已知内核能力依赖(如 overlayfs 新挂载接口、UFFD 特性),旧机器可能直接不可用。
建议加一个兼容矩阵表(最低要求):
- Host kernel 版本门槛。
- Firecracker 版本与 guest kernel 对应关系。
- nbd/hugepages/cgroup/network namespace 必要项。
- 文件系统与磁盘类型建议。
32. 多云与国内化差异说明
- 官方主路径是 GCP
self-host.md和iac/provider-gcp/*明显最完整。 - AWS 支持仍偏骨架
仓库存在
provider-aws,但覆盖度低于 GCP。 - 对象存储抽象有利于替换 即使官方模板偏 GCS,运行时代码通过 storage provider 抽象可落 S3 兼容服务。
推断: 从工程成熟度看,GCP 是“默认产品路径”,其他云厂商是“可移植但需要工程补完”。
33. 计量到计费闭环
现在文档写了“有计量”,但业务上还缺“如何结算”的闭环描述。
- 已有的计量输入
InstanceStarted/InstanceStopped事件、team/sandbox metrics、product usage 相关迁移历史。 - 已有的数据承载 ClickHouse 表与 TTL 策略(如 team metrics 延长到 90 天的迁移)。
- 已有的限额执行点 API 入口会依据 team limits 做硬拦截(并发、资源、时长)。
- 尚未在本仓库内完整可见的部分 账单出具、套餐定价、补扣费、对账流程更像在产品外围系统实现。
建议文档补充一个“计费闭环时序图”:
- usage 采集(create/pause/kill/build)。
- 归档与聚合(分钟/小时/天)。
- 限额联动(软提醒/硬拦截)。
- 对账与账单导出。
第三部分:收束与参考
A. 阅读边界
34. 补充后的阅读边界
到这里,这篇文章已经覆盖了“理解项目并可落地部署”的大部分关键面,但仍有两类内容建议后续单开文章:
- 实操 runbook 面向 SRE 的逐条故障手册(症状 -> 查询命令 -> 处理步骤)。
- 安全与合规 密钥生命周期、审计合规、数据保留与删除策略。