拓冰建站拓冰建站
首页 / 资讯中心 / 正文

CubeSandbox 持久化存储指南:Host Mount 宿主机目录挂载的完整实践与原理

CubeSandbox 持久化存储指南Host Mount 宿主机目录挂载的完整实践与原理【免费下载链接】CubeSandboxInstant, Concurrent, Secure Lightweight Sandbox for AI Agents.项目地址: https://gitcode.com/GitHub_Trending/cu/CubeSandboxCube Sandbox 运行在轻量级 MicroVM 内沙箱中写入的数据默认都是临时的随沙箱销毁而消失。Host Mount宿主机挂载是 Cube 特有的扩展能力它将沙箱宿主机节点上的目录绑定挂载到沙箱内部无需复制文件即可实现持久化与共享存储。本文以 docs/zh/guide/persistent-storage.md 为主线结合 CubeMaster、CubeAPI 与示例代码的源码实现系统讲解 Host Mount 的配置方式、路径安全校验、快照语义、多节点共享存储与多租户隔离方案帮助你为 AI Agent 沙箱设计出可落地的数据持久化架构。概念模型Host Mount 将沙箱宿主机节点上的绝对路径映射到沙箱 VM 内的路径。读写挂载的变更在两侧立即可见——无需同步、无需上传、零延迟。从调用链来看一次 Host Mount 请求会经历三个组件阶段组件动作请求透传CubeAPI把metadata[host-mount]提升为同名 annotation键名常量HOSTDIR_MOUNT_KEY定义于 CubeAPI/src/services/sandboxes.rs校验与注入CubeMaster解析挂载列表、逐个校验hostPath是否位于允许前缀之下再把 volume / volume-mount 注入沙箱规格实际挂载Cubelet在启动 VM 前对每个通过校验的hostPath执行 bind mountCubeMaster 侧的核心实现位于 hostdir_mount.goHostDirMountOption结构体以json标签定义了挂载描述符的三个字段hostPath、mountPath、readOnlyinjectHostDirMounts负责把 annotation 解析为挂载描述符并逐个调用ensureHostDirVolume/ensureHostDirVolumeMount注入沙箱规格最后交给 Cubelet 挂载。使用场景Host Mount 适合让沙箱直接访问宿主机上的已有数据或将沙箱产生的数据保留在宿主机上。典型场景包括共享数据集和模型权重以只读方式挂载大型数据目录供同一节点上的多个沙箱复用无需重复复制。持久化任务输出以读写方式挂载输出目录使日志、构建产物和计算结果在沙箱销毁后仍然保留。复用源码工作区将代码仓库挂载到沙箱中供开发、构建、测试或代码分析任务直接使用。共享依赖与缓存复用宿主机上的依赖包、工具链或构建缓存减少重复下载和初始化时间。Host Mount 适合节点上已有目录的快速共享针对跨沙箱生命周期管理的云存储场景可以考虑使用用户级持久卷并接入 COS/NFS 等后端请参阅 Volume 插件开发指南。快速开始首先在沙箱宿主机节点上准备目录注意这是运行沙箱的 Cubelet 节点不是执行 SDK 脚本的机器sudo mkdir -p /data/shared/rw /data/shared/ro echo hello from host | sudo tee /data/shared/ro/greeting.txt sudo chown -R 1000:1000 /data/shared/rw创建带 Host Mount 的沙箱Host Mount 通过Sandbox.create()的metadata字段中的host-mount键来指定。值为JSON 编码的数组每个元素是一个挂载描述符支持同时指定多个挂载每个挂载描述符都必须包含三个字段hostPath是沙箱宿主机节点上的绝对路径且必须位于允许的目录前缀下mountPath是沙箱 VM 内的目标路径readOnly用于设置访问模式true表示只读false表示读写。import json import os from cubesandbox import Sandbox with Sandbox.create( templateos.environ[CUBE_TEMPLATE_ID], metadata{ host-mount: json.dumps([ { hostPath: /data/shared/rw, mountPath: /mnt/rw, readOnly: False, }, { hostPath: /data/shared/ro, mountPath: /mnt/ro, readOnly: True, }, ]) }, ) as sandbox: result sandbox.commands.run(ls /mnt/rw /mnt/ro) print(mount contents:, result.stdout.strip())预期输出mount contents: /mnt/ro: greeting.txt /mnt/rw:仓库中的可运行示例位于 examples/host-mount/包含create_with_mount.py核心示例脚本、env_utils.py.env 加载工具与requirements.txt。运行前需配置E2B_API_URLCubeAPI 地址默认端口 3000与CUBE_TEMPLATE_ID可用cubemastercli tpl list查询模板可通过cubemastercli tpl create-from-image从镜像创建。源码级实现见 create_with_mount.py其注释明确说明挂载路径必须已存在于 Cubelet 节点上。写入沙箱销毁后仍保留的数据import json from cubesandbox import Sandbox import os mounts json.dumps([ {hostPath: /data/shared/rw, mountPath: /mnt/rw, readOnly: False}, ]) with Sandbox.create( templateos.environ[CUBE_TEMPLATE_ID], metadata{host-mount: mounts}, ) as sandbox: sandbox.commands.run(echo persist data /mnt/rw/output.txt)# 沙箱已销毁但文件保留在宿主机上 $ cat /data/shared/rw/output.txt persist dataHost Mount 沙箱的快照语义Host Mount 是对宿主机目录的外部引用。创建 Snapshot 时Cube 只保存 VM 的内存、根文件系统状态和挂载配置不会把hostPath中的文件复制进 Snapshot。因此宿主目录中的数据始终保持独立并遵循以下语义Pause / Resume 和 FromSnap恢复时会沿用原来的挂载配置并在源节点重新挂载相同的hostPath。带 Host Mount 的沙箱会固定在源节点不会跨节点恢复如果源节点不可用或宿主目录不存在恢复将失败。RollbackVM 内存以及 Host Mount 路径之外的根文件系统状态会回到 Snapshot 创建时的状态但 Host Mount 中的数据不会回滚。Snapshot 创建后在宿主目录中新增、修改或删除的文件仍保持最新状态。Clone每个克隆拥有独立的 VM 状态但会引用相同的宿主目录。任一沙箱对读写挂载所做的修改都可通过共享目录被源沙箱和其他克隆访问具体一致性遵循底层文件系统语义只读挂载在克隆中仍保持只读。销毁沙箱或删除 Snapshot不会删除 Host Mount 指向的宿主目录及其文件。::: warning 并发写入 FromSnap 或 Clone 可能使多个运行中的沙箱同时访问同一个读写hostPath。Host Mount 不会自动提供锁、版本控制或写入冲突协调应用需要自行保证并发访问安全也可以使用底层文件系统支持的锁机制。 :::上述固定在源节点的语义在源码中有直接佐证restoreplace/placement.go 中的Input.PinToOrigin字段专门用于 host-mount is bound to the origin host path 的场景crossNodeBlockedReason在PinToOrigin为真时直接返回host-mount从而禁止跨节点恢复——即使快照已经remote_ready可被 S3 等后端远程获取带 Host Mount 的快照也不会被调度到其他节点。路径安全限制出于安全考虑hostPath被限制在一组允许的目录前缀之内。默认情况下只有/data/shared/下的路径被允许。尝试挂载该范围之外的路径会在沙箱创建时被拒绝。::: warninghostPath指的是运行沙箱的宿主机节点的文件系统路径而不是执行 SDK 脚本的机器。如果你从远程机器调用 API请确保该路径存在于沙箱宿主机节点上而不是你的本地电脑上。 :::默认行为开箱即用时只有如下路径合法# ✅ 允许 {hostPath: /data/shared/models, mountPath: /models, readOnly: True} {hostPath: /data/shared/team-a/output, mountPath: /output, readOnly: False} # ❌ 被拒绝 {hostPath: /etc/passwd, mountPath: /mnt/x, readOnly: True} {hostPath: /tmp/data, mountPath: /mnt/data, readOnly: False} {hostPath: /data/shared/../etc, mountPath: /mnt/x, readOnly: True} # 路径穿越被阻止错误响应如果指定了不允许的路径SDK 会抛出ApiError异常from cubesandbox import Sandbox from cubesandbox import ApiError import os import json try: sandbox Sandbox.create( templateos.environ[CUBE_TEMPLATE_ID], metadata{host-mount: json.dumps([ {hostPath: /etc/passwd, mountPath: /mnt/x, readOnly: True} ])} ) except ApiError as e: print(e.status_code) # 400 print(str(e)) # CubeMaster returned error code 130400: host-mount entry[0]: hostPath /etc/passwd is not within an allowed mount prefix自定义允许的前缀集群管理员可在 CubeMaster 配置文件中添加额外的允许前缀extra_conf: allowed_host_mount_prefixes: - /data/shared/ - /data/team-assets/ - /mnt/nfs/datasets/列表为空或未配置时默认值为[/data/shared/]。根路径/被明确禁止——如果出现在列表中CubeMaster 将拒绝启动。源码层面的校验细节以下三条规则均可在源码中得到验证理解它们有助于排查路径为什么被拒绝规范化后再匹配validateHostPath见 hostdir_mount.go先通过filepath.Clean规范化hostPath消除..等路径成分再使用带末尾/的目录前缀进行匹配避免/data/shared_evil之类的路径伪装成/data/shared/的子目录。启动期拒绝根目录config.go的配置校验逻辑CubeMaster/pkg/base/config/config.go会逐个检查allowed_host_mount_prefixes条目filepath.Clean后若等于/、.或非绝对路径则直接报错防止整个宿主机文件系统被意外暴露。自动补全末尾斜杠与防御性拷贝GetAllowedHostMountPrefixesCubeMaster/pkg/base/config/config.go对未以/结尾的前缀自动补全如/data/shared→/data/shared/并返回防御性拷贝避免调用方意外篡改配置或包级默认值。对应的单元测试覆盖了前缀校验、默认值、自动补全与防御性拷贝等场景见 config_test.go。无论挂载请求来自 CubeAPI 传递的 annotation还是直接指定的 volume 路径都会执行相同的路径校验createRequestHasHostMount同时检查 annotation 与HostDirVolumeSources避免通过不同入口绕过限制。权限管理Host Mount 保留宿主目录原始的所有者和权限位。readOnly标志只控制 Cube 是否以只读模式挂载不会覆盖 Linux 文件权限。如果沙箱用户的 UID 与宿主目录 owner 不匹配写入会报Permission denied。快速解决# 将目录 owner 对齐为沙箱用户 sudo chown 1000:1000 /data/shared/rw完整的排障方案含示例报错与修复步骤见 Host Mount 权限排障其中包括对齐 owner沙箱命令默认以uid1000运行若宿主机目录属于其他 UID如uid1002即使readOnly: false写入也会失败chown 1000:1000即可解决POSIX ACL无法修改 owner 时可用 ACL 为沙箱 UID 单独授权root 执行可信 workload 中可指定sandbox.commands.run(..., userroot)但需注意它会在 host mount 中创建 root-owned 文件后续宿主机工具处理时可能遇到权限差异。多节点集群共享存储Host Mount 是节点本地的。在多节点集群中hostPath必须存在于调度沙箱的那台宿主机节点上。推荐的做法是使用共享存储将相同的文件系统挂载到所有宿主机节点的统一路径下。NFS 示例在每台沙箱宿主机节点上挂载 NFS# /etc/fstab 添加 nfs-server:/export/shared /data/shared nfs defaults,hard,intr 0 0sudo mount -a ls /data/shared # 所有节点看到相同内容之后沙箱可直接使用/data/shared/下的路径无论调度到哪个节点都能访问到数据。对象存储S3/COS示例使用 FUSE 工具如s3fs、cosfs将对象存储桶挂载为本地目录# 安装 cosfs腾讯云 COS sudo apt-get install cosfs # 配置凭证 echo my-bucket:AKIDxxxx:xxxxxx /etc/passwd-cosfs chmod 600 /etc/passwd-cosfs # 挂载到 /data/shared cosfs my-bucket /data/shared -ourlhttps://cos.ap-guangzhou.myqcloud.com \ -oallow_other -ouid1000 -ogid1000# AWS S3 使用 s3fs s3fs my-bucket /data/shared \ -o iam_roleauto \ -o urlhttps://s3.amazonaws.com \ -o allow_other -o uid1000 -o gid1000::: tip 对象存储挂载适合只读场景模型权重、数据集。写入性能和 POSIX 兼容性不如 NFS频繁小文件写入建议使用 NFS 或块存储。 :::多租户隔离在多租户场景下每个租户应只能访问自己的数据目录不能看到或操作其他租户的文件。推荐的做法是利用目录结构 应用层控制实现租户间隔离。目录规划按租户 ID 划分子目录/data/shared/ ├── tenant-a/ │ ├── datasets/ │ └── output/ ├── tenant-b/ │ ├── datasets/ │ └── output/ └── tenant-c/ └── ...每个租户的沙箱只挂载属于自己的子目录import json import os from cubesandbox import Sandbox tenant_id tenant-a mounts json.dumps([ { hostPath: f/data/shared/{tenant_id}/datasets, mountPath: /datasets, readOnly: True, }, { hostPath: f/data/shared/{tenant_id}/output, mountPath: /output, readOnly: False, }, ]) with Sandbox.create( templateos.environ[CUBE_TEMPLATE_ID], metadata{host-mount: mounts}, ) as sandbox: sandbox.commands.run(ls /datasets /output)租户 A 的沙箱只能看到/data/shared/tenant-a/下的内容无法访问tenant-b或tenant-c的目录。应用层强制隔离在调用Sandbox.create()的业务代码中根据当前认证用户的租户信息拼接hostPath禁止用户自行传入任意路径def create_tenant_sandbox(tenant_id: str, template_id: str): 由平台侧控制挂载路径租户无法指定任意 hostPath。 base f/data/shared/{tenant_id} mounts json.dumps([ {hostPath: f{base}/input, mountPath: /input, readOnly: True}, {hostPath: f{base}/output, mountPath: /output, readOnly: False}, ]) return Sandbox.create( templatetemplate_id, metadata{host-mount: mounts}, )文件系统权限加固可选结合 Linux 权限进一步确保隔离性——即使路径被绕过操作系统层面也拒绝越权访问# 为每个租户创建独立目录使用不同的 UID 或 GID sudo mkdir -p /data/shared/tenant-a /data/shared/tenant-b sudo chown 1001:1001 /data/shared/tenant-a sudo chown 1002:1002 /data/shared/tenant-b sudo chmod 0700 /data/shared/tenant-a sudo chmod 0700 /data/shared/tenant-b这样即使某个沙箱尝试越权访问通过路径穿越等方式也会被 CubeMaster 的路径校验和操作系统权限双重拦截。隔离层次总结层次机制作用目录规划按租户 ID 划分独立子目录每个沙箱只挂载本租户路径结构上隔离数据租户间互不可见应用层平台代码拼接路径不信任用户输入正常场景下的租户隔离操作系统目录 owner/mode 权限兜底防护防止任何绕过嵌套挂载共享可读、写入范围独立当一个挂载的mountPath位于另一个挂载的子目录下时这两个挂载构成嵌套挂载。它适用于一组沙箱共享同一个工作区、但每个沙箱只能写入自己子目录的场景。这里的组可以是 Agent Team、项目、部门或租户Cube Sandbox 本身不负责管理这些成员关系。例如一个 Agent Team 可以使用下面的宿主机目录结构/data/shared/tenant-a/team-blue/ ├── shared-input.txt └── members/ ├── agent-a/ └── agent-b/Agent A 以只读方式挂载共享工作区再把自己的目录嵌套挂载为读写import json mounts json.dumps([ { hostPath: /data/shared/tenant-a/team-blue, mountPath: /workspace, readOnly: True, }, { hostPath: /data/shared/tenant-a/team-blue/members/agent-a, mountPath: /workspace/members/agent-a, readOnly: False, }, ])Agent B 使用相同的只读父挂载只把members/agent-b挂载为读写。各 Agent 可以使用不同的沙箱模板存储目录和访问模式不需要因此改变。Agent A 可见的路径可读可写原因/workspace/shared-input.txt是否来自共享的只读父挂载/workspace/members/agent-a/是是该位置被 Agent A 的读写子挂载替换/workspace/members/agent-b/是否仍然来自共享的只读父挂载其他租户的工作区否否对应宿主机路径没有挂载到当前沙箱这是一种共享可读、写入范围独立的模型。写入范围独立表示只有指定沙箱会获得该目录的可写挂载如果只读父挂载暴露了这个目录并不代表其他成员无法读取它。挂载语义和约束AppSnapshot 恢复时无论描述符按什么顺序传入Cubelet 都会先应用父目标路径再应用嵌套的子目标路径避免后挂载的父目录遮住已经挂载的子目录。无关路径也会使用确定的顺序但这个顺序不表示它们存在父子关系。子挂载会在对应子树位置遮住父挂载两个目录视图不会合并。子挂载生效期间父挂载在该位置原有的文件会被隐藏。readOnly对每个挂载独立生效但仍需满足 Linux 的 UID、GID、mode 和 ACL 权限。即使子挂载是读写模式如果宿主机权限不允许沙箱用户写入仍会返回Permission denied。推荐使用一个只读父挂载和明确的读写子挂载。避免重复目标路径或相互重叠的读写挂载否则目录归属和最终可见结果会变得难以判断。mountPath应使用不含.或..路径段的规范绝对路径。不要依赖输入顺序处理相互重叠的目标路径。Host Mount 是节点本地的并且数据位于沙箱快照之外。带 raw host mount 的 Snapshot FromSnap 和 Pause/Resume 会固定在源节点其他节点上的同名路径不会被视为同一份存储。如需可移植的跨机快照恢复应使用由所有候选节点都能 Attach 的共享存储所支持的 Plugin Volume参见 Volume 插件开发指南。::: warning 鉴权边界 调用Sandbox.create()的平台必须根据已经认证的沙箱归属主体和适用的组边界如租户、团队或项目生成hostPath。不要接受用户任意传入的宿主机路径也不要把/data/shared/tenants/之类的全局根目录挂载到某个租户的沙箱中只读挂载只能阻止写入不能阻止该沙箱读取其他租户的数据。 :::最佳实践输入数据优先使用只读挂载数据集、模型、配置文件等。这可以防止沙箱意外写入是最安全的默认选项。为读写挂载使用专用目录。避免挂载/或/home等范围过大的路径创建专用目录如/data/shared/output并只挂载它。尽早检查权限。在沙箱内运行id和stat确认有效 UID 与宿主目录所有者一致再依赖写入操作。定期清理输出目录。宿主机上的输出目录会跨沙箱会话累积文件——与沙箱不同它们不会在沙箱销毁时自动清理。保持允许前缀尽量窄。只向allowed_host_mount_prefixes添加具体的受信路径。不要添加/data/这样的顶层路径优先使用更深的路径如/data/shared/。故障排查现象可能原因解决方法hostPath ... is not within an allowed mount prefix路径不在允许的前缀范围内将数据放到/data/shared/下或在 CubeMaster 配置中更新allowed_host_mount_prefixes沙箱内No such file or directory沙箱宿主机节点上hostPath不存在在运行前在节点上创建目录写入时Read-only file system使用了readOnly: true挂载改为readOnly: false写入时Permission denied宿主目录所有者与沙箱用户不匹配参见上方权限管理与 Host Mount 权限排障Template not found模板 ID 错误运行cubemastercli tpl list确认Connection refusedCubeAPI 不可达检查E2B_API_URL及端口 3000 是否开放参考可运行示例examples/host-mount/含create_with_mount.py与env_utils.py核心校验与注入实现CubeMaster/pkg/service/sandbox/hostdir_mount.go允许前缀配置与默认值CubeMaster/pkg/base/config/config.go及单元测试 config_test.goannotation 键定义与透传CubeAPI/src/services/sandboxes.rs跨节点恢复固定语义CubeMaster/pkg/restoreplace/placement.go相关指南Volume 插件开发指南、跨节点快照、快照/回滚/克隆【免费下载链接】CubeSandboxInstant, Concurrent, Secure Lightweight Sandbox for AI Agents.项目地址: https://gitcode.com/GitHub_Trending/cu/CubeSandbox创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门