---
url: https://cubesandbox.com/zh/changelog/v0.4.0.md
---

## 2026.06.14 发布 v0.4.0

CubeSandbox 0.4.0 引入了 **CubeEgress**，一个基于 OpenResty 的安全代理，为沙箱出站流量提供凭据注入、域名过滤和访问审计能力。本版本还带来了**容器日志转发**及配套的 `cubecli logs` 命令、**节点组件版本矩阵**（集群范围版本可见性）、**模板副本兼容性检查**、**无守护进程的模板镜像构建管线**，以及显著的**网络性能提升**（网络 P50 延迟降低 35%）。构建基础镜像已降级至 `ubuntu:20.04`，将最低 glibc 要求从 2.34 降低到 2.31，以覆盖更广泛的发行版。58 个 commits，15 位贡献者。

### 🎯 主要特性

#### CubeEgress：安全代理

CubeEgress 是一个全新的基于 OpenResty 的出站网关，通过 TPROXY 截获沙箱出站流量，在请求离开集群之前执行 L7 策略。它由运行在 OpenResty/nginx 上的 9 个 Lua 模块（约 2200 行代码）以及 Go 端的集成组成——CubeMaster（CA 颁发、策略下发）、network-agent（TPROXY iptables 规则）和 Cubelet（按沙箱路由、protobuf 出站规则模型）。

* **凭据注入**（#518）：每个沙箱的密钥通过 `EgressRule.inject` 在代理层附加到出站请求中——沙箱内的用户代码永远不会接触到原始凭据。`CubeNetworkConfig` protobuf 消息（原 `CubeVSContext`）现在携带 L7 出站规则，包含匹配条件（SNI、host、method、path、scheme）和动作（allow/deny、audit、inject）。凭据信息在 CubeMaster 安全日志输出中被替换为 `***REDACTED***`（#520）。
* **域名过滤**（#518）：基于策略的 allow/deny 列表控制沙箱可访问的目标地址，按 L7 请求进行首次匹配即生效。即使设置了基于域名的 allow-out 规则，DNS 查询仍被允许（38fe9977）。
* **访问审计**（#518）：每个出站请求的结构化 JSON 日志，支持通过 `redactor` Lua 模块进行可选的请求体脱敏，便于下游合规审查。
* **Kernel 5.4 兼容**（38fe9977）：安全代理可在 kernel 5.4+ 上运行，扩大了部署覆盖范围。
* **CubeVS 快速路径加固**（#527）：在端口映射的 BPF 快速路径中拒绝纯 SYN 包，防止客户发起的连接尝试绕过出站策略。
* **TAP TX 卸载**（#505）：在 TAP 设备上启用 TX checksum/TSO 卸载和 `tx-tcp-mangleid-segmentation`，使重定向的数据包在到达客户机之前无需进行 GSO。
* **CubeEgress 版本上报**（9d76195e）：CubeEgress 接入节点组件版本矩阵，具备构建时版本元数据注入、`/admin/v1/health` 端点扩展、发布清单条目和 cubelet 端基于文件的版本采集能力。

新增文件：`CubeEgress/`（20 个文件——Lua 模块、nginx 配置、Dockerfile、iptables 脚本、systemd 单元、CA 生成）；`CubeMaster/pkg/service/httpservice/cube/ca_download.go`；`CubeMaster/pkg/templatecenter/cube_egress_ca/`；`CubeMaster/pkg/templatecenter/cube_egress_ca_bake.go`；数据库迁移 `0005_cube_egress.sql`。

#### 容器日志转发

容器 init 进程的 stdout/stderr 现在通过专用的 vsock 连接从 agent 流式传输到 shim，并追加到宿主机的日志文件中。新增的 `cubecli cubebox logs` 子命令允许运维人员从沙箱外部读取这些日志。

* **日志流式传输**（#535）：shim 向 OCI 规范注入 `cube.container.log_forwarding=true` 注解，指示 agent 为 init 进程创建 stdout/stderr 管道（1 MiB 缓冲区，`O_NONBLOCK`）。专用的 vsock 通道将日志流传输到 shim，在模板构建期间写入 `/data/log/template/<id>/stdout|stderr`，普通沙箱写入 bundle 目录下的 `./stdout` 和 `./stderr`。日志转发在暂停/快照/销毁之前会被干净地取消，进程退出时管道的写端文件描述符被关闭以确保读取端收到 EOF（#541）。exec I/O 中继（基于 FIFO）与 init 日志转发保持分离。
* **`cubecli cubebox logs`**（#528）：新增子命令，用于从 `/data/cubelet/state/io.containerd.runtime.v2.task/default/<id>/stdout|stderr` 读取容器 stdout/stderr。支持 `--tail N`、`--head N`、`--all` 和 `--stderr` 选项。由于日志文件位于 cubelet 的挂载命名空间内，该命令通过 `pkg/cubemnt/nsenter.c` 中现有的 C 构造函数重新执行自身，在任何 Go 代码运行之前安全地进入该命名空间。包含 `openNoFollow()` 路径验证以防止符号链接跟随攻击。

