1. 项目定位与整体架构思路1.1 这个“cloudflare-os”到底做了什么先聊清楚标题的含义。很多朋友看到“cloudflare-os”这个词第一反应是“Cloudflare 是不是出了个操作系统”其实不是。它更像是一种工程实践的代号把一套完整的、可用的业务系统全部跑在 Cloudflare 的边缘网络生态上让整个应用从静态资源托管、后端 API 执行、对象存储、数据库、鉴权认证到安全防护都不再依赖传统服务器或云主机。我最初产生这个想法是因为做一个中小型项目时实在受够了“买服务器、配 Nginx、折腾 HTTPS、维护环境依赖”这一套流程。尤其是个人项目或小团队项目流量不大但又不希望基础设施占据太多精力。Cloudflare 的 Workers、Pages、R2、KV、D1、Tunnels 这些产品和能力组合在一起恰好能把“一套业务系统”的所有环节都搬到全球边缘网络上。你不需要在深夜爬起来处理服务器宕机也不用考虑流量突增时扩容因为边缘网络本身就是分布式的。这个方案适合谁适合那些已经能熟练写业务代码、但不想花太多时间运维基础设施的开发者也适合做原型、做小工具、做个人作品集的程序员甚至适合小团队跑一个体量不大的生产级应用。如果你做的业务对合规、数据本地化有严格要求那么这套方案还需要谨慎评估但如果你做的是互联网工具类产品、内容站、个人 SaaS那么 cloudflare-os 这套思路真的可以把基础设施复杂度降到极低。1.2 为什么选 Cloudflare 全家桶而不是单独混搭通常我们去搭建一个全栈应用比较常见的路径是前端静态资源放在 Vercel 或 Netlify后端接口部署在 Render 或 Fly.io数据库用 MongoDB Atlas 或 Supabase文件存储用 AWS S3域名解析和 CDN 再切到 Cloudflare。听起来每个环节都选了最流行的工具但实际维护时会发现——需要管理五六个控制台、处理五六个不同的 Bun 包、记住每个平台的部署配置和限制集成时还要关注 API 兼容性、CORS 规则、数据迁移等一堆问题。而 Cloudflare 全家桶的好处是整套系统的每个环节都长在同一片生态里运行时模型、鉴权方式、绑定关系都是同一套语言。比如 Workers 里可以直接绑定 R2、绑定 D1、绑定 KV配置几行就好不需要去维护一套复杂的 SDK 初始化逻辑。前端发布到 Pages 后通过自定义域名就能无缝接入 WAF、Bot 管理、缓存规则等全套能力。这种“统一”带来的维护成本下降在项目跑起来之后体会特别深。另外Cloudflare 的全球边缘节点覆盖非常广任何地方访问都走最近的边缘节点接口延迟很低。这点跟传统“服务器在上海美国访问要跨太平洋”的体验完全是两回事。我对 cloudflare-os 的整体盘算是把数据面静态资源、接口响应放到边缘把状态面数据库、缓存、队列也尽量放到边缘产品上最后只保留必须临时的任务或重型计算不走 Cloudflare。这个架构思路贯穿了后面所有选型和实现细节。2. 核心组件选型与原理拆解2.1 Workers 与 Hono 框架为什么不用原生 FetchWorkers 是这套系统的运行时核心。它本质上是在边缘节点上运行的 V8 isolate不像传统服务器的进程常驻而是“请求到来时启动、执行完成即释放”。这种模型带来的好处是天然缩容到零没有流量时你也不花一分钱。但它也带来一些约束比如执行时长限制、CPU 时间限制、包体积限制所以选框架时不能像写 Node.js 后端那样随便堆依赖。我选的是 Hono。它就是一个适配边缘运行时的极简 Web 框架API 风格类似 Express路由、中间件、参数校验都有但整体体积非常小巧压缩后只有几十 KB。你当然也可以用原生 Fetch 去写路由写几个接口还行但一旦涉及路由分组、中间件、鉴权钩子代码会很快变得杂乱。另外Workers 的代码是直接运行在你自己的域名下的绑定 KV、R2、D1 时不需要在代码里写 accessKey而是在 wrangler.toml 里声明 binding运行时由 Cloudflare 注入。这样代码里就不会出现密钥泄露的问题也不需要单独去管理和轮换密钥。2.2 R2 对象存储图片与文件上传的可靠落点R2 是这套系统里的“硬盘”。它兼容 S3 API这意味着你之前写过的 S3 SDK 代码可以直接改个 endpoint 迁移过来而且最大的亮点是——它不收出口流量费。做内容类应用最怕什么怕图片被人访问量大S3 的流量费能让你喝西北风。R2 免费额度给得也可以存储 10GB、读操作 100 万次、写操作 100 万次对小项目来说非常宽裕。实际使用时我建议不要把公开资源直接暴露成“公开读”而是通过 Workers 做一个简单的鉴权代理或者用 R2 的预签名 URL 功能生成临时直传链接。上传文件时前端先请求 Workers 获取一个带时效的上传凭证然后前端直传 R2这样既绕过了 Workers 的执行时长限制也能避免大量文件数据从 Worker 中转发造成流量成本。2.3 KV 与 D1 的分工逻辑谁管缓存谁管业务数据KV 是一个全球复制的键值存储读的延迟很低但它是“最终一致性”的写入后需要大约 60 秒才能在全球所有节点完全生效。所以它适合存放配置项、会话状态、临时缓存、以及不要求强一致的数据。我用 KV 来存 JWT 的临时黑名单、用户偏好设置、热榜缓存。注意——不要把 KV 当成业务数据库来用尤其是涉及账户余额、订单状态这类数据最终一致性会给你带来事故。D1 则是 Cloudflare 基于 SQLite 打造的 serverless 关系型数据库。它是强一致性的支持标准的 SQL 语法适合放用户表、文章表、评论表这些结构化数据。不过 D1 毕竟是 SQLite 内核扩展来的自动增量备份、并发写能力都不如传统 PostgreSQL所以设计时要主动规避高并发写入的场景。比如点赞计数我不会直接 UPDATE 数据库而是先写 KV 再异步批量回刷 D1这样既保住了接口响应速度也不会把 D1 的并发读写打满。2.4 Tunnel 与安全边界把内网服务藏起来cloudflare-os 里边还有一个容易忽略但特别好用的组件Cloudflare Tunnel。它可以让一台没有任何公网 IP 的本地机器通过隧道直接映射到你自己的域名下。等于把 localhost 安全地暴露在公网上但请求必须先经过 Cloudflare 的边缘节点源站不会暴露 IP。这个能力在开发调试和交付验证场景中非常有用。我做外部接口联调时直接把本地开发环境起一个 Tunnel对方访问我的临时域名就和访问线上接口一样省去了反复部署的流程。更关键的是如果你有一些必须在传统服务器上跑的服务比如需要长连接、需要特定系统依赖你依然可以通过 Tunnel 把它接入 cloudflare-os 的域名体系里在安全层面完全收口在边缘节点。3. 从零搭建的整体实操过程3.1 工程结构初始化先理清自定义域名的映射关系我建议直接把项目按 Monorepo 组织分成 apps/web 和 apps/api 两大部分再加上一个 packages/shared 共享类型定义。虽然 cloudflare-os 的部署是前后端分离的但代码放在一个仓库里类型和工具函数可以共享联调时不用复制定义。先用 pnpm 初始化工作区然后分别创建 web 和 api 两个子目录。web 端用 Vite React 走 Pages 部署api 用 Hono 走 Workers 部署。这里要特别提醒一下域名映射关系的规划Pages 负责承载 www 主域名和静态资源Workers 则绑定一个带 /api 前缀的子路径比如 api.yourdomain.com 或者 customdomain.com/api/*。你可以在 Cloudflare Dashboard 的 Workers 路由配置里把接口路径都指向同一个 Worker这样前后端域名的 cookie 作用域也比跨域更友好。3.2 Workers API 的具体实现绑定 R2、D1、KV 的配置写法有一个容易踩坑的点本地开发和线上环境对 binding 的处理方式不一样。线上可以在 wrangler.toml 里直接配置本地开发则建议用 wrangler dev 的 --remote 参数去连线上资源避免本地还得搭一套模拟环境。我的 wrangler.toml 看起来像这样name cloudflare-os-api main src/index.ts compatibility_date 2024-11-01 workers_dev false routes [ { pattern api.yourdomain.com, zone_name yourdomain.com } ] [[kv_namespaces]] binding SESSION_KV id 你的KV命名空间ID [[d1_databases]] binding DB database_name cloudflare_os_db database_id 你的D1数据库ID [[r2_buckets]] binding FILE_BUCKET bucket_name cloudflare-os-files注意 routes 这一段如果你希望 Workers 只处理 api 子域名的所有请求就按上面的写法。如果希望让某个主域名下的所有 /api/* 都走 Worker那就用 pattern yourdomain.com/api/*。如果是纯 Pages 项目通过 next-on-pages 之类的方式集成那还需要额外处理函数的编译我这次没有走那条路线而是保持前后端彻底分离职责更清晰。然后看接口代码。我用了 Hono 的 R2 绑定实现一个最简单的文件上传获取签名 URL 的接口import { Hono } from hono import { getSignedUrl } from ./r2-helpers type Bindings { FILE_BUCKET: R2Bucket SESSION_KV: KVNamespace DB: D1Database } const app new Hono{ Bindings: Bindings }() app.post(/upload/ticket, async (c) { const { fileName, contentType, size } await c.req.json() // 这里可以做用户鉴权校验通过后签发一个5分钟有效的直传链接 const url await getSignedUrl(c.env.FILE_BUCKET, fileName, contentType, size) return c.json({ ok: true, url }) }) export default app生成预签名 URL 时需要用到 S3 兼容签名算法代码里我封装了 crypto 签名逻辑这块网上有现成的库可以做也可以用aws-sdk/s3-request-presigner配合 R2 的 S3 endpoint。实测下来直接传 R2 真的很稳文件不上 Worker请求体不用在边缘节点和 R2 之间做二次转发。业务数据方面D1 的初始化就是建表CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, email TEXT UNIQUE NOT NULL, nickname TEXT NOT NULL, created_at INTEGER NOT NULL );Workers 里执行 D1 查询非常简单const { results } await c.env.DB.prepare(SELECT * FROM users WHERE email ?) .bind(email) .all()如果你习惯了 ORM可以直接上 Drizzle ORM它支持 D1 的 driver类型安全体验很好。但如果查询不复杂我建议直接用 SQL 写少一层抽象排查问题更直接。3.3 前端 Pages 与 API 的对接细节Pages 部署静态资源没什么好讲的构建命令配好就行。我要重点说的是前端请求 API 时的鉴权和域名策略。推荐用同一个主域名让 Web 端调用/api/*走 Workers 路由这样不需要跨域配置cookie 也能自动带上省掉 CORS 的很多麻烦。如果你就是不喜欢同域非要前后端分属不同子域名那就要在 Workers 里显式配置 CORS 头同时处理 OPTIONS 预检请求。还有一个坑无论你配置 Access-Control-Allow-Origin 为*还是具体域名cookie 跨域的时候都不能用*必须指明具体来源并且要求前端请求带上credentials: include。实际项目里我还会在 Workers 中间件里统一注入安全头HSTS、X-Content-Type-Options、X-Frame-Options 这几个是基础的。然后公共 API 加一个简单的速率限制Cloudflare 默认有 DNS 级别的防护但应用层面我依然自己写了一个基于 KV 计数的限流中间件确保单个 IP 每秒最多打 10 次接口。代码如下app.use(/api/*, async (c, next) { const id c.req.header(cf-connecting-ip) || unknown const key rate:${id}:${Math.floor(Date.now() / 1000)} const count await c.env.SESSION_KV.get(key) if (count Number(count) 10) { return c.json({ error: too many requests }, 429) } await c.env.SESSION_KV.put(key, String(Number(count || 0) 1), { expirationTtl: 5 }) await next() })这里cf-connecting-ip是 Cloudflare 在边缘注入的真实客户端 IP 头不要用 request 里默认的 IP 字段那个在套了 CDN 之后一定是 CF 节点的地址。3.4 部署发布与持续集成的配置思路发布这件事在 cloudflare-os 里简单得有点不真实。后端只需要在项目根目录执行wrangler deploy前端构建产物推到 Pages 仓库Cloudflare 检测到分支更新就会自动构建发布。我实际工作中的流程是GitHub 上建两个 workflow一个在 push 到 main 时自动部署 API另一个把最新 web 代码推送到 Pages 绑定的仓库分支实现前端自动上线。为了部署过程更顺畅我强烈建议本地安装 wrangler 并预先登录因为第一次跑wrangler deploy时会引导你去浏览器授权CI 环境里没有浏览器权限时容易卡住。CI 里直接通过环境变量配置CLOUDFLARE_API_TOKEN和CLOUDFLARE_ACCOUNT_ID就能无感部署了。此外Pages 绑定自定义域名之后可以顺手把 SSL/TLS 模式调成“灵活”或“完全严格”。我推荐用“完全严格”因为 Pages 会自动签发证书选择这个模式能保证边缘到源站之间的连接也走加密避免中间人截取风险。Cloudflare Proxy 开启状态下所有访客的真实 IP 都由 CF 处理安全层面相当省心。4. 常见问题与排查技巧实录4.1 KV 取不到最新值最终一致性带来的坑这个坑几乎人人都会遇到。你刚用 KV 写入了一个新值紧接着读取发现拿到的还是旧值。这是因为 KV 数据从写入边缘节点到全球节点同步需要时间不是写后立即可见。解决方式很简单区分业务属性。对一致性要求高的数据放在 D1 里KV 只放“容忍几十秒延迟”的数据。我自己的做法是把 KV 的 key 设计成带版本号的格式比如user:profile:{userId}:{updatedAt}读取时先从 KV 拿没拿到再回源 D1 并把结果回填到 KV。这样即使 KV 延迟生效接口也能通过 D1 兜底拿到最新值。还有个小技巧配置cacheTtl区别对待冷热数据热数据 TTL 设短一点避免缓存堆积。4.2 R2 的 CORS 直传问题和公开桶的坑用 R2 做直传时最容易遇到的问题就是浏览器侧的 CORS 失效。即使你通过 Workers 生成了预签名 URL如果 R2 桶没有配置正确的 CORS 规则浏览器直传时依然会被拦截。配置方法是在 R2 管理界面的“设置”里添加 CORS 策略AllowedOrigin 填你的前端域名AllowedMethod 填 PUT、GETAllowedHeader 填 Content-Type 和 Authorization。另一个需要注意的坑是证书和自定义域名的绑定。R2 的默认访问域名是r2.dev结尾体验不太好且有限速。我建议通过“自定义域名”把 R2 桶映射到自己的子域名比如files.yourdomain.com这样既可以进一步挂到 Cloudflare 的缓存加速下面还能统一用主站域名体系来维护证书。绑定好之后预签名 URL 的主机名也会变成你自己的域名体验会很统一。4.3 D1 的 SQL 限制和并发写问题D1 虽然以 SQLite 为内核但并不是无限能力。它的本地复制节点主要在主区域跨区域访问会稍慢。所以在建库时要指定主数据中心而且尽量把你业务的主要用户群体所在区域作为主区域。如果全球用户都需要低延迟访问D1 不如传统集中式数据库主从架构灵话。写并发是另一个要重视的问题。D1 对单条记录的写并发有限制我碰到过一次评论服务被瞬时刷爆的情况大量 UPDATE 操作打到同一条记录上出现持续的错误。最后改成“先更新 KV 计数再用队列慢慢把计数回写到 D1”高峰期问题立刻消失了。所以做这类系统时要早早在设计层面区分“热路径”和“最终路径”不能让边缘上所有实时请求都打到最底层的数据库。4.4 免费额度与限流怎么让项目跑得长久Cloudflare 的免费额度对个人项目非常友好但并不是无限详情。Workers 免费版每天有 10 万次请求KV 每天读写各 1000 次免费超出后按量计费或直接限流。R2 的流量免费是亮点但操作次数依然有上限。我个人的经验是在架构设计里加一层“缓存优先”策略所有可缓存的接口都设置合理的 CDN cache TTL静态资源的缓存时间直接设为一个月以上。还有一点容易被忽略Workers 的 CPU 时间限制。免费版单个请求的 CPU 时间为 10 毫秒。如果你的接口有复杂的计算或大 JSON 解析很容易超时。解决方案是把重计算挪到前端做或者把结果提前预计算存入 KV/R2。我在实际项目中遇到过用 Workers 做 OpenGraph 图片合成这个操作太吃 CPU超时概率很高。后来我把合成任务放到 R2 队列异步处理请求端只拿最终 URL体验完全不同。5. 一些实用扩展Analytics 与访问日志处理很多人在搭建完系统后最容易忽略的一环是日志和用户行为分析。传统方案是接第三方统计 SDK但数据要发给别人总有点不放心。Cloudflare 有一套边缘侧的 Web Analytics只需在 Pages 里加一小段代码就能无侵入地统计页面访问量和来源不需要把数据发到第三方。接口层面的埋点和日志我推荐用 Workers Logs 加一个自定义的 JSON 日志采集函数把关键时刻的耗时、请求体大小、错误码统一聚合到 R2 里。每天凌晨用一个定时 Worker 把前一天的 JSON 日志聚合成摘要写到 D1 的报表表里。这样整个系统自包含没有一个环节依赖外部的数据管道数据这块的隐私问题也被最小化了。关于成本控制再提一句R2 的存储成本极低适合长期存放日志和静态文件但 KV 和 D1 的数据量增长需要注意尤其是 D1 的存储计费是按字节数来的。为了避免无限膨胀养成给 D1 表加“归档过期”的习惯定期把老数据导出到 R2 后删掉保持核心数据库一直轻快。6. 最后再分享一点个人心得跑 cloudflare-os 这套体系到现在最直观的感受是“运维基本不存在了”。以前做一个小项目最焦虑的是半夜收到服务器报警短信现在这个焦虑彻底消失了。Cloudflare 的稳定性很强边缘网络的容错能力远超我自己搭一台单机服务器。但我也要说清楚它并不是万能的。如果你的业务依赖 WebSocket 长连接Workers 目前的状态支持不那么理想你还是要靠 Tunnel 去映射一个传统服务如果你的数据量级到了几十 GB 且有复杂聚合查询D1 也会吃力还是要引入专门数据库。cloudflare-os 最适合的状态是业务逻辑清晰、数据量可控、希望用最少的运维成本跑出全球低延迟体验的中小型应用。最后分享一个我踩过几次坑后学到的习惯每次修改 wrangler.toml 里的绑定配置先跑一遍wrangler dev --remote确认线上资源都能连得通再执行wrangler deploy。一次部署失败往往不是业务代码的问题而是绑定 ID 填错、区域不匹配、或者变量名不一致。本地的tsc --noEmit也要每次跑Workers 运行时对类型定义比较严格运行时才发现错误会浪费好几轮排查时间。如果你也准备把业务搬到边缘网络上我建议先拿一个非核心的小工具练手跑通全流程之后再逐步迁移核心服务。这样踩坑的成本低而且能快速建立起对整个体系的信任感。等你好几个月没有打开过“维护”页面的时候回头再来看 cloudflare-os 这套方案会觉得当初的折腾非常值得。
阅读完成 · 觉得有帮助?