ᕕ( ᐛ )ᕗ Jimyag's Blog

从 OCI 镜像到毫秒级 Sandbox:CubeSandbox 与 E2B 模板实现对比

容器镜像描述文件层和运行配置,MicroVM Sandbox 还需要内核、内存和设备状态。CubeSandbox 和 E2B 都会在构建阶段把 OCI 镜像转成 ext4 rootfs,预启动 MicroVM 并保存快照,但它们把成本放在了不同位置:

  • CubeSandbox 先把完整 ext4 分发到目标 Cubelet,再在每个节点本地制作快照。
  • E2B 把 rootfs、内存和 Firecracker 状态拆成可寻址对象,运行节点通过多级缓存按需取块。

分析范围

本文基于 CubeSandbox bebdf020 和 E2B infra 110fa5be。只分析开源代码可验证的行为,不把 E2B 托管服务的运营参数当成源码事实。共享 Volume 是另一条数据链,见《CubeSandbox 与 E2B 共享存储实现原理》。

从镜像到 Sandbox

1
2
3
4
5
6
7
OCI image
  ↓ 还原 layer,制作 ext4
rootfs
  ↓ 注入 agent/配置,启动 MicroVM
template snapshot = rootfs + memory + VM state + metadata
  ↓ 恢复并叠加独立 CoW
sandbox instance

OCI image 不包含 Firecracker 内存与设备状态;template 是共享的不变基线,实例写入进入自己的 CoW 层。

CubeSandbox:完整分发,逐节点预启动

create-from-image 是一条异步作业:PULLINGUNPACKINGDISTRIBUTING → 创建 template replica。主链路见 runTemplateImageJob

  flowchart LR
  I[OCI registry] --> A[CubeMaster ext4 artifact]
  A --> N1[Cubelet A]
  A --> N2[Cubelet B]
  N1 --> R1[local READY replica]
  N2 --> R2[local READY replica]
  R1 --> C1[CubeCoW instance]
  R2 --> C2[CubeCoW instance]

rootfs exporter

CubeMaster 默认走 native exporter;显式关闭后,PATH 中同时有 skopeo 和 umoci 才走 dockerless 路径,否则回退到 Docker-compatible Engine/CLI。选择顺序见 PrepareSource,默认值见 nativeRootfsExportEnabled。三条路径的差异只在 rootfs 获取阶段,后续注入、ext4、artifact 和分发流程相同。

路径 还原方式 依赖与边界
native go-containerregistry 直连 registry,默认 6 并发预取 layer,再用 containerd archive.Apply 顺序应用 不依赖 Docker/skopeo/umoci,也不借用 daemon layer cache;平台固定为 linux/GOARCH
skopeo + umoci skopeo copy 生成 OCI layout,umoci unpack 还原 bundle 无 daemon,但需要两个 CLI 和更多临时磁盘;非 root 的 --rootless 会压平 UID/GID 并跳过 device node
Docker-compatible Engine/CLI Engine API inspect/pull,失败后用 docker CLI;通过 docker createdocker export 还原 需要 daemon/socket、名为 docker 的 CLI 和 tar;源码不检测 podman,按调用方式推断,Podman 必须同时提供 Docker-compatible socket 和 docker 命令兼容层

native 和 Docker 可直接写入 loop-mounted ext4,skopeo/umoci 只走先还原目录再制作 ext4 的路径,见 BuildExt4。native 不依赖外部镜像工具,但 archive.Apply 仍可因 root/CAP_MKNOD、xattr、file capabilities 或 device node 恢复失败,见 StreamRegistryToDir。dockerless 和 Docker 的具体导出分别见 dockerlessExportImageRootfsdockerExportImageRootfs

artifact 和 template replica

layer 按 OCI 顺序应用后,CubeMaster 注入 envd 和 CubeEgress CA,再制作 ext4,见 buildRootfsArtifact。artifact 的 fingerprint 包含 image digest、CA 指纹、envd SHA 和模板规格;只有 fingerprint 一致且 ext4 校验通过才复用,见 ensureRootfsArtifact