#### 节点组件版本矩阵

全新的版本追踪基础设施为运维人员提供了集群范围内所有节点组件版本的可见性，并配有专属的 Web UI 页面。

* **版本采集与矩阵**（#500）：Cubelet 采集各组件版本（guest-image、cube-agent、kernel，以及来自发布清单的控制面组件），并上报给 CubeMaster。CubeMaster 在 `node_component_version` 表（数据库迁移 `0004`）中维护版本矩阵，按组件对报告同一版本的节点进行分组，暴露版本偏差，并通过 CubeAPI 对外提供汇总和详情接口。
* **统一的版本注入**（#493）：所有 Go 和 Rust 二进制文件现在通过 ldflags / `build.rs` 接收 version、commit 和 build-time 元数据。一键部署发布包中生成机器可读的 `release-manifest.json`，确保每个构建产物都可追溯到同一发布版本。`cubecli version` 和 `cubemastercli version` 的输出格式在各组件间保持一致。
* **Web UI 版本页面**（#500, #481）：新增 `Versions.tsx` 页面（762 行），支持中英文国际化，展示各组件在节点间的版本分布。侧边栏和设置页的"关于"部分现在显示实际的发布标签（构建时注入为 `__APP_VERSION__`），而非硬编码的版本号。

新增文件：`CubeMaster/pkg/nodemeta/versionmatrix.go`；`web/src/pages/Versions.tsx`；`web/src/locales/en/versions.json`、`zh/versions.json`；数据库迁移 `0004_node_component_version.sql`。

#### 模板副本兼容性

模板副本现在与节点组件版本进行对比检查，过时/缺失的副本会在 API 和 Web UI 中暴露出来。

* **兼容性矩阵与版本绑定**（#510）：模板兼容性系统将每个模板绑定的组件版本（guest-image、cube-agent、kernel）与每个节点当前上报的版本进行比较。结果存储在 `template_versions` 表（数据库迁移 `0006`）中，通过 `/templates/compat`（汇总）和 `/templates/compat/{id}`（单个模板详情）接口对外暴露。版本绑定管理允许运维人员在创建时将模板固定到特定的组件版本。
* **Web UI**（#545）：模板详情页现在展示每个副本的兼容性标记、绑定版本与当前组件版本之间的版本差异，以及过时副本的警告横幅（含重建触发器）。新增组件：`CompatBadge`、`CompatSection`、`CompatWarning`、`CompatNodeCard`、`VersionDeltaList`。

新增文件：`CubeMaster/pkg/templatecenter/compat.go`；`CubeMaster/pkg/service/httpservice/cube/template_compat.go`；数据库迁移 `0006_template_replica_compat.sql`。

#### 模板镜像构建管线重构

模板镜像构建管线经过重新架构，支持通过 skopeo/umoci 进行无守护进程操作，峰值磁盘使用量降低 72%，并具备文件级内容去重能力。

