Skip to content

Cube Sandbox v0.4.0:从"隔离 Agent"到"治理 Agent"

继两周前 v0.3.0 发布了快照、克隆、回滚"三件套"后,本周 v0.4.0 正式上线。这一版的更新清单看起来庞杂,但如果只用一句话概括,就是:Cube 正在从一个"高性能隔离运行时",长成一个"可治理的 Agent 基础设施"

隔离做到了极致之后,下一个问题不仅仅是"怎么把 Agent 关起来",而是演变成:

  • 出站治理——Agent 在沙箱里能往外打什么流量?
  • 可观测——沙箱里发生的事,外面看得见吗?
  • 一致性——集群里几十台机器,跑的到底是不是同一个版本?

本次 v0.4.0 核心解决的就是这三个问题。

一、域名过滤

Cube 的网络模型从第一个版本起即支持 AllowOut / DenyOut 的 IP/CIDR 过滤,可以按 IP 粒度对沙箱出站流量做精细控制,但真实业务场景的出站访问往往以域名的方式,通过 DNS 解析获得 IP 地址,配置策略时还不确定域名对应的 IP 地址,并且域名对应的 IP 随时可能发生变化。这套模型在原型阶段够用,但在生产环境下就开始捉襟见肘。

基于域名的过滤增强了出站流量管控,更贴合真实 Agent 使用沙箱的方式。在 AllowOut 中配置 1 个或多个域名,意味着放通该域名对应 IP 的流量;在 AllowOut 中可以同时配置域名和 IP/CIDR。域名不仅支持精确匹配,还支持后缀通配符方式,如 *.qq.com 即放通任何以 .qq.com 为后缀的子域名。DenyOut 不支持域名,当 AllowOut 中有至少 1 个域名时,DenyOut 必须包含 0.0.0.0/0,以拒绝任何未放通域名的流量。

二、CubeEgress:沙箱第一次有了"L7 出境管控"

不管是 Cube 第一个版本起即支持的 AllowOut / DenyOut 的 IP/CIDR 过滤,还是域名过滤,都是以 IP 地址粒度对沙箱出站做精细控制,类似业内常见的做法——用 NetworkPolicy 做 L3/L4 隔离。但 Agent 的世界已经不在 L3/L4 这个抽象层了,Agent 调用的是 https://api.openai.com/v1/chat/completions,是 host + path + method 的组合。NetworkPolicy 看不到这一层。

CubeEgress 是一个全新的基于 OpenResty 的出站网关,通过 TPROXY 截获沙箱出站流量,在请求离开集群之前执行 L7 策略。它由运行在 OpenResty/nginx 上的 9 个 Lua 模块(约 2200 行代码)以及 Go 端的集成组成——CubeAPI(提供 API 配置沙箱出站策略)、CubeMaster(生成并烘焙 CA 证书到模版)、NetworkAgent(策略下发)和 CubeEgress(策略执行)。在 rules 策略中配置的 SNI 和 host 会自动在 AllowOut 中放通域名,且该域名对应 80/443 的 HTTP/HTTPS 流量将从 CubeVS 重定向到 CubeEgress 处理。

**因此,CubeEgress 的设计就是把管控点放在 L7。**它的工作方式,是通过 TPROXY 透明按需截获沙箱的出站流量——沙箱内的代码无需任何感知,请求像往常一样发出去,但在离开集群之前必须先过它这一关。

**策略匹配维度是五个:SNI、host、method、path、scheme。动作是四个:allow、deny、audit、inject,**同时也支持写 Lua 脚本轻松扩展任何需要的功能。

2.1 L7 应用层过滤

IP/CIDR/域名的过滤,都是针对目的 IP 粒度的过滤。CubeEgress 支持 SNI、host、method、path、scheme 五元组的过滤,SNI、host 都支持后缀通配符,path 支持前缀通配符,每个元素也可以不配置,但 SNI/host 至少配置其一,以将流量引流到 CubeEgress。

每个沙箱可以配置若干 rule,每个 rule 可以配置五元组过滤,顺序匹配,每个规则可以 allow 或 deny 以实现精细的过滤策略。

2.2 HTTPS 代理

CubeEgress 处理沙箱发出的 HTTPS 流量时需要对流量解密。即 CubeEgress 支持自分发证书服务,沙箱进行 HTTPS 访问时接收到 CubeEgress 分发的证书,并与 CubeEgress 完成加解密处理后,CubeEgress 再与原目的服务进行 HTTPS 交互。

制作模板时 Cube 自动将 CubeEgress 的根证书烘焙到沙箱镜像中,以使沙箱证书验证成功,这一切都在 Cube 内部闭环完成。

2.3 关于凭据注入:Agent 代码再也不必接触你的 API Key

