跳转到内容
本站为社区维护的非官方中文翻译,与 Cloudflare, Inc. 无关联。本站不提供账户登录,请勿在任何非官方站点输入您的 Cloudflare 账户信息。官方文档:developers.cloudflare.com

迁移存储桶挂载

最后更新 查看 MarkdownAgent 设置

将 0.12 存储桶挂载迁移到 S3Mount,它会挂载相同的存储桶前缀,且容器中没有存储桶凭据。Worker 中的 S3Gateway 为每个存储请求签名。在 0.12 中,mountBucket() 将凭据放入容器,或通过 R2 绑定挂载。

开始之前

你的 1.0 类已替换 0.12 Sandbox 类,并具有 替换 Sandbox 类 中的 container getter、ensureRunning() 和 startContainer() 方法,以及 ENV 常量。其镜像包含 S3Mount 使用的 sandbox-shim。S3Mount 通过 this.ctx.exports 到达 S3Gateway,这需要 compatibility date 2025-11-17 或更晚,或 enable_ctx_exports 标志。

本页替换 mountBucket()、unmountBucket()、它们的选项以及它们的错误。要在新项目中构建相同挂载,请参阅 挂载 R2 存储桶。

挂载存储桶

  1. 将 FUSE 和 s3fs 添加到 Dockerfile:

    Dockerfiledockerfile
    RUN apt-get update \
    	&& apt-get install -y --no-install-recommends \
    		ca-certificates fuse3 git python3 s3fs \
    	&& rm -rf /var/lib/apt/lists/*
  2. 从 Worker 的主模块导出 S3Gateway:

    src/index.tsts
    import {
    	S3Mount,
    	SandboxS3MountError,
    } from "@cloudflare/sandbox";
    
    // S3Mount sends each storage request to this entrypoint.
    export { S3Gateway } from "@cloudflare/sandbox";

    然后添加存储桶的端点和名称作为变量,并将其凭据作为密钥,如 挂载 R2 存储桶 的第 3 步和第 4 步所示。0.12 从环境读取 AWS_* 和 R2_* 变量。S3Mount 只使用你传给它的凭据。

    1.0 无法通过 R2 绑定挂载存储桶。如果你的 0.12 代码用绑定名称调用 mountBucket() 且没有 endpoint,请为该存储桶创建具有 Object Read & Write 权限的 R2 API 令牌。

  3. 将 mountBucket() 替换为为 sandbox 挂载前缀的方法:

    src/index.ts (0.12)ts
    const sandbox = getSandbox(env.Sandbox, job);
    await sandbox.mountBucket(env.S3_BUCKET, "/data", {
    	endpoint: env.S3_ENDPOINT,
    	prefix: `/jobs/${job}/`,
    });
    src/index.ts (1.0)ts
    export class MySandbox extends DurableObject<Env> {
    	// ...
    
    	// One S3Mount serves every container of this Durable Object.
    	private readonly mounts = new S3Mount(
    		this.container,
    		this.ctx.exports.S3Gateway,
    	);
    
    	private async mountData(): Promise<void> {
    		// The name that your Worker passed to getByName(),
    		// or to getSandbox() in 0.12
    		const job = this.ctx.id.name;
    
    		if (!job) {
    			throw new Error("Open the sandbox with getByName()");
    		}
    
    		await this.mounts.mount({
    			mountPath: "/data",
    			source: {
    				type: "s3",
    				endpoint: this.env.S3_ENDPOINT,
    				region: "auto",
    				bucket: this.env.S3_BUCKET,
    				credentials: {
    					type: "static",
    					accessKeyId: this.env.S3_ACCESS_KEY_ID,
    					secretAccessKey: this.env.S3_SECRET_ACCESS_KEY,
    				},
    			},
    			keyPrefix: `jobs/${job}`,
    			access: "read-write",
    		});
    	}
    }

    选项变化如下:

    0.12 1.0
    bucket 参数 source.bucket
    endpoint source.endpoint,以及 region(R2 使用 auto)
    prefix: "/jobs/ada/" keyPrefix: "jobs/ada"。不能以 / 开头,S3Mount 会添加尾部 /
    readOnly: true access: "read-only"。access 是必填的
    s3fsOptions: ["name=value"] s3fsOptions: { name: "value" }。S3Mount 始终设置 nomixupload(0.12 仅对 R2 添加它),并在此处拒绝该选项
    credentials source.credentials,带有 type: "static"
    provider、credentialProxy 移除它们。S3Gateway 为每个请求签名
  4. 在每个使用该前缀的方法中挂载它:

    src/index.tsts
    export class MySandbox extends DurableObject<Env> {
    	// ...
    
    	async listFiles(): Promise<string> {
    		await this.ensureRunning();
    		await this.mountData();
    
    		const ls = await this.container.exec(["ls", "-la"], {
    			cwd: "/data",
    			env: ENV,
    		});
    		const output = await ls.output();
    		return new TextDecoder().decode(output.stdout);
    	}
    }

    挂载会持续到容器停止,在 0.12 和 1.0 中都是如此。使用相同请求再次调用 mount() 会复用该挂载。对已挂载路径使用其他设置的请求会抛出代码为 S3_MOUNT_CONFLICT 的 SandboxS3MountError,而 0.12 会抛出 InvalidMountConfigError。

  5. 替换 unmountBucket(),并通过 code 检查错误:

    src/index.ts (0.12)ts
    await sandbox.unmountBucket("/data");
    src/index.ts (1.0)ts
    export class MySandbox extends DurableObject<Env> {
    	// ...
    
    	async unmountData(): Promise<string> {
    		try {
    			await this.mounts.unmount("/data");
    			return "unmounted";
    		} catch (error) {
    			if (SandboxS3MountError.is(error)) {
    				return error.code;
    			}
    
    			throw error;
    		}
    	}
    }

    S3_MOUNT_BUSY 表示仍有进程使用该路径。对该路径的请求会保持被拒绝。停止该进程后再次调用 unmount()。0.12 错误的映射如下:

    0.12 1.0
    InvalidMountConfigError 无效请求(例如以 / 开头的 keyPrefix)为 TypeError,路径已被占用时为 S3_MOUNT_CONFLICT
    MissingCredentialsError 无。source.credentials 是必填的
    S3FSMountError、BucketMountError S3_MOUNT_FAILED
    BucketUnmountError 有进程使用该路径时为 S3_MOUNT_BUSY

如果你已迁移出站规则

每个挂载都会为其自己的主机名注册 intercept。只有当挂载在 迁移出站规则 中的 interceptAllOutboundHttp() 调用之前运行时,该 intercept 才会接收请求。在该调用之后挂载会失败并出现 cannot verify the new FUSE connection,因为你的 Outbound entrypoint 会接收存储请求。稍后再次注册 catch-all 不会影响先出现的挂载。

在 startContainer() 的 try 块中、在 intercept() 之前挂载前缀:

src/index.tsts
export class MySandbox extends DurableObject<Env> {
	// ...

	private async startContainer(): Promise<void> {
		const newContainer = !this.container.running;

		if (newContainer) {
			this.container.start({
				image: this.container.images.sandbox,
				instance: "standard-1",
				env: ENV,
				enableInternet: ENABLE_INTERNET,
			});
		}

		try {
			if (newContainer) {
				// Replaces onStart().
				await this.ctx.storage.put("startedAt", Date.now());
			}
			// Mount before the catch-all intercept.
			await this.mountData();
			await this.intercept();
			await this.trust();
			await this.container.setInactivityTimeout(INACTIVITY_TIMEOUT_MS);
		} catch (error) {
			// The next request starts a new container.
			await this.container.destroy();
			throw error;
		}
	}
}

从其他方法中移除 mountData() 调用。在 intercept() 之后挂载的路径无法工作,因此请在此处挂载 sandbox 需要的每个路径。要稍后挂载路径,请打开新的 sandbox。重启后的 Durable Object 会再次运行 startContainer(),mount() 会复用已匹配的挂载。

每个挂载使用容器 64 个 intercept 目标 中的一个,unmount() 不会释放它。迁移出站规则会使用两个。请给每个作业使用自己的 sandbox 名称,而不是在一个容器中为每个作业挂载新前缀。

替换 localBucket

0.12 在 wrangler dev 下使用 localBucket: true 挂载,方法是在容器和 R2 绑定之间复制文件。S3Mount 在 wrangler dev 下改为配合本机上兼容 S3 的服务器工作。请参阅 本地开发。

切换时的挂载

0.12 挂载会随 0.12 容器结束,对象留在存储桶中。切换后,直到 mountData() 运行前,新容器中不存在该挂载路径。0.12 在 prefix: "/jobs/ada/" 下写入的文件,会出现在 keyPrefix: "jobs/ada" 下的相同路径。

如果你的 0.12 类也有出站规则,已存储的规则可能包含名为 r2EgressMount 和 s3CredentialProxyMount 的 handler,用于切换时处于活动状态的挂载。来自迁移出站规则的 entrypoint 会跳过它们。

检查挂载

部署切换后,在 0.12 中挂载过其前缀的 sandbox 上调用 listFiles()。它会列出 0.12 写入的文件:

total 9
drwxrwxrwx 1 root root 4096 Jan  1  1970 .
drwxr-xr-x 1 2346 2346  140 Sep 27 20:20 ..
-rw-r--r-- 1 root root   20 Sep 27 20:11 hello.txt
drwxr-xr-x 1 root root 4096 Sep 27 20:11 sub

this.mounts.inspect("/data") 会报告挂载状态。正常工作的挂载会返回 attachment 状态 managed、fuse 状态 connected,以及 gateway 状态 reachable 且 upstream 状态为 usable。

相关资源

这篇文档对您有帮助吗?