CubeSandbox 从 OCI Config 只保留 EntrypointCmdEnvWorkingDirUser,不继承 ExposedPortsVolumesLabelsStopSignalHealthcheck,见 convertV1Config。端口和 HTTP probe 需要显式配置;最多 3 个自定义端口,49983 保留,见 normalizeTemplateExposedPorts

artifact 会并发分发到目标 Cubelet,每个节点单独记录结果,见 distributeRootfsArtifact。Cubelet 拿到完整 ext4 后启动临时 Sandbox,制作内存和 rootfs 快照;AppSnapshot前置检查强制要求 CubeCoW,快照过程见 AppSnapshot。内存与 metadata 留在本地 catalog,当前 S3 只导出 template rootfs,见 UploadTemplateRootfs。因此运行就绪的单位是「某个节点上的 READY replica」。

E2B:分层快照,按需恢复

E2B 在 base、user、用户 steps 和 finalize 阶段反复启动或恢复 Firecracker VM,见 Builder.Build

  flowchart LR
  I[OCI image] --> B[ext4 plus E2B layers]
  B --> P[provision]
  P --> U[user steps]
  U --> F[finalize]
  P -. recipe hash .-> C[(layer cache)]
  U -. recipe hash .-> C
  F --> S[(object storage)]
  S -->|UFFD memory and NBD rootfs| X[Sandbox]

构建和缓存

控制面先用 RegisterBuild 在一个事务中写入 waiting build、tag assignment 和 active build,见 RegisterBuild。build start 只接受 pending build,会选择 builder、记录其 CPU 信息,然后调用 CreateTemplate,见 template_start_build_v2CreateTemplate 先发出 gRPC TemplateCreate,再标记 in_progress,后台用 BuildStatusSync 追踪结果,见构建启动与同步;启动链失败会标记 failed,见失败处理

builder 按 TARGET_ARCH 选择并校验 OCI manifest,见 DefaultPlatformverifyImagePlatform。它追加 envd、BusyBox、provision script 和 init service 等 layer,然后制作 ext4,见 Rootfs.Build。发行版 provisioning 完成后,模板由显式的 start/ready command 驱动,不直接继承 Docker HEALTHCHECKEXPOSEVOLUME

每个构建阶段计算 recipe hash,并受 CacheScope 隔离。未命中时,LayerExecutor 恢复上一层、执行 action,再 PauseAndUpload,见 BuildLayer。快照先进入本地 cache,但只有全部 artifact 上传完成后,recipe hash 到 build ID 的映射才对其他 builder 可见,见 UploadSnapshot

存储和恢复

E2B 按 build ID 保存 memfilerootfs.ext4snapfilemetadata.json 及内存/rootfs header,见 storage.Paths。header 描述块所属和差分祖先链。对象存储前可叠加本地/NFS cache,也可通过 Redis 定位持有某个 build ID 的 peer,见 Cache.GetTemplate

运行节点的 Server.Create 先用 build ID 取 template,再按 metadata 选择 RebootSandboxResumeSandbox,见 Server.Create。常规恢复通过 userfaultfd 按页加载内存,rootfs 由 NBD 提供差分块设备;快照对象未上传或已不存在时返回 FailedPrecondition,不会带着缺失状态启动,见 runtime 架构错误分支。finalize snapshot 还会试运行一次以收集 prefetch mapping;优化失败不会让构建失败,见 OptimizeBuilder.Build

核心差异

