Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

バケットをマウントする

最終更新 Markdown で表示Agent セットアップ

S3 互換のオブジェクトストレージバケットを、ローカルのファイルシステムパスとしてマウントします。標準のファイル操作でオブジェクトストレージにアクセスできます。本番の Cloudflare R2 では、Worker の R2 バインディング名でマウントし、認証情報を Worker ランタイム内に置けます。

本番での R2 バインディングマウントの前提条件

コンテナへ認証情報を渡さずに本番で R2 バケットをマウントするには、R2 バインディングを追加し、Worker のエントリポイントから ContainerProxy をエクスポートします。

{
  "$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() の第 1 引数は MY_BUCKET のような Worker の R2 バインディング名である必要があります。

バケットをマウントするタイミング

次が必要なときに、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_BUCKETwrangler.toml のバインディング名です。ダッシュボード上のバケット名と一致させる必要はありません。多くのプロジェクトでは同じ名前を使います。

認証情報

R2 バインディングマウントに認証情報は不要です。リモートエンドポイントマウントは、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 リクエストを傍受し、本物の認証情報で再署名してから上流へ転送します。コンテナが持つのはダミーの認証情報だけで、プロキシの外では使えません。

これは S3 互換エンドポイント(R2 を含む)向けの AWS SigV4 署名と、Google Cloud Storage 向けの HMAC 署名に対応します。エンドポイント指定のマウントでは、すべて 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 filesystem
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 filesystem

ローカル開発

wrangler dev 中も、localBucket オプションを渡して R2 バケットをマウントできます。本番の 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));

readOnlyprefix は、ローカルモードでも同じように動作します。

// 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 側の変更はコンテナへ同期します。どちらも同じ定期同期モデルです。

バケットをアンマウントする

// 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 wiki を参照してください。

トラブルシューティング

R2 バインディングが見つからないエラー

エラー: 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

対処: このエラーは、endpoint を設定してリモートの S3 互換エンドポイントをマウントするときにだけ発生します。認証情報を 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 が正しくない
  • 認証情報が無効
  • ContainerProxy のエクスポートがない。古い 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'] });

ベストプラクティス

  • 早めにマウントする - サンドボックス初期化時にバケットをマウントします
  • マウントモードを選ぶ - Worker 管理の R2 アクセスなら R2 バインディングマウント、明示的な R2、S3、GCS、そのほかの S3 互換プロバイダーなら endpoint を使います
  • 認証情報を安全に扱う - 常に Worker シークレットを使い、ハードコードしないでください
  • 可能なら読み取り専用にする - 読み取り専用マウントでデータを保護します
  • 最も狭いパスをマウントする - プレフィックスで、サンドボックスが必要なデータだけを公開します
  • マウントパス - /data/storage/mnt/* を優先します。/workspace 配下へマウントする場合は、本番でそのパスにオーバーレイされることを考慮します
  • エラーを扱う - マウント操作は try...catch で囲みます
  • アクセスを最適化する - よく使うファイルはローカルへコピーします

関連リソース

役に立ちましたか?