腾讯云 Cube Sandbox 实战笔记

2026-04-24。环境:Ubuntu 24.04 + AMD EPYC 7601(64 vCPU / 125 GB),宿主机 KVM;在 QEMU 开发 VM(OpenCloudOS 9,16 vCPU / 16 GB,嵌套 KVM)里装 Cube 服务;MicroVM 在嵌套 KVM 里起。所以下面的性能数据都带"嵌套虚拟化惩罚",裸金属会显著更好。

TL;DR

Cube Sandbox 是腾讯云 2026-04-21 开源的 AI Agent 沙箱底座,基于 RustVMM + KVM。核心卖点就两句话:

  1. 冷启动 < 60 ms,单实例内存开销 < 5 MB — 让单台 96 vCPU 机器能同时跑 2000+ 个"真 VM 隔离"的沙箱。
  2. E2B SDK drop-in 兼容 — 只改一个 E2B_API_URL 环境变量,原本付费用 E2B 云跑的 Agent 就能无缝切到自建。

定位上,它填的是 AI Agent 执行环境这个空白:

Docker 容器传统 VMCube Sandbox
隔离级别共享内核 namespace独立内核独立内核 + eBPF 网络
冷启动~200 ms秒级< 60 ms
内存开销低(共享内核)高(完整 OS)< 5 MB(裁剪后)
单机密度高低千级/台
E2B SDK✗✗✅ drop-in

(指标来自官方 README,官方给的基准条件:≤ 32 GB 规格,50 并发创建时平均 67 ms / P95 90 ms / P99 137 ms)

为什么值得关注

LLM 跑代码必须扔进沙箱,否则随便给 Agent 一个 os.system 工具就能出事。现在业界的选择:

  • Docker — 便宜快,但容器逃逸历史不少,namespace 共享内核在 kernel.unprivileged_userns_clone 这类面前不够硬。
  • E2B / Modal / Daytona — 云服务,按用量付费,企业数据出境也是个坎。
  • Firecracker / Kata Containers — 自己搭确实能做 MicroVM 隔离,但冷启动、编排、网络隔离都得自己拼,没有现成的 Agent 视角的"一键部署 + E2B 协议"。

Cube 基本是把 Firecracker 那一类方案做成了"带 SDK 兼容层的开箱即用服务",并且把 Rust VMM 栈里能抠的每一毫秒、每一 MB 都抠了一遍。

架构概览

分六层,从上到下:

组件角色
CubeAPIRust 写的 REST 网关,兼容 E2B 协议
CubeMaster集群编排、资源调度
CubeProxy反向代理,按 <port>-<sandbox_id>.<domain> 解析 Host 头路由到具体沙箱
Cubelet节点本地调度,管沙箱生命周期
CubeVSeBPF 实现的内核级网络隔离 —— 这是区别于纯 Firecracker 的关键
CubeHypervisor + CubeShimKVM MicroVM 管理 + containerd Shim v2 集成

代码语言构成:Rust 52% / Go 27% / C 19%(Rust 是 hypervisor 和 API 层,Go 是编排层,C 是 eBPF 程序)。

实际跑起来:踩到的坑

按官方文档四步走:prepare_image.sh → run_vm.sh → 进去 online-install.sh → 建模板 + 跑 E2B SDK。

坑 1:宿主机需要 KVM 嵌套虚拟化

Cube 自己已经是 MicroVM,但官方 dev-env 会再套一层开发 VM(OpenCloudOS 9)。所以要嵌套 KVM:

bash
cat /sys/module/kvm_amd/parameters/nested   # 我这里是 1 ✅
# Intel 机器看 /sys/module/kvm_intel/parameters/nested

如果是裸金属 Linux 服务器可以直接跳过开发 VM 这一层,online-install.sh 直接跑在宿主上。但官方脚本把 cube-proxy 塞进 Docker + 改系统 DNS + 装 mkcert 根证书,污染宿主机不小,值得用 dev-env 隔离。

坑 2:/dev/kvm 的权限会被 udev 重置

我第一次跑 prepare_image.sh,镜像下载、resize 都完了,到了启动 VM 环节挂了:

qemu-system-x86_64: failed to initialize kvm: Permission denied

原因:/dev/kvm 属于 root:kvm,我账号不在 kvm 组,手动 setfacl -m u:jinxu:rw /dev/kvm 能临时解决,但 udev 重载会把 ACL 抹掉。

稳定做法是写个 udev rule:

bash
sudo tee /etc/udev/rules.d/65-kvm.rules <<'EOF'
KERNEL=="kvm", GROUP="kvm", MODE="0660", RUN+="/usr/bin/setfacl -m u:jinxu:rw /dev/kvm"
EOF
sudo udevadm control --reload-rules
sudo usermod -aG kvm $USER   # 新 session 生效

当前 session 没 kvm 组的话,用 sg kvm -c './prepare_image.sh' 临时取组运行就行。