* **无守护进程导出路径**（#492, #506）：当 CubeMaster 节点上可用 skopeo 和 umoci 时，模板镜像通过 `skopeo copy` 拉取到本地 OCI 布局，并用 `umoci unpack --rootless` 解包，完全消除对 Docker 守护进程的依赖。不可用时自动回退到 Docker 以保证向后兼容。导出策略在镜像解析时一次性选定，确保准备和导出阶段保持一致。
* **制品管理**（#506）：新增 job runner 编排完整管线（镜像导出 → rootfs 制品构建 → 分发），支持 redo（重做）操作并可从中断的阶段恢复。文件级内容指纹（SHA256）实现制品跨构建去重，制品清理通过结构化的生命周期进行管理。Redo 操作现在通过 working request 携带正确的模板 ID（#544）。
* **磁盘使用优化**（#472）：通过五项互补优化，将镜像到 ext4 构建过程中的峰值磁盘使用量从约 4.2 倍镜像大小降低到约 1.2 倍：

  1. **管道流式导出**：Docker export 的标准输出通过 1 MiB 管道（`F_SETPIPE_SZ`）直接连接到 `tar -xf` 的标准输入，消除中间的 `rootfs.tar` 文件。
  2. **提前清理工作目录**：rootfs 到达存储目录后立即删除临时工作目录，而非等到 ext4 创建完成之后。
  3. **精确的 ext4 空间估算**：将 2 的幂次对齐替换为三重开销模型（固定 256 MiB + 数据的 10% + 每文件 1 KiB），按 256 MiB 边界对齐。
  4. **直接导出到存储目录**：在本地快速文件系统上（通过 statfs magic 检测），rootfs 直接导出到存储目录，跳过 workDir→storeDir 的搬迁步骤。NFS/CIFS 回退到搬迁路径以避免跨设备复制。
  5. **磁盘空间预检查**：构建开始前对存储目录父目录进行 statfs 检查，配合可配置的安全边界（`CUBEMASTER_DISK_SPACE_SAFETY_MARGIN`，默认 1.5 倍）。

  SHA256 计算使用 4 MiB 缓冲区以减少 read 系统调用。基于 loop-mount 的流式 ext4 构建阶段（由 `CUBEMASTER_LOOP_MOUNT_EXT4_ENABLED` 控制，默认关闭）也已实现，含 `CAP_SYS_ADMIN` 能力检测。
* **SDK 对齐**（#485）：CubeAPI `POST /templates` 以及 Python/Go SDK 现在支持 DNS、出口 CIDR、镜像仓库认证、command/args、网络类型和节点范围等选项，与 `cubemastercli template create-from-image` 的完整选项集保持一致。

新增文件：`CubeMaster/pkg/templatecenter/image/`（export、ext4、disk、command、ref、source、types、paths、util）；`CubeMaster/pkg/templatecenter/artifact_build.go`、`artifact_cleanup.go`、`distribution.go`、`fingerprint.go`、`image_job_runner.go`、`job_constants.go`、`job_dto.go`。

#### 网络性能

* **TAP fd 获取优化**（#487）：三层的 `GetTapFile` 策略取代了旧的单一获取路径：

  * **快速路径**：当 `state.tap.File` 已缓存时立即返回（0 次系统调用）。
  * **热路径**：对于 fd 已关闭的池化 tap，仅需 2 次系统调用（`open` + `TUNSETIFF`）重新打开，跳过昂贵的 `restoreTap` 流程（netlink 查找、`LinkSetUp`、`SetMTU`、TC filter 挂载、ARP 条目）。
  * **恢复路径**：仅当没有内存状态或 tap 被外部持有时，才回退到完整的 `restoreTap`。

  fdserver JSON 响应现在包含 ifindex，使 cubelet 可以跳过自身的 `netlink.LinkByName` 调用——消除了并发沙箱创建过程中的一个序列化点。当 ifindex 为 0 时 cubelet 回退到 `LinkByName`（与旧版 agent 向后兼容）。

  通过用注册在同一临界区内的 per-sandbox `creating` 守护通道替换 singleflight 风格的去重，修复了 `EnsureNetwork` 和 `ReleaseNetwork` 之间的 TOCTOU 竞态条件。包含 pprof 调试服务器（`--pprof-listen` 选项）和 390 行并发测试（6 个测试函数，64 协程压力测试通过 `-race` 检测）。

  基准测试（BMI5, Xeon Platinum 8255C, kernel 6.6.119）：网络 P50 35.3→23.1ms（**提升 35%**），网络 P99 86.6→51.2ms（**提升 41%**），总 P50 106.1→92.0ms（**提升 13%**），吞吐量 194.8→209.8 sandboxes/s（**提升 8%**）。

* **BPF 校验和优化**（#469）：在 `from_world` 和 `from_cube` 两个 BPF 程序中，将 `bpf_csum_diff()` 替换为 `bpf_{l3,l4}_csum_replace` 辅助函数。结合 TAP TX 卸载工作（#505），使 TSO/UFO/CSUM 卸载得以在 virtio-net TAP 上重新启用（回滚 #110），同时取消了对宿主机网卡的 `disableGRO()` 要求。

### ✨ 功能增强

#### 调度

* **可配置的超卖比例和 Redis 分配旁路**（#525）：新增两个调度器配置参数：`overcommit_ratio`（默认 CPU=3, Mem=2），支持通过 `overcommit_ratio_conf` 按实例类型覆盖；`ignore_redis_allocation`（默认 false），将 Redis 中记录的已分配资源视为零。在 filter 和 score 插件中一致生效，非正数的比例值会被重置为默认值。物理负载保护（CPU 利用率上限、实时空闲内存）被有意保留。

