跳转到主要内容
每个沙箱实例默认分配 20GB 的系统盘存储空间,用于临时存储数据。沙箱关闭后,该存储空间中的所有数据将被自动清理。因此,建议您将需要长期保存的数据存储到独立的云存储服务中。
沙箱实例分配的存储空间大小后续可能会调整,具体以 计费说明 文档说明为准。
对象存储(Object Storage)是一种稳定、高性价比、高性能的云存储服务,主流云服务商均提供此类产品。您可以在沙箱中使用云服务商提供的 SDK 或 CLI 工具直接操作对象存储,也可以使用 FUSE 文件系统工具将对象存储 Bucket 挂载到沙箱的指定目录,从而像操作本地文件系统一样使用对象存储。
FUSE(Filesystem in Userspace)是一个用户空间文件系统框架,它可用于将各种云存储服务 “伪装成” 本地普通文件夹,只需要像操作普通文件夹一样使用它们。
本文档将介绍如何将主流云服务商提供的对象存储 Bucket 挂载到沙箱中,以便后续使用。
通过以下方式挂载对象存储 Bucket 到本地目录,通常文件读写性能较差,如果您的业务对文件读写性能敏感,不建议使用这种方式。同时通过这种方式操作对象存储并不是原子性的,也就是存在本地操作成功但对象存储操作失败的风险,进而导致数据丢失。因此这种方式比较适用于对读写性能不敏感,且没有频繁写操作的场景。否则建议您优先使用云服务商提供的 SDK 或 CLI 工具在业务逻辑中直接操作对象存储。

Amazon S3 对象存储

可以通过 s3fs 工具将 Amazon S3 对象存储 Bucket 挂载到沙箱中。 您可以在制作 沙箱模板 时在 ppio.Dockerfile 文件中添加安装 s3fs 的命令,或者在沙箱实例内执行安装命令来临时安装。 以下是一个 ppio.Dockerfile 示例,展示了如何在制作沙箱模板时安装 s3fs 工具。
# 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。
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()

阿里云对象存储(OSS)

可以使用 ossfs 工具将阿里云对象存储(OSS)Bucket 挂载到沙箱中。 以下是一个 ppio.Dockerfile 示例,展示了如何在制作 沙箱模板 时安装 ossfs 工具。
# 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。
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()

腾讯云对象存储(COS)

可以使用 GooseFS-Lite 工具将腾讯云对象存储(COS)Bucket 挂载到沙箱中。 以下是一个 ppio.Dockerfile 示例,展示了如何在制作 沙箱模板 时安装 GooseFS-Lite 工具。
# 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。
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()
I