腾讯云服务器跑通 Cube Sandbox:从 PVM 内核到 65 ms 冷启动的全程实战
腾讯云服务器实战部署Cube Sandbox沙箱环境 本文详细记录了在腾讯云CVM(OpenCloudOS 9.4/8C16G)上部署Cube Sandbox的全过程。关键步骤包括: 通过PVM宿主机内核解决云服务器无KVM支持问题 下载并安装Cube维护的PVM内核(575MB) 配置GRUB启动参数并重启 加载kvm_pvm模块使/dev/kvm可用 一键安装Cube Sandbox(2分25
腾讯云服务器跑通 Cube Sandbox:从 PVM 内核到 65 ms 冷启动的全程实战
适合第一次想把 Cube Sandbox 真正跑起来的开发者。本文用一台普通腾讯云 CVM(OpenCloudOS 9.4 / 8C16G / 无嵌套虚拟化),从空白系统一路推到
Sandbox.create()65ms 完成、E2B SDK 直接打印 Hello World,全程命令、日志、截图都来自真实复现,无遮挡。关键词:Cube Sandbox · OpenCloudOS 9 · PVM 内核 · E2B 兼容 · AI Agent 沙箱 · MicroVM

0. 这台机器长什么样
先把机器档案摆在前面,方便你拿自己的 CVM 对照:
| 项 | 值 |
|---|---|
| OS | OpenCloudOS 9.4 |
| 默认内核 | 6.6.119-47.8.oc9.x86_64 |
| CPU | AMD EPYC 9K65(8 vCPU) |
| 内存 | 15 GiB |
| 系统盘 | 200 GB XFS |
| 公网 IP | 129.211.223.113 |
| 嵌套虚拟化 | 未开放(/proc/cpuinfo 无 vmx / svm,/dev/kvm 不存在) |
第一件事永远是先跑一遍核对脚本(脚本是我上一篇文章沉淀下来的 cube_preflight.sh,与官方 online-install.sh 的 preflight 一一对齐),免得你后面踩到坑还以为是自己 yum 配错了:
scp cube_preflight.sh root@129.211.223.113:/root/
ssh root@129.211.223.113 'bash /root/cube_preflight.sh'
实测输出(红=阻断 / 黄=提示 / 绿=通过):

读图三秒看懂:
- ✅ OS / 架构 / Root / 命令 / 内存 15GB /
/是 XFS / 磁盘 193 GB / NetworkManager / 镜像出网 全都过; - ❌ 仅
/dev/kvm不存在(CPU flags 里既没有vmx也没有svm)。
这种"只差一刀"的环境正是 Cube Sandbox 设计的主战场之一 —— 普通云服务器没有嵌套虚拟化,靠官方提供的 PVM 宿主机内核把 KVM 能力补上来,再走和裸金属一样的一键安装流程。下面我就一步步把这台机器从"差一刀"推到"全绿可用"。
1. 第一步:从 GitHub Releases 下载 PVM 宿主机内核
PVM 内核就是 Cube Sandbox 项目自己维护的一个 Linux 内核 fork,把"普通云上没有 KVM"这件事在内核态解决掉。它只通过 GitHub Releases 单包发布(不走 yum 源),文件大小 575 MB。
直接 github.com 下载在国内会卡到 100KB/s,先看官方 CNB 镜像:
URL_CNB="https://cnb.cool/CubeSandbox/CubeSandbox/-/releases/download/v0.2.2/kernel-6.6.69_cube.pvm.host.005.x_gb85200d80fa2-1.x86_64.rpm"
curl -L -C - --retry 5 --retry-delay 3 -o kernel-pvm.rpm "$URL_CNB" \
-w "\n[done] http=%{http_code} size=%{size_download} time=%{time_total}s avg=%{speed_download}B/s\n"
sha256sum kernel-pvm.rpm
实测输出:

速度比 GitHub 直连快了一个量级:56.9s 跑完 575 MB,平均 10.1 MB/s。sha256 校验我也顺手做了 —— 装内核之前一定要做这一步,这是后面所有故事的地基。
找最新版本号的小窍门:
curl -fsSL https://api.github.com/repos/tencentcloud/CubeSandbox/releases/latest | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['tag_name']); [print(a['name']) for a in d['assets']]",文件名带cube.pvm.host的就是宿主机内核。
2. 第二步:装内核 + 改 GRUB + 重启
# 1. 安装内核(--oldpackage 跳过版本对比,避免比已装更老时报错)
rpm -ivh --oldpackage kernel-pvm.rpm
# 2. grubby 列出所有内核 + 当前默认
grubby --info=ALL | grep -E "^kernel|^index|^title"
grubby --default-kernel
实测:

注意一个很省心的细节:RPM 装完 grubby 已经自动把 PVM 内核切到 index=0,所以官方文档里那条 grubby --set-default-index=<index> 在 OpenCloudOS 9 上默认可以省掉。
接下来跑官方提供的 host_grub_config.sh,它会把 PVM 内核需要的一长串启动参数(elevator=noop、transparent_hugepage=never、kvm.nx_huge_pages=never、pti=off 等等)合并到 GRUB_CMDLINE_LINUX,并按 key 去重,再 grub2-mkconfig 一次。我先把脚本下到本地看了一眼内容再跑(74 行纯 bash,不下软件不联网,安全):
curl -fsSL https://cnb.cool/CubeSandbox/CubeSandbox/-/git/raw/master/deploy/pvm/grub/host_grub_config.sh \
-o host_grub_config.sh
cat host_grub_config.sh # 看一眼,确认安全
bash host_grub_config.sh
执行结果:

最后 reboot。我用一个 SSH 探活循环监控:服务器 26 秒就回来了,且 uname -r 已经是 6.6.69-cube.pvm.host.005.x-gb85200d80fa2,主机已成功进入 PVM 内核。
3. 第三步:modprobe kvm_pvm,让 /dev/kvm 出现
进入新内核后还差最后一道:加载 kvm_pvm 模块。这一步很短但效果"立竿见影":
modprobe kvm_pvm
lsmod | grep kvm
ls -la /dev/kvm
echo 'kvm_pvm' > /etc/modules-load.d/kvm-pvm.conf # 开机自动加载

读图重点:
kvm_pvm49 KB → 拉起kvm1.18 MB → 关联kmem_cache与irqbypass,整条 KVM 模块链路就绪。/dev/kvm真的出现了:crw-rw-rw- 1 root kvm 10, 232 May 22 17:04 /dev/kvm。- 内存仍是 15 GiB,PVM 内核基本不吃内存,这一点很关键。
到这里这台 CVM 在 Cube Sandbox 看来就和一台有 KVM 能力的机器完全等价了。
4. 第四步:一键安装 Cube Sandbox,2 分 25 秒拿到完整服务栈
curl -sL https://cnb.cool/CubeSandbox/CubeSandbox/-/git/raw/master/deploy/one-click/online-install.sh \
| CUBE_PVM_ENABLE=1 MIRROR=cn bash
CUBE_PVM_ENABLE=1 是给 Cube Sandbox 安装器的暗号 —— 让它在沙箱(guest)端用 PVM 优化版的 vmlinux,否则一旦回退到普通 guest 内核,整个性能预期会大打折扣。
实测整套部署只用了 2 分 25 秒(17:04:51 → 17:07:16)。安装器干的事可以从日志里读出来,简化版如下:
[online-install] downloading cube-sandbox-one-click-46424e7.tar.gz... 438 MB, 平均 10.5 MB/s
[one-click-runtime] installed PVM guest kernel as .../cube-kernel-scf/vmlinux
[one-click-runtime] starting cube-proxy / cube-sandbox-redis / cube-sandbox-mysql ...
[one-click-runtime] cube proxy dns ready via cube-proxy-coredns
[one-click-runtime] started network-agent pid=8459
[one-click-runtime] started cubemaster pid=8460
[one-click-runtime] started cube-api pid=8461
[one-click-runtime] started cubelet pid=8462
[quickcheck] 1/5 check network-agent healthz ... OK
[quickcheck] 2/5 check network-agent readyz ... OK
[quickcheck] 3/5 check cubemaster /notify/health ... OK
[quickcheck] 4/5 check cube-api /health ... OK
[quickcheck] 5/5 check essential sockets and config ... OK
[one-click-runtime] core services ready
[one-click-runtime] webui listening on 12088
[one-click] install complete (role=control)
装完之后,整台机器的运行时全景如下(这张图我会建议你也保存一份,相当于一张"Cube Sandbox 拓扑速记"):

读图重点:
- 5 个 Docker 容器全部 healthy:
cube-webui(12088)、cube-proxy(80/443)、cube-proxy-coredns、cube-sandbox-redis(6379)、cube-sandbox-mysql(3306)。 - 4 个独立进程:
cube-api(3000) 是 E2B 兼容的 REST 网关、cubemaster(8089) 是调度器、cubelet(9966/9998/9999) 是节点 agent、network-agent(19090) 给 CubeVS 做 eBPF 网络。 /usr/local/bin装入 4 个公开命令:cubemastercli(管模板/沙箱)、cubecli(管节点)、cube-runtime与containerd-shim-cube-rs(containerd Shim v2 集成)。
到这一步,机器已经是一台**“开机即可对外提供 E2B 兼容 API 的 Agent 沙箱节点”**。
5. 第五步:建代码解释器模板(19 秒 READY)
cubemastercli tpl create-from-image \
--image cube-sandbox-cn.tencentcloudcr.com/cube-sandbox/sandbox-code:latest \
--writable-layer-size 1G \
--expose-port 49999 --expose-port 49983 --probe 49999
输出立刻给出 job_id 和 template_id:

然后用 cubemastercli tpl watch --job-id <id> 跟进度,整个模板从 PULLING 到 READY 只用了 19 秒(17:07:35 → 17:07:54),最后 tpl list 看一眼:

记住这串 tpl-90d8079679a2410c8b64c7b0,下一步 Python SDK 会用它。
6. 第六步:装 e2b-code-interpreter,写第一段 Agent 代码
yum install -y python3 python3-pip
pip3 config set global.index-url https://mirrors.ustc.edu.cn/pypi/simple
pip3 install e2b-code-interpreter
export E2B_API_URL="http://127.0.0.1:3000"
export E2B_API_KEY="dummy"
export CUBE_TEMPLATE_ID="tpl-90d8079679a2410c8b64c7b0"
export SSL_CERT_FILE="/root/.local/share/mkcert/rootCA.pem" # 一键脚本生成的 mkcert 根证书
第一段 Hello World 我刻意写得"啰嗦一点",在沙箱内打印环境信息、跑一次重计算、再写一个文件验证 IO:
import os, time
from e2b_code_interpreter import Sandbox
t0 = time.perf_counter()
sb = Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"], timeout=120)
print(f"[host] sandbox created in {(time.perf_counter() - t0) * 1000:.1f} ms, id={sb.sandbox_id}")
res = sb.run_code("""
import platform, sys, os, socket
print('hello from cube sandbox')
print('python :', sys.version.split()[0])
print('platform :', platform.platform())
print('hostname :', socket.gethostname())
print('cpus :', os.cpu_count())
print('cwd :', os.getcwd())
""")
print(res.logs.stdout)
跑一次:

几个细节非常值得读:
- 沙箱端到端创建只花了 73.1 ms(从 SDK
Sandbox.create()调用到sandbox_id返回,含网络往返)。 - 沙箱里
uname -r是6.6.69-cube.pvm.guest.005.x-gb85200d80fa2,注意是pvm.guest,和宿主的pvm.host配对 —— 这就是CUBE_PVM_ENABLE=1起的作用。 hostname=tpl-90d8:与模板 ID 前缀一致,便于排查。- 沙箱内
cpus=2是模板默认配置;/sandbox-data/note.txt写入读出一切正常。 listdir /看见的是一个独立的 Linux 根(bin/etc/lib/proc...),不是宿主机的/,这就是"内核级隔离"在文件系统层的直观体现。
7. 第七步:把它当 Agent 用 —— shell + 数据 + 画图 + 多沙箱隔离
光打印 Hello 没什么意思,这一节我用一个稍长的脚本把 Cube Sandbox 的几大核心能力一次性跑全:
- 沙箱内执行 shell(
id/uname/df); - 沙箱内
pip install numpy matplotlib; - 沙箱内画 sin/cos 图,写入
/workspace/plot.png; - 用
sb.files.read()把图取回宿主机; - 同时再开两个沙箱,在
sb1写/tmp/secret.txt,验证sb2看不见。
跑一次:

然后是沙箱里画的、宿主机拉回来的那张图:

回看运行结果:
- 三个沙箱依次 65 / 77 / 65 ms 冷启动;
- 隔离验证:
sb2 sees /tmp/secret.txt ? False—— 完美隔离; - 沙箱内
df -h /显示overlay2 1.1G正好对应--writable-layer-size 1G; - 沙箱里
pip install成功(走 USTC 镜像)—— 说明 Cube Sandbox 默认给沙箱开了出网(你也可以用 CubeVS 做细粒度网络策略,那是另一个话题)。
如果你做过 E2B 集成会发现:这段代码里完全没出现 Cube 字样。from e2b_code_interpreter import Sandbox + Sandbox.create(...) 是原汁原味 E2B SDK 调用,唯一改的就是 E2B_API_URL 这一个环境变量。这就是官方说的"零成本迁移"。
8. 重头戏:冷启动延迟 benchmark(连开 30 个)
README 上写"60 ms 单并发 / P95 90 ms 50 并发",到底是不是真的?我连续创建了 30 个沙箱:
times_ms = []
for i in range(30):
t0 = time.perf_counter()
sb = Sandbox.create(template=T, timeout=60)
dt = (time.perf_counter() - t0) * 1000
times_ms.append(dt); print(f"[{i+1:>2}/30] {dt:7.1f} ms")
实测:

| 指标 | 实测 | README 官方(参考) |
|---|---|---|
| min | 57.8 ms | - |
| P50 | 65.4 ms | 60 ms(裸金属,单并发) |
| mean | 66.1 ms | 67 ms(裸金属,50 并发) |
| P95 | 78.3 ms | 90 ms(裸金属,50 并发) |
| max | 81.0 ms | - |
这是在普通云 CVM + PVM 这种"二次虚拟化"场景下测出来的数据,P50 65.4 ms、P95 78.3 ms —— 完全配得上 README 里那条"百毫秒级"基线。要知道,30 次连开里没有一次破百,对比 Docker 的 ~200 ms 已经领先 3-5 倍,对比传统 VM(秒级)就是数量级的差距。
回想这个数据是怎么实现的:模板启动时,Cube Sandbox 已经在节点上预先做好了 rootfs 解包、运行时初始化、网络栈准备,一次 Sandbox.create 实际上是在预热好的资源池里 Clone 一个 MicroVM,这就是 README 里"基于资源池化预置和快照克隆技术,跳过耗时初始化流程"的工程含义。
9. 这台机器现在是什么状态
把上面所有步骤的产物归纳一下:
| 维度 | 之前 | 现在 |
|---|---|---|
| 内核 | 6.6.119-47.8.oc9.x86_64(默认) |
6.6.69-cube.pvm.host.005.x-gb85200d80fa2(PVM 宿主) |
/dev/kvm |
不存在 | crw-rw-rw- 已生成 |
| Cube Sandbox 服务 | 未安装 | 5 个 Docker 容器 + 4 个独立进程,5/5 quickcheck 全过 |
cubemastercli tpl list |
/ | tpl-90d8079679a2410c8b64c7b0 STATUS=READY |
e2b-code-interpreter |
/ | v2.6.2,环境变量已 export |
| 一次冷启动 | / | 65 ms 量级(P50 65.4ms) |
| 端口 | / | 80/443 (cube-proxy)、3000 (cube-api)、12088 (webui) 等 |
接下来你可以做什么? 几个方向我自己马上会去试:
- 把它接到现有的 LangChain / LangGraph / LlamaIndex Agent:因为是 E2B 兼容,绝大多数 Agent 框架"换一个
E2B_API_URL"就能直接跑你私有部署的 Cube Sandbox,不用重写工具。 - 跑 SWE-Bench 这种基准:README 视频演示里就有 RL + SWE-Bench 场景;这台机器 8C16G + Cube 已经够开 30+ 沙箱并发跑评测。
- 开 CubeVS 网络策略:限制沙箱只能访问白名单域名,这是防"prompt 注入诱导 curl 外泄"的关键一道。
- 打 webui:浏览器开
http://<IP>:12088就能看到沙箱、模板、节点的可视化面板(前提是把 12088 加到安全组)。
10. 写在最后:为什么这条路值得走
在写这篇之前我做了一台 2C2G 机器的"环境核对"练习,结论是:Cube Sandbox 对环境的硬要求其实只有三条 —— /dev/kvm、内存 ≥ 8GB、/data/cubelet 在 XFS 上。前两条把绝大多数普通 CVM 挡在门外,但这并不意味着你必须买裸金属:
- PVM 内核这条路是真的能走通,而且不慢:给一台没有嵌套虚拟化的腾讯云 CVM 装上 PVM 内核 + 一键脚本部署 + 建模板 + 跑通 SDK,全程不到 10 分钟(我从 17:04 开始下载内核,17:18 已经在跑 30 沙箱 benchmark);
- OpenCloudOS 9 是 Cube Sandbox 的"first-class"目标平台:必需命令、文件系统类型、NetworkManager、yum、Docker 一键装 —— 全程零适配;
- 冷启动 65 ms 是真的,不是营销数字,普通 CVM + PVM 都能复现。
如果你是在做 AI Agent / 代码执行 / RL 训练 / E2B 私有化,我会给一个朴素建议:在你的开发环境里先按这篇文章跑通一遍。等你接下来给 Agent 写第二个、第三个工具时,"这段代码能不能放心跑在生产里"这个问题会有一个非常具体的答案——一个 65 ms 起、内核级隔离、E2B 完全兼容、可由你自己审计的 MicroVM。
附录 A:cube_preflight.sh 完整版
与官方
online-install.sh的 preflight 完全对齐,可独立跑,1 秒出结果。
#!/usr/bin/env bash
set -u
c_red='\033[0;31m'; c_grn='\033[0;32m'; c_yel='\033[0;33m'; c_cyn='\033[0;36m'; c_rst='\033[0m'
ok() { printf "${c_grn}[ OK ]${c_rst} %s\n" "$*"; }
warn() { printf "${c_yel}[WARN]${c_rst} %s\n" "$*"; }
fail() { printf "${c_red}[FAIL]${c_rst} %s\n" "$*"; }
hd() { printf "\n${c_cyn}== %s ==${c_rst}\n" "$*"; }
hd "1. 操作系统"; . /etc/os-release 2>/dev/null || true
echo " PRETTY_NAME : ${PRETTY_NAME:-unknown}"
case "${ID:-}" in opencloudos) ok "OpenCloudOS 检测到" ;; *) warn "非 OpenCloudOS(${ID:-unknown})" ;; esac
hd "2. 内核 & 架构"; echo " uname -r : $(uname -r)"; echo " uname -m : $(uname -m)"
[[ "$(uname -m)" == "x86_64" ]] && ok "x86_64" || fail "需要 x86_64"
hd "3. Root 权限"
[[ "${EUID}" -eq 0 ]] && ok "root" || fail "必须 root"
hd "4. 必需命令"
for cmd in tar awk curl wget python3 systemctl; do
command -v "$cmd" >/dev/null && ok "$cmd -> $(command -v "$cmd")" || fail "缺命令: $cmd"
done
hd "5. KVM 能力(/dev/kvm)"
if [[ -e /dev/kvm ]]; then
ok "/dev/kvm -> $(ls -l /dev/kvm)"
else
fail "/dev/kvm 不存在"
flags=$(grep -oE '(vmx|svm)' /proc/cpuinfo | sort -u | tr '\n' ' ')
[[ -n "$flags" ]] && warn "CPU flags: $flags(可能可启用嵌套 KVM)" \
|| warn "无 vmx/svm flag,必须走 PVM 路径"
fi
hd "6. 内存 (>= 8 GB)"
mem_kb=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
mem_gb=$(awk -v k="$mem_kb" 'BEGIN{printf "%.2f", k/1024/1024}')
echo " MemTotal: ${mem_kb} KB (~${mem_gb} GB)"
[[ "$mem_kb" -ge 7500000 ]] && ok "内存达标" || fail "内存不达标(min_mem_kb=7500000 写死)"
hd "7. /data/cubelet 所在分区 (要求 XFS)"
target=/data/cubelet; probe="$target"
while [[ ! -e "$probe" ]]; do parent=$(dirname "$probe"); [[ "$parent" == "$probe" ]] && break; probe="$parent"; done
fs_type=$(df -T "$probe" 2>/dev/null | awk 'NR==2 {print $2}')
echo " 探测路径 : $probe"; echo " 文件系统 : ${fs_type:-unknown}"
[[ "$fs_type" == "xfs" ]] && ok "XFS" || fail "需要 XFS"
hd "8. 磁盘空闲空间"
avail_kb=$(df -k / | awk 'NR==2 {print $4}')
avail_gb=$(awk -v k="$avail_kb" 'BEGIN{printf "%.1f", k/1024/1024}')
echo " / 可用 : ${avail_gb} GB"
[[ "$avail_kb" -ge 31457280 ]] && ok "充足" || warn "建议预留 30GB+"
hd "9. DNS 能力"
if command -v resolvectl >/dev/null; then ok "resolvectl 可用"
else
state=$(systemctl show -p LoadState --value NetworkManager 2>/dev/null || echo "?")
[[ "$state" == "loaded" ]] && ok "NetworkManager loaded" || fail "需要 resolvectl 或 NetworkManager"
fi
hd "10. Docker"
command -v docker >/dev/null && ok "$(docker --version)" || warn "未装 docker(一键脚本会自动装)"
hd "11. 网络出网"
for u in https://cnb.cool https://mirrors.ustc.edu.cn; do
code=$(curl -o /dev/null -s -w "%{http_code}" --max-time 8 "$u" || echo "000")
[[ "$code" =~ ^[23] ]] && ok "$u -> HTTP $code" || warn "$u -> HTTP $code"
done
hd "汇总"
echo " 本机: ${PRETTY_NAME:-?} | $(uname -r) | $(uname -m) | mem ${mem_gb}GB | / fs ${fs_type:-?}"
echo " 时间: $(date '+%F %T %Z')"
附录 B:参考链接
- Cube Sandbox GitHub:https://github.com/TencentCloud/CubeSandbox
- Cube Sandbox CNB 镜像:https://cnb.cool/CubeSandbox/CubeSandbox
- 官方文档首页:https://cubesandbox.com/
- PVM 部署文档:https://cubesandbox.com/guide/pvm-deploy.html
- E2B Code Interpreter SDK:https://github.com/e2b-dev/code-interpreter
- OpenCloudOS:https://www.opencloudos.org/
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)