/ examples / host-volume-mount / README_zh.md
README_zh.md
  1  # 宿主机目录挂载示例
  2  
  3  本示例演示如何使用 OpenSandbox Volume API 将宿主机目录挂载到沙箱容器中。宿主机目录挂载支持宿主机与沙箱环境之间的双向文件共享,适用于共享数据集、模型检查点、配置文件或收集沙箱输出等场景。
  4  
  5  ## 演示场景
  6  
  7  | # | 场景 | 说明 |
  8  |---|------|------|
  9  | 1 | **读写挂载** | 挂载宿主机目录,支持双向文件读写 |
 10  | 2 | **只读挂载** | 提供沙箱不可修改的共享数据 |
 11  | 3 | **SubPath 挂载** | 仅挂载宿主机路径下的指定子目录 |
 12  
 13  ## 前置条件
 14  
 15  ### 1. 启动 OpenSandbox 服务
 16  
 17  ```shell
 18  git clone git@github.com:alibaba/OpenSandbox.git
 19  cd OpenSandbox/server
 20  cp opensandbox_server/examples/example.config.zh.toml ~/.sandbox.toml
 21  uv sync && uv run python -m opensandbox_server.main
 22  ```
 23  
 24  ### 2. 配置允许的宿主机路径
 25  
 26  出于安全考虑,服务端会限制可挂载的宿主机路径。请在 `~/.sandbox.toml` 中添加 `[storage]` 配置段:
 27  
 28  ```toml
 29  [storage]
 30  # 允许进行 bind mount 的宿主机路径前缀白名单。
 31  # 仅匹配这些前缀的路径才能被挂载到沙箱中。
 32  # 如果为空,则允许所有路径(不建议在生产环境使用)。
 33  allowed_host_paths = ["/tmp/opensandbox-data", "/data/shared"]
 34  ```
 35  
 36  > **安全提示**:在生产环境中,请务必设置明确的 `allowed_host_paths`,以防止沙箱访问敏感的宿主机目录。空列表表示允许所有路径,适合本地开发,但不适用于共享环境。
 37  
 38  ### 3. 创建宿主机目录
 39  
 40  ```shell
 41  # 创建与沙箱共享的目录
 42  mkdir -p /tmp/opensandbox-data
 43  echo "hello-from-host" > /tmp/opensandbox-data/marker.txt
 44  
 45  # 创建用于 subpath 演示的子目录
 46  mkdir -p /tmp/opensandbox-data/datasets/train
 47  echo -e "id,value\n1,100\n2,200\n3,300" > /tmp/opensandbox-data/datasets/train/data.csv
 48  ```
 49  
 50  ### 4. 从源码安装 SDK
 51  
 52  Volume 功能需要从源码安装最新版 SDK:
 53  
 54  ```shell
 55  # 在项目根目录下执行(推荐使用 uv)
 56  uv pip install -e sdks/sandbox/python
 57  
 58  # 或者使用 pip(需要在虚拟环境中执行)
 59  # python3 -m venv .venv && source .venv/bin/activate
 60  # pip install -e sdks/sandbox/python
 61  ```
 62  
 63  ### 5. 拉取沙箱镜像
 64  
 65  ```shell
 66  docker pull registry.cn-hangzhou.aliyuncs.com/acs/ubuntu:latest
 67  ```
 68  
 69  ## 运行
 70  
 71  ```shell
 72  SANDBOX_IMAGE=registry.cn-hangzhou.aliyuncs.com/acs/ubuntu:latest \
 73    HOST_VOLUME_PATH=/tmp/opensandbox-data uv run python examples/host-volume-mount/main.py
 74  ```
 75  
 76  ## 预期输出
 77  
 78  ```text
 79  Using HOST_VOLUME_PATH: /tmp/opensandbox-data
 80  
 81  OpenSandbox server : localhost:8080
 82  Sandbox image      : registry.cn-hangzhou.aliyuncs.com/acs/ubuntu:latest
 83  Host volume path   : /tmp/opensandbox-data
 84  
 85  ============================================================
 86  Scenario 1: Read-Write Host Volume Mount
 87  ============================================================
 88    Host path : /tmp/opensandbox-data
 89    Mount path: /mnt/shared
 90  
 91    [1] Listing files visible from inside the sandbox:
 92    total 4
 93  drwxr-xr-x 1 root root 128 Feb  6 09:24 .
 94  drwxr-xr-x 1 root root  12 Feb  6 11:50 ..
 95  drwxr-xr-x 1 root root  96 Feb  6 09:24 datasets
 96  -rw-r--r-- 1 root root  16 Feb  6 09:24 marker.txt
 97  
 98    [2] Writing a file from inside the sandbox:
 99    -> Written: /mnt/shared/sandbox-greeting.txt
100  
101    [3] Reading back the file:
102    Hello from sandbox!
103  
104    [4] Verified on host: /tmp/opensandbox-data/sandbox-greeting.txt
105        Content: Hello from sandbox!
106  
107    Scenario 1 completed.
108  
109  ============================================================
110  Scenario 2: Read-Only Host Volume Mount
111  ============================================================
112    Host path : /tmp/opensandbox-data
113    Mount path: /mnt/readonly
114  
115    [1] Reading files from read-only mount:
116    total 8
117  drwxr-xr-x 1 root root 160 Feb  6 11:50 .
118  drwxr-xr-x 1 root root  16 Feb  6 11:50 ..
119  drwxr-xr-x 1 root root  96 Feb  6 09:24 datasets
120  -rw-r--r-- 1 root root  16 Feb  6 09:24 marker.txt
121  -rw-r--r-- 1 root root  20 Feb  6 11:50 sandbox-greeting.txt
122  
123    [2] Reading marker.txt:
124    hello-from-host
125  
126    [3] Attempting to write (should fail):
127    touch: cannot touch '/mnt/readonly/should-fail.txt': Read-only file system
128    Write denied (expected)
129  
130    Scenario 2 completed.
131  
132  ============================================================
133  Scenario 3: SubPath Host Volume Mount
134  ============================================================
135    Host path : /tmp/opensandbox-data
136    SubPath   : datasets/train
137    Mount path: /mnt/training-data
138  
139    [1] Listing mounted subpath content:
140    total 4
141  drwxr-xr-x 1 root root 96 Feb  6 09:24 .
142  drwxr-xr-x 1 root root 26 Feb  6 11:50 ..
143  -rw-r--r-- 1 root root 27 Feb  6 11:50 data.csv
144  
145    [2] Reading data.csv:
146    id,value
147  1,100
148  2,200
149  3,300
150  
151    Scenario 3 completed.
152  
153  ============================================================
154  All scenarios completed successfully!
155  ============================================================
156  ```
157  
158  ## 各 SDK 用法速览
159  
160  ### Python(异步)
161  
162  ```python
163  from opensandbox import Sandbox
164  from opensandbox.models.sandboxes import Host, Volume
165  
166  sandbox = await Sandbox.create(
167      image="ubuntu",
168      volumes=[
169          Volume(
170              name="my-data",
171              host=Host(path="/data/shared"),
172              mountPath="/mnt/data",
173              readOnly=False,       # 可选,默认为 False
174              subPath="subdir",     # 可选,挂载子目录
175          ),
176      ],
177  )
178  ```
179  
180  ### Python(同步)
181  
182  ```python
183  from opensandbox import SandboxSync
184  from opensandbox.models.sandboxes import Host, Volume
185  
186  sandbox = SandboxSync.create(
187      image="ubuntu",
188      volumes=[
189          Volume(
190              name="my-data",
191              host=Host(path="/data/shared"),
192              mountPath="/mnt/data",
193          ),
194      ],
195  )
196  ```
197  
198  ### JavaScript / TypeScript
199  
200  ```typescript
201  import { Sandbox } from "@alibaba-group/opensandbox";
202  
203  const sandbox = await Sandbox.create({
204    image: "ubuntu",
205    volumes: [
206      {
207        name: "my-data",
208        host: { path: "/data/shared" },
209        mountPath: "/mnt/data",
210        readOnly: false,
211      },
212    ],
213  });
214  ```
215  
216  ### Java / Kotlin
217  
218  ```java
219  Volume volume = Volume.builder()
220      .name("my-data")
221      .host(Host.of("/data/shared"))
222      .mountPath("/mnt/data")
223      .readOnly(false)
224      .build();
225  
226  Sandbox sandbox = Sandbox.builder()
227      .image("ubuntu")
228      .volume(volume)
229      .build();
230  ```
231  
232  ## 参考资料
233  
234  - [OSEP-0003: Volume 与 VolumeBinding 支持](../../oseps/0003-volume-and-volumebinding-support.md) — 设计提案
235  - [Sandbox Lifecycle API 规范](../../specs/sandbox-lifecycle.yml) — Volume 定义的 OpenAPI 规范
236  - [服务端配置示例](../../server/example.config.zh.toml) — `[storage]` 段中的 `allowed_host_paths` 配置