#### 亲和性

* **自定义节点亲和性选择器**（#504, #467）：`com.nodeaffinity.selector` 注解现在接受任意的 `NodeSelectorRequirements`（In、NotIn、Exists、DoesNotExist、Gt、Lt），以 JSON 数组 `{key, operator, values}` 的形式传入。节点注册标签通过 `Node.NodeLabels` 传递，合并到 `Labels()` 中，并使用 `atomic.Pointer` 缓存和 `InvalidateLabelsCache()` 确保变更安全。DoS 防护：最大注解大小 4 KB，每个请求最多 10 个选择器，每个 In/NotIn 最多 50 个值。可配置的允许键默认包括 zone、cluster-id、cpu-type、memory-size、cpu-cores、instance-type。872 行测试覆盖 47 个场景。

#### 模板管理

* **tpl- 前缀强制**（#474）：所有创建路径（API、CLI、Web UI、沙箱提交）的模板 ID 现在统一自动生成 `tpl-` 前缀。为保持向后兼容，用户指定的 ID 仍然被接受但会被静默忽略——服务端始终返回自动生成的 `tpl-` 前缀 ID 作为权威模板标识符。验证逻辑拒绝裸的 `tpl-` / `snap-` 前缀以及不符合规范的注解前缀。
* **构建镜像降级至 ubuntu:20.04**（#468）：构建基础镜像从 `ubuntu:22.04` 更换为 `ubuntu:20.04`，将最低 glibc 要求从 2.34 降至 2.31。影响 `Dockerfile.builder`、一键部署预检脚本、CI 工作流和文档。

#### Web UI

* **模板策略展示**（#486）：模板详情页现在展示环境变量、网络类型、互联网访问权限、DNS 服务器、allow-out 规则和 deny-out 规则（从 `createRequest` 解析）。新增的"网络策略"区域包含每条规则的快捷复制按钮。`BoolBadge` 组件被提取为共享 UI 原语。
* **CubeAPI 容器镜像**（#513）：为 cube-api 服务提供容器构建，产出适合一键部署和编排部署的自包含运行时镜像，构建上下文精简。

#### SDK

* **Python SDK v0.3.0**（#521）：升级至 0.3.0，新增安全代理相关的 API。

#### PVM

* **内核 LOCALVERSION 重命名**（#511, #534）：PVM 宿主机和客户机内核的 `LOCALVERSION` 被重命名为清晰、自描述的风格，使发行版基础和宿主机/客户机角色可通过 `uname -r` 一目了然。部署配置、用户指南和博客引用均已同步更新。

### 🐛 Bug 修复

以下修复针对 v0.3.1 中已存在的问题：

