← Sandbox SDK / sandbox / guides
Подключение бакетов
Подключайте бакеты S3-совместимого объектного хранилища как пути локальной файловой системы. Обращайтесь к объектному хранилищу с помощью стандартных файловых операций. Для Cloudflare R2 в продакшене можно также подключать по имени привязки Worker R2, чтобы учетные данные оставались в среде выполнения Worker.
Требования для использования монтирований R2 binding в продакшене
Чтобы смонтировать бакет R2 в продакшене без передачи учётных данных в контейнер, добавьте привязку R2 и экспортируйте ContainerProxy из точки входа вашего Worker.
{
"$schema": "./node_modules/wrangler/config-schema.json",
"r2_buckets": [
{
"binding": "MY_BUCKET",
"bucket_name": "my-r2-bucket"
}
]
}[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-r2-bucket"import { ContainerProxy } from "@cloudflare/sandbox";
export { ContainerProxy };import { ContainerProxy } from "@cloudflare/sandbox";
export { ContainerProxy };Если не указать endpoint, первый аргумент mountBucket() должен быть именем привязки R2 в Worker, например MY_BUCKET.
Когда монтировать бакеты
Подключайте S3-совместимые бакеты, если вам нужно:
- Постоянные данные - Данные сохраняются после уничтожения песочницы
- Большие наборы данных - Обработка данных без скачивания
- Общее хранилище - Несколько песочниц обращаются к одним и тем же данным
- Экономичное хранение данных - Дешевле, чем поддерживать песочницы активными
Подключение бакета R2
import { getSandbox } from "@cloudflare/sandbox";
const sandbox = getSandbox(env.Sandbox, "data-processor");
// Mount R2 bucket by Worker binding name
await sandbox.mountBucket("MY_BUCKET", "/data");
// Access bucket with standard filesystem operations
await sandbox.exec("ls", { args: ["/data"] });
await sandbox.writeFile("/data/results.json", JSON.stringify(results));
// Use from Python
await sandbox.exec("python", {
args: [
"-c",
`
import pandas as pd
df = pd.read_csv('/data/input.csv')
df.describe().to_csv('/data/summary.csv')
`,
],
});import { getSandbox } from '@cloudflare/sandbox';
const sandbox = getSandbox(env.Sandbox, 'data-processor');
// Mount R2 bucket by Worker binding name
await sandbox.mountBucket('MY_BUCKET', '/data');
// Access bucket with standard filesystem operations
await sandbox.exec('ls', { args: ['/data'] });
await sandbox.writeFile('/data/results.json', JSON.stringify(results));
// Use from Python
await sandbox.exec('python', { args: ['-c', `
import pandas as pd
df = pd.read_csv('/data/input.csv')
df.describe().to_csv('/data/summary.csv')
`] });В этом примере MY_BUCKET это имя привязки из wrangler.toml. Оно не обязано совпадать с именем bucket в дашборде, хотя многие проекты используют одинаковые имена.
Учётные данные
Монтирования R2 binding не требуют учётных данных. Монтирования через удалённую конечную точку по-прежнему поддерживаются для Cloudflare R2 и других S3-совместимых провайдеров, и в этих сценариях можно использовать как автоматическое определение учётных данных, так и явно заданные учётные данные.
Автоматическое обнаружение
Если вы включаете endpoint, задайте учетные данные как секреты Worker, и SDK автоматически их обнаружит:
npx wrangler secret put R2_ACCESS_KEY_ID
npx wrangler secret put R2_SECRET_ACCESS_KEY// Credentials automatically detected from environment for remote endpoint mounts
await sandbox.mountBucket("my-r2-bucket", "/data", {
endpoint: "https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com",
});// Credentials automatically detected from environment for remote endpoint mounts
await sandbox.mountBucket('my-r2-bucket', '/data', {
endpoint: 'https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com'
});Явные учётные данные
При необходимости передавайте учётные данные напрямую:
await sandbox.mountBucket("my-r2-bucket", "/data", {
endpoint: "https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com",
credentials: {
accessKeyId: env.R2_ACCESS_KEY_ID,
secretAccessKey: env.R2_SECRET_ACCESS_KEY,
},
});await sandbox.mountBucket('my-r2-bucket', '/data', {
endpoint: 'https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com',
credentials: {
accessKeyId: env.R2_ACCESS_KEY_ID,
secretAccessKey: env.R2_SECRET_ACCESS_KEY
}
});Прокси учётных данных
При монтировании с явными учётными данными s3fs записывает их в файл паролей на диске контейнера. Скомпрометированный процесс контейнера может прочитать и похитить эти учётные данные или использовать их для доступа к хранилищу за пределами предполагаемого бакета.
Задайте credentialProxy: true чтобы учётные данные вообще не попадали в контейнер. Вместо передачи реальных учётных данных в контейнер Durable Object перехватывает все исходящие запросы S3 на сетевом уровне, повторно подписывает их реальными учётными данными и пересылает дальше. В контейнере хранятся только фиктивные учётные данные, бесполезные за пределами прокси.
Это работает с AWS SigV4 ↗ подпись для S3-совместимых конечных точек (включая R2) и подпись HMAC для Google Cloud Storage. Рекомендуется задать credentialProxy: true для всех точек монтирования эндпоинтов. По умолчанию используется значение false для обратной совместимости и станет значением по умолчанию в будущей версии Sandbox SDK.
await sandbox.mountBucket("my-bucket", "/data", {
endpoint: "https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com",
provider: "r2",
credentials: {
accessKeyId: env.R2_ACCESS_KEY_ID,
secretAccessKey: env.R2_SECRET_ACCESS_KEY,
},
credentialProxy: true,
});await sandbox.mountBucket('my-bucket', '/data', {
endpoint: 'https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com',
provider: 'r2',
credentials: {
accessKeyId: env.R2_ACCESS_KEY_ID,
secretAccessKey: env.R2_SECRET_ACCESS_KEY
},
credentialProxy: true
});Подключение подкаталогов бакета
Подключайте определенный подкаталог внутри бакета с помощью prefix опция. В точке монтирования видно только содержимое под этим префиксом:
// Mount only the /uploads/images/ subdirectory
await sandbox.mountBucket("MY_BUCKET", "/images", {
prefix: "/uploads/images/",
});
// Files appear at mount point without the prefix
// Bound bucket: my-r2-bucket/uploads/images/photo.jpg
// Mounted path: /images/photo.jpg
await sandbox.exec("ls", { args: ["/images"] });
// Write to subdirectory
await sandbox.writeFile("/images/photo.jpg", imageData);
// Creates my-r2-bucket/uploads/images/photo.jpg
// Mount different prefixes to different paths
await sandbox.mountBucket("MY_BUCKET", "/training-data", {
prefix: "/ml/training/",
});
await sandbox.mountBucket("MY_BUCKET", "/test-data", {
prefix: "/ml/testing/",
});// Mount only the /uploads/images/ subdirectory
await sandbox.mountBucket('MY_BUCKET', '/images', {
prefix: '/uploads/images/'
});
// Files appear at mount point without the prefix
// Bound bucket: my-r2-bucket/uploads/images/photo.jpg
// Mounted path: /images/photo.jpg
await sandbox.exec('ls', { args: ['/images'] });
// Write to subdirectory
await sandbox.writeFile('/images/photo.jpg', imageData);
// Creates my-r2-bucket/uploads/images/photo.jpg
// Mount different prefixes to different paths
await sandbox.mountBucket('MY_BUCKET', '/training-data', {
prefix: '/ml/training/'
});
await sandbox.mountBucket('MY_BUCKET', '/test-data', {
prefix: '/ml/testing/'
});Монтирования только для чтения
Защитите данные, монтируя бакеты в режиме только для чтения:
await sandbox.mountBucket("MY_BUCKET", "/data", {
readOnly: true,
});
// Reads work
await sandbox.exec("cat", { args: ["/data/dataset.csv"] });
// Writes fail
await sandbox.writeFile("/data/new-file.txt", "data"); // Error: Read-only filesystemawait sandbox.mountBucket('MY_BUCKET', '/data', {
readOnly: true
});
// Reads work
await sandbox.exec('cat', { args: ['/data/dataset.csv'] });
// Writes fail
await sandbox.writeFile('/data/new-file.txt', 'data'); // Error: Read-only filesystemЛокальная разработка
Вы также можете монтировать бакеты R2 при локальной разработке с помощью wrangler dev передав localBucket опция. Продакшен-монтирования привязки R2 и локальные localBucket монтирования в обоих случаях обходятся без явных учётных данных, но представляют собой разные пути выполнения. В продакшене используется перехват исходящего трафика без учётных данных с наложением на целевой путь. При локальной разработке применяется периодическая синхронизация с привязкой R2.
await sandbox.mountBucket("MY_BUCKET", "/data", {
localBucket: true,
});
// Access files using standard operations
await sandbox.exec("ls", { args: ["/data"] });
await sandbox.writeFile("/data/results.json", JSON.stringify(results));await sandbox.mountBucket('MY_BUCKET', '/data', {
localBucket: true
});
// Access files using standard operations
await sandbox.exec('ls', { args: ['/data'] });
await sandbox.writeFile('/data/results.json', JSON.stringify(results)); readOnly и prefix опции работают так же в локальном режиме:
// Read-only local mount
await sandbox.mountBucket("MY_BUCKET", "/data", {
localBucket: true,
readOnly: true,
});
// Mount a subdirectory
await sandbox.mountBucket("MY_BUCKET", "/images", {
localBucket: true,
prefix: "/uploads/images/",
});// Read-only local mount
await sandbox.mountBucket('MY_BUCKET', '/data', {
localBucket: true,
readOnly: true
});
// Mount a subdirectory
await sandbox.mountBucket('MY_BUCKET', '/images', {
localBucket: true,
prefix: '/uploads/images/'
});Особенности локальной разработки
При локальной разработке файлы синхронизируются между R2 и контейнером с помощью периодического процесса синхронизации, а не через прямое подключение файловой системы. Учитывайте следующее:
- Окно синхронизации - Между записью файла и его появлением на другой стороне существует небольшая задержка. Например, если вы загрузите файл в R2 и сразу же попытаетесь прочитать его по смонтированному пути в контейнере, файл может быть ещё недоступен. Прежде чем читать недавно записанные данные, дайте синхронизации немного времени на завершение.
- Частые операции записи - Быстрые последовательные записи в один и тот же файл могут распространяться с небольшой задержкой. Чтобы избежать проблем, не записывайте один и тот же файл одновременно из R2 и контейнера.
- Двусторонняя синхронизация - Изменения в контейнере синхронизируются с R2, а изменения в R2 синхронизируются с контейнером. Оба направления используют одну и ту же модель периодической синхронизации.
Отмонтирование buckets
// Mount for processing
await sandbox.mountBucket("MY_BUCKET", "/data");
// Do work
await sandbox.exec("python process_data.py");
// Clean up
await sandbox.unmountBucket("/data");// Mount for processing
await sandbox.mountBucket('MY_BUCKET', '/data');
// Do work
await sandbox.exec('python process_data.py');
// Clean up
await sandbox.unmountBucket('/data');Другие провайдеры
SDK поддерживает любое S3-совместимое объектное хранилище. Ниже приведены примеры для популярных провайдеров:
Amazon S3
await sandbox.mountBucket("my-s3-bucket", "/data", {
endpoint: "https://s3.us-west-2.amazonaws.com",
credentials: {
accessKeyId: env.AWS_ACCESS_KEY_ID,
secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
},
});await sandbox.mountBucket('my-s3-bucket', '/data', {
endpoint: 'https://s3.us-west-2.amazonaws.com',
credentials: {
accessKeyId: env.AWS_ACCESS_KEY_ID,
secretAccessKey: env.AWS_SECRET_ACCESS_KEY
}
});Google Cloud Storage
await sandbox.mountBucket("my-gcs-bucket", "/data", {
endpoint: "https://storage.googleapis.com",
credentials: {
accessKeyId: env.GCS_ACCESS_KEY_ID,
secretAccessKey: env.GCS_SECRET_ACCESS_KEY,
},
});await sandbox.mountBucket('my-gcs-bucket', '/data', {
endpoint: 'https://storage.googleapis.com',
credentials: {
accessKeyId: env.GCS_ACCESS_KEY_ID,
secretAccessKey: env.GCS_SECRET_ACCESS_KEY
}
});Другие S3-совместимые провайдеры
Для таких провайдеров, как Backblaze B2, MinIO, Wasabi и других, используйте стандартную схему монтирования:
await sandbox.mountBucket("my-bucket", "/data", {
endpoint: "https://s3.us-west-000.backblazeb2.com",
credentials: {
accessKeyId: env.ACCESS_KEY_ID,
secretAccessKey: env.SECRET_ACCESS_KEY,
},
});await sandbox.mountBucket('my-bucket', '/data', {
endpoint: 'https://s3.us-west-000.backblazeb2.com',
credentials: {
accessKeyId: env.ACCESS_KEY_ID,
secretAccessKey: env.SECRET_ACCESS_KEY
}
});Настройки для конкретных провайдеров см. в вики s3fs-fuse ↗ для поддерживаемых провайдеров и рекомендуемых флагов.
Устранение неполадок
Ошибка «R2 binding не найден»
Ошибка: R2 binding "MY_BUCKET" not found in Worker env
Решение: Убедитесь, что у Worker есть r2_buckets привязку и то, что mountBucket() использует имя привязки, а не имя бакета в панели управления:
{
"$schema": "./node_modules/wrangler/config-schema.json",
"r2_buckets": [
{
"binding": "MY_BUCKET",
"bucket_name": "my-r2-bucket"
}
]
}[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-r2-bucket"Монтирование R2 без учётных данных сразу завершается ошибкой
Решение: Убедитесь, что точка входа Worker экспортирует ContainerProxy. Если вы используете более старую версию Wrangler, вам может также понадобиться enable_ctx_exports флаг совместимости.
Ошибка отсутствия учетных данных
Ошибка: MissingCredentialsError: No credentials found
Решение: Эта ошибка возникает только при подключении удалённой S3-совместимой конечной точки с помощью параметра endpoint. Задайте учётные данные как секреты Worker:
npx wrangler secret put R2_ACCESS_KEY_ID
npx wrangler secret put R2_SECRET_ACCESS_KEYили
npx wrangler secret put AWS_ACCESS_KEY_ID
npx wrangler secret put AWS_SECRET_ACCESS_KEYОшибка сбоя подключения
Ошибка: S3FSMountError: mount failed
Распространённые причины:
- Неверный URL конечной точки
- Неверные учётные данные
- Отсутствует
ContainerProxyexport, либо в старых версиях Wrangler отсутствующийenable_ctx_exports - Бакет не существует
- Проблемы с сетевым подключением
Проверьте конфигурацию привязки или эндпоинта:
try {
await sandbox.mountBucket("MY_BUCKET", "/data");
} catch (error) {
console.error("Mount failed:", error.message);
// Check binding name, ContainerProxy export, or remote endpoint configuration
}try {
await sandbox.mountBucket('MY_BUCKET', '/data');
} catch (error) {
console.error('Mount failed:', error.message);
// Check binding name, ContainerProxy export, or remote endpoint configuration
}Ошибка «путь уже смонтирован»
Ошибка: InvalidMountConfigError: Mount path already in use
Решение: Сначала отмонтируйте или используйте другой путь:
// Unmount existing
await sandbox.unmountBucket("/data");
// Or use different path
await sandbox.mountBucket("bucket2", "/storage", { endpoint: "..." });// Unmount existing
await sandbox.unmountBucket('/data');
// Or use different path
await sandbox.mountBucket('bucket2', '/storage', { endpoint: '...' });Медленный доступ к файлам
Файловые операции с примонтированными бакетами выполняются медленнее, чем с локальной файловой системой, из-за сетевых задержек.
Решение: Скопируйте часто используемые файлы локально:
// Copy to local filesystem
await sandbox.exec("cp", {
args: ["/data/large-dataset.csv", "/workspace/dataset.csv"],
});
// Work with local copy (faster)
await sandbox.exec("python", {
args: ["process.py", "/workspace/dataset.csv"],
});
// Save results back to bucket
await sandbox.exec("cp", {
args: ["/workspace/results.json", "/data/results/output.json"],
});// Copy to local filesystem
await sandbox.exec('cp', { args: ['/data/large-dataset.csv', '/workspace/dataset.csv'] });
// Work with local copy (faster)
await sandbox.exec('python', { args: ['process.py', '/workspace/dataset.csv'] });
// Save results back to bucket
await sandbox.exec('cp', { args: ['/workspace/results.json', '/data/results/output.json'] });Рекомендации
- Подключайте заранее - Монтируйте бакеты при инициализации песочницы
- Выбор подходящего режима монтирования - Используйте монтирование через привязку R2, если хотите, чтобы доступом к R2 управлял Worker, либо используйте
endpointдля явного указания R2, S3, GCS и других S3-совместимых провайдеров - Безопасные учётные данные - Всегда используйте секреты Worker, никогда не встраивайте их в код
- Только для чтения, когда это возможно - Защита данных с помощью монтирования в режиме только для чтения
- Подключайте самый узкий путь - Используйте префиксы, чтобы открывать доступ только к тем данным, которые нужны песочнице
- Пути подключения - Предпочитайте
/data,/storage, или/mnt/*; если вы монтируете в/workspace, учитывайте монтирование, которое перекрывает этот путь в продакшене - Обработка ошибок - Оборачивайте операции монтирования в
try...catchблокирует - Оптимизация доступа - Копируйте часто используемые файлы локально
Дополнительные материалы
- Руководство по постоянному хранению данных - Полный пример работы с R2
- Справочник Storage API - Полная документация по методам
- Переменные окружения - Конфигурация учетных данных для монтирования удаленных конечных точек
- Конфигурация Wrangler - Настройте привязки R2 и флаги совместимости
- Документация R2 - Узнайте больше о Cloudflare R2
- Исходящий трафик - Узнайте, как
ContainerProxyи перехват исходящих запросов работают