传统做法里,沙箱里的 Agent 要调 OpenAI,OPENAI_API_KEY 通常以环境变量的形式注入沙箱。这意味着:Agent 的 Python 代码读得到、它 import 的任何第三方库读得到、它跑的 LLM 上下文里可能出现、它打的日志里可能落盘。任何一环失守,Key 就出去了。

CubeEgress 的做法是反过来——Key 只存在于代理层,永远不进沙箱

你在 EgressRule.inject 里配置好"凡是发往 api.openai.com 的请求,自动追加 Authorization: Bearer ${KEY}",沙箱内的 Agent 代码只管发裸请求或携带任意 KEY,真实 Key 都由 CubeEgress 代理在转发时附加到 Header。从代码到日志,Agent 自始至终接触不到原始 Key。在 CubeEgress 自己的安全审计日志里,凭据字段也会被替换为 ***REDACTED***,避免审计日志反过来成为泄密通道。

2.4 关于审计:每一次出站,都留下可追溯的结构化记录

每个经过 CubeEgress 的请求,都会生成一条结构化 JSON 日志——去了哪个域名、用了什么方法、Header 里有什么、Body 里有什么。配合 redactor Lua 模块做请求体脱敏,你既能事后追溯 Agent 访问了什么,又不会把用户隐私写进日志。

三、容器日志转发:沙箱内的输出,第一次能被宿主机"看见"

之前的沙箱存在一个问题:容器进程 stdout/stderr 的输出留在沙箱内部,宿主机上看不到。调试要 cubecli exec 进去翻,生产环境的日志聚合根本无从下手。

这件事的难点在于:沙箱是硬件隔离的 MicroVM,不是普通容器,stdout/stderr 不能像 docker logs 那样直接读宿主机进程的 fd。你必须在沙箱内部捕获、跨虚拟机边界传输、宿主机侧落盘——而且不能借用业务网络,否则日志和应用流量混在一起,又快又乱。

v0.4.0 的做法是开了一条专用 vsock 通道

  • 沙箱 agent 侧:通过 shim 注入的 cube.container.log_forwarding=true 注解感知到要开日志通道,为 init 进程的 stdout/stderr 创建管道,1 MiB 缓冲、O_NONBLOCK 非阻塞——这两个参数是关键,保证日志写慢了不会反过来阻塞 Agent 进程
  • 传输:日志走专用 vsock 连接(与控制面、业务网络物理隔离),shim 在宿主机侧消费。
  • 查看:沙箱日志查看:cubecli logs ${sandboxid},模版日志查看:cubecli logs --tpl ${templateid}
  • 退出时序:进程退出时主动关闭写端 FD 确保读端收到 EOF。这是个看似琐碎但极易踩坑的细节——少了它,最后几行日志可能永远收不到。

配套新增了 cubecli cubebox logs 子命令,支持 --tail/--head/--all/--stderr

四、节点组件版本矩阵:让"版本漂移"无处藏身

你升级了 CubeMaster,忘了某台节点的 Cubelet;你刷新了 guest-image,有两个节点 kernel 没跟上。这种不一致不会立刻爆炸,但会在某一天以一种你完全意想不到的方式出问题——某个沙箱在 A 节点能跑、在 B 节点起不来,排查到最后才发现是某个组件版本差了两个小版本。

Kubernetes 用了多年才在这件事上形成共识:版本可见性是集群运维的零号问题。v0.4.0 把这一层基础设施搭起来了,分为了三步:

4.1 第一步:编译期统一注入版本元数据

所有 Go 二进制通过 ldflags、所有 Rust 二进制通过 build.rs,在编译期注入 version / commit / build-time 三元组。一键部署脚本生成 release-manifest.jsoncubecli versioncubemastercli version 的输出格式完全一致。

听起来简单,但做过多语言异构项目的人都知道——让 Go/Rust 两套构建链产生格式一致、来源一致的版本信息,是一件需要刻意约束才能做到的事。在此之前,"同一个组件,不同工具查出来版本不一样"是 Cube 也存在过的现实。

4.2 第二步:节点自动上报、CubeMaster 维护矩阵

Cubelet 在节点侧自动采集 guest-image、cube-agent、kernel 及控制面组件的版本,上报 CubeMaster。CubeMaster 在 node_component_version 表(迁移 0004)维护全集群版本矩阵,按组件分组报告同版本节点,版本偏差以集合差的形式暴露——哪台节点和大部队不一样,一眼就能看到。

CubeAPI 同时提供了汇总和详情接口,Web UI 新增 Versions.tsx 页面做可视化呈现。

4.3 第三步:模板和组件版本"对账"

这一步比前两步更进一步。Cube 引入了 template_versions 表(迁移 0006)来记录每个模板副本依赖的组件版本,并提供两个端点:

  • /templates/compat:全集群模板兼容性视图
  • /templates/compat/{id}:单模板详情

