opensandbox 在 host 模式下多应用使用,多沙箱磁盘路径串通问题技术说明
多沙箱磁盘路径串通问题技术说明问题现象在 Docker host 网络模式下同时创建两个沙箱沙箱 A 和沙箱 B分别挂载不同的外部数据盘/mnt/disk_a、/mnt/disk_b通过docker exec进入容器内部查看各自的挂载路径正确但通过 Python SDK 调用时沙箱 B 的文件操作看到的是沙箱 A 的磁盘内容反之亦然。即SDK 的请求被错误地路由到了另一个沙箱的 execd。根因分析沙箱通信架构回顾OpenSandbox 的 SDK 与沙箱的通信链路如下SDK 客户端 │ ├─(1)─→ Server API (:8080) REST: create / get endpoint │ └─(2)─→ 沙箱 execd execd proxy: files / commands / health关键点SDK 在创建沙箱后直连 execd——文件读写、命令执行、健康检查都不经过 Server而是直接请求沙箱的 execd 端口。Host 网络模式下的端口固定问题Host 网络模式下容器与宿主机共享网络命名空间容器内进程直接使用宿主机端口宿主机 ├─ 沙箱 A 容器 │ └─ execd 监听 0.0.0.0:44772 ├─ 沙箱 B 容器 │ └─ execd 尝试监听 0.0.0.0:44772 ← 端口冲突TCP 协议规定同一IP:Port只能被一个进程绑定。沙箱 B 的 execd 无法绑定 44772已被沙箱 A 占用要么启动失败要么直接退出。endpoint 解析逻辑修复前修复前的 Host 模式get_endpoint()networking.py:218-223ifself.network_modeHOST_NETWORK_MODE:endpointEndpoint(endpointf{public_host}:{port})# port 44772固定returnendpoint无论查询哪个沙箱的 endpoint返回的都是host:44772——所有沙箱公用的地址。路径串通全过程创建沙箱 A allocate_port: 跳过host 模式不做端口分配 env: 无 OPENSANDBOX_EXECD_PORT execd 绑定 44772 ✅第一个启动 get_endpoint → host:44772 创建沙箱 B allocate_port: 跳过host 模式不做端口分配 env: 无 OPENSANDBOX_EXECD_PORT execd 尝试绑定 44772 ❌被 A 占用 get_endpoint → host:44772还是这个地址 SDK 沙箱 B 调用 请求 host:44772 → 宿主机端口 44772 属于沙箱 A 的 execd → execd 在沙箱 A 的容器文件系统中执行操作 → 看到的是沙箱 A 的磁盘/mnt/disk_a → 而不是沙箱 B 的磁盘/mnt/disk_b修复方案核心思路为每个沙箱分配一个唯一的宿主机端口通过环境变量告知 execd 绑定到分配端口而非固定 44772。涉及三个组件的改动组件文件改动execd (Go)components/execd/pkg/flag/parser.go新增OPENSANDBOX_EXECD_PORT环境变量支持Server (Python)server/.../docker/docker_service.pyhost 模式下分配端口 注入环境变量 写 labelServer (Python)server/.../docker/networking.pyendpoint 解析从 label 读取分配端口execd 环境变量端口Goexecd 已有--portCLI flag新增同名的环境变量方式与JUPYTER_HOST、EXECD_ACCESS_TOKEN保持一致constserverPortEnvOPENSANDBOX_EXECD_PORTifportFromEnv:os.Getenv(serverPortEnv);portFromEnv!{port,err:strconv.Atoi(portFromEnv)// 校验 1~65535ServerPortport}优先级环境变量 CLI--port 默认 44772Host 模式端口分配Server修复后host 模式下不再跳过端口分配docker_service.py:946-963# 修复后host 模式也分配端口host_port_bindingsallocate_port_bindings([44772,8080],min_portself.app_config.docker.port_range_min,max_portself.app_config.docker.port_range_max,)host_execd_porthost_port_bindings[44772][1]# 沙箱 A→40001, 沙箱 B→40003host_http_porthost_port_bindings[8080][1]# 写入容器 labelendpoint 解析用到labels[SANDBOX_EMBEDDING_PROXY_PORT_LABEL]str(host_execd_port)labels[SANDBOX_HTTP_PORT_LABEL]str(host_http_port)# 注入环境变量execd 启动时读取environment.append(fOPENSANDBOX_EXECD_PORT{host_execd_port})endpoint 解析适配ServerHost 模式下从 label 读取分配端口networking.py:227-238ifself.network_modeHOST_NETWORK_MODE:containerself._get_container_by_sandbox_id(sandbox_id)labelscontainer.attrs.get(Config,{}).get(Labels)or{}iflabels.get(SANDBOX_EMBEDDING_PROXY_PORT_LABEL):# 有 label → 使用分配端口returnself._resolve_host_mapped_endpoint(public_host,labels,port)# 无 label → 旧沙箱向后兼容回退原始格式endpointEndpoint(endpointf{public_host}:{port})returnendpoint修复后的完整链路创建沙箱 A allocate_port_bindings → 44772→40001, 8080→40002 label: embedding-proxy-port40001, http-port40002 env: OPENSANDBOX_EXECD_PORT40001 execd 绑定 0.0.0.0:40001 ✅ get_endpoint → host:40001/proxy/44772 ✅ 创建沙箱 B allocate_port_bindings → 44772→40003, 8080→40004 label: embedding-proxy-port40003, http-port40004 env: OPENSANDBOX_EXECD_PORT40003 execd 绑定 0.0.0.0:40003 ✅ ← 不与 A 冲突 get_endpoint → host:40003/proxy/44772 ✅ ← 与 A 不同 SDK 沙箱 A: 请求 host:40001 → 沙箱 A execd → /mnt/disk_a ✅ SDK 沙箱 B: 请求 host:40003 → 沙箱 B execd → /mnt/disk_b ✅端口范围配置修复同时引入了可配置的端口分配范围[docker] port_range_min 40000 # 端口分配下限默认 40000 port_range_max 60000 # 端口分配上限默认 60000每个沙箱占用端口数场景占用端口数量默认无 egressexecd 44772, HTTP 80802有 egress sidecar egress API 180803Windows 系统 RDP 3389, noVNC 80064端口并发上限默认范围(60000 - 40000) ÷ 2≈ 10000 个沙箱。影响范围维度说明Host 模式核心修复——解决 execd 固定端口导致的多沙箱请求串通Bridge 模式不受影响——Docker 已在容器层做端口映射隔离Egress sidecarhost 模式下不支持networkPolicy限制未变向后兼容旧沙箱无embedding-proxy-portlabelendpoint 回退到原始host:port格式SDK无需改动——SDK 始终通过get_endpointAPI 获取 endpoint URL自动适配相关 PR| PR 分支 | 内容 | 状态|---------|------|-------|feature/docker-configurable-port-range| 端口范围配置port_range_min/port_range_max | 合并到 主分支|feature/host-mode-port-isolation| Host 模式 execd 端口隔离核心修复 | 社区不同意合并具体的pr 记录https://github.com/opensandbox-group/OpenSandbox/pull/1187我 fork 的仓库地址https://github.com/1363653611/OpenSandbox/tree/feature/host-mode-port-isolation