免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

OpenSandbox 实战:在 Kubernetes 中挂载 PVC 持久化卷,让 AI Agent 沙箱数据跨生命周期留存

OpenSandbox 实战:在 Kubernetes 中挂载 PVC 持久化卷,让 AI Agent 沙箱数据跨生命周期留存 OpenSandbox 实战在 Kubernetes 中挂载 PVC 持久化卷让 AI Agent 沙箱数据跨生命周期留存【免费下载链接】OpenSandboxSecure, Fast, and Extensible Sandbox runtime for AI agents.项目地址: https://gitcode.com/GitHub_Trending/ope/OpenSandbox导读本指南以 OpenSandbox 仓库中的 kubernetes-pvc-volume-mount 示例 与配套文档 docs/examples/kubernetes-pvc-volume-mount.md 为主体系统讲解如何把 Kubernetes PersistentVolumeClaimPVC挂载进 OpenSandbox 沙箱使写入的数据在沙箱销毁后依然存在供后续沙箱复用。你将掌握「自带 PVCBring Your Own」「服务端托管持久化」「服务端托管临时存储」三种模式的选型与配置、Pool 模式下共享 PVC 的预挂载方式以及 PVC 生命周期清理的底层机制并能在自己的 Kubernetes 集群中直接跑通完整示例。背景为什么沙箱需要 PVC 卷挂载AI Agent 的沙箱默认是瞬态的——沙箱进程结束后文件系统随之消失。对于需要长期积累产物的场景网页快照、报告、模型权重缓存、共享数据集OpenSandbox 提供了运行时无关的卷模型在 OSEP-0003: Volume and VolumeBinding Support 中定义了volumes[]字段每个卷条目包含一个强类型后端结构host、ossfs、pvc、nfs以及公共挂载属性name、mountPath、readOnly、subPath。其中pvc后端是运行时无关的抽象在 Kubernetes 上映射为 PersistentVolumeClaim在 Docker 上映射为 named volume。也就是说同一个 SDK 请求可以在不同运行时上表达一致的「挂载持久化命名卷」语义。volumes: - name: shared-data pvc: claimName: my-shared-volume mountPath: /mnt/data从源码看Python SDK 在 sdks/sandbox/python/src/opensandbox/models/sandboxes.py 中定义了对应的PVC与Volume模型。PVC模型的核心字段如下字段SDK字段API默认值说明claim_nameclaimName必填Kubernetes 中的 PVC 名称或 Docker named volume 名称create_if_not_existscreateIfNotExistsTrue为true时卷不存在则自动创建delete_on_sandbox_terminationdeleteOnSandboxTerminationFalse为true时沙箱删除时移除自动创建的卷预存在的卷永远不会被移除storage_classstorageClassNone自动创建 PVC 使用的 StorageClassNone表示集群默认Docker 运行时忽略storagestorageNone自动创建 PVC 的容量请求如1GiDocker 运行时忽略access_modesaccessModesNone自动创建 PVC 的访问模式如[ReadWriteOnce]Docker 运行时忽略而Volume模型通过model_validator强制校验每个卷条目必须且只能指定一个后端host/pvc/ossfsmountPath必须是绝对路径——这些约束在客户端即可提前拦截非法请求。两种模式的选择PVC 生命周期归谁所有OpenSandbox 支持两种 PVC 供应模式区别在于「谁拥有 PVC 的生命周期」模式createIfNotExistsdeleteOnSandboxTerminationPVC 归属适用场景自带 PVCBYOfalse忽略你在集群中预先创建长生命周期共享存储模型缓存多个沙箱复用的基线数据集服务端托管·持久truefalse默认你首次创建后归你首次使用时按需创建跨沙箱生命周期保留服务端托管·临时truetrue服务端仅限单个沙箱的临时存储沙箱终止时自动清理两种模式在挂载方式上完全一致区别只在供应与清理环节。选择的关键问题是PVC 的生命周期应该由你的平台管理还是由沙箱自己管理前置条件CSI 驱动Kubernetes 的 PVC 需要 Container Storage Interface (CSI) 驱动来供应和挂载存储。选择一个与你的存储后端匹配的驱动例如 Alibaba Cloud CSI Driver 支持Cloud DiskEBS——块存储高性能单节点读写NAS——共享文件存储多节点读写ReadWriteManyOSS——对象存储大规模共享读CPFS——高性能并行文件系统LVM——本地卷管理OpenSandbox Server服务端必须运行在 Kubernetes runtime 上并使用 BatchSandbox 工作负载提供者。官方 Helm chart 已授予 BYO 挂载与服务端自动供应所需的 RBAC 权限对persistentvolumeclaims的get/create/list/delete/patch。这一点在服务端源码中有印证_ensure_pvc_volumes会在挂载前做 PVC 所有权预检如果服务账号缺少get权限403会fail-closed拒绝挂载而不是降级继续——因为无法确认所有权时挂载一个已被其他沙箱托管的 PVC 会让当前沙箱暴露在被清理的风险中见 server/opensandbox_server/services/k8s/kubernetes_service.py。Python SDKuv pip install opensandbox模式一自带 PVCBring Your Own适用于 PVC 属于你平台基础设施的一部分的场景——例如多个沙箱复用的共享 NAS或预置了模型权重的磁盘。1. 创建 PVC# pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: my-pvc namespace: opensandbox spec: accessModes: - ReadWriteOnce storageClassName: your-storage-class resources: requests: storage: 10Gikubectl apply -f pvc.yaml kubectl get pvc my-pvc -n opensandbox # 应显示 Bound2. 在沙箱中挂载它from opensandbox import Sandbox from opensandbox.models.sandboxes import PVC, Volume sandbox await Sandbox.create( imagepython:3.11, volumes[ Volume( namedata-volume, pvcPVC( claimNamemy-pvc, createIfNotExistsFalse, # 绝不自动供应 BYO claim ), mountPath/mnt/data, readOnlyFalse, ), ], ) result await sandbox.commands.run(ls -la /mnt/data) print(\n.join(msg.text for msg in result.logs.stdout))OpenSandbox 永远不会删除这个 PVC沙箱终止后 claim 保持 Bound数据对下一个挂载它的沙箱依然可用。3. 运行端到端示例仓库提供了完整的可运行脚本 examples/kubernetes-pvc-volume-mount/main.py它依次完成创建沙箱 → 向/mnt/data写入标记文件 → 杀掉沙箱 → 创建第二个绑定同一 PVC 的沙箱 → 确认标记文件仍在。export OPEN_SANDBOX_API_KEYyour-api-key export OPEN_SANDBOX_BASE_URLhttp://localhost:8080 export SANDBOX_PVC_NAMEmy-pvc python examples/kubernetes-pvc-volume-mount/main.py脚本中的关键参数均可用环境变量覆盖SANDBOX_PVC_NAME默认my-pvc、SANDBOX_IMAGE默认python:3.11。同时脚本为ConnectionConfig设置了request_timeouttimedelta(minutes10)因为创建带卷的沙箱需要更长等待时间。执行成功后的输出如下图中展示了完整的两步验证流程第一个沙箱写入Hello from OpenSandbox!后被 kill第二个沙箱挂载同一 PVC 读取到相同内容最终输出Data persistence verified!。Pool 模式预挂载共享 PVCPool 的 Pod 在沙箱分配之前就已创建因此 Pool 共享的存储必须内置于 Pool Pod 模板中。先创建 PVC再通过Pool.spec.template把它挂载到每个预热 PodapiVersion: v1 kind: PersistentVolumeClaim metadata: name: shared-workspace-pvc namespace: opensandbox spec: accessModes: [ReadWriteMany] storageClassName: your-rwx-storage-class resources: requests: storage: 100Gi --- apiVersion: sandbox.opensandbox.io/v1alpha1 kind: Pool metadata: name: shared-workspace-pool namespace: opensandbox spec: template: spec: containers: - name: sandbox-container image: python:3.11 command: [sleep, 3600] volumeMounts: - name: shared-workspace mountPath: /workspace volumes: - name: shared-workspace persistentVolumeClaim: claimName: shared-workspace-pvc capacitySpec: bufferMax: 10 bufferMin: 2 poolMax: 20 poolMin: 5先向集群应用 Pool 清单再从中分配沙箱。沙箱通过extensions.poolRef选择预配置的 Pool且不再传递每沙箱的volumes列表。::: warning Pool 静态存储限制 PVC 必须已存在且其访问模式与存储后端必须允许所有被调度的预热 Pod 挂载它。OpenSandbox 不会创建、变更或删除 Pool 模板引用的 PVC。沙箱创建请求不能在同时使用extensions.poolRef时再添加volumes因为所选预热 Pod 已存在。 :::模式二服务端托管 PVC适用于沙箱应该自己拥有存储的场景。服务端在 claim 名称第一次被引用时按需创建 PVC是否在沙箱终止后保留由deleteOnSandboxTermination控制。按需供应并持久保留默认这是经典的「首个沙箱供应、后续沙箱复用」模式——例如一个崩溃、重启后依然存活的预热缓存但永远不需要手动kubectl apply。sandbox await Sandbox.create( imagepython:3.11, volumes[ Volume( namecache, pvcPVC( claimNameagent-cache, createIfNotExistsTrue, # 首次使用时自动供应 deleteOnSandboxTerminationFalse, # 默认保留 PVC storageClassalibaba-cloud-disk-ssd, # 可选默认使用集群默认 storage20Gi, # 可选默认取服务端配置 accessModes[ReadWriteOnce], # 可选默认 ReadWriteOnce ), mountPath/mnt/cache, ), ], )第一次调用供应agent-cache后续使用相同claimName的调用会挂载已有 PVC 并跳过供应。PVC 会一直保留直到你用kubectl删除它。服务端供应时若未显式指定storage会回退到服务端配置的storage.volume_default_size见 kubernetes_service.py 中default_size self.app_config.storage.volume_default_size的实现。按需供应并自动清理仅限单个沙箱的临时存储——沙箱终止包括 TTL 到期时服务端回收 PVC。sandbox await Sandbox.create( imagepython:3.11, timeout600, # 10 分钟沙箱 volumes[ Volume( namescratch, pvcPVC( claimNamefscratch-{run_id}, # 每次运行唯一opt-in 的 PVC 被独占 createIfNotExistsTrue, deleteOnSandboxTerminationTrue, storage5Gi, ), mountPath/mnt/scratch, ), ], )::: tip 清理范围 服务端只会删除它按此 opt-in 供应的 PVC。预存在的 PVC 和以deleteOnSandboxTerminationfalse供应的 PVC 永远不会被触碰。Opt-in 的 PVC 由创建它的沙箱独占——第二个沙箱尝试挂载相同claimName会被以409 CONFLICT拒绝。请为每个沙箱使用唯一的claimName如果需要共享存储请使用非 opt-in 或预存在的 PVC。 :::PVC 生命周期服务端如何做到精确清理清理行为由创建时的模式决定来源沙箱终止时的清理预存在 PVCBYO服务端永不触碰自动创建deleteOnSandboxTerminationfalse默认PVC 保留由调用方负责清理自动创建deleteOnSandboxTerminationtrue服务端删除 PVC对于 opt-in 的 PVC服务端会打上opensandbox.io/volume-managed-byserver和opensandbox.io/idsandbox-id两个标签然后通过两条分层路径执行清理源码实现见 kubernetes_service.pyownerReferences主路径——PVC 创建后立即被 patch 指向沙箱的工作负载自定义资源。一旦该 CR 被删除包括由控制器驱动的 TTL 到期这种路径根本不会到达DELETE /sandboxes/{id}APIKubernetes 垃圾回收就会级联删除 PVC。标签选择器清扫兜底——在DELETE /sandboxes/{id}时服务端按上述标签列出 PVC 并尽力删除。这能覆盖ownerReferencespatch 失败例如 RBAC 问题的情况确保 API 返回时 PVC 已消失。两条路径都只匹配服务端打标的 PVC因此 BYO 和 opt-out 的 claim 永远不会被回收。PVC 删除后底层 PV 遵循其StorageClass.reclaimPolicy决定去留。值得一提的是服务端在创建沙箱前会做所有权预检即使createIfNotExistsfalse的请求指向了一个已被其他沙箱打标托管的 PVC也会被_reject_pvc_owned_by_other_sandbox拦截避免该沙箱的存储被原 owner 的清理逻辑误删。重要注意事项::: warning每沙箱的volumes不能与extensions.poolRef组合使用。Pool 模式下请按上文所述在Pool.spec.template中预挂载已有共享 PVC。多个沙箱可以挂载同一 PVC如果访问模式允许如ReadWriteMany——但仅当该 PVC未opt-in 自动清理时。以deleteOnSandboxTerminationtrue创建的 PVC 由创建沙箱独占其他沙箱挂载会被服务端以409 CONFLICT拒绝。同一请求中对相同claimName的所有挂载createIfNotExists和deleteOnSandboxTermination必须一致不一致会被以400 INVALID_PARAMETER拒绝。服务端在_ensure_pvc_volumes中按 claim 聚合校验这些标志一旦发现冲突在任何副作用发生前直接返回 400见 kubernetes_service.py。 :::继续深入卷模型的完整设计含host、ossfs、nfs等后端、跨运行时映射与安全约束oseps/0003-volume-and-volumebinding-support.md可运行的示例脚本examples/kubernetes-pvc-volume-mount/main.pyPython SDK 卷模型源码sdks/sandbox/python/src/opensandbox/models/sandboxes.py服务端 Kubernetes PVC 供应与清理实现server/opensandbox_server/services/k8s/kubernetes_service.py服务端测试覆盖PVC 挂载、生命周期与冲突拒绝逻辑见 server/tests/test_routes_snapshots.py 同目录的 k8s 相关测试更完整的示例说明文档docs/examples/kubernetes-pvc-volume-mount.md【免费下载链接】OpenSandboxSecure, Fast, and Extensible Sandbox runtime for AI agents.项目地址: https://gitcode.com/GitHub_Trending/ope/OpenSandbox创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表