Web UI 配套引入了 CompatBadge / CompatSection / CompatWarning / CompatNodeCard / VersionDeltaList 五个组件——过时的副本、版本差异、需要重建的横幅,全部可视化。还支持"版本绑定",把模板固定到特定组件版本,避免节点升级后模板悄无声息地坏掉。

五、那些不在 Highlight 里、但同样重要的事

5.1 网络性能:P99 延迟降 41%

GetTapFile 这条沙箱启动关键路径被重写成三层策略:缓存命中走快速路径(0 系统调用)、池化 fd 已关闭走热路径(2 系统调用,跳过昂贵的 restoreTap)、无状态走完整恢复路径。fdserver 的 JSON 响应携带 ifindex,让 cubelet 直接跳过 netlink.LinkByName——这是个常被忽略但成本极高的并发序列化点。同时把 EnsureNetwork/ReleaseNetwork 之间的 TOCTOU 竞态,用 per-sandbox creating 守护通道替代了 singleflight。

BMI5 环境实测:

  • 网络 P50:35.3 → 23.1ms(-35%
  • 网络 P99:86.6 → 51.2ms(-41%
  • 整体启动 P50:106.1 → 92.0ms(-13%
  • 吞吐:194.8 → 209.8 sandboxes/s

5.2 模板构建:峰值磁盘从 4.2 倍降到 1.2 倍

模板镜像构建管线做了一次彻底重构——引入 skopeo + umoci 走无守护进程路径

其中,Cube 在 5 个细节上做了磁盘优化:包括 Docker export stdout 通过 1 MiB 管道直连 tar -xf stdin,消除中间 rootfs.tar;ext4 空间估算用三重开销精确模型(256 MiB 固定 + 数据 10% + 每文件 1 KiB),按 256 MiB 边界对齐等。

最终优化结果是使峰值磁盘使用从约 4.2 倍镜像大小降到约 1.2 倍。文件级 SHA256 指纹还实现了跨构建去重。SDK 侧的 POST /templates 同步支持了 DNS、出口 CIDR、镜像仓库认证、command/args、网络类型和节点范围等更完整的选项。

此外,在调度层面,实现了可配置超卖、自定义节点亲和性。同时,也修复了一系列核心 Bug,例如:禁用互联网时不再自动放行 DNS(之前是 deny-all 策略下的隐蔽漏洞,影响安全语义一致性)、DNS 服务器 IP 自动加入 AllowOut(之前需要用户手动配置,新手必踩)等。

Coming soon...

v0.4.0 把"出站治理"和"可观测性"两块拼图补上了,但我们还会继续在下一个版本里将这些功能推送给大家:

  • ARM 物理机支持:目前 Cube 还是一个 x86-first 的运行时,但 AI 基础设施侧的现实早已是双架构并行,越来越多团队希望"把 Agent 跑在 ARM 上"。下一版 Cube 计划将 MicroVM 内核、guest-image、cube-agent 全栈打通 ARM。

  • Volume SDK:v0.3.0 的快照/克隆/回滚解决了"沙箱状态"的管理,但用户数据和沙箱生命周期仍然是绑死的。接下来,Volume SDK 会允许独立创建、可在沙箱之间挂载迁移、可独立做快照和备份。这意味着 Agent 从此可以拥有一个跨会话、跨实例的"工作空间",而不只是一个一次性运行环境。

  • 自动暂停与恢复(autopause):闲置沙箱占着内存和 CPU 配额是一笔实实在在的成本——Agent 等用户输入、等长任务回调、等下一轮 RL rollout 的间隙,沙箱大部分时间其实是"空跑"。autopause 会让沙箱在空闲时完整释放宿主机内存与 CPU 配额、只保留磁盘状态,被访问时再毫秒级唤醒。这个功能叠加 v0.3.0 的快照机制后,Cube 能把"暂停"做到接近 0 内存占用、"恢复"做到亚百毫秒级。

当前,Cube 已经走过了"造出一个高性能 MicroVM 沙箱"的阶段。从 v0.4.0 开始,它要回答的问题是:当 AI Agent 真的大规模跑在生产环境里,沙箱该提供什么样的治理能力?

如果你正在构建 Agent 工作流、Agentic RL 训练平台或者面向开发者的代码执行服务,欢迎来 GitHub 拍砖、提 issue、发 PR。

GitHub 仓库: https://github.com/TencentCloud/CubeSandbox

v0.4.0 完整 Release Note: https://github.com/TencentCloud/CubeSandbox/blob/master/docs/zh/changelog/v0.4.0.md

如果你有关于 CubeSandbox 的文章想贡献,欢迎提交 PR!前往 GitHub 贡献 →