
Cloudflare Pulumi 架构模式实战从组件化 Worker 到版本化发布【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills本篇指南以仓库内 patterns.md 为骨架系统讲解如何用pulumi/cloudflarev6.x编排 Cloudflare Workers 平台资源覆盖组件化封装、全栈绑定、多环境、队列处理、微服务、事件驱动与蓝绿/金丝雀发布等十种高频架构模式。读完本文你将能直接复刻这些模式把 Pulumi 的 IaC 能力与 Workers 生态结合起来搭建可维护、可演进、可灰度发布的云上应用。前置认知本文模式所依赖的 Pulumi 基础所有模式都建立在同一个 Pulumi Provider 之上在动手前先确认以下前提详见 README.md 与 configuration.md包与版本TypeScript/JS 使用pulumi/cloudflare本文全部示例基于 v6.x API认证优先使用 API TokenCLOUDFLARE_API_TOKEN环境变量而不是遗留的 API Key配置accountId应放入 stack 配置Pulumi.stack.yaml统一管理大多数资源都要求它代码形态使用 ES modules 时务必设置module: true并始终设置compatibilityDate锁定 Worker 行为依赖管理通过*BindingsKV/D1/R2/Queue 绑定把存储资源连接到 Worker绑定名必须与 Worker 代码中env访问的名称完全一致大小写敏感。这些原则在下面的每个模式中都会被反复用到是理解代码的钥匙。组件化资源用ComponentResource封装 Worker 应用当应用由 KV 命名空间、Worker 脚本和自定义域名三件套组成时可以将其封装为一个 Pulumi 组件资源实现“一处定义、多处复用”class WorkerApp extends pulumi.ComponentResource { constructor(name: string, args: WorkerAppArgs, opts?) { super(custom:cloudflare:WorkerApp, name, {}, opts); const defaultOpts {parent: this}; this.kv new cloudflare.WorkersKvNamespace(${name}-kv, {accountId: args.accountId, title: ${name}-kv}, defaultOpts); this.worker new cloudflare.WorkerScript(${name}-worker, { accountId: args.accountId, name: ${name}-worker, content: args.workerCode, module: true, kvNamespaceBindings: [{name: KV, namespaceId: this.kv.id}], }, defaultOpts); this.domain new cloudflare.WorkersDomain(${name}-domain, { accountId: args.accountId, hostname: args.domain, service: this.worker.name, }, defaultOpts); } }要点拆解类型标识super(custom:cloudflare:WorkerApp, ...)中的custom:前缀是自定义组件资源的约定Pulumi 会以此作为该组件在状态中的类型父级关系{parent: this}把内部资源挂到组件下删除组件时会级联清理其子资源pulumi up的输出也会按树形展示便于查看统一命名KV、Worker、域名均以组件name为前缀生成唯一资源名避免命名冲突对外暴露kv、worker、domain作为组件属性调用方可以继续读取.id、.name等输出Output用于后续组装域名绑定WorkersDomain需要service: this.worker.name指向 Worker 脚本名这就是 Worker 与自定义域名的连接点详见 configuration.md 中 Workers Domains/Routes 一节。封装完成后创建一套完整应用只需要一行const app new WorkerApp(api, {accountId, workerCode: code, domain: api.example.com});全栈 Worker 应用KV / D1 / R2 一站式绑定一个典型的全栈应用同时需要 KV缓存/配置、D1关系型数据与 R2对象存储。模式代码如下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}], });几个关键细节内容来源content直接读取构建产物./dist/api.jsPulumi 不会替你做打包——它会把读到的内容原样上传见 gotchas.md 中 “No bundler/build step” 一节因此生产环境必须先用npm run build产出dist目录绑定即依赖namespaceId: kv.id、databaseId: db.id这类 Output 引用会自动建立隐式依赖Pulumi 会保证存储资源先创建、Worker 后创建无需手写dependsOn绑定命名规范绑定名CACHE、DB、ASSETS是 Worker 代码中env.CACHE、env.DB、env.ASSETS的入口必须与代码一致否则运行时会得到env.XXX is undefinedmodule: true以 ES module 形式上传脚本这是现代 Worker 的推荐形态。完整存储资源定义KV 键值写入、R2 的location、D1 迁移可以参考 configuration.md。多环境管理按 Stack 隔离部署Pulumi 的 Stack 天然适合区分dev、staging、prod环境通过pulumi.getStack()获取当前栈名并注入资源命名const stack pulumi.getStack(); const worker new cloudflare.WorkerScript(worker-${stack}, { accountId, name: my-worker-${stack}, content: code, plainTextBindings: [{name: ENVIRONMENT, text: stack}], });模式价值同一份代码通过pulumi up -s dev/pulumi up -s prod即可分别创建my-worker-dev与my-worker-prod互不干扰plainTextBindings把栈名作为普通文本绑定注入 Worker代码里通过env.ENVIRONMENT即可感知当前环境用于日志标记、配置分支等不同环境的差异化配置如accountId、域名、配额放在各自的Pulumi.stack.yaml中由 stack 配置统一管理。队列驱动处理Producer / Consumer 解耦面向异步处理的场景cloudflare.Queue配合生产端queueBindings与消费端queueConsumers构成完整的队列模式const queue new cloudflare.Queue(processing-queue, {accountId, name: image-processing}); // Producer: API receives requests const apiWorker new cloudflare.WorkerScript(api, { accountId, name: api-worker, content: apiCode, queueBindings: [{name: PROCESSING_QUEUE, queue: queue.id}], }); // Consumer: Process async const processorWorker new cloudflare.WorkerScript(processor, { accountId, name: processor-worker, content: processorCode, queueConsumers: [{queue: queue.name, maxBatchSize: 10, maxRetries: 3, maxWaitTimeMs: 5000}], r2BucketBindings: [{name: OUTPUT_BUCKET, bucketName: outputBucket.name}], });各字段含义queueBindings把队列绑定到生产端 Worker代码中通过env.PROCESSING_QUEUE.send(message)投递消息queueConsumers把 Worker 注册为队列消费者queue传入队列名maxBatchSize控制单批最大消息数示例为 10maxRetries控制失败重试次数示例为 3maxWaitTimeMs控制最长批等待时间示例为 5000ms消费端组合消费 Worker 可以同时绑定其他资源——示例里处理器把处理结果写入 R2OUTPUT_BUCKET体现“消费 产出”的流水线组合。微服务与服务绑定Worker 间直接调用多个 Worker 组成微服务时用serviceBindings在服务间建立直接调用通道避免走公网const authWorker new cloudflare.WorkerScript(auth, {accountId, name: auth-service, content: authCode}); const apiWorker new cloudflare.WorkerScript(api, { accountId, name: api-service, content: apiCode, serviceBindings: [{name: AUTH, service: authWorker.name}], });service字段引用目标 Worker 的name如auth-service与代码中env.AUTH.fetch(request)的调用方式一一对应与直接拼接 URL 相比服务绑定走内部网络、时延更低且由平台负责服务发现绑定建立后apiWorker与authWorker之间即形成隐式依赖Pulumi 会保证被调服务先部署。事件驱动架构统一事件总线当多个下游需要对同一事件流做出反应时可以引入事件队列作为总线生产端只负责投递消费端按需订阅const eventQueue new cloudflare.Queue(events, {accountId, name: event-bus}); const producer new cloudflare.WorkerScript(producer, { accountId, name: api-producer, content: producerCode, queueBindings: [{name: EVENTS, queue: eventQueue.id}], }); const consumer new cloudflare.WorkerScript(consumer, { accountId, name: email-consumer, content: consumerCode, queueConsumers: [{queue: eventQueue.name, maxBatchSize: 10}], });与“队列驱动处理”模式的区别在于意图这里的事件队列承担总线角色event-bus可以挂载多个语义不同的消费者邮件通知、指标统计、审计日志等新增下游只需新增一个带queueConsumers的 Worker而不改动生产端——这正是事件驱动架构的松耦合红利。v6.x 版本化部署蓝绿与金丝雀发布v6.x 引入“Worker WorkerVersion WorkersDeployment”三段式资源把代码版本与流量分配分离从而支持按百分比灰度const worker new cloudflare.Worker(api, {accountId, name: api-worker}); const v1 new cloudflare.WorkerVersion(v1, {accountId, workerId: worker.id, content: fs.readFileSync(./dist/v1.js, utf8), compatibilityDate: 2025-01-01}); const v2 new cloudflare.WorkerVersion(v2, {accountId, workerId: worker.id, content: fs.readFileSync(./dist/v2.js, utf8), compatibilityDate: 2025-01-01}); // Gradual rollout: 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}], });模式要点与 configuration.md、api.md 的版本化章节一致Worker版本容器本身不含代码只定义nameWorkerVersion不可变的代码 配置快照content、compatibilityDate、compatibilityFlagsWorkersDeployment把若干版本按percentage切分流量并在部署层统一声明绑定KV/D1/R2 等示例实现 10% v2 / 90% v1 的金丝雀调整percentage到 100% 即完成全量切换蓝绿适用场景金丝雀发布、A/B 测试、蓝绿部署不适用场景绝大多数单版本应用应使用cloudflare.WorkerScript——它会自动完成版本管理无需手动维护三段资源详见 gotchas.md 中 “v6.x Worker versioning confusion” 一节。若发现部署后 Worker 未接到流量先检查是否误用了缺少WorkersDeployment的三段式写法。生成 wrangler.toml桥接 IaC 与本地开发Pulumi 部署时不会读取wrangler.toml见 gotchas.md 中 “wrangler.toml not consumed” 一节本地wrangler dev与云端配置因此容易漂移。解决思路是反向生成用pulumi/command在资源创建后自动写出wrangler.toml让本地开发与生产使用同一套绑定import * as command from pulumi/command; const workerConfig { name: my-worker, compatibilityDate: 2025-01-01, compatibilityFlags: [nodejs_compat], }; // Create resources const kv new cloudflare.WorkersKvNamespace(kv, {accountId, title: my-kv}); const db new cloudflare.D1Database(db, {accountId, name: my-db}); const bucket new cloudflare.R2Bucket(bucket, {accountId, name: my-bucket}); // Generate wrangler.toml after resources created const wranglerGen new command.local.Command(gen-wrangler, { create: pulumi.interpolatecat wrangler.toml EOF name ${workerConfig.name} main src/index.ts compatibility_date ${workerConfig.compatibilityDate} compatibility_flags ${JSON.stringify(workerConfig.compatibilityFlags)} [[kv_namespaces]] binding MY_KV id ${kv.id} [[d1_databases]] binding DB database_id ${db.id} database_name ${db.name} [[r2_buckets]] binding MY_BUCKET bucket_name ${bucket.name} EOF, }, {dependsOn: [kv, db, bucket]}); // Deploy worker after wrangler.toml generated const worker new cloudflare.WorkerScript(worker, { accountId, name: workerConfig.name, content: code, compatibilityDate: workerConfig.compatibilityDate, compatibilityFlags: workerConfig.compatibilityFlags, kvNamespaceBindings: [{name: MY_KV, namespaceId: kv.id}], d1DatabaseBindings: [{name: DB, databaseId: db.id}], r2BucketBindings: [{name: MY_BUCKET, bucketName: bucket.name}], }, {dependsOn: [wranglerGen]});模式收益与前提wrangler dev与生产环境使用完全相同的绑定KV/D1/R2 名称与 ID 均来自 Pulumi Output杜绝配置漂移单一事实来源一切以 Pulumi 配置为准wrangler.toml只是派生物pulumi.interpolate负责把 Output 安全地插值进 heredoc 字符串dependsOn确保 KV/D1/R2 创建完成后再生成配置文件Worker 则等配置文件就绪后再部署反向模式如果你的团队以 wrangler 为事实来源配置文件由人维护则反过来在 Pulumi 中读取wrangler.toml保持同一方向上的同步即可。二者选其一不要两套并存互不同步。构建与部署流水线先构建、后上传由于 Pulumi 不会打包 Worker 代码构建步骤必须显式编排进 IaC。用command.local.Command把npm run build纳入资源图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]});构建命令在./worker目录执行产出./worker/dist/index.jscontent通过build.stdout.apply(...)延迟读取构建产物dependsOn: [build]保证顺序正确这是 gotchas.md 中 “No bundler/build step” 问题的标准解法直接把src/index.ts原样上传会导致 Worker 报 “Cannot use import statement outside a module”务必先构建、后部署。内容 SHA 强制更新打破“误判无变更”Pulumi 通过内容哈希判断是否需要更新 Worker。当代码仅发生空白符、注释等不影响哈希的变化时会出现“代码改了但pulumi up显示无变更”的假象。强制更新的惯用技巧是注入一个随每次发布变化的版本号绑定const version Date.now().toString(); const worker new cloudflare.WorkerScript(worker, { accountId, name: my-worker, content: code, plainTextBindings: [{name: VERSION, text: version}], // Forces deployment });每次pulumi up都会生成新的时间戳文本导致绑定值变化从而强制触发一次新部署附带收益env.VERSION让 Worker 运行时能上报自身构建版本便于排障与日志追踪代价是每次部署都会产生新的绑定差异适合需要确定性“每次都部署”的场景详见 gotchas.md 中 “False no-changes detection” 一节。模式落地时的常见陷阱与最佳实践把上述模式组合进真实项目时请对照 gotchas.md 自查绑定名大小写敏感Pulumi 侧kvNamespaceBindings: [{name: MY_KV, ...}]必须与 Worker 代码env.MY_KV完全一致否则运行时报env.MY_KV is undefinedD1 迁移不进pulumi upD1Database只负责建库不会执行 SQL。需要用一个command.local.Command如wrangler d1 execute ${db.name} --file ./schema.sql并dependsOn: [db]再让 WorkerdependsOn: [migration]保证迁移先于部署API Token 权限若遇到authentication error (10000)说明 Token 缺少权限需要至少授予Account.Workers Scripts:Edit与Account.Account Settings:Read导入后资源“异常变更”pulumi import后若pulumi preview显示变更是状态与真实资源属性不一致需调整 Pulumi 代码与真实资源对齐可参考 api.md 中的导入命令清单始终设置compatibilityDate锁定 Worker 运行时行为避免 Cloudflare 平台升级引发破坏性变更。相关参考本文对应仓库中的完整参考集pulumi/README.mdProvider 总览、认证方式、核心原则与阅读顺序pulumi/configuration.mdWorker/KV/D1/R2/Queue/Pages/DNS 等资源完整配置pulumi/api.mdOutput、依赖、数据源、动态 Provider、导入与密钥管理pulumi/gotchas.md常见错误、最佳实践与平台限制cloudflare-deploy/SKILL.mdCloudflare 部署技能总览含决策树与产品索引横向对比可参考 terraform另一套 IaC 方案与 wranglerCLI 部署方案。从单一 Worker 到事件驱动微服务、再到金丝雀发布这十种模式覆盖了 Cloudflare 平台上从“能跑”到“可灰度、可演进”的完整路径。按需选取、组合运用即可用纯代码把整套云上基础设施管理起来。【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考