* **shareDirs 为空时跳过 virtiofs 配置**（#533）：当没有指定共享目录时，Cubelet 不再生成 virtiofs 配置或注解，防止产生无效的配置。
* **自动将 DNS 服务器 IP 加入 AllowOut**（#526）：当配置了任何 DNS 规则时，DNS 服务器 IP 现在会被自动加入 `AllowOut`，确保 DNS 解析能够通过出站策略。包含回归测试覆盖。
* **Cubelog nil trace panic**（#512）：没有请求追踪上下文的后台 worker 和分离的 job 上下文不再因空指针解引用而 panic——trace 处理现在对缺失的 trace 具备容错能力。
* **存储清理时的符号链接解析**（#530）：`cleanupHostDirVolumes` 现在遍历沙箱目录时会解析基础路径的符号链接，使得位于符号链接路径下（如 `/data → /mnt/ssd/data`）的 bind mount 能够被正确识别并卸载，避免泄漏或误删后端目录。
* **网络插件启动警告**（#491）：Cubelet 启动时不再将合法的网络配置键记录为"未知 TOML 字段"——读取启动覆盖配置时现在复用现有的配置结构体。
* **禁用互联网时不自动放行 DNS**（#490）：当 `AllowInternetAccess=false` 时，解析出的 DNS 服务器不再被追加到 `allow_out`，使 deny-all 出站策略能够一致地阻断 DNS 解析。修复 #408。
* **移除一键部署运行时对 ripgrep 的依赖**（#496）：一键部署安装和启动路径不再要求或自动安装 `ripgrep`。Shell 检查已改用基于 grep 的辅助函数。
* **Virtiofs migration\_on\_error 设为 GuestError**（#482）：原生 virtiofs 服务器现在使用 `MigrationOnError::GuestError` 替代 `Abort`。快照恢复期间的单文件错误会以客户机 FS 错误（ENOENT/EIO）的形式体现在受影响的路径上，而不会中断整个热迁移。
* **VMM virtio-fs 队列容错**（#464）：`process_queue_serial()` 不再因畸形描述符而 panic。失败时通过向客户机回复 EIO FUSE 错误并继续处理队列来恢复。新增 `device_memory` 视图用于设备后端内存区域（virtio-pmem、virtio-fs DAX、ivshmem/zshm BAR）。
* **Cgroup v2 管理器创建**（#488）：Agent 现在使用 `cgroups-rs` 提供的 cgroup v2 创建路径，并通过 `cgroup.procs` 挂载容器进程，避免了在 unified cgroup 模式下使用 v1 控制器名称导致的失败。清理和信号发送的进程 ID 收集也从 `cgroup.procs` 读取。
* **心跳超时后节点健康过期**（#455）：节点健康现在从心跳新鲜度中推导——过时的心跳在 nodemeta 读取、localcache 读取和调度器 prefilter 中都会被正确地报告为不健康。共享的辅助函数统一了这三条路径中的超时规则。
* **一键部署安装后恢复 SELinux 上下文**（#471）：安装前缀下的文件上下文现在会在启动 systemd 服务之前恢复，修复了 SELinux Enforcing 主机上的一键部署安装问题。修复 #465。
* **Glibc 预检 pipefail 竞态**（#473）：`ldd --version` 的输出现在会在解析前完整捕获，避免严格模式下的预检因预期的 SIGPIPE 而退出。
* **Python SDK 流式请求体读取**（377a99dc）：`IPOverrideTransport` 中的请求体现在在复制前会被缓冲，使 multipart 上传不再因 `RequestNotRead` 而失败。
* **CLI 帮助文本修正**（#478）：修正了 `cubecli` 和 `cubemastercli` 中错误的命令名称（如 `cuebcli` → `cubecli`）、拼写错误、过时的弃用提示以及截断的描述文本。

### 📚 文档

* **DEB 安装说明**（#532）：在快速入门指南中新增 apt（DEB）安装说明，与现有的 yum（RPM）步骤并列。
* **基准测试博客环境变量修正**（#497）：修复了混淆不同客户端栈环境变量的基准测试示例——`e2b_code_interpreter` 示例使用 E2B 变量，CubeSandbox SDK 示例使用 `CUBE_API_URL` + CubeProxy 设置。
* **CNCF Landscape 徽章**（#477）：在 README 中新增 CNCF Landscape 徽章和页脚说明（中英文双版）。
* **模板 ID 文档清理**（#476）：从所有 `create-from-image` 文档和示例中移除 `--template-id` 选项，因为模板 ID 现在会自动生成 `tpl-` 前缀。
* **基准测试博文中的安装指南链接**（#475）：在所有四篇基准测试博文（中英文、裸金属和 PVM）的 §2.1 硬件部分添加安装指南链接。
* **排障链接**（#466）：在 `install.sh`、`online-install.sh` 和 `check-deps.sh` 的 XFS 文件系统检查错误消息中添加 GitHub issue #311 的排障链接。更新安装文档，使用指向 Releases 页面的直接链接。
* **CODEOWNERS**（#522）：新增 CubeEgress 维护者条目。

### ⚙️ 工程改进

* **构建系统重构**（#529）：将原来单一的整体 `.PHONY` 声明按目标拆分。新增 `clean-rust-target-dirs` 目标，清理每个顶层 Rust 工作区下的 `target/` 目录。`all` 目标现在通过共享的 `BINARIES` 列表驱动。
* **格式化检查 CI**（#524）：为所有组件的 Makefile 添加 `fmt` 目标（Go 和 Rust），并新增 `.github/workflows/fmt-check.yml` CI 工作流在 PR 上运行格式化检查。Agent 的 `fmt` 目标会在格式化前自动生成必要的文件（`version.rs`、协议 `.rs` 文件）。
* **CI 审查评论通过 stdin 传入**（#494）：PR 审查评论现在通过 stdin（`--body-file -`）传递，而非临时文件，避免审查内容残留在 checkout 目录中。
* **CI 自动审查评论复用**（#489）：自动审查评论现在在重复的 PR 同步时更新 bot 已有的标记评论，而非每次都创建新的顶层评论。
* **指标上报抖动**（#479）：Cubelet CLS 指标上报循环现在添加了随机抖动（在 `[t, 1.5t]` 范围内均匀分布），防止多个 agent 同时启动时产生惊群效应。
