一个周末,我把手上几个散落的个人项目全部收敛到了一个统一体系里:静态博客、照片备份、API 服务、内网工具,甚至 DNS 解析都归到了一处。整理完再看控制台,那一排排服务面板让我恍惚觉得自己不是在用免费套餐攒主机,而是在使用一台由 Cloudflare 全家桶拼装出来的“个人操作系统”。于是我给这套东西起了个名字:cloudflare-os。
这个名字不是指某个可以在笔记本上安装的发行版,而是一种架构思路——把所有需要自己维护的小型网络服务,全部交给 Cloudflare 的边缘网络去承载,我这边不再操心服务器、证书、端口映射和防攻击,只管写配置和堆业务。对个人开发者来说,这套思路几乎等于把“运维”两个字的成本直接抹掉了。
这篇文章就聊聊我是怎么理解、设计并实际落地这套 cloudflare-os 的。如果你也有一堆个人项目想统一管理,或者你正纠结要不要把服务逐个迁到 Cloudflare 上,这篇应该能给你不少可抄作业的参考。
1. 为什么把个人服务做成“操作系统”:打破服务器思维
先说清楚我当初的处境。我在本地 NAS 上跑过一段时间的博客和备份服务,后来又租了一台小主机放 API。问题是每台设备都有自己的 IP、自己的证书体系、自己的重启和更新节奏,今天这台机器证书到期,明天那台服务因为内存不足被 OOM Killer 干掉。我发现自己 80% 的时间不是在写功能,而是在维护这些散落的“物理资产”。
后来我意识到一个反直觉的事实:个人项目真正需要维护的东西,比我以为的少得多。所谓“系统”,本质上是需要一套统一的规则来管理所有服务和资源的入口。Cloudflare 的优势恰恰在于它天然具备了这个入口的所有要素:一个统一的 DNS 面板、一套内容分发网络、一个边缘计算运行时、一组对象存储空间,再加上一层访问控制。让这些服务协同工作,就能把原本分别维护的服务器逻辑,抽象成一个个可以被统一调度的“系统组件”。
我把这套思路类比成操作系统的分层:
- 内核态:Cloudflare 的 DNS 和网络入口,负责所有流量的路由与分发
- 用户态:Workers 脚本、Pages 站点,承载具体业务逻辑
- 存储层:R2 对象存储,承担文件、图片等静态数据的保存与读取
- 外设层:Tunnel 隧道,把本地的私有服务安全地暴露到统一域名体系下
- 进程管理:Cloudflare 的配置面板与 API,相当于统一进程控制台
有了这个分层,我心里长期存在的那种“我的服务散落在世界各地”的焦虑感消失了。所有业务不再以“哪台机器”为坐标,而是以“哪个域名路径”为坐标。我更关心逻辑归属,不再关心物理位置——这大概是“云原生”对个人开发者最直观的体验。
很多人觉得 Cloudflare 只是“套了一层 CDN”,但我更愿意理解成一个可编程的接入平台。CDN 只是它的顺带功能,真正的价值在于它提供了统一的流量治理入口:域名、证书、缓存、限流、鉴权全部可以在一个控制台里完成编排,这非常接近操作系统的“统一资源管理”哲学。
2. 零件盘点:Cloudflare 生态里到底有哪些“系统组件”可以用
要组装一台机器,你总得先了解手头有哪些零件。Cloudflare 的组件很多,但对我这套 cloudflare-os 来说,核心有价值的也就几样,我把它们逐个过一遍。
2.1 DNS 与域名管理:系统的“总线”
Cloudflare DNS 是我第一个上手的模块。它本质上是一个权威 DNS 托管平台,但真正好用之处在于它是所有 Cloudflare 服务的入口。只要域名接入到这个体系,之后再挂载 Pages、Workers、Tunnel 都不需要额外配置源站 IP,直接在 DNS 层面就能把流量导向对应的“内部服务”。
实操层面,我只需在 Cloudflare 上添加站点,把域名注册商那边的 NS 记录换成 Cloudflare 提供的两个地址。等全球生效之后,整个域名下的所有子域名全部纳入了统一管理。之后新增一个子域时,不再需要去折腾 A 记录指向哪台服务器,只需在控制台选择要绑定的 Pages 项目或 Worker,系统会自动生成对应的 DNS 记录。
这一层我把它视为整台“操作系统”的总线,所有其他组件都挂在它上面。
2.2 Pages:静态内容与前端应用的“运行环境”
Pages 是 Cloudflare 的静态站点托管服务,也是我的博客和前端项目的宿主。它可以直接连接 Git 仓库,每次 push 自动构建、自动部署,还自带全球 CDN 加速。我只需要把静态资源往上一放,证书和缓存都由平台处理,这基本就是“格式化硬盘、装好系统、开机就能用”的体验。
Pages 最吸引我的一个细节是它提供了内置的 Headers 配置、重定向规则和 _redirects 文件机制。比如我希望访问/api时自动跳转到一个 Worker 端点,可以直接在_redirects文件里写一行规则:
/api/* https://api.example.com/:splat 200这是一个很“操作系统化”的特性——逻辑与部署分离,我只需要维护规则文件,不必在业务代码里写重定向逻辑。
2.3 Workers:系统里的“用户态进程”
Workers 是 Cloudflare 的边缘计算运行时,是我的这套体系里最灵活的一环。它支持 JavaScript、TypeScript、Python 等语言,部署之后可以在全球边缘节点执行代码,而不必回源到服务器。对于那些需要动态响应的场景——比如表单提交、API 代理、数据加工——我用 Workers 写函数脚本,发布后直接获得一个 HTTPS 端点。
Workers 给我的感觉就像操作系统的“可执行文件”:写一个脚本,绑定一个路由,剩下的并发、灾备、网络调优都由平台接管。我写过一个很简单的请求路由 Worker,作用是把/blog/*的请求直接转发到 Pages 项目,同时给响应头加上安全策略。这个 Worker 的完整代码看起来非常简单:
export default { async fetch(request, ctx) { const url = new URL(request.url); if (url.pathname.startsWith('/blog/')) { return fetch('https://my-blog.pages.dev' + url.pathname); } return new Response('Not Found', { status: 404 }); } };部署这段代码比部署传统服务器上的反向代理轻松太多——不用装 Nginx、不用配置 SSL 证书、不用担心进程崩溃后需要守护进程。这在个人基础设施体系里,是非常典型的“运行环境抽象”。
2.4 R2 对象存储:系统的“磁盘”
R2 是 Cloudflare 的对象存储服务,兼容 S3 API,但最大的亮点是没有流量出口费。我个人的照片备份、网页静态资源、日志归档,全部放在 R2 里。从另一个角度看,R2 相当于我系统的“磁盘”,只不过这块磁盘不需要挂在某一台服务器上,而是由平台直接管理,任何 Worker 和 Pages 项目都可以读它。
比如我用一个 Worker 提供一个简单的文件列表页,代码可以写成:
import { S3Client, ListObjectsV2Command } from '@aws-sdk/client-s3'; const s3 = new S3Client({ region: 'auto', endpoint: 'https://<account_id>.r2.cloudflarestorage.com', credentials: { accessKeyId: '<access_key>', secretAccessKey: '<secret_key>' } }); export default { async fetch(request) { const list = await s3.send(new ListObjectsV2Command({ Bucket: 'my-bucket' })); const names = list.Contents.map(obj => obj.Key).join('\n'); return new Response(names, { headers: { 'Content-Type': 'text/plain' } }); } };这比我自己在一台 VPS 上挂 NFS 或者 MinIO 省心得多,因为我完全不需要管理存储节点的容量、磁盘健康和备份策略。所有备份工作都交给平台的冗余机制负责。
2.5 Tunnel:连接本地私有服务的“驱动桥”
如果说前面几个组件都是“云上服务”,那么 Tunnel 则解决了“本地服务如何安全进入统一域名体系”的问题。我有些工具必须跑在家里的机器上,比如智能家居的网关脚本、内网的一些监控面板。以前要访问它们,我得做端口映射、动态 DNS,还要担心暴露到公网的安全隐患。
Tunnel 的思路是:在本地运行一个名为 cloudflared 的轻量客户端进程,由它主动向外建立加密连接,我的域名流量通过 Cloudflare 边缘节点转发到这个 Tunnel,最终到达本地服务。整个过程不需要开放任何入站端口,也不需要在路由器上做映射。
部署 Tunnel 的命令很简单:
cloudflared tunnel login cloudflared tunnel create my-home cloudflared tunnel route dns my-home home.example.com cloudflared tunnel run my-home执行完这几步之后,home.example.com就能在本地端口 8080 和公网之间建立安全通道。它就像为“系统”安装了一个远程设备驱动——本地硬件与云上系统融为一体,但不暴露物理网络结构。
2.6 Access 与安全策略:系统的“权限控制台”
我使用 Access 模块为整套系统增加了一道身份验证层。它可以给某个子域配置访问策略,比如只有通过邮箱验证或特定规则的人才能访问。这相当于操作系统的用户权限管理:不同的“用户”(访问者)对同一套“系统”拥有不同程度的访问权限。
以我的照片备份面板为例,它绑定了一个子域,如果直接暴露在公网,等于任何人都能浏览我的家庭相册,这肯定不行。用 Access 加一个前置验证后,访问者需要先进行一次 Cloudflare 邮箱验证,才能看到内容。这个机制用于保护所有带有隐私属性的本地服务非常有效,配置也只需要在控制台上选一下信箱规则即可。
3. 组装实录:从域名解析到全站上线的完整过程和操作细节
组件了解清楚之后,组装的过程其实也不复杂,但里面有一些容易踩的细节,我按用了最短路径把整套体系跑通的顺序讲一遍。
3.1 第一步:把域名和解析全部归口到 Cloudflare
我的第一步操作是把我的域名注册商的 DNS 服务器地址改掉。进入 Cloudflare 控制台,选择“Add a site”,输入我的域名,平台会提示我把 NS 记录换成它指定的两个地址。这一步做完之后,Cloudflare 会接管整个域名的 DNS 权威体系,这个过程通常需要几分钟到几十分钟,取决于注册商的生效速度。
等状态变为 Active,我立刻在 DNS 面板看到所有原域名注册商那边的解析记录被自动导入了。我建议你保留原来的全部记录,因为有些子域可能还在跑旧服务,停掉会中断线上服务。我在迁移时,是把所有记录保留,然后逐步把需要更新的子域切到 Cloudflare 的服务上,确认每一项都正常后再删除无效记录。这样切换过程是平滑的。
3.2 第二步:部署静态站点到 Pages 并绑定自定义域
博客和几个静态站点是我第一批迁移的对象。在 Pages 控制台选择“Connect to Git”,授权我的仓库,然后设置构建命令(如果不用框架,这个步骤留空就行)。构建完成之后,Pages 会分配一个*.pages.dev域名。
我这里建议大家直接在 Pages 的自定义域设置里,填上自己的域名,比如blog.example.com。Cloudflare 会自动在当前域名下创建对应 DNS 记录。整个过程不需要手动添加解析,也不需要上传 SSL 证书。浏览器访问时,证书是自动匹配的,这一点对于没有接触过证书管理的新手非常友好。
3.3 第三步:用 Workers 做动态接口
接下来是动态服务。我在业务逻辑拆解完之后,优先把那些轻量的 API 用 Workers 实现。创建 Worker 的方式有两种:一种是在控制台里直接在线写代码,另一种是通过 Wrangler 命令行在本地写代码后部署。
我偏爱 Wrangler,因为它能让我在本地的代码库保持版本管理,上线时只需要一条命令:
npx wrangler deploy部署完成后,Cloudflare 会给我一个*.workers.dev地址。然后我在自定义路由中绑定到api.example.com,配置秒级生效。这里提醒一个容易忽略的点:如果你还想让 Pages 站点和 Worker 共享同一个父域,即blog.example.com和api.example.com同时存在,请确保两个路由之间没有冲突,比如不要把 worker 绑定到泛域名*.example.com,否则它会吞掉其他子域的流量。
3.4 第四步:创建 R2 存储桶并迁移静态文件
现在处理数据存储。我在 R2 控制台创建了一个名为public-assets的存储桶,然后把博客里的图片、PDF 等文件整体迁移进去。迁移的方式我推荐直接用 rclone 配 R2 的 S3 兼容端点,之前在本地服务器迁移数据时验证过非常稳定。
写入 R2 后,我调整博客的图片链接为https://assets.example.com/images/xxx.jpg。在 Pages 里我可以通过设置一个叫做_headers的文件来为这些资源设置缓存策略,比如图片缓存 30 天,音频文件缓存 7 天,HTML 不缓存:
/assets/* Cache-Control: public, max-age=2592000 /media/* Cache-Control: public, max-age=604800静态资源从自己的服务器迁到 R2 之后,我的源站负载直接降下来了。图片不再占服务器带宽,因为响应的边缘缓存节点会直接返回,不再回源。
3.5 第五步:用 Tunnel 接入内网服务
最后是那台跑在家里的小服务器。我安装 cloudflared 之后执行了上面提过的 login、create、route 三条指令,然后为了让它长期稳定运行,我把它安装成了系统服务:
sudo cloudflared service install安装完成后,服务会随系统启动。我给内网面板绑定了一个子域,同时把这个子域套上 Access 策略。这样我能从任何地方安全地访问家里的监控面板,而不需要在路由器上做任何端口转发。
值得注意的是,Tunnel 的连接是本地进程主动外连的,所以家宽没有固定公网 IP 也没关系,只要本地能联网,隧道就一直在。这省掉了许多绿联、花生壳之类的第三方内网穿透服务依赖,安全性也更高,因为出站方向是一条加密连接,没有在公网上暴露任何端口。
4. 治理经验:配置管理、安全策略与成本控制的心得
系统跑通只是开始,真正的长期挑战是“治理”,也就是如何持续稳定地维护这套系统。这一节会聊几个我实际沉淀下来的经验。
4.1 通过仪表盘观察全系统状态
Cloudflare 控制台相当于系统监控的中心仪表盘。每个域名、每一条 DNS 记录、每一个 Worker 的调用次数、每一份静态资源的缓存命中率,都在各自的项目页里展示。我在使用过程中会定期查看 Workers 的请求成功率和 R2 的使用量,这两个指标最容易反映出系统是否异常。
如果发现某个 Worker 的请求成功率下降了,首要排查的是代码逻辑是否引入了未捕获异常,其次是路由规则是否有误操作。这在极大程度上替代了我以往“登录服务器看日志”的排查模式。虚拟化的日志系统配合 Workers 的 Trace 请求追踪,可以对每次请求的链路进行定位,这已经接近商业级 APM 的部分功能了。
4.2 把配置当代码管理
我把 Pages 的_redirects和_headers文件当作代码管理,每一个改动都走 Git 提交。这样一来,整套系统的入口规则有版本记录,回滚时可以快速定位到某一次的改动。
Workers 的代码我同样走版本库。用 Wrangler 有一个好处,就是部署时可以通过--version-id参数指定回滚到某个历史版本。操作上非常简单,但带来的价值很大:以前在服务器上改 Nginx 配置时,如果没有备份,改坏了只能瞎猜恢复。现在我的所有路由和边缘逻辑都有明确的历史版本,可以随时回档。
4.3 安全策略的分层防护
安全层面,我的经验是把防护做成分层:
- 第一层:在域名级开启 Cloudflare 的安全防护,包括人机验证、速率限制规则
- 第二层:对涉及内部信息的子域套上 Access
- 第三层:在 Worker 代码内部做简单的鉴权逻辑
这三层形成了纵深防御。即便某一层被绕过,还有下一层兜底。尤其适合那些需要暴露在公网的个人服务。我在 Access 里配置的策略是:所有访问home.example.com和internal.example.com的请求,都必须通过邮箱验证码。每次访问会生成一个有效期为 24 小时的会话 Cookie,体验和大多数公司内部系统接近。
4.4 成本账单:几乎为零
成本方面,这也是我选择这套体系的核心原因之一。Cloudflare 的免费套餐覆盖了绝大多数个人场景:Pages 免费额度足够跑几个静态站,Workers 每天有十万次免费请求,R2 提供 10GB 免费存储,Tunnel 无限流量。我把所有服务都加起来,开销仍然只有域名续费费用。
不过要留意一点:免费额度是按账号整体计算的,如果你的项目多,要注意 Workers 请求数和 CDN 的流量。我个人的使用量目前远未触达限制,但我会在控制台设置用量提醒,一旦贴近配额会收到邮件。这避免了月底突然发现某个服务被限流的情况。
5. 踩坑清单:搭建过程中最折腾我的几个问题及最终解法
这套体系整体体验很顺,但搭建过程中总有几个环节格外费劲,我把它们单独拎出来说,当作一份避坑索引。
5.1 Pages 重定向规则与框架路由的冲突
我的博客使用了一个自带客户端路由的前端框架。第一次部署完成后,我发现直接访问某个深层路径(比如/post/123)会返回 404,刷新时尤其明显。原因是 Pages 默认只托管构建生成的静态文件,对前端框架内部的客户端路由不感知,所以深层路径找不到对应静态文件。
解决方法是添加一个重写规则,把所有不带后缀的路径都重写到首页 HTML,让前端路由接管:
/* /index.html 200这个规则要小心不要覆盖到静态资源,好在它只影响没有文件后缀的路径,图片和 JS 自带后缀不受影响。这个问题如果不踩一次,新手会误以为是框架问题,实际上根源在宿主环境的路径解析方式。
5.2 R2 的自定义域绑定和 CORS 跨域
给 R2 存储桶绑定自定义域后,我遇到过一个非常隐蔽的坑:如果某个图片要从assets.example.com加载,而引用它的页面是blog.example.com,浏览器会发起跨域请求。R2 的 S3 API 默认返回的 CORS 头并不一定满足前端页面的需要,我在控制台上必须显式配置 CORS 规则。
我最终的配置允许来自任何源的 GET 请求,并开放Content-Type和ETag响应头。如果忘了这一步,你可能不会立刻发现问题,因为img标签加载图片不受 CORS 限制,但如果你用 JavaScript 的fetch去读取图片二进制,就会报错。这在做前端图像处理时尤为常见。
5.3 Tunnel 服务没有开机自启
cloudflared 以手动方式启动非常容易,但一旦重启家中的服务器,隧道便会消失。我第一次以为配置写错了,后来查文档发现问题在于没有正确安装为系统服务。正确的做法是在完成cloudflared tunnel login之后执行sudo cloudflared service install,并且要确保~/.cloudflared目录中的凭据文件对所有需要运行该服务的用户可见。我遇到的情况是服务已经注册了,但启动时找不到配置,最后重新设置了证书文件权限,服务就能开机自动连接了。
5.4 Workers 路由匹配的吞并问题
前面提过的泛域匹配问题,我在这里细说。我的 api 子域绑定了一个 Worker,有一次我想临时绑定.env配置到同一路径,直接在 Worker 路由中设置成*.example.com/*,结果意外地把博客子域的请求也接管了,Pages 项目瞬间无法访问。排查步骤是先看 Workers 的自定义路由列表,再检查 DNS 记录是否指向了 Worker。
经验是:除非你真的需要全局拦截流量,否则永远不要在路由规则中使用裸泛域名。自带一个明确的子域前缀,能让不同服务边界清晰,避免流量被错误地路由到某个不该处理它的进程上。
5.5 缓存的脏数据问题
还有一次,我更新了博客的一张图片,却发现线上访问的还是旧版。排查后发现是 Cloudflare CDN 缓存了该文件,默认的缓存过期时间还没有到。解决办法是在_headers文件里对更新频繁的目录使用较短的缓存时间,或者在更新资源时更改文件名,添加版本号参数。这个属于 CDN 类服务通用的问题,凡是用 CDN 的人都绕不开,但它的影响通常只在静态资源更新不频繁时才不明显。
6. 这套“操作系统”落地后,日常维护发生了什么变化
最后说说实际运行后的感受。以前我维护一个博客、两个 API、一个照片备份服务,每天需要查看的“表面”至少有三个:VPS 的 SSH、NAS 的管理后台、域名注册商的解析面板。现在,所有收口到 Cloudflare 的控制台,我只需要打开一个面板就能看到所有域名的 DNS、所有 Worker 的请求量、所有 R2 桶的使用状态和所有 Tunnel 的连接情况。
最明显的效果是“告警变少了”。以前服务器半夜会收到磁盘告警,或者证书到期提醒,每次都要登上去处理。现在这些问题在 Cloudflare 平台里几乎不外露——存储冗余由平台负责、证书自动续期、负载均衡由全球边缘网络完成。我甚至很久没有登过源服务器的 SSH 了,因为所有对外服务已经剥离出本机。
另一个体会是,这套体系的弹性远超我的预期。有一次博客突然被转载,流量涨了一波,对于传统单机服务器,这个量级可能已经让 Nginx 陷入高负载。但在 Cloudflare 上,静态内容直接命中边缘缓存,我甚至没有观察到任何延迟变化。这种感觉非常接近“拥有了一个随手能伸缩的基础设施”,但对个人开发者而言,它零成本、零门槛地做到了这一点。
如果你正打算把自己的个人项目也统一到一个体系里,我建议你先从静态网站迁到 Pages 开始,然后加一个 Worker 尝试动态请求,再逐步把 R2 和 Tunnel 加进来。这套东西最大的价值不是某一个单独的组件,而是它们共享同一套配置、同一个控制台、同一层安全策略。当你不再需要操心“哪台机器服务哪个域名”的时候,你那点有限的精力才能真正省下来,放到业务本身上去。