ARTICLE DETAIL

资讯详情

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

OpenSandbox Isolation Session 实战:用 bubblewrap 隔离会话在单个沙箱内并行跑多个互不干扰的任务

OpenSandbox Isolation Session 实战:用 bubblewrap 隔离会话在单个沙箱内并行跑多个互不干扰的任务 OpenSandbox Isolation Session 实战用 bubblewrap 隔离会话在单个沙箱内并行跑多个互不干扰的任务【免费下载链接】OpenSandboxSecure, Fast, and Extensible Sandbox runtime for AI agents.项目地址: https://gitcode.com/GitHub_Trending/ope/OpenSandbox在跑批量任务时常见需求是不想为每个任务都新起一个容器但又不能让任务之间互相污染进程、环境变量和文件系统。OpenSandbox 的 Isolation Session 解决这个问题——它让沙箱内的execd为每个会话 fork 一个 bubblewrapbwrap子进程bwrap建好 Linux namespace 后在其中exec一个长驻bash每个会话拥有独立的 PID、mount、tmpfs 和 env namespace。一个沙箱 pod 因此可以承载多个互不隔离的短任务会话创建约 100 ms之后同一会话内的每次run开销接近于零。适用环境来自文档的组件版本要求execd 1.0.20推荐 1.0.21binds、List sessions、uid_mode: userns与默认可写白名单需要 1.0.21opensandbox-server 0.2.1镜像声明bootstrap.execd.isolation时server 会注入CAP_SYS_ADMIN、apparmorunconfined和bwrap所需的 tmpfs mountPython SDK 0.1.14isolation.run_once/isolation.sessionJavaScript/TypeScript SDK 0.1.10Kotlin 1.0.16C# 0.1.4Go 1.0.4主机要求execd 镜像内自带bwrap二进制与受信任的 native workload gate、CAP_SYS_ADMIN、内核支持overlayfsoverlay工作区模式需要。Linux 源码构建 execd 时需先make build-session-gate再sudo make install-session-gate把辅助程序装到/opt/opensandbox/opensandbox-session-gate且该路径必须 root-owned、不允许组/全局可写缺少它时隔离能力探测 fail-closed适合RL rollouts、批量代码判分、多工具 agent 运行——一个 worker 一个沙箱沙箱内跑大量互相隔离的任务。不适合跨语言内核用/code、交互式 REPL用/session、对抗内核漏洞的硬信任边界用 gVisor/Kata见 Secure Container Runtime。先探测确认当前沙箱支持隔离会话所有接口挂在 execd 的/v1/isolated/下execd 默认监听 44772 端口。如果 execd 配置了 access token请求要带X-EXECD-ACCESS-TOKEN头。curl -s http://localhost:44772/v1/isolated/capabilities文档示例输出{ available: true, isolator: bwrap, version: 0.9.0, setpriv_available: true, userns_available: false, commit_supported: false, diff_supported: false }判断方法available: true说明可以创建隔离会话。available: false对应三种原因native workload gate 缺失或不受信任、bwrap缺失、主机无法创建所需 namespace缺CAP_SYS_ADMIN、user-ns sysctl 受限等。setpriv_available/userns_available分别表示uid_mode: setpriv默认真实 setuid/setgid 降权和uid_mode: usernsuser namespace 重映射能否建会话。这两个字段是 execd v1.0.21 之后才有的旧版 execd 不返回客户端要容忍缺失。commit_supported/diff_supported目前是 Phase 2 占位当前返回503。注意缺少overlayfs不会翻转available但workspace.mode: overlay的会话创建仍可能在运行时失败依赖 overlay 模式的主机需自行验证overlayfs支持。创建会话并执行第一个任务最短主路径curlSSE 流返回stdout/error/complete事件# 创建会话strict profile overlay 工作区 SESSION$(curl -s -X POST http://localhost:44772/v1/isolated/session \ -H Content-Type: application/json \ -d { profile: strict, workspace: {path: /workspace, mode: overlay}, idle_timeout_seconds: 300 } | jq -r .session_id) # 第一次 run curl -N -X POST http://localhost:44772/v1/isolated/session/$SESSION/run \ -H Content-Type: application/json \ -d {code: export X1; echo $X, timeout_seconds: 30} # 第二次 run 复用 shell 状态会打印 1 curl -N -X POST http://localhost:44772/v1/isolated/session/$SESSION/run \ -H Content-Type: application/json \ -d {code: echo $X} # 用完后销毁 curl -X DELETE http://localhost:44772/v1/isolated/session/$SESSIONbash是长驻的所以同一会话内一次run里export X1对下一次run可见——但只在同一会话内可见另一个会话看不到。这就是互不干扰的核心环境变量、进程和/tmpstrictprofile 下是私有 tmpfs都按会话隔离。用 Python SDK 的话同一任务可以写成from opensandbox import Sandbox from opensandbox.models.isolated import ( CreateIsolatedSessionRequest, IsolatedWorkspaceSpec, IsolatedRunOpts, ) async with (await Sandbox.create(python:3.11)) as sandbox: # 一次性执行 await sandbox.isolation.run_once( python -c print(42), workspace/workspace, profilestrict, ) # 长驻会话 async with sandbox.isolation.session( CreateIsolatedSessionRequest( workspaceIsolatedWorkspaceSpec(path/workspace, modeoverlay), profilestrict, idle_timeout_seconds300, ) ) as session: await session.run(export STAGEtrain) await session.run(python train.py, optsIsolatedRunOpts(timeout_seconds600))客户端重启后可以用sandbox.isolation.attach(known_session_id)重新挂回已知会话。并行跑多个互不干扰的任务文档明确给出并行模型的边界同一会话内的run是串行的要做并行就创建多个会话。每个会话各自持有独立的 namespace 和 bash 进程A 会话里 export 的变量、写/tmp的文件B 会话都不可见。# 两个相互隔离的会话 S1$(curl -s -X POST http://localhost:44772/v1/isolated/session \ -H Content-Type: application/json \ -d {profile:strict,workspace:{path:/workspace,mode:overlay},idle_timeout_seconds:300} \ | jq -r .session_id) S2$(curl -s -X POST http://localhost:44772/v1/isolated/session \ -H Content-Type: application/json \ -d {profile:strict,workspace:{path:/workspace,mode:overlay},idle_timeout_seconds:300} \ | jq -r .session_id) # 两个 run 同时在两个会话里执行 curl -N -X POST http://localhost:44772/v1/isolated/session/$S1/run \ -H Content-Type: application/json \ -d {code:export TASKa; sleep 3; echo task-a in $TASK} curl -N -X POST http://localhost:44772/v1/isolated/session/$S2/run \ -H Content-Type: application/json \ -d {code:export TASKb; sleep 3; echo task-b in $TASK} wait # 验证互不干扰S1 看不到 S2 的 TASK 值 curl -N -X POST http://localhost:44772/v1/isolated/session/$S1/run \ -H Content-Type: application/json \ -d {code:echo TASK$TASK} # 输出 a若输出 b 说明隔离失败创建多个会话时的成本结构要心里有数创建会话约 100 msexecd 在bwrap启动后短暂等待以检测立即退出的子进程之后同一会话内每次POST /run复用同一个 bash开销可忽略。所以批量任务的设计原则是把创建成本摊薄到一个会话内的多次run上而不是每跑一条命令就建删会话。选择工作区模式任务之间文件系统怎么隔离workspace.mode决定会话看到的/workspace语义与 profile 相互独立省略时 execd 会把mode归一化为overlay模式语义rw读写 bind-mount写入持久化到宿主机overlay默认overlayfs copy-on-write写入落在每会话独立的 upper 目录DELETE会话后消失ro只读 bind-mount写入报EROFS并行任务的默认选择就是overlay各会话写同一path互不影响会话销毁后写入自动消失也避免了一个池化沙箱的占用者把数据泄漏给下一个占用者。只有任务间需要交换文件时才用rw写入落宿主机或额外 bind。额外暴露路径用两个字段source 都会先做 symlink 解析再对照allowed_writable白名单默认/workspace、/mnt、/media、/data含子路径空白名单拒绝一切额外 bind{ workspace: { path: /workspace, mode: rw }, extra_writable: [/data/scratch], binds: [ { source: /data/in, dest: /mnt/in, readonly: true }, { source: /data/out, dest: /mnt/out } ] }binds的dest必须已存在于沙箱镜像内bwrap无法在只读根下新建挂载点需要时打进镜像。长任务同一会话内的后台 run如果希望某个任务跑着的同时还能在同一会话上继续其他工作用 background run文档中作为同一会话内的补充手段# 启动后台 run202 返回 {session_id, run_id, started_at} RUN$(curl -s -X POST http://localhost:44772/v1/isolated/session/$SESSION/run \ -H Content-Type: application/json \ -d {code: sleep 5 echo done, background: true} | jq -r .run_id) # 轮询状态直到 runningfalse curl -s http://localhost:44772/v1/isolated/session/$SESSION/runs/$RUN # 读输出纯文本EXECD-ISOLATED-TAIL-CURSOR 响应头给出增量读取的下一字节偏移 curl -s http://localhost:44772/v1/isolated/session/$SESSION/runs/$RUN/logs?cursor123后台 run 的边界timeout_seconds只对前台 run 生效后台 run 不限时后台 run 活跃期间该会话的 idle GC 挂起ro工作区会因无可写日志位置而拒绝后台 run400rw/overlay可以每次logs最多返回 16 MiB单 run 日志保留上限也是 16 MiB超出部分在 run 结束时丢弃需要更多时要增量拉取。验证隔离是否按预期工作结合上面的步骤核对点是能力探测/v1/isolated/capabilities返回available: true、isolator: bwrap。会话状态隔离在会话 A 里export的变量会话 B 里查不到SSE 输出里应为空或旧值同一会话内的第二次run能看到第一次的export结果。工作区隔离两个overlay会话对/workspace各写一个文件互相ls不应看到对方的写入DELETE任一会话后其 overlay 写入消失。会话清单GET /v1/isolated/sessions列出当前活跃会话GET /v1/isolated/session/{id}返回完整状态并回显创建参数无状态客户端可据此重建句柄。销毁DELETE /v1/isolated/session/{id}后对已销毁会话的run会返回IsolatedErrorsession process has exited或ErrContextNotFound。失败处理与限制非正常路径的行为文档有明确定义处理时可以照表判断现象行为处理前台run的timeout_seconds到期execd 取消 run context向 bwrap 进程组发SIGINT发IsolatedErrorSSE 事件会话本身存活换更短的命令分段或调大超时后重跑客户端在 SSE 中途断开请求 context 取消execd 对运行中命令发SIGINTrun不会转入后台继续需要跑完就改用background: truebwrap进程退出下一次run返回IsolatedErrorsession process has exitedDELETE后重建会话idle 超时到达GC 执行与DELETE相同的拆除把idle_timeout_seconds设为0可禁用 idle GC改为显式DELETE限制项直接决定这套方案能用到哪里diff/commit是 Phase 2 占位当前返回503见 OSEP-0013。没有硬件级保证只有 namespace seccomp需要对抗内核漏洞时配合 gVisor/Kata 安全运行时。仅支持 Linux非 Linux 构建返回available: false。同一会话内 run 串行并行必须多会话。share_net省略时默认共享沙箱网络 namespace沙箱级的 egress 与 Credential Vault 策略仍然生效。私有网络会话share_net: false无法跨越 execd 重启恢复启用了它的部署要把 execd 视为沙箱关键进程execd 退出时整个沙箱需要重建。会话状态只存在于 execd 内存中execd 重启后所有会话状态不保留upper_root下残留的 overlay upper 目录会在启动时被回收。服务端配置可选隔离行为由 execd 读取的可选 TOML 控制通过--isolation-config或环境变量EXECD_ISOLATION_CONFIG指定完整示例见 isolation.example.toml# 每会话 overlay upper 目录的父目录 upper_root /var/lib/execd/isolation # 所有会话 upper 目录的总大小硬上限字节默认 8 GiB0 表示禁用配额 upper_max_bytes 8589934592 # extra_writable / binds 允许的 source 前缀symlink 解析后检查 # 默认 [/workspace, /mnt, /media, /data]置空则拒绝所有额外 bind allowed_writable [/workspace, /mnt, /media, /data]多会话并行写放大时upper_max_bytes是每个沙箱内全部会话共享的配额会话可能因超配额而创建失败需要按并发任务数和工作集大小调整。进一步阅读execd 组件文档、OSEP-0013 Isolated Execution API、Secure Container Runtime。【免费下载链接】OpenSandboxSecure, Fast, and Extensible Sandbox runtime for AI agents.项目地址: https://gitcode.com/GitHub_Trending/ope/OpenSandbox创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表