Cloudflare Pulumi Provider 资源配置全解:用 @pulumi/cloudflare v6.x 管理 Workers、KV、D1、R2 与 Queues
【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills
本文以 skills 仓库中 cloudflare-deploy 技能 的 Pulumi 资源配置参考 为主体,系统讲解如何用 TypeScript 编写 Pulumi 程序,以 IaC 方式程序化创建 Cloudflare Workers 及其绑定资源。读完本文,你将掌握
@pulumi/cloudflarev6.x 下 WorkerScript、Workers KV、R2、D1、Queues、Pages、DNS、自定义域名、静态资源托管以及 v6.x 版本化部署的完整配置写法,并理解 binding 名称匹配、依赖编排与常见排错要点,可直接照搬到自己的部署项目中。
一、配置之前:认证与 Stack 基础
configuration.md中的所有资源示例都依赖两个前置事实:一个可用的 Cloudflare Provider 实例,以及从 stack 配置中读取的accountId。这部分前置配置定义在配套的 pulumi/README.md 中,是整个资源配置能够运行的前提。
1. 认证方式(推荐 API Token)
官方推荐使用 API Token 而非旧的 API Key:
import * as cloudflare from "@pulumi/cloudflare"; // API Token(推荐):从 CLOUDFLARE_API_TOKEN 环境变量读取 const provider = new cloudflare.Provider("cf", { apiToken: process.env.CLOUDFLARE_API_TOKEN }); // API Key(旧式):CLOUDFLARE_API_KEY + CLOUDFLARE_EMAIL 环境变量 const provider = new cloudflare.Provider("cf", { apiKey: process.env.CLOUDFLARE_API_KEY, email: process.env.CLOUDFLARE_EMAIL }); // API User Service Key:CLOUDFLARE_API_USER_SERVICE_KEY 环境变量 const provider = new cloudflare.Provider("cf", { apiUserServiceKey: process.env.CLOUDFLARE_API_USER_SERVICE_KEY });2. Stack 配置:accountId 与 apiToken 的存放位置
在Pulumi.yaml中声明项目与运行时,并把 token 交给 Pulumi 配置系统:
# Pulumi.yaml name: my-cloudflare-app runtime: nodejs config: cloudflare:apiToken: value: ${CLOUDFLARE_API_TOKEN}在每个环境的Pulumi.<stack>.yaml中存放账号 ID(accountId是绝大多数资源必填的属性):
# Pulumi.<stack>.yaml,例如 Pulumi.prod.yaml config: cloudflare:accountId: "abc123..."在入口文件index.ts中通过pulumi.Config读取:
import * as pulumi from "@pulumi/pulumi"; import * as cloudflare from "@pulumi/cloudflare"; const accountId = new pulumi.Config("cloudflare").require("accountId");3. 五项核心原则
来自 pulumi/README.md 的 v6.x 使用原则,贯穿本文所有示例:
- 使用 API Token 而非旧式 API Key;
- 将
accountId存放在 stack 配置中; - 让 binding 名称在代码与配置之间严格一致;
- Worker 使用
module: true声明 ES modules; - 始终设置
compatibilityDate锁定运行时行为。
二、资源类型总览
@pulumi/cloudflarev6.x 中本文会涉及的常用资源类型(来自 pulumi/README.md):
| 资源类型 | 对应 Cloudflare 产品 | 用途 |
|---|---|---|
Provider | —— | Provider 配置(认证) |
WorkerScript | Workers | 部署 Worker 脚本 |
WorkersKvNamespace | Workers KV | 键值存储命名空间 |
R2Bucket | R2 | 对象存储桶 |
D1Database | D1 | 关系型 SQL 数据库 |
Queue | Queues | 消息队列 |
PagesProject | Pages | 全栈站点项目 |
DnsRecord | DNS | DNS 记录 |
WorkerRoute | Workers Routes | 基于 pattern 的路由 |
WorkersDomain | Workers Custom Domains | 专属子域名 |
关键公共属性:accountId(多数资源必填)、zoneId(DNS/域名必填)、name/title(资源标识符)、*Bindings(将资源连接到 Worker 的绑定族)。
三、Workers:cloudflare.WorkerScript 的完整配置
WorkerScript是部署 Worker 的核心资源,configuration.md给出的完整示例覆盖了从模块格式、兼容性配置到可观测性、Placement 与全部绑定类型:
import * as cloudflare from "@pulumi/cloudflare"; import * as fs from "fs"; const worker = new cloudflare.WorkerScript("my-worker", { accountId: accountId, name: "my-worker", content: fs.readFileSync("./dist/worker.js", "utf8"), module: true, // ES modules compatibilityDate: "2025-01-01", compatibilityFlags: ["nodejs_compat"], // v6.x: Observability logpush: true, // 启用 Workers Logpush tailConsumers: [{service: "log-consumer"}], // 将日志流式转发给另一个 Worker // v6.x: Placement placement: {mode: "smart"}, // Smart Placement 优化延迟 // Bindings kvNamespaceBindings: [{name: "MY_KV", namespaceId: kv.id}], r2BucketBindings: [{name: "MY_BUCKET", bucketName: bucket.name}], d1DatabaseBindings: [{name: "DB", databaseId: db.id}], queueBindings: [{name: "MY_QUEUE", queue: queue.id}], serviceBindings: [{name: "OTHER_SERVICE", service: other.name}], plainTextBindings: [{name: "ENV_VAR", text: "value"}], secretTextBindings: [{name: "API_KEY", text: secret}], // v6.x: Advanced bindings analyticsEngineBindings: [{name: "ANALYTICS", dataset: "my-dataset"}], browserBinding: {name: "BROWSER"}, // Browser Rendering aiBinding: {name: "AI"}, // Workers AI hyperdriveBindings: [{name: "HYPERDRIVE", id: hyperdriveConfig.id}], });关键参数逐项说明
content:Worker 代码内容,直接读取构建产物(如./dist/worker.js)。这里有一个重要前提——Pulumi 不会帮你打包/bundle 代码,它只上传你提供的内容。这一点在 pulumi/gotchas.md 中有明确警告:如果直接读取原始index.ts而非构建后的 JS,Worker 会报Cannot use import statement outside a module。正确做法是先构建再部署(详见下文"实战编排"小节)。module: true:声明 Worker 使用 ES modules 格式;对应的compatibilityFlags: ["nodejs_compat"]开启 Node.js 兼容层,便于使用node:*模块。compatibilityDate:锁定 Worker 运行时的兼容性日期,防止 Cloudflare 侧行为变更造成破坏,是官方推荐必须设置的值。logpush/tailConsumers(v6.x Observability):logpush: true启用 Workers Logpush 把日志推送到外部;tailConsumers则把实时日志流式转发给另一个 Worker(service指向其名称),适合日志消费/观测场景。placement: {mode: "smart"}(v6.x):开启 Smart Placement,让 Worker 就近后端源站运行以优化延迟。- 绑定(Bindings)族:连接 KV / R2 / D1 / Queue / Service / 环境变量 / 密钥 / Analytics Engine / Browser Rendering / Workers AI / Hyperdrive。绑定名称(如
MY_KV、API_KEY)会注入 Worker 运行时的env对象中。
绑定名称必须精确匹配
gotchas.md 强调:绑定名称区分大小写,Pulumi 侧的name必须与 Worker 代码中env的引用完全一致,否则运行时会报env.MY_KV is undefined:
// Pulumi 侧 kvNamespaceBindings: [{name: "MY_KV", namespaceId: kv.id}]// Worker 代码侧 export default { async fetch(request, env) { await env.MY_KV.get("key"); }}四、Workers KV:命名空间与键值写入
KV 由命名空间(namespace)与具体的键值(value)两层组成:
const kv = new cloudflare.WorkersKvNamespace("my-kv", { accountId: accountId, title: "my-kv-namespace", }); // 写入值 const kvValue = new cloudflare.WorkersKvValue("config", { accountId: accountId, namespaceId: kv.id, key: "config", value: JSON.stringify({foo: "bar"}), });注意 KV 命名空间的资源标识字段是title(而非name),创建后通过kv.id引用;WorkersKvValue允许在 IaC 阶段就把初始化数据(如配置 JSON)写入命名空间,适合会话、缓存、配置类数据。
五、R2 Buckets:对象存储
const bucket = new cloudflare.R2Bucket("my-bucket", { accountId: accountId, name: "my-bucket", location: "auto", // 或 "wnam" 等地域标识 });location: "auto"表示由 Cloudflare 自动选择存储地域;需要固定地域时可按供应商文档指定如wnam(西美)等取值。创建后用bucket.name注入 Worker 的r2BucketBindings。
六、D1 数据库与迁移编排
创建 D1 数据库后,Pulumi不会自动执行迁移。configuration.md给出的标准做法是结合@pulumi/command的command.local.Command资源,在数据库创建完成后用 wrangler CLI 执行 SQL:
const db = new cloudflare.D1Database("my-db", {accountId, name: "my-database"}); // 通过 wrangler 执行迁移 import * as command from "@pulumi/command"; const migration = new command.local.Command("d1-migration", { create: pulumi.interpolate`wrangler d1 execute ${db.name} --file ./schema.sql`, }, {dependsOn: [db]});dependsOn: [db]显式声明依赖,确保数据库先创建、迁移后执行。同理,如果 Worker 依赖迁移完成后的表结构,应让 Worker 的d1DatabaseBindings所在资源dependsOn: [migration](详见 pulumi/api.md 的显式依赖示例)。
七、Queues:生产者与消费者配置
队列由Queue资源定义,生产者通过queueBindings写入,消费者通过queueConsumers订阅:
const queue = new cloudflare.Queue("my-queue", {accountId, name: "my-queue"}); // Producer:把消息写入队列 const producer = new cloudflare.WorkerScript("producer", { accountId, name: "producer", content: code, queueBindings: [{name: "MY_QUEUE", queue: queue.id}], }); // Consumer:异步消费 const consumer = new cloudflare.WorkerScript("consumer", { accountId, name: "consumer", content: code, queueConsumers: [{queue: queue.name, maxBatchSize: 10, maxRetries: 3}], });消费者侧可配置的关键参数:maxBatchSize(单批最大消息数,示例为 10)、maxRetries(最大重试次数,示例为 3);在 patterns.md 的队列处理模式中还出现了maxWaitTimeMs: 5000(最长批量等待时间)。这一配置直接支撑"API 接收请求 → 队列削峰 → Worker 异步处理"的典型架构。
八、Pages Projects:全栈站点配置
PagesProject支持 Git 源自动构建与生产环境配置注入:
const pages = new cloudflare.PagesProject("my-site", { accountId, name: "my-site", productionBranch: "main", buildConfig: {buildCommand: "npm run build", destinationDir: "dist"}, source: { type: "github", config: {owner: "my-org", repoName: "my-repo", productionBranch: "main"}, }, deploymentConfigs: { production: { environmentVariables: {NODE_VERSION: "18"}, kvNamespaces: {MY_KV: kv.id}, d1Databases: {DB: db.id}, }, }, });要点拆解:
buildConfig:声明构建命令(buildCommand)与产物目录(destinationDir),对应 Pages 的构建配置;source:关联 GitHub 仓库(type: "github"+ 仓库所有者的config),配置后 Pages 支持 Git 触发自动部署;deploymentConfigs.production:为生产环境注入环境变量(NODE_VERSION等)以及把 KV、D1 等资源绑定到 Pages 运行时,其中键(如MY_KV、DB)就是 Pages Functions 侧引用的绑定名。
九、DNS Records:Zone 数据源与记录创建
需要先通过cloudflare.getZone数据源查询目标域名对应的 zone,再创建记录:
const zone = cloudflare.getZone({name: "example.com"}); const record = new cloudflare.DnsRecord("www", { zoneId: zone.then(z => z.id), name: "www", type: "A", content: "192.0.2.1", ttl: 3600, proxied: true, });zoneId:通过getZone数据源异步获取(zone.then(z => z.id)拿到 Output 值);type/content/ttl:记录类型、解析值、TTL(秒,3600 为 1 小时);proxied: true:开启 Cloudflare 代理(橙色云朵),流量先经过 Cloudflare 边缘。
十、Workers Domains 与 Routes:流量接入的两种方式
把 Worker 暴露到域名有两种方式,按需选择:
// 方式一:Route(基于 pattern 的路由,作用于 Zone 内路径) const route = new cloudflare.WorkerRoute("my-route", { zoneId: zoneId, pattern: "example.com/api/*", scriptName: worker.name, }); // 方式二:Domain(专属子域名) const domain = new cloudflare.WorkersDomain("my-domain", { accountId: accountId, hostname: "api.example.com", service: worker.name, zoneId: zoneId, });WorkerRoute:把某个域名下的路径模式(example.com/api/*)路由到指定 Worker,scriptName引用 Worker 的name;WorkersDomain:为 Worker 分配专属子域名(hostname),service指定 Worker 名称,需要accountId与zoneId同时提供。
十一、Assets 静态资源配置(v6.x)
v6.x 允许 Worker 直接托管本地静态资源目录,无需单独的对象存储:
const worker = new cloudflare.WorkerScript("app", { accountId: accountId, name: "my-app", content: code, assets: { path: "./public", // 本地目录,上传后由 Workers 直接托管 }, });assets.path指向本地静态目录(如构建后的前端产物./public),Cloudflare 会将其中文件作为 Worker 的静态资源上传并服务,适合单仓库同时交付 API 与前端静态文件的场景。
十二、v6.x 版本化部署:三资源模式
v6.x 引入了 Worker 版本化机制。configuration.md给出的高级模式由三个资源协作,实现渐进式发布:
// 1. Worker:作为版本的容器 const worker = new cloudflare.Worker("api", { accountId: accountId, name: "api-worker", }); // 2. Version:不可变的代码 + 配置 const version = new cloudflare.WorkerVersion("v1", { accountId: accountId, workerId: worker.id, content: fs.readFileSync("./dist/worker.js", "utf8"), compatibilityDate: "2025-01-01", compatibilityFlags: ["nodejs_compat"], // 注意:Bindings 在 Deployment 层配置 }); // 3. Deployment:版本 + 绑定 + 流量分配 const deployment = new cloudflare.WorkersDeployment("prod", { accountId: accountId, workerId: worker.id, versionId: version.id, // Bindings 作用于 Deployment kvNamespaceBindings: [{name: "MY_KV", namespaceId: kv.id}], });适用场景:蓝绿部署(blue-green)、金丝雀发布(canary)、渐进式灰度(gradual rollout)。结合 patterns.md 的示例,WorkersDeployment还可以通过versions数组按百分比切流:
// 渐进式发布:10% 流量到 v2,90% 到 v1 const deployment = new cloudflare.WorkersDeployment("canary", { accountId, workerId: worker.id, versions: [{versionId: v2.id, percentage: 10}, {versionId: v1.id, percentage: 90}], kvNamespaceBindings: [{name: "MY_KV", namespaceId: kv.id}], });不适用场景:简单的单版本部署——此时应直接使用WorkerScript(自带自动版本管理,是绝大多数应用的默认选择)。gotchas.md 特别提醒:如果创建了Worker+WorkerVersion+WorkersDeployment但流量没有打到 Worker,往往是三资源链路不完整导致;简单场景务必回到WorkerScript。
十三、实战编排:一个完整全栈应用的资源依赖
把以上资源串起来,参考 patterns.md 的 Full-Stack 模式,一个带 KV 缓存、D1 数据库、R2 对象存储的 API Worker 的完整编排如下:
const kv = new cloudflare.WorkersKvNamespace("cache", {accountId, title: "api-cache"}); const db = new cloudflare.D1Database("db", {accountId, name: "app-database"}); const bucket = new cloudflare.R2Bucket("assets", {accountId, name: "app-assets"}); const apiWorker = new cloudflare.WorkerScript("api", { accountId, name: "api-worker", content: fs.readFileSync("./dist/api.js", "utf8"), module: true, kvNamespaceBindings: [{name: "CACHE", namespaceId: kv.id}], d1DatabaseBindings: [{name: "DB", databaseId: db.id}], r2BucketBindings: [{name: "ASSETS", bucketName: bucket.name}], });这里的依赖是隐式的:kv.id、db.id、bucket.name作为 Input 传入绑定后,Pulumi 自动建立"Worker 依赖这三个资源"的关系(详见 api.md 的隐式依赖说明),无需手写dependsOn。
另外两种与构建/部署强相关的编排模式:
先构建后部署(解决 Pulumi 不打包的问题):
import * as command from "@pulumi/command"; const build = new command.local.Command("build", {create: "npm run build", dir: "./worker"}); const worker = new cloudflare.WorkerScript("worker", { accountId, name: "my-worker", content: build.stdout.apply(() => fs.readFileSync("./worker/dist/index.js", "utf8")), }, {dependsOn: [build]});内容版本强制刷新(解决"内容哈希未变导致误判无变更"的问题):
const version = Date.now().toString(); const worker = new cloudflare.WorkerScript("worker", { accountId, name: "my-worker", content: code, plainTextBindings: [{name: "VERSION", text: version}], // 强制触发新部署 });gotchas.md 记录了此类问题的成因:当代码仅有空白/注释级别的变化时内容哈希相同,Pulumi 会误判"no changes",注入时间戳版本的plainTextBindings是推荐的规避手段。
十四、配置相关的常见错误速查
来自 pulumi/gotchas.md,与本文配置内容直接相关的几类高频问题:
| 报错/现象 | 根因 | 解法 |
|---|---|---|
Error: Missing required property 'accountId' | 资源未提供 accountId | 在Pulumi.<stack>.yaml中添加cloudflare:accountId |
env.MY_KV is undefined | Pulumi 绑定名与 Worker 代码不一致 | 严格按大小写对齐绑定名 |
Cannot use import statement outside a module | Pulumi 直接上传了未构建的 TS 源码 | 先npm run build再用command.local.Command驱动部署 |
本地wrangler dev正常但pulumi up失败 | Pulumi 不读取wrangler.toml | 将 Pulumi 配置导出生成wrangler.toml,保持单一事实源 |
| D1 建库后 schema 未生效 | Pulumi 只建库不跑迁移 | 用command.local.Command+dependsOn执行wrangler d1 execute |
| 代码改了但 Pulumi 显示 "no changes" | 内容哈希未变 | 注入VERSION时间戳绑定强制更新 |
认证报错authentication error (10000) | Token 权限不足 | 授予Account.Workers Scripts:Edit、Account.Account Settings:Read等权限 |
推荐的最佳实践归纳:始终设置compatibilityDate;构建先于部署;绑定名大小写精确匹配;迁移用dependsOn串行;敏感信息通过pulumi config set --secret存入 stack 配置(在 state 中加密存储,见 api.md 的 Secrets 管理)。
十五、延伸阅读
- 本技能入口与决策树:cloudflare-deploy/SKILL.md("我需要基础设施即代码"分支指向本参考)
- Pulumi 参考整体导读与认证/Stack 配置:pulumi/README.md
- 输出、依赖、导入与密钥管理:pulumi/api.md
- 多环境、组件化资源、队列与微服务架构模式:pulumi/patterns.md
- 排错与最佳实践:pulumi/gotchas.md
- 相关替代方案:Terraform 参考 terraform/、CLI 部署参考 wrangler/、绑定体系 bindings/、Worker 运行时 workers/
【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考