坑 3:dev-env 脚本依赖 ripgrep 却没写进错误提示

prepare_image.sh 用 rg 在 QEMU 串口 log 里找 sshd 启动失败字样。系统没装 ripgrep 的话会在 need_cmd rg 处 fail fast。Ubuntu 24 要 sudo apt-get install ripgrep。

坑 4:镜像体积

  • OpenCloudOS 9 cloud image:原始 ~500 MB,resize 到 100 GB(稀疏分配,实际占用 ~1 GB)
  • online-install.sh 下的 release 包 cube-sandbox-one-click-<sha>.tar.gz:还得几百 MB
  • MySQL 8 + Redis 7 + cube-proxy 镜像:再几百 MB

整个 setup 至少预留 5 GB 磁盘 + 2 GB 内存给 dev VM(我给了 16 GB)。

部署层设计的几处小亮点

  • E2B 协议走 HTTP/3000,CubeProxy 走 HTTPS/443 + CoreDNS:沙箱内服务通过 <port>-<sandbox_id>.cube.app 域名暴露,CubeProxy 用 mkcert 生成 cube.app 证书,注入 CoreDNS 做本地解析,这个拆分让沙箱内起的 Web 服务能直接用 HTTPS 访问,不用手搓 TLS。
  • CubeShim 走 containerd Shim v2:把 MicroVM 塞进了标准容器运行时接口,意味着理论上 crictl、nerdctl 这些工具都能当调试入口。
  • autostart systemd unit 默认装但不 enable:cube-sandbox-oneclick.service 装好了但默认不自启动,需要 ./cube-autostart.sh enable 明确开启。这个设计挺克制。

实测数据(嵌套 KVM 下)

跑 10 次顺序冷启动 + 4 路并发 + run_code 延迟。模板基于官方 ccr.ccs.tencentyun.com/ags-image/sandbox-code:latest,含 Python 3.12.13 + Jupyter kernel + numpy 等。

冷启动延迟(顺序 × 10,客户端端到端)

指标值
mean917.2 ms
median921.7 ms
min896.6 ms
max939.4 ms
stdev15.3 ms

σ 只有 15 ms,几乎零抖动,比我以前测 Docker run 都稳。官方裸金属基准是 avg 67 ms / P95 90 ms,嵌套 VM 里 917 ms 意味着大约 ~13x 纯嵌套虚拟化惩罚,这个惩罚跟单层 VM 正常预期差不多。

其中 CubeShim 上报的 MicroVM BootVm 耗时 69 ms(和官方 <60 ms 一致),CreatePodSandbox 累计 9.7 s(包含 pmem/vsock 设备激活 — 嵌套 VM 下设备激活明显慢)。**应用级快照(v2 appsnapshot)**把这个冷启动路径绕过去,从而稳定到 900 ms 区间。

run_code("1+1") 延迟(同一 sandbox 内)

调用延迟
第 1 次2133 ms(Jupyter kernel 首次交互 + HTTPS 握手)
第 2-5 次59 / 69 / 61 / 76 ms

热路径下稳定在 60-75 ms 量级。

4 路并发 create

单 worker 平均1149 ms
wall time3054 ms
并行加速比1.51x

嵌套 VM 下 CPU 竞争明显,裸金属官方给的 50 并发 P95 90 ms 应该能看到好得多的并行度。

隔离性验证

在 sandbox 内跑 id 和 uname -r:

  • id → uid=0(root) — 沙箱内 root
  • uname -r → 6.6.1199-0009-03_2.0.1 — 和宿主 VM 内核(6.6.114-43.oc9)完全不同,说明确实是独立 guest 内核,不是共享 namespace

跟 E2B 的关系

E2B 是目前 Agent 沙箱的事实协议。Cube 直接吃这套:

python
from e2b_code_interpreter import Sandbox   # 原封不动的 E2B SDK
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as sb:
    sb.run_code("print('hi')")

唯一要改的环境变量:

bash
export E2B_API_URL="http://127.0.0.1:3000"   # 指向自己的 CubeAPI
export E2B_API_KEY="dummy"                    # SDK 要求非空,内容无所谓

如果已经有在 E2B 云上跑的 Agent,迁移成本约等于零。

还有几个坑值得说

坑 5:一键安装的 quickcheck 会"假报失败"

online-install.sh 内嵌 up.sh 在所有服务起来后要跑 30 次 quickcheck.sh(每次间隔 2s = 60s 窗口)。在我的嵌套 VM 里,cubelet 首次启动要读完所有插件配置、建 /data/cubelet 的 shared mount、竞争若干文件锁(Cubelet: mainMutexLock: timeout 是典型现象),首次拉起偶尔会被超时判死。

判断方法:即使 install 脚本退 1,也先手动跑一次 quickcheck.sh:

bash
ONE_CLICK_TOOLBOX_ROOT=/usr/local/services/cubetoolbox \
/usr/local/services/cubetoolbox/scripts/one-click/quickcheck.sh

5/5 都通过 + curl http://127.0.0.1:3000/health 返回 {"status":"ok","sandboxes":0} 就是实装好了。

坑 6:模板创建必须带 --probe

不带 probe 的 template 创建会成功,但 sandbox 里起来之后 CubeProxy 前端是 openresty,用户代码还没 listen 住暴露端口时请求会 502。务必 --probe 49999(对 sandbox-code 镜像就是 jupyter kernel 的健康端口)让 Cubelet 等到 probe 通过再做应用级快照。这样后续所有基于这个模板起的 sandbox 冷启动 → snapshot 恢复 → 立刻可用。

坑 7:模板首次创建在嵌套 VM 里得等 7-8 分钟

解包 OCI 镜像到 ext4 rootfs(8 GiB)+ 首次 microVM 引导 + 应用级 snapshot + 快照验证。第二次(相同 image)快得多,因为 rfs-<fingerprint> 可复用。

适用场景判断

  • ✅ 自建 Agent 平台:不想把用户代码或数据走第三方云。
  • ✅ RL 训练 / SWE-Bench 类 benchmark:官方带了 mini-rl-training 例子,大规模并行环境开销比 Docker 小。
  • ✅ 高密度函数计算:5 MB 内存开销做到真隔离,FaaS 底座可以考虑。
  • ❌ 只跑可信代码:Docker 足够,装 Cube 是大炮打蚊子。
  • ❌ 纯 ARM 环境:目前只支持 x86_64。

总结

  • 定位准确:填了 "E2B 协议兼容 + Firecracker 级隔离 + 开箱即用部署" 这块空白,不是又一个 Docker 皮肤。
  • 技术扎实:9.7s 的设备激活开销被应用级快照绕过,冷启动稳定到 900ms(嵌套 VM),核心 BootVm 69ms 与官方数据一致。
  • 迁移真·零成本:E2B SDK drop-in 兼容,改一个环境变量就能从 E2B 云切到自建。
  • 生产化:一键脚本 + systemd autostart + mkcert 自签证书 + CoreDNS split routing 这一套 b2b 细节做得很完备,不是简单 demo。
  • 短板:只支持 x86_64,目前 MicroVM 的 RL 场景支持还比较前期(snapshot rollback 是 coming soon),文档以英文/简中混排居多。

想自己跑一遍,如果有裸金属 Linux + KVM,直接在宿主机上跑 online-install.sh,跳过 dev-env VM 这一层,性能应该能接近官方数字。

链接


附:VM 镜像复用(2026-04-24 补)

这个 dev-env 的 qcow2 镜像(~58 GB)包含完整状态:

  • OpenCloudOS 9 guest OS + 所有 provisioning
  • Cube 全部组件(CubeAPI / CubeMaster / Cubelet / CubeProxy)
  • MySQL / Redis docker compose 数据
  • 拉过的 OCI 镜像 + 已 READY 的 template 都在 /data/cubelet/storage/

下次要用的复用路径(跳过 prepare_image.sh 和 online-install.sh,冷启动到可用 ~30 秒):

bash
cd ~/vibecoding/gpu-bench/CubeSandbox/dev-env

# 1. /dev/kvm 权限(如果 udev 又把 ACL 抹了)
sudo setfacl -m u:$USER:rw /dev/kvm

# 2. 后台启动 VM
sg kvm -c "VM_BACKGROUND=1 VM_MEMORY_MB=16384 VM_CPUS=16 ./run_vm.sh"

# 3. SSH 进去
./login.sh

# 4. (VM 内)拉起 Cube 服务栈
/usr/local/services/cubetoolbox/scripts/one-click/up-with-deps.sh

想让 Cube 服务开机自启

初次 prepare_image.sh 装了 cube-sandbox-oneclick.service 但默认没 enable。永久自启:

bash
# 宿主侧执行,脚本通过 SSH 进 VM 开启 unit
cd ~/vibecoding/gpu-bench/CubeSandbox/dev-env
ASSUME_YES=1 ./cube-autostart.sh enable

之后只要 run_vm.sh 启动 VM,Cube 服务就自动起来,连第 4 步 up-with-deps.sh 都省了。

宿主侧端口回顾(run_vm.sh 固化的)

端口映射作用
127.0.0.1:10022 → guest:22SSH
127.0.0.1:13000 → guest:3000CubeAPI(E2B 协议) ← E2B SDK 指这里
127.0.0.1:11080 → guest:80CubeProxy HTTP
127.0.0.1:11443 → guest:443CubeProxy HTTPS

从宿主机调 E2B SDK 只要:E2B_API_URL=http://127.0.0.1:13000。

Comments (0)

No comments yet

imfht
April 24, 2026 · Updated April 24, 2026