维度 CubeSandbox E2B
OCI 转换 逐层 apply,注入 envd/CA,生成不变 ext4 追加系统 layer,写入 ext4,再在 VM 中 provisioning
构建缓存 按完整规格 fingerprint 复用 rootfs artifact CacheScope + recipe hash 复用阶段性 VM 快照
快照位置 每个 Cubelet 的本地 catalog/CubeCoW 对象存储持久化,前面叠加多级 cache
分发方式 先下载完整 ext4,再预启动 根据 header 和块范围懒加载
新节点代价 分发 artifact 并生成 READY replica 可直接恢复兼容快照,但有冷缓存读取
实例写入 CubeCoW child NBD CoW diff

CubeSandbox 在模板就绪前完成分发和预启动;E2B 把部分加载延迟到恢复路径。

模板如何跨不同 CPU 机器

这个问题包含三层:ISA(amd64/arm64)、同 ISA 下的 vendor/model/features,以及 host kernel、KVM、Firecracker 和 guest kernel 组合。rootfs 主要受 ISA 限制,内存快照同时绑定三层状态。

CubeSandbox

常规 template replica 在每个目标节点单独启动和制作:同一 ext4 可以在同 ISA、不同 CPU model 节点各自生成本地快照,但新节点不能直接使用别的节点的内存快照。native exporter 只拉 linux/$GOARCH,artifact 分发链未再按 OCI architecture 过滤节点,因此 amd64 和 arm64 应分开构建与分发,见 defaultPlatform

运行中 Sandbox 暂停或 commit 后的跨节点恢复更严格。Cubelet 从 /proc/cpuinfo 生成 cpuid_hash,恢复策略要求目标节点的 cpuid_hashhost_kernel_release 严格相等,并拒绝 KVM module 带可疑 taint 的节点,见 EvaluateSnapshotRestoreCompat。还必须满足 S3 remote_status=ready 且没有 host mount;调度优先回原节点,再考虑兼容 peer,见 restoreplace.Decidecpuid_hash 只读第一个逻辑 CPU block,在 big.LITTLE、Intel P/E core 或混合 socket 机器上可能误判,见 parseCPUInfo

E2B

E2B 把 builder 的 CPU architecture、family、model 和 flags 写入 build,placement 用这些信息筛节点,见 template_start_build_v2isNodeCPUCompatible。兼容规则不会通用地判断 CPUID feature subset:architecture/family 必须一致,model 默认也要一致;当前唯一显式例外是 Intel Ice Lake model 106 → Emerald Rapids model 207,反向不允许,见 MachineInfo.IsCompatibleWith。CPU flags 虽被持久化,当前没有参与子集判定;老 build 缺 architecture 时为了兼容会 fail open。

template builder 选择要求 architecture/family/model 完全匹配,不使用运行时的跨代兼容表,见 GetAvailableTemplateBuilder。暂停快照继续绑定 source build 的 CPU 约束,不会因为 Sandbox 在新 CPU 上暂停就改变兼容集,见 buildUpsertSnapshotParams

场景 CubeSandbox E2B
amd64 ↔ arm64 需要独立 artifact/template,目标节点池应按 ISA 隔离 OCI 拉取和 placement 都按 architecture 隔离
同 ISA、同 model template 在每个节点本地生成;暂停快照还要同 cpuid_hash 和 host kernel 允许调度
同 ISA、不同 model 可分别制作 template replica;已暂停快照通常不能跨机 默认禁止;当前只有 Ice Lake → Emerald Rapids
Intel ↔ AMD x86_64 template 可分别本地制作,同一暂停快照不通用 family/model 不同,禁止
CPU flags 不同 进入 cpuid_hash,阻止跨节点恢复 已记录,但当前不直接参与兼容计算
新节点 下载 ext4 并重做 replica 兼容即可调度,但会遇到冷 cache

内存快照还绑定 guest kernel、Firecracker、vCPU 和 RAM。E2B 把版本写入 layer metadata,CubeSandbox 把 CPU/内存写入快照规格,见 BuildLayerAppSnapshot。修改这些参数需要新模板,不能把旧内存快照当成普通磁盘镜像搬迁。

其他实现限制

