> ## Documentation Index
> Fetch the complete documentation index at: https://ppio.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 挂载对象存储 Bucket

> 查阅 PPIO Agent 沙箱挂载对象存储 Bucket 的归档指南，了解该版本连接外部存储的方式，为维护数据访问流程提供参考。

每个沙箱实例默认分配 20GB 的系统盘存储空间，用于临时存储数据。沙箱关闭后，该存储空间中的所有数据将被自动清理。因此，建议您将需要长期保存的数据存储到独立的云存储服务中。

> [!TIP]
>
> 沙箱实例分配的存储空间大小后续可能会调整，具体以 [计费说明](/docs/sandbox/pricing) 文档说明为准。

对象存储（Object Storage）是一种稳定、高性价比、高性能的云存储服务，主流云服务商均提供此类产品。您可以在沙箱中使用云服务商提供的 SDK 或 CLI 工具直接操作对象存储，也可以使用 FUSE 文件系统工具将对象存储 Bucket 挂载到沙箱的指定目录，从而像操作本地文件系统一样使用对象存储。

> [!TIP]
>
> [FUSE（Filesystem in Userspace）](https://www.kernel.org/doc/html/next/filesystems/fuse.html)是一个用户空间文件系统框架，它可用于将各种云存储服务 “伪装成” 本地普通文件夹，只需要像操作普通文件夹一样使用它们。

本文档将介绍如何将主流云服务商提供的对象存储 Bucket 挂载到沙箱中，以便后续使用。

> [!WARNING]
>
> 通过以下方式挂载对象存储 Bucket 到本地目录，通常文件读写性能较差，如果您的业务对文件读写性能敏感，不建议使用这种方式。同时通过这种方式操作对象存储并不是原子性的，也就是存在本地操作成功但对象存储操作失败的风险，进而导致数据丢失。
>
> 因此这种方式比较适用于对读写性能不敏感，且没有频繁写操作的场景。否则建议您优先使用云服务商提供的 SDK 或 CLI 工具在业务逻辑中直接操作对象存储。

## Amazon S3 对象存储

可以通过 [s3fs](https://github.com/s3fs-fuse/s3fs-fuse) 工具将 Amazon S3 对象存储 Bucket 挂载到沙箱中。

您可以在制作 [沙箱模板](/docs/sandbox/archive/sandbox-template) 时在 `ppio.Dockerfile` 文件中添加安装 s3fs 的命令，或者在沙箱实例内执行安装命令来临时安装。

以下是一个 `ppio.Dockerfile` 示例，展示了如何在制作沙箱模板时安装 s3fs 工具。

```dockerfile ppio.Dockerfile icon="docker" theme={null}
# You can use most Debian-based base images
FROM ubuntu:latest

# 注意，安装的 s3fs 版本较低，可能会导致挂载失败，请保证安装的 s3fs 版本高于 1.93
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y s3fs
```

下面展示了如何在沙箱实例中通过 s3fs 来挂载 Amazon S3 Bucket。

```js JavaScript & TypeScript icon="js" theme={null}
import { Sandbox } from 'ppio-sandbox'

const TEMPLATE_ID = process.env.PPIO_TEMPLATE_ID
const AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID
const AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY
const AWS_BUCKET_NAME = process.env.AWS_BUCKET_NAME
if (!TEMPLATE_ID || !AWS_ACCESS_KEY_ID || !AWS_SECRET_ACCESS_KEY || !AWS_BUCKET_NAME) {
    throw new Error('environment variable PPIO_TEMPLATE_ID or AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY or AWS_BUCKET_NAME is not set')
}

const MOUNT_DIRECTORY = "/mnt/s3-bucket"

const sandbox = await Sandbox.create(TEMPLATE_ID)

// 创建挂载目录
await sandbox.files.makeDir(MOUNT_DIRECTORY)

// 创建 credentials 文件
// s3fs 会默认从 /root/.passwd-s3fs 中读取 AWS 访问密钥信息
await sandbox.files.write('/root/.passwd-s3fs', `${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}`)

// 设置 credentials 文件的权限
await sandbox.commands.run('sudo chmod 600 /root/.passwd-s3fs')

// 挂载 Amazon S3 Bucket 到指定目录
// 常用挂载参数：
// - allow_other: 允许其他用户访问挂载点
// - endpoint: 指定 S3 bucket 的所在区域
// 详细参数请参考：https://manpages.ubuntu.com/manpages/noble/en/man1/s3fs.1.html
const mountOptions = 'allow_other,endpoint=us-east-1'
await sandbox.commands.run(`sudo s3fs ${AWS_BUCKET_NAME} ${MOUNT_DIRECTORY} -o ${mountOptions}`)

// 测试写入文件
await sandbox.files.write(`${MOUNT_DIRECTORY}/test-file.txt`, 'test-file-content')

// 测试读取文件
const content = await sandbox.files.read(`${MOUNT_DIRECTORY}/test-file.txt`)
console.log(content)

await sandbox.kill()
```

```python Python icon="python" theme={null}
import os
from ppio_sandbox.core import Sandbox

TEMPLATE_ID = os.environ.get("PPIO_TEMPLATE_ID")
AWS_ACCESS_KEY_ID = os.environ.get("AWS_ACCESS_KEY_ID")
AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY")
AWS_BUCKET_NAME = os.environ.get("AWS_BUCKET_NAME")
if not TEMPLATE_ID or not AWS_ACCESS_KEY_ID or not AWS_SECRET_ACCESS_KEY or not AWS_BUCKET_NAME:
    raise Exception("environment variable PPIO_TEMPLATE_ID or AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY or AWS_BUCKET_NAME is not set")

MOUNT_DIRECTORY = "/mnt/s3-bucket"

sandbox = Sandbox.create(TEMPLATE_ID)

# 创建挂载目录
sandbox.files.make_dir(MOUNT_DIRECTORY)

# 创建 credentials 文件
# s3fs 会默认从 /root/.passwd-s3fs 中读取 AWS 访问密钥信息，如果使用其他路径，需要在 s3fs 命令中添加该路径。
sandbox.files.write("/root/.passwd-s3fs", f"{AWS_ACCESS_KEY_ID}:{AWS_SECRET_ACCESS_KEY}")

# 设置 credentials 文件的权限
sandbox.commands.run("sudo chmod 600 /root/.passwd-s3fs")

# 挂载 Amazon S3 Bucket 到指定目录
# 重要参数说明：
# - allow_other: 允许其他用户访问挂载点
# - endpoint: 指定 S3 bucket 的所在区域
# 详细参数请参考：https://manpages.ubuntu.com/manpages/noble/en/man1/s3fs.1.html
mount_options = "allow_other,endpoint=cn-north-1"
sandbox.commands.run(f"sudo s3fs {AWS_BUCKET_NAME} {MOUNT_DIRECTORY} -o {mount_options}")

# 测试写入文件
write_result = sandbox.files.write(f"{MOUNT_DIRECTORY}/test-file.txt", "test-file-content")

# 测试读取文件
content = sandbox.files.read(f"{MOUNT_DIRECTORY}/test-file.txt")
print("File content:", content)

sandbox.kill()
```

## 阿里云对象存储（OSS）

可以使用 [ossfs](https://help.aliyun.com/zh/oss/developer-reference/install-ossfs-1-0) 工具将阿里云对象存储（OSS）Bucket 挂载到沙箱中。

以下是一个 `ppio.Dockerfile` 示例，展示了如何在制作 [沙箱模板](/docs/sandbox/archive/sandbox-template) 时安装 ossfs 工具。

```dockerfile ppio.Dockerfile icon="docker" theme={null}
# You can use most Debian-based base images
FROM ubuntu:latest

# Install dependencies and customize sandbox
RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
    apt-get install -y wget gdebi-core && \
    # 从这里复制常用系统的软件包安装地址：https://help.aliyun.com/zh/oss/developer-reference/install-ossfs-1-0
    wget https://gosspublic.alicdn.com/ossfs/ossfs_1.91.8_ubuntu24.04_amd64.deb &&\
    gdebi -n ossfs_1.91.8_ubuntu24.04_amd64.deb && \
    rm ossfs_1.91.8_ubuntu24.04_amd64.deb
```

下面展示了如何在沙箱实例中通过 ossfs 来挂载阿里云对象存储（OSS）Bucket。

```js JavaScript & TypeScript icon="js" theme={null}
import { Sandbox } from 'ppio-sandbox'

const TEMPLATE_ID = process.env.PPIO_OSS_TEMPLATE_ID
const OSS_ACCESS_KEY_ID = process.env.OSS_ACCESS_KEY_ID
const OSS_ACCESS_KEY_SECRET = process.env.OSS_ACCESS_KEY_SECRET
const OSS_BUCKET_NAME = process.env.OSS_BUCKET_NAME
if (!TEMPLATE_ID || !OSS_ACCESS_KEY_ID || !OSS_ACCESS_KEY_SECRET || !OSS_BUCKET_NAME) {
    throw new Error('environment variable PPIO_OSS_TEMPLATE_ID or OSS_ACCESS_KEY_ID or OSS_ACCESS_KEY_SECRET or OSS_BUCKET_NAME is not set')
}

const MOUNT_DIRECTORY = "/mnt/oss-bucket"

const sandbox = await Sandbox.create(TEMPLATE_ID)

// 创建挂载目录
await sandbox.files.makeDir(MOUNT_DIRECTORY)

// 创建 credentials 文件
// ossfs 会默认从 /etc/passwd-ossfs 中读取 OSS 访问密钥信息
// 如果使用其他路径，可以使用 -o passwd_file 参数指定自定义配置文件的路径
// 更多参数请参考：https://help.aliyun.com/zh/oss/developer-reference/common-options
await sandbox.files.write('/etc/passwd-ossfs', `${OSS_BUCKET_NAME}:${OSS_ACCESS_KEY_ID}:${OSS_ACCESS_KEY_SECRET}`)

// 设置 credentials 文件的权限
await sandbox.commands.run('sudo chmod 600 /etc/passwd-ossfs')

// 挂载 OSS Bucket 到指定目录
// -o url 必须和 Bucket 的所在区域一致，参考：https://help.aliyun.com/zh/oss/regions-and-endpoints 查看更多区域信息。
await sandbox.commands.run(`sudo ossfs ${OSS_BUCKET_NAME} ${MOUNT_DIRECTORY} -o allow_other -o passwd_file=/etc/passwd-ossfs -o url=https://oss-cn-beijing.aliyuncs.com`)

// 注意：此处不能通过 sandbox.files.write 方法创建文件。由于 ossfs 将挂载点视为一个 "虚拟的" 超大磁盘空间，sandbox.files.write 方法会尝试获取实际磁盘使用情况并检查空间是否足够，但该信息在虚拟挂载下容易溢出或异常，进而导致误报 "磁盘空间不足" 错误。
// 测试写入文件
await sandbox.commands.run(`echo "test-file-content" > ${MOUNT_DIRECTORY}/test-file.txt`)

// 测试读取文件
const result = await sandbox.commands.run(`cat ${MOUNT_DIRECTORY}/test-file.txt`)
console.log(result)

await sandbox.kill()
```

```python Python icon="python" theme={null}
import os
from ppio_sandbox.core import Sandbox

TEMPLATE_ID = os.environ.get("PPIO_OSS_TEMPLATE_ID")
OSS_ACCESS_KEY_ID = os.environ.get("OSS_ACCESS_KEY_ID")
OSS_ACCESS_KEY_SECRET = os.environ.get("OSS_ACCESS_KEY_SECRET")
OSS_BUCKET_NAME = os.environ.get("OSS_BUCKET_NAME")
if not TEMPLATE_ID or not OSS_ACCESS_KEY_ID or not OSS_ACCESS_KEY_SECRET or not OSS_BUCKET_NAME:
    raise Exception("environment variable PPIO_TEMPLATE_ID or OSS_ACCESS_KEY_ID or OSS_ACCESS_KEY_SECRET or OSS_BUCKET_NAME is not set")

MOUNT_DIRECTORY = "/mnt/oss-bucket"

sandbox = Sandbox.create(TEMPLATE_ID)

# 创建挂载目录
sandbox.files.make_dir(MOUNT_DIRECTORY)

# 创建 credentials 文件
# ossfs 会默认从 /etc/passwd-ossfs 中读取 OSS 访问密钥信息
# 如果使用其他路径，可以使用 -o passwd_file 参数指定自定义配置文件的路径
# 更多参数请参考：https://help.aliyun.com/zh/oss/developer-reference/common-options
sandbox.files.write("/etc/passwd-ossfs", f'{OSS_BUCKET_NAME}:{OSS_ACCESS_KEY_ID}:{OSS_ACCESS_KEY_SECRET}')

# 设置 credentials 文件的权限
sandbox.commands.run("sudo chmod 600 /etc/passwd-ossfs")

# 挂载 OSS Bucket 到指定目录
# -o url 必须和 Bucket 的所在区域一致，参考：https://help.aliyun.com/zh/oss/regions-and-endpoints 查看更多区域信息。
sandbox.commands.run(f"sudo ossfs {OSS_BUCKET_NAME} {MOUNT_DIRECTORY} -o allow_other -o passwd_file=/etc/passwd-ossfs -o url=https://oss-cn-beijing.aliyuncs.com")

# 注意：此处不能通过 sandbox.files.write 方法创建文件。由于 ossfs 将挂载点视为一个 "虚拟的" 超大磁盘空间，sandbox.files.write 方法会尝试获取实际磁盘使用情况并检查空间是否足够，但该信息在虚拟挂载下容易溢出或异常，进而导致误报 "磁盘空间不足" 错误。
# 测试写入文件
sandbox.commands.run(f"echo 'test-file-content' > {MOUNT_DIRECTORY}/test-file.txt")

# 测试读取文件
result = sandbox.commands.run(f"cat {MOUNT_DIRECTORY}/test-file.txt")
print("File content:", result)

sandbox.kill()
```

## 腾讯云对象存储（COS）

可以使用 [GooseFS-Lite](https://cloud.tencent.com/document/product/436/115507) 工具将腾讯云对象存储（COS）Bucket 挂载到沙箱中。

以下是一个 `ppio.Dockerfile` 示例，展示了如何在制作 [沙箱模板](/docs/sandbox/archive/sandbox-template) 时安装 GooseFS-Lite 工具。

```dockerfile ppio.Dockerfile icon="docker" theme={null}
# You can use most Debian-based base images
FROM ubuntu:latest

# Install dependencies and customize sandbox
RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
    apt install -y curl libfuse-dev wget && \
    curl -fssL https://downloads.tencentgoosefs.cn/goosefs-lite/install.sh | sh -x && \
    cd goosefs-lite-* && \
    bash bin/install.sh && \
    bash bin/install-jdk.sh https://github.com/Tencent/TencentKona-11/releases/download/kona11.0.22/TencentKona-11.0.22.b1-jdk_linux-x86_64.tar.gz
```

下面展示了如何在沙箱实例中通过 GooseFS-Lite 来挂载腾讯云对象存储（COS）Bucket。

```js JavaScript & TypeScript icon="js" theme={null}
import { Sandbox } from 'ppio-sandbox'

const TEMPLATE_ID = process.env.PPIO_COS_TEMPLATE_ID
const COS_ACCESS_KEY_ID = process.env.COS_ACCESS_KEY_ID
const COS_SECRET_ACCESS_KEY = process.env.COS_SECRET_ACCESS_KEY
const COS_BUCKET_NAME = process.env.COS_BUCKET_NAME
const COS_BUCKET_REGION = process.env.COS_BUCKET_REGION
if (!TEMPLATE_ID || !COS_ACCESS_KEY_ID || !COS_SECRET_ACCESS_KEY || !COS_BUCKET_NAME || !COS_BUCKET_REGION) {
    throw new Error('environment variable PPIO_COS_TEMPLATE_ID or COS_ACCESS_KEY_ID or COS_SECRET_ACCESS_KEY or COS_BUCKET_NAME or COS_BUCKET_REGION is not set')
}

const MOUNT_DIRECTORY = "/mnt/cos-bucket"

const sandbox = await Sandbox.create(TEMPLATE_ID)

// 创建挂载目录
await sandbox.files.makeDir(MOUNT_DIRECTORY)

// 修改 conf/core-site.xml 文件
// 配置 COS Bucket 的访问密钥和区域
// 更具可读性和安全性，避免字符串拼接并用模板字符串实现，且一条命令一行方便维护
const cmdUpdateCoreSiteXml = [
    `sudo sed -i "/<name>fs.cosn.userinfo.secretId<\\/name>/{N;s/<value>[^<]*<\\/value>/<value>${COS_ACCESS_KEY_ID}<\\/value>/}" conf/core-site.xml`,
    `sudo sed -i "/<name>fs.cosn.userinfo.secretKey<\\/name>/{N;s/<value>[^<]*<\\/value>/<value>${COS_SECRET_ACCESS_KEY}<\\/value>/}" conf/core-site.xml`,
    `sudo sed -i "/<name>fs.cosn.bucket.region<\\/name>/{N;s/<value>[^<]*<\\/value>/<value>${COS_BUCKET_REGION}<\\/value>/}" conf/core-site.xml`
].join('; ');

// 更新 core-site.xml 文件
await sandbox.commands.run(`cd /goosefs-lite-* && (${cmdUpdateCoreSiteXml})`)

// 挂载 COS Bucket 到指定目录
await sandbox.commands.run(`cd /goosefs-lite-* && sudo ./bin/goosefs-lite mount -o "allow_other" ${MOUNT_DIRECTORY} cosn://${COS_BUCKET_NAME}/`)

// 测试写入文件
await sandbox.files.write(`${MOUNT_DIRECTORY}/test-file.txt`, 'test-file-content')

// 测试读取文件
const content = await sandbox.files.read(`${MOUNT_DIRECTORY}/test-file.txt`)
console.log(content)

await sandbox.kill()
```

```python Python icon="python" theme={null}
import os
from ppio_sandbox.core import Sandbox

TEMPLATE_ID = os.environ.get("PPIO_COS_TEMPLATE_ID")
COS_ACCESS_KEY_ID = os.environ.get("COS_ACCESS_KEY_ID")
COS_SECRET_ACCESS_KEY = os.environ.get("COS_SECRET_ACCESS_KEY")
COS_BUCKET_NAME = os.environ.get("COS_BUCKET_NAME")
COS_BUCKET_REGION = os.environ.get("COS_BUCKET_REGION")
if not TEMPLATE_ID or not COS_ACCESS_KEY_ID or not COS_SECRET_ACCESS_KEY or not COS_BUCKET_NAME or not COS_BUCKET_REGION:
    raise Exception(
        "environment variable PPIO_COS_TEMPLATE_ID or COS_ACCESS_KEY_ID or COS_SECRET_ACCESS_KEY or COS_BUCKET_NAME or COS_BUCKET_REGION is not set")

MOUNT_DIRECTORY = "/mnt/cos-bucket"

sandbox = Sandbox.create(TEMPLATE_ID)

# 创建挂载目录
sandbox.files.make_dir(MOUNT_DIRECTORY)

# 修改 conf/core-site.xml 文件
# 配置 COS Bucket 的访问密钥和区域
cmd_update_core_site_xml = (
    "sudo sed -i '/<name>fs.cosn.userinfo.secretId<\/name>/{N;s/<value>[^<]*<\/value>/<value>" +
    COS_ACCESS_KEY_ID+"<\/value>/}' conf/core-site.xml;"
    "sudo sed -i '/<name>fs.cosn.userinfo.secretKey<\/name>/{N;s/<value>[^<]*<\/value>/<value>" +
    COS_SECRET_ACCESS_KEY+"<\/value>/}' conf/core-site.xml;"
    "sudo sed -i '/<name>fs.cosn.bucket.region<\/name>/{N;s/<value>[^<]*<\/value>/<value>" +
    COS_BUCKET_REGION+"<\/value>/}' conf/core-site.xml"
)

# 更新 core-site.xml 文件
sandbox.commands.run(f"cd /goosefs-lite-* && ({cmd_update_core_site_xml})")

# 挂载 COS Bucket 到指定目录
sandbox.commands.run(f"cd /goosefs-lite-* && sudo ./bin/goosefs-lite mount -o \"allow_other\" {MOUNT_DIRECTORY} cosn://{COS_BUCKET_NAME}/")

# 测试写入文件
sandbox.files.write(f"{MOUNT_DIRECTORY}/test-file.txt", "test-file-content")

# 测试读取文件
sandbox.files.read(f"{MOUNT_DIRECTORY}/test-file.txt")

sandbox.kill()
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.