ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

cloudflare-os:一键复现的Cloudflare开发环境镜像

cloudflare-os:一键复现的Cloudflare开发环境镜像 “cloudflare-os”这个项目说白了就是我自己折腾出来的一套以 Cloudflare 生态为中心的开发机镜像。以前每次换电脑、开新项目都要重新装 Node.js、装 Wrangler、配 API Token、搞 Tunnel装完还要处理各种版本冲突。后来我干脆把整套环境固化成一个可重复部署的“操作系统模板”只要一条命令就能把一台干净的 Linux 机器变成顺手的 Cloudflare 开发工作站。这篇文章就从设计思路、核心组件、部署流程到排坑完整复盘一下这套东西是怎么做出来的希望能给同样以 Cloudflare 为主要开发平台的朋友一点参考。1. 为什么会有 cloudflare-os 这个“系统”1.1 直接原因一套环境反复配配完还踩坑我做 Cloudflare Workers、Pages 这类边缘应用已经有挺长时间了日常开发离不开一套固定工具链Git、Node.js、Wrangler、Cloudflare Tunnel还有偶尔会用的 R2 管理工具。问题在于这套工具链不是装完就完事的它跟系统版本、Node 版本、Wrangler 版本之间的兼容性关系非常微妙。比如 Wrangler 2 和 Wrangler 3 的配置字段就有不少差异升级系统后 Python 依赖可能变Tunnel 的 systemd 服务有时候也会因为启动顺序问题挂掉。以前我的做法是“用哪台机器就在哪台机器上现装”结果就是每台机器的状态都不一样有的 Wrangler 是 2.x有的已经 3.x有的 Tunnel 配了开机自启有的忘了配有的 Node 是 20有的还是 16。一旦遇到问题排查成本非常高因为你根本不知道当前环境跟上次成功部署的环境差在哪。这其实是开发环境管理里最典型的问题——环境漂移。cloudflare-os 的出发点很简单把一套“确定的、验证过的、可重复构建”的 Cloudflare 开发环境固化成自己的定制系统镜像。1.2 设计目标不是新系统而是 Cloudflare 生态的“工作台”cloudflare-os 不是要从零写一个操作系统内核它是在成熟的 Linux 发行版基础上做一层定制化封装。你可以把它理解成“组装电脑”基础系统是主板和电源Cloudflare 工具链是 CPU、内存和显卡我做的只是把它们插到固定的插槽上并把 BIOS 默认调好。所以它的核心设计目标有三个。第一开箱即用一台裸机或虚拟机安装完成后不需要再手动装任何 Cloudflare 相关工具所有命令直接就能跑。第二版本锁定Node、Wrangler、Tunnel 等重要组件的版本都在构建脚本里固定避免“昨天还能跑今天依赖一升级就挂”的尴尬。第三场景导向这套环境不只是为了写代码还要能直接测试边缘部署、调试 Tunnel、操作 DNS 记录所以网络相关配置和 systemd 服务也会预置好。这套设计思路其实不只适用于 Cloudflare任何有固定技术栈的开发者都可以借鉴只是我恰好把重心放在 Cloudflare 生态上。如果你平时主要用 AWS、Vercel 或者其他平台也可以按同样的思路定制自己的版本核心方法论是通用的。2. 核心组件拆解cloudflare-os 里装了什么为什么这么选2.1 基础系统的选择Debian 还是 Arch我最终选了 Debian 作为基础系统具体是 Debian 12。原因很简单稳定、文档多、systemd 支持成熟。Cloudflare 的 Tunnel 官方文档里有大量 systemd 配置示例Wrangler 官方文档也明确支持 Linux x64Debian 12 的包管理方式对长期维护非常友好。有人可能会问为什么不选 Ubuntu 或者 ArchUbuntu 其实也可以但它默认带的 snap 版本 Node.js 有时候路径和权限会跟普通用户习惯不一致容易埋坑。Arch 的优势是软件版本新但滚动更新的特性跟“版本锁定”的设计目标冲突Rolling release 说不定哪次更新就把 Wrangler 依赖的 Node 版本顶掉了。所以我更推荐在稳定版 Debian 上手工安装指定版本工具链这样可控性最强。我选择 Debian 12 的另一个理由是 Cloudflare Tunnel 的 deb 源支持得过官方提供了 cloudflared 的 apt 仓库直接添加源就能用apt install cloudflared安装省去了手动解压二进制文件的麻烦。构建脚本里 lock 住版本号后每次生成的 cloudflare-os 镜像都完全一致这一点对复现环境来说太重要了。2.2 工具链从 Wrangler 到 Tunnelcloudflare-os 里预置的工具清单如下表格所示组件版本/来源用途Debian 12官方 netinst ISO基础系统Node.js20.x LTSnvm 管理Wrangler 及 Workers 运行环境Wrangler3.x 最新稳定版部署 Worker、管理 Pages、操作 R2cloudflared官方 apt 源最新稳定版创建 Tunnel 并将本地服务映射到 CloudflareGitDebian 官方源版本管理jqDebian 官方源解析 API 返回的 JSONmakeDebian 官方源执行自动化任务Docker官方源运行本地依赖服务Wrangler 是这套环境里最核心的 CLI。 Workers 项目的初始化、本地开发、预览、部署全都靠它。而 cloudflared 解决的是“本机服务暴露到互联网”的问题在开发回调调试、临时演示、自托管应用等场景下非常有用。Docker 则用来跑数据库、Redis 这类本地依赖让 Worker 在wrangler dev --remote之前能在本地模拟完整链路。这些工具单独装都很简单但合在一起就会遇到互相牵扯的问题比如 Wrangler 需要 Node 18 以上而 Node 如果通过 apt 装可能会拿到很旧的版本。cloudflare-os 里我统一用 nvm 安装 Node 20 LTS并且把默认版本写在用户的.bashrc中这样每次登录 shell 都能直接调用到正确的 Node 和 npx。2.3 目录与配置文件一次约定处处可用除了工具本身cloudflare-os 还约定了一套目录结构和默认配置。我给普通用户dev建立了/home/dev/cloudflare目录专门用来存放所有与 Cloudflare 相关的项目并在其中划分workers/、pages/、tunnels/、scripts/四个子目录。workers/存放 Workers 项目每个项目有自己独立的wrangler.toml。pages/存放 Pages 项目前端代码构建后通过 Wrangler 部署。tunnels/存放每个 Tunnel 对应的配置文件如my-tunnel.yml。scripts/存放日常使用的辅助脚本比如批量同步 R2 Bucket 的脚本。这样的约定在单机环境下看起来可能有点多余但当你同时在多个云服务器、GitHub Codespaces、本地虚拟机里使用 cloudflare-os 时统一的路径和命名规范能显著降低切换成本。我再也不需要打开终端后先pwd确认自己身在何处所有环境都长一个样。配置文件部分我做了两个关键动作。第一预置一份~/.cloudflared/config.yml把协议升级、重试间隔、日志目录都调好避免每次都重新手写。第二把CLOUDFLARE_API_TOKEN和CLOUDFLARE_ACCOUNT_ID放到环境变量文件里但不写入镜像本身而是在安装后的首次启动脚本中提示用户手动填入防止把密钥带到镜像快照里造成泄露。3. 从 ISO 到实机cloudflare-os 的构建与部署实操3.1 构建前的准备在动手构建 cloudflare-os 前需要先准备一台基础环境或构建虚拟机。我一般用 Packer 和 QEMU 来构建也可以通过 cloud-init 在 Proxmox 上直接跑。这里为了尽量简单我用一台 Debian 12 虚拟机作为构建机先在其中创建好定制脚本最终把系统打包成可复用的虚拟机模板或者 Docker 镜像。构建机本身没有特殊要求只要内存大于 2GB、磁盘 30GB 以上即可。因为整个过程主要是 apt 更新、下载安装包、写配置文件并不需要跑重型编译。如果你是新手建议直接用 VirtualBox 手动安装一次基础 Debian 12 系统然后在这个系统上跑下面的脚本可以跑完再打包成 OVA 或虚拟机快照这样最容易理解。另外你需要提前准备好一个 Cloudflare API Token权限至少包含Workers Scripts:Edit、Cloudflare Tunnel:Edit、Account Settings:Read。如果后续还要操作 R2需要加上R2 Bucket:Edit。这个 Token 只用于验证安装完成后能用 Wrangler 正常访问账户不要写进脚本本身的默认值里。3.2 基础系统构建脚本以下是一个精简版的 cloudflare-os 构建脚本我用的是 Bash它做的事情是安装基础包、配置 SSH、创建用户目录结构。#!/bin/bash set -euo pipefail # 更新系统并安装基础工具 apt update apt upgrade -y apt install -y curl git jq make openssh-server sudo ufw # 创建云开发用户 useradd -m -s /bin/bash dev echo dev ALL(ALL) NOPASSWD:ALL /etc/sudoers.d/dev # 创建 Cloudflare 工作区目录 mkdir -p /home/dev/cloudflare/{workers,pages,tunnels,scripts} chown -R dev:dev /home/dev/cloudflare # 配置 SSH 密钥目录 mkdir -p /home/dev/.ssh touch /home/dev/.ssh/authorized_keys chown -R dev:dev /home/dev/.ssh chmod 700 /home/dev/.ssh chmod 600 /home/dev/.ssh/authorized_keys这一步有一个很容易被忽略的坑Ucloudflare 的 Wrangler 在普通用户下运行所以必须确保dev用户对cloudflare目录有完整权限而且当前 shell 的所有环境变量都属于这个用户。很多人在 root 下配好了环境切换到普通用户后就发现wrangler命令找不到了就是因为 Node 安装路径或 nvm 初始化脚本只写在 root 的.bashrc里没有写入目标用户的.bashrc。3.3 安装 Node.js 与 Wrangler我用 nvm 来安装 Node 20 LTS避免直接 apt 安装的 Node 版本过旧。首先安装 nvmcurl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash然后在/home/dev/.bashrc中添加 nvm 初始化内容并切换为dev用户继续执行export NVM_DIR$HOME/.nvm [ -s $NVM_DIR/nvm.sh ] \. $NVM_DIR/nvm.sh nvm install 20.0.0 nvm alias default 20.0.0 npm install -g wrangler3这里我刻意把 Wrangler 版本锁定为 3.x而不是直接npm install -g wrangler装 latest。原因在于最新版本经常会引入 breaking change而 Wrangler 的配置字段变动尤其频繁。比如 Workers 的vars从顶层挪到[vars]区块这类调整不同版本之间不兼容。在 cloudflare-os 里锁定版本后整套环境的可预期性会高很多后续如果你想升级可以单独改版本号重新构建一次镜像。安装完成后执行wrangler --version验证。如果在普通用户下跑不起来大概率是 nvm 的 Node 路径没有进$PATH检查一下.bashrc的末尾有没有 nvm 的 export 内容即可。3.4 安装并配置 cloudflaredcloudflared 的安装通过官方 apt 源完成# 添加 Cloudflare 官方的 apt 仓库 apt install -y apt-transport-https gnupg curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | gpg --dearmor -o /usr/share/keyrings/cloudflare.gpg echo deb [signed-by/usr/share/keyrings/cloudflare.gpg] https://pkg.cloudflare.com/cloudflared bookworm main /etc/apt/sources.list.d/cloudflared.list apt update apt install -y cloudflared安装好后默认的配置文件在/etc/cloudflared/config.yml。我会先创建一个基本的 tunnel 配置并设置成 systemd 服务这样即使重启机器tunnel 也会自动恢复。继续以dev用户身份执行登录授权cloudflared tunnel login这个命令会把浏览器打开的授权链接打出来完成授权后本地会生成~/.cloudflared/cert.pem文件。这是后续创建 Tunnel 和域名映射的基础。没有这个文件你甚至不能创建新的 Tunnel。接着创建一个用于开发环境穿透的 Tunnel 示例cloudflared tunnel create dev-tunnel cloudflared tunnel route dns dev-tunnel dev.example.com之后将生成的 Tunnel ID 写入/home/dev/.cloudflared/config.ymltunnel: dev-tunnel credentials-file: /home/dev/.cloudflared/dev-tunnel.json ingress: - hostname: dev.example.com service: http://localhost:8787 - service: http_status:404这里有个细节默认的最末条规则是 catch-all返回 404这能防止未匹配的域名流量被错误转发到本地服务。我在第一次配置时忘了加这条规则结果所有流量都进到了本地另一个端口花了不少时间排查。把它当作默认值写进模板后就再也没出过这种问题。3.5 初始化一个 Worker 项目并部署所有组件就绪后我们用 Wrangler 初始化一个项目来验证整套 cloudflare-os 是否可用cd /home/dev/cloudflare/workers npx wrangler init hello-worker cd hello-worker npx wrangler deploy这一步如果顺利终端会输出部署后的 URL。如果中途报 token 错误说明CLOUDFLARE_API_TOKEN环境变量没有正确设置。你可以手动写入/home/dev/.cloudflared/env并在.bashrc中 sourceexport CLOUDFLARE_API_TOKEN你的token export CLOUDFLARE_ACCOUNT_ID你的账户ID别忘了source ~/.bashrc或者重新登录 shell 才能生效。整个部署流程下来你已经从零具备了一套完整的 Cloudflare 开发环境后续写业务代码只需要进入workers/目录就能无缝开发。这基本就是 cloudflare-os 最核心的价值所在。4. 使用 cloudflare-os 时遇到的坑与排查方法4.1 API Token 权限不足最常见的报错是Error: Unauthorized或Invalid token。很多次不是 Token 本身错了而是 Token 的权限范围没勾选好。比如你要部署 Worker 到某个域但 Token 只给了 Account 的权限没有给 Zone 的权限。Wrangler 需要同时拥有 Account 和 Zone 两个层级的权限。我建议在 Cloudflare Dash 里创建 Token 时直接选择模板 “Edit Cloudflare Workers”它会自动带上大部分需要的权限。如果还要操作 Tunnel就在自定义 Token 里补上Cloudflare Tunnel:Edit。记得 Token 创建后只显示一次一定要妥善保存到密码管理器里写进 cloudflare-os 模板前再确认一遍格式。4.2 Tunnel 连接不稳定tunnel 和服务器之间的连接理论上很稳定但在重启或网络切换后systemd 服务偶尔会报Cannot determine default tunnel credentials错误。这通常是因为 cloudflared 服务启动时没有找到cert.pem或 Tunnel 的 JSON 凭证文件。排查思路是先用systemctl status cloudflared查看日志然后检查/home/dev/.cloudflared/下是否有cert.pem和对应的.json文件。如果文件存在再看服务配置里有没有读取到正确的用户环境。因为 systemd 服务默认是以 root 运行的不一定读dev用户的 HOME所以我在 cloudflare-os 里专门给 cloudflared 写了一个 service unit 文件在[Service]段里用Userdev和EnvironmentFile/home/dev/.cloudflared/env指定用户和变量。服务重启命令应该是sudo systemctl reload cloudflared sudo systemctl restart cloudflared每次修改 config.yml 后都建议reload而不是restart否则线上连接会中断几秒。4.3 Wrangler 各版本差异前面说过我锁定了 Wrangler 3.x但实际使用中还是会在不同项目里碰到版本差异。有些老项目的wrangler.toml用的是 2.x 语法比如workers_dev true这个字段在 3.x 已经废弃部署时会提示Error: ValidationError: workers_dev is not allowed。如果遇到老项目不要硬改配置而是在项目内单独安装一个适配版本cd /home/dev/cloudflare/workers/legacy-project npm install wrangler2.0.0 npx wrangler deploycloudflare-os 的全局环境保持最新 3.x但每个项目可以用本地依赖覆盖版本这样既保证了全局一致性又兼容历史项目。这个思路和 Python 的虚拟环境有点像是一种很实用的多版本共存策略。4.4 DNS 解析延迟和缓存部署 Worker 后直接访问域名经常发现还是旧版本或者 404很多人第一时间怀疑是部署失败其实多数情况是 DNS 缓存。Cloudflare 的 DNS 记录通过 Tunnel 创建后TTL 通常默认是 1 小时本地浏览器和系统解析器也可能有缓存。排查方式是先用curl -I直接请求域名再对比nslookup和 Cloudflare Dash 中的解析结果。如果 Dash 里记录存在且指向 Tunnel但本地解析出来是旧 IP那就等 TTL 过期或手动刷新系统 DNS。Wrangler 部署返回的发布地址通常是最新的直接用那个地址验证业务是否正常会更省时间。4.5 快速错误速查表报错信息可能原因解决办法Error: UnauthorizedAPI Token 无效或权限不足检查 Token 权限和CLOUDFLARE_API_TOKEN环境变量Cannot determine default tunnel credentials缺少cert.pem或 JSON 凭证文件重新运行cloudflared tunnel loginValidationError: workers_dev is not allowedWrangler 版本过高在项目目录安装兼容的低版本 WranglerError: A Zone with that hostname already existsDNS 记录冲突删除旧 DNS 记录或换一个子域名connect ECONNREFUSED本地服务未启动或端口不对检查wrangler dev是否在运行端口是否匹配getaddrinfo ENOTFOUNDDNS 解析失败或域不存在检查域名的 NS 和解析记录是否已切换到 Cloudflare这个速查表是我踩坑过程中的实操总结你可以在本地维护一份后续遇到类似问题能快速缩小范围。这里面的错误很多都和环境有关而不是业务代码导致所以一旦把环境标准化问题数量会直线下降。5. 在 cloudflare-os 基础上还能扩展什么5.1 把开发机变成边缘实验台cloudflare-os 不只是一个开发环境还能当成家庭实验室的边缘实验台。你可以在同一个 tunnel 下面挂多个本地服务比如 Grafana、Home Assistant、临时 API 服务。只要按 hostname 区分 ingress 规则就能用一套 Cloudflare 账号统一管理。做好访问控制后还可以给自己加一个简单的认证层避免匿名访问。我目前就在/home/dev/cloudflare/tunnels/config.yml里加了dev-grafana.internal.example.com的规则把本地 Grafana 面板映射出去。这样出差在外也能看家里的监控数据。这套玩法严格依赖 cloudflare-os 预置的 systemd 服务能力如果没有这台基础系统我可能要在每个服务上单独配守护进程维护成本完全不同。5.2 用 CI/CD 自动构建镜像刚开始 cloudflare-os 只是我一个人在用的本地脚本后来我发现这套脚本完全可以放进 GitHub Actions 或者 GitLab CI 里自动构建虚拟机镜像并推送到私有镜像仓库。这样每次更新基础组件或配置后不需要手动重建整台机器直接从 CI 拉取最新镜像模板批量部署即可。具体的做法是写一个Dockerfile把 cloudflare-os 的脚本作为构建步骤FROM debian:12 COPY scripts/ /tmp/scripts/ RUN bash /tmp/scripts/install-base.sh \ bash /tmp/scripts/install-cloudflared.sh \ bash /tmp/scripts/install-wrangler.sh CMD [bash]然后交给 CI 定时构建。如果你想以 Docker 镜像的形式使用 cloudflare-os可以用这个镜像作为开发容器或者远程 Codespace 的基础镜像。这样除了虚拟机模板又多了一种使用方式。5.3 备份和版本管理最后说一下版本管理。cloudflare-os 的脚本本身要放进 Git 仓库用 tag 标记版本号比如cloudflare-os-v1.0.0。所有配置文件除了密钥都应该提交密钥则通过环境变量注入。这样当某一天机器坏了你拿起任意一台电脑把仓库 clone 下来跑一遍脚本就又能复原整个开发环境。我在使用中发现不要把wrangler.toml里的账户 ID 写死在仓库里因为不同环境可能对应不同账户环境变量和.env文件才是更合适的注入方式。我个人在实际操作中的体会是环境标准化带来的收益前期可能看不到但当你同时维护四五台机器、两三个项目、以及长期运行的 Tunnel 服务时就能明显感觉到差距。cloudflare-os 不是什么惊天动地的项目它只是一个把重复劳动提前做完的模板但就是这个模板让我这几年的开发工作从“反复救火”变成了“开箱即用”。如果你也长期依赖 Cloudflare 的整套工具链不妨按这个思路把属于你自己的 clouflare-os 构建出来。
返回列表