类别 限制与后果
容器语义 两者都使用自己的 guest kernel、init、agent 和网络。依赖 Docker socket、host namespace、宿主机 module/设备、特定 LSM/SELinux 的应用需重新设计
发行版 CubeSandbox 没有 E2B 式的 profile 白名单,但镜像仍要适配 guest kernel 和启动方式。E2B 显式支持若干 Debian/Fedora/Arch/Alpine/NixOS 系 profile,ID_LIKE 只是 best effort,并拒绝 rhelolamzn,见 distro.ProfilesRejectedIDs
builder 权限 还原 OCI 文件需保留 UID/GID、whiteout、xattr、capabilities 和 device node。Cube 需 ext4 工具,可选 loop mount;E2B 还要 root、mount/overlayfs 和 Firecracker builder VM,当前 overlayfs fsconfig 路径要求 Linux 6.8+,见 MountOverlay
注入二进制 Cube 可选 envd 必须是非空 ELF、不超过 16 MiB,并匹配 rootfs OS/ISA,见模板教程。E2B 从 builder 读取 host envd,BusyBox 路径则包含 runtime.GOARCH,见 additionalOCILayers
容量 Cube 的 writable layer size 进入 artifact fingerprint。E2B base rootfs 默认上限为 25,000 MB,但可由 feature flag 调整,见 BuildBaseRootfsSizeLimitMB。大镜像会增加构建磁盘、分发/上传和冷 cache 成本
版本漂移 快照不保证跨 guest kernel/Firecracker 版本兼容。Cube 会把 guest-image/agent/kernel 差异标为 STALE,但 STALE replica 仍可调度,这是运维信号而非强隔离,见 ScanNodeCompat
存储与地域性 Cube AppSnapshot 必须使用 CubeCoW,内存和 metadata 默认在节点本地;E2B 把对象存储作为持久层,NFS/P2P 只是 cache。Cube 的 READY 要看 replica,E2B 的 READY 还要结合 CPU placement
部分失败 Cube 有成功也有失败 replica 时返回 PARTIALLY_READY,见 summarizeStatus;E2B 只在层 artifact 全部上传后发布 hash 索引。全局 template ID 不能代替对节点/层完整性的检查
跨节点恢复 Cube 的暂停快照要求 S3 ready、无 host mount 且通过 CPU/kernel 校验;E2B 的快照同样是 diff chain,placement 优先原节点再选兼容节点。「已上传」不等于「任意节点可恢复」
内存不可用 Cube 常规 template 需在目标节点重做 replica。E2B 暂停 Sandbox 可显式请求 filesystem-only cold boot,但受 feature flag 控制,auto-resume 不会自动降级,见 Architecture。丢弃内存是语义变化,必须由调用方选择
快照链与冷 cache E2B 的子快照依赖 .header 中的祖先块;缺失祖先会使恢复不完整。本地 template cache 默认 25 小时过期,过期影响下次冷恢复延迟,不改变对象存储的正确性,见 templateExpiration
数据持久性 实例 rootfs 写入默认不回写模板。持久成新基线需显式 snapshot/commit;共享业务数据仍应使用 Volume。共享模板块不等于多 Sandbox 共享文件语义
网络可达性 CubeMaster/E2B builder 必须能访问 registry,Cubelet 要能下载 artifact,E2B Orchestrator 要能访问 storage/cache。私有仓库 auth 不解决 DNS、代理、限流和大文件链路问题

动态 feature flag、私有化配置和托管服务 quota 还可以在这些源码边界上继续收紧。

小结

CubeSandbox 把模板实现为分发到节点的预启动副本:就绪前成本更高,恢复时数据已在本地。E2B 把构建中间步骤和最终模板都保存为分层 VM 快照:新节点不需要预先复制全量数据,但恢复路径必须处理 CPU 兼容、快照链和冷 cache。

#Sandbox #MicroVM #OCI #Firecracker #CubeSandbox #E2B