ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CubeSandbox COS Volume 插件(binary 类型)实战指南:基于 cosfs + coscmd 的沙箱持久化存储

CubeSandbox COS Volume 插件(binary 类型)实战指南:基于 cosfs + coscmd 的沙箱持久化存储 CubeSandbox COS Volume 插件binary 类型实战指南基于 cosfs coscmd 的沙箱持久化存储【免费下载链接】CubeSandboxInstant, Concurrent, Secure Lightweight Sandbox for AI Agents.项目地址: https://gitcode.com/GitHub_Trending/cu/CubeSandbox导读本文是 CubeSandbox 中腾讯云 COS Volume 插件binary 类型driver 名cos的完整技术指南。该插件用cosfsFUSE将 COS bucket 子目录挂载为宿主机本地目录每次 Hook 由 CubeMaster / Cubelet fork 子进程、以 CLI 参数传入请求并以 stdout 单行 JSON 返回结果是 CubeSandbox Volume 插件框架 中最容易跑通的参考实现。读完本文你将掌握COS bucket 的目录布局与两阶段生命周期原理、CubeMaster 与 Cubelet 两侧的插件部署与配置、create / destroy / attach / detach四个 Hook 的代码级行为以及用 Python SDK 完成创建 Volume → 挂载到沙箱 → 读写 → 解绑 → 删除的完整闭环并具备手动触发插件与排查 cosfs 挂载问题的能力。插件原理一个 volume 一个 cosfs 进程COS binary 插件用cosfsFUSE 客户端把 COS bucket 的子路径挂载为本地目录。每个 volume 对应 bucket 内的一个独立子目录volumes/volume_id/由一个独立的 cosfs 进程管理COS Bucket └── volumes/ ├── vol-aaa/ ← volume A一个 cosfs 进程 │ ├── .keep │ └── data.bin └── vol-bbb/ ← volume B另一个 cosfs 进程 └── model.pt隔离性保证每个 volume 拥有独立的 cosfs 进程和挂载点。销毁沙箱 A卸载 vol-aaa不会影响沙箱 B 正在使用的 vol-bbb天然避免了误卸载他人数据。两阶段生命周期与框架的 Controller / Node 双角色模型对应见 docs/zh/guide/volume-plugin.md阶段触发操作createVolume.create()coscmd 在 COS 上创建volumes/id/.keep目录占位符attachsandbox 创建cosfs 挂载BUCKET:/volumes/id到宿主机目录返回host_pathdetachsandbox 销毁最后一个引用卸载时fusermount 卸载该 volumedestroyVolume.destroy()coscmd 递归删除volumes/id/目录其中 create / destroy 属于管理面由 CubeMaster 调用attach / detach 属于数据面由 Cubelet 调用。数据面的host_path最终由 Cubelet 经 virtiofs bind-mount 进 microVM沙箱内看到的挂载路径与宿主机路径通过volume_plugin_base_dir关联默认/data/cube-shared/volume。体验前准备依赖装在哪台机器步骤内容1完成 examples/volume/cos/README.zh.md 中的前置条件运行中的 Cube 集群、可用模板、COS bucket 与子账号密钥2Cubelet 节点cosfs jq依赖安装与校验3CubeMaster 节点coscmd jq同上4部署本目录 cube-volume-cos.sh 与volume-cos.conf5配置 CubeMaster Cubelet6SDK 验证依赖矩阵binary 类型需要全部工具工具安装节点用途HookcosfsCubeletattach / detachFUSE 挂载 COScoscmdCubeMastercreate / destroy在 COS 上建/删目录jqCubeMaster 与 Cubeletbinary 插件构造 / 解析 stdout JSON本示例不使用 COS Go SDK那是 rpc 类型 的实现方式binary 类型的管理面操作完全依赖 coscmd CLI。在裸机 / one-click 部署下可用一键脚本安装参考 install-deps.sh 的实现# Cubelet 节点 sudo /usr/local/services/cubetoolbox/Cubelet/plugin/install-deps.sh --cosfs --jq # CubeMaster 节点 sudo /usr/local/services/cubetoolbox/CubeMaster/plugin/install-deps.sh --coscmd --jq # 单机全套CubeMaster 与 Cubelet 同机 sudo /usr/local/services/cubetoolbox/Cubelet/plugin/install-deps.sh --all--check-only仅检查不安装。容器镜像Kubernetes / 镜像部署已内置 cosfs、coscmd、jq无需再执行。安装成功校验Cubelet 节点执行ls /dev/fuse which cosfs cosfs --version缺/dev/fuse时 attach 必然失败CubeMaster 节点执行which coscmd coscmd --version两侧执行printf %s {ok:true} | jq -r .ok应输出true。架构限制官方 cosfs 仅提供 x86_64 / amd64 包不支持 ARM / aarch64install-deps.sh 中的require_x86_64_for_cosfs会直接拒绝在非 x86_64 架构上安装。ARM 场景可自行尝试用 s3fs 替代。部署插件脚本 凭证 双侧配置1. 安装插件脚本一键部署one-click后binary 插件已自动放置在/usr/local/services/cubetoolbox/CubeMaster/plugin/cube-volume-cosController与/usr/local/services/cubetoolbox/Cubelet/plugin/cube-volume-cosNode。从源码手动安装非 one-click方式PREFIX/usr/local/services/cubetoolbox sudo install -m 0755 examples/volume/cos/binary/cube-volume-cos.sh \ $PREFIX/CubeMaster/plugin/cube-volume-cos sudo install -m 0755 examples/volume/cos/binary/cube-volume-cos.sh \ $PREFIX/Cubelet/plugin/cube-volume-cos2. 创建配置文件插件脚本同目录放置volume-cos.confCubeMaster / Cubelet 各一份分节点部署时在对应节点编辑PREFIX/usr/local/services/cubetoolbox sudo install -m 0600 examples/volume/cos/volume-cos.conf.example \ $PREFIX/CubeMaster/plugin/volume-cos.conf sudo install -m 0600 examples/volume/cos/volume-cos.conf.example \ $PREFIX/Cubelet/plugin/volume-cos.conf sudo ${EDITOR:-vi} $PREFIX/CubeMaster/plugin/volume-cos.conf sudo ${EDITOR:-vi} $PREFIX/Cubelet/plugin/volume-cos.conf或手动写入示例字段完整模板见 volume-cos.conf.examplePREFIX/usr/local/services/cubetoolbox sudo tee $PREFIX/CubeMaster/plugin/volume-cos.conf /dev/null EOF SECRET_IDAKIDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx SECRET_KEYxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx BUCKETmybucket-1250000000 REGIONap-guangzhou # 挂载路径由 Cubelet 的 --volume-base-dir 决定默认 /data/cube-shared/volume/cos-id无需在此配置。 EOF sudo chmod 600 $PREFIX/CubeMaster/plugin/volume-cos.conf # Cubelet 节点同样编辑 $PREFIX/Cubelet/plugin/volume-cos.conf配置项说明字段说明示例SECRET_ID腾讯云 API 密钥 IDAKIDxxxSECRET_KEY腾讯云 API 密钥 KeyxxxxxBUCKETCOS bucket格式BucketName-APPIDmybucket-1250000000REGIONCOS 地域ap-guangzhou挂载路径不在此文件配置——由 Cubelet 在 attach 时传入--volume-base-dir配置项volume_plugin_base_dir默认/data/cube-shared/volume插件返回的host_path必须是其下的子路径例如/data/cube-shared/volume/cos-id。安全建议建议使用仅拥有该 bucket 读写权限的子账号密钥避免使用主账号密钥。插件源码 cube-volume-cos.sh 的load_config()会在启动时校验四个字段非空缺失任一字段即报错退出。3. 配置 Cubelet编辑/usr/local/services/cubetoolbox/Cubelet/config/config.toml在[plugins.io.cubelet.internal.v1.storage]下添加[[plugins.io.cubelet.internal.v1.storage.volume_plugins]] name cos type binary binary_path /usr/local/services/cubetoolbox/Cubelet/plugin/cube-volume-cos可选在该段下确认挂载父目录volume_plugin_base_dir /data/cube-shared/volume默认值未配置时生效。4. 配置 CubeMaster编辑/usr/local/services/cubetoolbox/CubeMaster/conf.yaml添加volume_plugins: - name: cos type: binary binary_path: /usr/local/services/cubetoolbox/CubeMaster/plugin/cube-volume-cos关键语义name: cos就是 API / SDK 里的driver。CubeMaster 与 Cubelet 两侧的name必须完全一致此处均为cos且name在同一进程内必须唯一。volume_plugins列表顺序决定省略driver时的默认插件——默认安装现以s3为第一项因此用 COS 时应显式传drivercos不要依赖省略行为。5. 重启服务并确认加载systemctl restart cube-sandbox-cubemaster systemctl restart cube-sandbox-cubelet确认插件已加载grep -aF [volume] registered /data/log/CubeMaster/cubemaster-req.log | tail -5 grep -aF [plugin_volume] initialized /data/log/Cubelet/Cubelet-req.log | tail -5期望输出形如binary 示例[volume] registered binary plugin cos at /usr/local/services/cubetoolbox/CubeMaster/plugin/cube-volume-cos [plugin_volume] initialized binary plugin cos at /usr/local/services/cubetoolbox/CubeMaster/plugin/cube-volume-cos使用Python SDK 完整生命周期以下示例使用Python SDKcubesandbox≥ 0.6.0。先安装 SDK 并配置环境变量CUBE_API_URL、CUBE_TEMPLATE_ID、远程读写时还需CUBE_PROXY_NODE_IP见 框架指南 §2.1。创建 Volumefrom cubesandbox import Volume # e2b 兼容省略 driver vol Volume.create(my-vol) # 等价于CubeMaster volume_plugins 第一项为 cos 时 # vol Volume.create(my-vol, drivercos) print(vol.volume_id, vol.name, vol.token) # volume_idmy-vol namemy-vol token指定name时volume_id通常与name相同省略name则服务端生成 UUID。名称须匹配^[a-zA-Z0-9_-]$最长 128 字符。这一步会在 COS 上创建volumes/my-vol/.keep占位符。可用 coscmd 在 COS 侧核对source /usr/local/services/cubetoolbox/CubeMaster/plugin/volume-cos.conf coscmd -b $BUCKET -r $REGION list volumes/my-vol/创建沙箱并挂载 Volumefrom cubesandbox import Sandbox, Volume sb Sandbox.create( volume_mounts{/mnt/data: vol}, ) try: sb.files.write(/mnt/data/hello.txt, persisted in COS) print(sb.files.read(/mnt/data/hello.txt)) finally: sb.kill()沙箱内/mnt/data即对应 COSBUCKET:/volumes/my-vol/。验证挂载在 Cubelet mntns 里查看# Binary 插件在 Cubelet 的 mount namespace 里执行挂载 # 宿主机 /proc/mounts 看不到需要 nsenter CPID$(pgrep -f cubelet --config | head -1) nsenter -t $CPID -m -- cat /proc/mounts | grep cosfs这正是 框架调试指引 强调的行为Cubelet 通过unshare(CLONE_NEWNS)运行在独立 mount namespace 中binary 插件由 Cubelet fork、自动继承 mntns但宿主机根 mntns 的/proc/mounts看不到这些 FUSE 挂载必须 nsenter 进入 Cubelet 的 mntns 验证。销毁沙箱解绑sb.kill() # 或 with Sandbox.create(...) as sb: 退出时自动销毁沙箱销毁后当该 volume 的最后一个引用被卸载时cosfs 进程自动退出、挂载点自动卸载。COS 上的数据不会被删除——数据生命周期独立于沙箱只受 detach卸载挂载与 destroy删除后端数据两重控制。删除 VolumeVolume.destroy(vol.volume_id)这一步会调用coscmd delete -r -f volumes/my-vol/递归删除 COS 上该 volume 的所有数据操作不可逆。删除前需销毁所有挂载该 volume 的沙箱平台侧t_cube_volume.refcount非零时管理面会拒绝删除HTTP 409详见 框架指南 · RefCount。插件代码详解四个 Hook 逐步拆解插件本体是一个 Shell 脚本 cube-volume-cos.sh同时实现了 CubeMaster 侧create/destroy和 Cubelet 侧attach/detach的操作。调用约定与 Cubelet binary driver 完全对应Cubelet/plugins/volume/binary/driver.go中的Attach/Detach每次操作都会 fork 子进程并拼装--op op [--key value ...]参数然后解析 stdout 的单行 JSONhost_path/metadata/error字段进程退出码非 0 或error非空即视为失败。参数解析CubeMaster / Cubelet 每次调用插件时都会传入--op和若干参数脚本解析后跳转到对应函数while [[ $# -gt 0 ]]; do case $1 in --op) OP$2; shift 2 ;; # which hook: create|destroy|attach|detach --volume-id) VOLUME_ID$2; shift 2 ;; # volume identifier --sandbox-id) SANDBOX_ID$2; shift 2 ;; # sandbox using the volume (attach/detach) --ref-count) REF_COUNT$2; shift 2 ;; # how many sandboxes still use this volume --metadata) METADATA$2; shift 2 ;; # JSON saved at attach time (for detach) # ... esac done脚本set -euo pipefail严格失败log()输出到 stderr最终由 Cubelet 转发到 journaldok_json()/err_json()构造 stdout 结果 JSON。create 核心逻辑管控面用户创建 Volume 时在 COS 桶里为该 volume 建一个空目录不涉及节点挂载do_create() { local volume_id$1 name$2 load_config # Step 1: read SECRET_ID/KEY, BUCKET, REGION cos_create_dir $volume_id # Step 2: upload volumes/id/.keep via coscmd jq -cn --arg pd volumes/${volume_id}/ \ { token: , private_data: $pd, error: } # Step 3: private_data 会转给 Attach }其中cos_create_dir用mktemp建一个临时空文件再通过coscmd upload上传为volumes/id/.keep。值得注意的错误处理细节cube-volume-cos.shcoscmd 有时会以退出码 0 却打印 XML/JSON 错误体典型如密钥错误时的InvalidAccessKeyId脚本用coscmd_output_indicates_failure()正则匹配Error|InvalidAccessKeyId|AccessDenied|...将这类软失败视为失败返回非零退出码。该行为有专门的测试脚本 test_create_error.sh 用伪造的坏密钥 coscmd 验证create必须以非零退出码返回且 stdout 含非空error字段。private_data是 Create → Attach 的插件私有状态框架限制最长 1024 字节写入t_cube_volume不对 API/SDK 暴露。本示例用它携带 COS key 前缀volumes/id/供 Attach 记录与复用。destroy 核心逻辑管控面用户删除 Volume 时从 COS 桶里递归删除该 volume 的目录数据不可恢复do_destroy() { local volume_id$1 load_config # Step 1: read COS credentials cos_remove_dir $volume_id # Step 2: coscmd delete -r volumes/id/ ok_json # Step 3: return success }cos_remove_dir调用coscmd delete -r -f volumes/id/。它只忽略明确的 NotFoundnot found|NoSuchKey|does not exist|404|No such file其他失败必须向上传播——这是刻意设计如果 coscmd 删除失败而插件返回成功CubeMaster 会删除 DB 记录而 COS 上对象残留造成数据泄漏。attach 核心逻辑数据面沙箱挂载 Volume 时在节点上用 cosfs 把 COS 目录挂到本地路径并把host_path还给 Cubelet由 Cubelet bind-mount 进沙箱do_attach() { local sandbox_id$1 volume_id$2 ref_count$3 # VOLUME_BASE_DIR comes from --volume-base-dir (default /data/cube-shared/volume) load_config # Step 1: read COS credentials ensure_passwd_file # Step 2: write /etc/cube/.passwd-cosfs for cosfs volume_lock_acquire $volume_id # Step 3: flock — one attach at a time per volume trap volume_lock_release EXIT cosfs_mount_volume $volume_id # Step 4: mount BUCKET:/volumes/id (skip if already up) local mnt$(volume_mountpoint $volume_id) # e.g. /data/cube-shared/volume/cos-my-vol # Step 5: return host_path; Cubelet bind-mounts it into the sandbox jq -cn --arg path $mnt --arg vid $volume_id \ { host_path: $path, metadata: { mount_dir: $path, volume_id: $vid }, error: } }attach 的幂等性cosfs_mount_volume内部先mountpoint -q检查已挂载则直接返回不重复挂载。cosfs实际挂载参数为-ourlhttps://cos.${REGION}.myqcloud.com -opasswd_file${PASSWD_FILE} -oallow_other -ononempty -odbglevelinfo -onoxattr且挂载后再次mountpoint -q校验真实挂载点——cosfs 可能退出码 0 但鉴权失败仅凭退出码不可靠。挂载失败时rmdir清理挂载点目录。并发保护volume_lock_acquire对/run/cube-volume-cos/volume_id.lock执行flock打开 fd 200保证同一 volume 的并发 attach / detach 串行化避免双重挂载或卸载竞态。RefCount 语义ref_count 0表示本 node 已有沙箱挂载该 volume此时返回同一个host_path供多沙箱共享不重复挂载。这与框架 RefCount 定义 完全一致。detach 核心逻辑数据面沙箱卸载 Volume 时只有当本节点上没有沙箱再使用该 volumeref_count 降为 0时才真正卸载 cosfsCOS 上的数据仍保留需 destroy 才删do_detach() { local sandbox_id$1 volume_id$2 ref_count$3 if [[ $ref_count -gt 0 ]]; then # Step 1: others still mounted — do nothing ok_json; return fi volume_lock_acquire $volume_id # Step 2: flock before unmount trap volume_lock_release EXIT # Step 3: mount path from attach metadata, or recompute from volume_id local mnt$(printf %s $metadata_json | jq -r .mount_dir // empty) [[ -n $mnt ]] || mnt$(volume_mountpoint $volume_id) cosfs_unmount_volume $mnt # Step 4: fusermount — last user gone ok_json }cosfs_unmount_volume先mountpoint -q判断已挂载则fusermount -u失败时回退umount -l随后rmdir移除挂载点目录目录非空时仅告警。detach 只拆除宿主机侧挂载、绝不删除后端持久数据这与框架Node Detach 不删数据的开发要点一致。passwd 文件格式cosfs 的 passwd 文件格式为BucketName-APPID:SecretId:SecretKey必须包含 bucket 前缀权限必须是 600ensure_passwd_file() { local content${BUCKET}:${SECRET_ID}:${SECRET_KEY} # cosfs credential line printf %s\n $content /etc/cube/.passwd-cosfs chmod 600 /etc/cube/.passwd-cosfs # cosfs refuses world-readable files }脚本做了增量写入优化仅当文件内容与当前凭证不一致时才重写并chmod 600cosfs 拒绝读取权限过宽的口令文件。若变更了凭证而挂载仍失败通常需要卸载后重新 attach 以刷新口令文件。验证与调试手动触发 Hook手动测试 attach在 Cubelet 节点、Cubelet mntns 外执行即可脚本会写 cosfs passwd/usr/local/services/cubetoolbox/Cubelet/plugin/cube-volume-cos \ --op attach \ --sandbox-id test-sandbox-001 \ --namespace default \ --volume-id my-vol \ --ref-count 0 \ --volume-base-dir /data/cube-shared/volume # → {host_path:/data/cube-shared/volume/cos-my-vol,metadata:{...},error:} # 验证挂载在 Cubelet mntns 里 CPID$(pgrep -f cubelet --config | head -1) nsenter -t $CPID -m -- mountpoint /data/cube-shared/volume/cos-my-vol # → /data/cube-shared/volume/cos-my-vol is a mountpoint手动测试 detach/usr/local/services/cubetoolbox/Cubelet/plugin/cube-volume-cos \ --op detach \ --sandbox-id test-sandbox-001 \ --namespace default \ --volume-id my-vol \ --ref-count 0 \ --metadata {mount_dir:/data/cube-shared/volume/cos-my-vol} # → {error:}手动调用时注意ref-count的语义方向attach 传入的是挂载前计数detach 传入的是卸载后计数。模拟最后一个沙箱卸载时应传0。自动化验证Python仓库提供了覆盖 HTTP 契约、逐 driver 生命周期、多沙箱共用与负向场景的验证脚本 verify_volume.pycd examples/volume/cos pip install cubesandbox0.6.0 requests export CUBE_API_URLhttp://127.0.0.1:3000 export CUBE_TEMPLATE_IDtpl-xxxx export CUBE_PROXY_NODE_IP127.0.0.1 export CUBE_VOLUME_DRIVERScos # 或 cos-rpc python3 verify_volume.py脚本会输出分组报告通过 / 失败 / 跳过任一断言失败时退出码非 0。关键环境变量变量必填说明CUBE_API_URL是CubeAPI 地址CUBE_TEMPLATE_ID是沙箱模板 IDCUBE_PROXY_NODE_IP建议数据面访问在 CubeProxy 机器上可用127.0.0.1CUBE_VOLUME_DRIVERS否逗号分隔 driver默认cosrpc 插件用cos-rpcCUBE_VOLUME_MOUNT_PATH否沙箱内挂载路径默认/workspace查看插件日志插件把所有日志写到 stderrCubelet 会将其转发到 journaldjournalctl -u cube-sandbox-cubelet --since 5 min ago | grep cube-volume-cos常见问题cosfs 挂载后宿主机/proc/mounts看不到正常现象。cosfs 由 Cubelet fork在 Cubelet 的 mount namespace 里挂载宿主机看不到。需要 nsenter 进入 Cubelet 的 mntns 查看CPID$(pgrep -f cubelet --config | head -1) nsenter -t $CPID -m -- cat /proc/mounts | grep cosfslibcrypto.so.1.1: cannot open shared object fileTencentOS 4.x 默认使用 OpenSSL 3官方 cosfs RPM 链接的是旧版 OpenSSL soname需要安装对应的 compat 包yum install -y compat-openssl11更通用的规则见 install-deps.shcentos7 的 cosfs RPM 依赖libcrypto.so.10→compat-openssl10centos8 的 RPM 依赖libcrypto.so.1.1→compat-openssl11。脚本在安装与--check-only阶段都会检测该 soname并在检测失败时尝试自动修复安装对应 compat 包。coscmd: command not found需要安装 coscmd 并创建 wrapperpython3 -m venv /opt/coscmd-venv /opt/coscmd-venv/bin/pip install coscmd ln -sf /opt/coscmd-venv/bin/coscmd /usr/local/bin/coscmd与 install-deps.sh 中install_coscmd的做法一致venv 安装后生成/usr/local/bin/coscmdwrapper 脚本。Duplicate entry vol-xxx for key uniq_volume_id创建同名 volume 报错说明该volumeID对应的 Volume 仍存在未软删除。请先Volume.destroy(id)再重建。其他常见排障对照来自 COS 体验指南常见问题现象排查unknown driver: cosCubeMastervolume_plugins未配或未重启no plugin registered for driver cosCubelet 未配同名插件或未重启沙箱创建失败 / attach 报错Cubelet 日志搜[plugin_volume]、cosfs确认 cosfs、FUSE、volume-cos.confSDK 写文件失败是否设置CUBE_PROXY_NODE_IPCubeAPI / 模板是否 READYVolume.create无 driver 但不是 cos默认 driver 现为s3volume_plugins第一项用 COS 请显式传drivercos延伸阅读与源码索引本示例父文档前置条件、依赖安装、SDK 环境、rpc 路径examples/volume/cos/README.zh.md插件脚本源码examples/volume/cos/binary/cube-volume-cos.sh插件配置文件模板examples/volume/cos/volume-cos.conf.examplecreate 软失败测试examples/volume/cos/binary/test_create_error.sh依赖一键安装脚本examples/volume/cos/install-deps.sh自动化验证脚本examples/volume/cos/verify_volume.pyBinary 插件驱动Hook → CLI 映射Cubelet/plugins/volume/binary/driver.goVolume 插件框架协议、Hook 字段、RefCount、排障docs/zh/guide/volume-plugin.mdGo rpc 类型示例长驻 gRPC 进程 COS Go SDKexamples/volume/cos/rpc/【免费下载链接】CubeSandboxInstant, Concurrent, Secure Lightweight Sandbox for AI Agents.项目地址: https://gitcode.com/GitHub_Trending/cu/CubeSandbox创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表