上线:域名、自动部署、回滚与监控
用 wrangler 完成首次部署,绑定自定义域名,用 GitHub Actions 实现推送即部署并给 API Token 最小权限,再备好检查清单、回滚方案与基础监控。
学完这一课,你能
- 能按正确顺序完成首次部署:迁移、构建、部署、设置密钥
- 能给 Worker 绑定自定义域名,并说清常见的冲突
- 能写出 GitHub Actions 工作流,并为 Cloudflare API Token 授予最小权限
- 能用版本回滚和 D1 备份应对线上事故,用日志与分析观察线上运行
本地跑得好好的网站,上线当天常见的三类事故:数据库迁移忘了在线上执行,页面一片 500;密钥没设置,管理页进不去;改了一行代码推上去,线上整个挂掉,却不知道怎么退回去。
这一课是整个路径的收尾:第一次上线怎么做才稳,之后怎样每次都稳,出了事怎么办。
第一次部署:顺序比命令重要
首次部署手动完成,目的是亲眼看每一步的结果,之后再交给自动化。顺序如下,每一步都有原因:
# 0. 本地全绿再上线$ npx vitest run && npx playwright test && npm run build# 1. 先对远程库执行迁移:新代码上线时表必须已经存在$ npx wrangler d1 migrations list my-shelf-db --remote$ npx wrangler d1 migrations apply my-shelf-db --remote# 2. 构建并试跑(不真正部署)$ npx wrangler deploy --dry-run# 3. 部署。成功后终端会打印 workers.dev 地址$ npx wrangler deploy# 4. Worker 存在之后再设置线上密钥(命令会立即新建版本并生效)$ npx wrangler secret put ADMIN_TOKEN 两点说明。第一,第 4 步在部署之后:这样密钥是挂在已存在的 Worker 上的。代价是第 3 步到第 4 步之间管理令牌为空,这正是第 4 课 isAdmin 要求 token.length > 0 并与 env.ADMIN_TOKEN 严格相等的原因:配置缺失时要「拒绝」而不是「放行」。第二,第 1 步和第 3 步都是写线上的操作,让智能体帮你准备命令,但执行时由你确认,这也是 CLAUDE.md 红线里写明「不自行 deploy」的用意。
部署后马上用 curl -i https://你的地址/api/books 和浏览器各访问一次,再用管理页新增一本书,确认摘要生成。
绑定自定义域名
workers.dev 地址能用,但你想要的是 shelf.你的域名.com。官方文档里自定义域名的前提有三条:域名已经作为 zone 托管在你的 Cloudflare 账号下、Worker 已部署、目标主机名上没有已存在的 CNAME 记录。
有两种做法,选一种即可。面板:Workers & Pages,选择你的 Worker,进入 Settings,Domains & Routes,Add,Custom Domain。或者写进 wrangler.jsonc,随代码一起管理:
{
"routes": [
{ "pattern": "shelf.example.com", "custom_domain": true }
]
}
然后 npx wrangler deploy。Cloudflare 会替你创建 DNS 记录并签发证书,不需要手动改 DNS。几个容易踩的坑:
- 已有 CNAME 会冲突。主机名上如果原来有 CNAME,要先删掉再添加。
- 精确匹配,不含通配符。
example.com和www.example.com是两个主机名,只绑定一个,另一个不会自动指过来,需要再加重定向。 - 自定义域名不等于路由。官方区分了 Custom Domain 与 Routes 两种机制,我们用的是前者:它直接让 Worker 成为该主机名的源站。
GitHub Actions:推送即部署
手动部署适合第一次,之后应该交给自动化:每次合并到 main,自动测试、迁移、部署。Cloudflare 自己也提供 Git 集成的构建服务(Workers Builds),但本课用 GitHub Actions,因为工作流写在仓库里,可评审、可复用,也方便你控制「先测试再部署」的顺序。
# .github/workflows/deploy.yml
name: CI and Deploy
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
- run: npm ci
- run: npx vitest run
- run: npm run build
deploy:
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run build
- name: 应用数据库迁移
uses: cloudflare/wrangler-action@v4
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: d1 migrations apply my-shelf-db --remote
- name: 部署 Worker
uses: cloudflare/wrangler-action@v4
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy
设计要点:
pull_request只跑test。GitHub 不会把仓库密钥交给来自 fork 的 PR,所以陌生人的 PR 既部署不了,也读不到你的 Token。部署任务限定为main上的push。- 先迁移、后部署。迁移必须向后兼容(新增列、新增表),这样老代码在迁移后到新代码上线前的几十秒里仍然能工作。要删列、改列,分两次发布完成。
permissions: contents: read。工作流自己的GITHUB_TOKEN也只给读权限。- 进阶:官方示例用版本标签(
@v4),更严格的做法是把第三方 Action 固定到具体提交哈希。 - 端到端测试暂时没放进 CI,因为它需要本地 D1 初始化和测试令牌。想加可以作为练习,令牌用单独的测试值。
API Token:只给它需要的
工作流里的 CLOUDFLARE_API_TOKEN 是整个流程最敏感的东西,泄露等于别人能往你的账号部署代码。创建方式:Cloudflare 面板里选账号级别的 API Tokens,Create Token。官方示例从「Edit Cloudflare Workers」模板起步,再自己收紧。权限的取舍可以这样想:
| 权限 | 作用 | 是否需要 |
|---|---|---|
| 账号 · Workers Scripts · Edit | 上传、更新 Worker | 需要 |
| 账号 · D1 · Edit | CI 里执行 d1 migrations apply --remote |
需要(若 CI 不跑迁移就不要) |
| 账号 · Workers AI | AI 绑定是运行时能力 | 通常不需要,以实际报错为准 |
| 区域 · Workers Routes / DNS | 部署时创建自定义域名记录 | 取决于是否每次由 CI 维护域名 |
我没法替你逐项验证每个模板里到底含哪些权限,所以最稳妥的办法是:从最小集合开始,让第一次失败的部署告诉你缺什么,缺一项补一项,而不是一开始就勾上所有 Edit。同时:
- 范围限定到单个账号,区域权限限定到你的那个域名;
- 设置过期时间(TTL);GitHub 托管的运行器 IP 不固定,所以不要启用 IP 白名单;
- 新生成的 Token 前缀为
cfut_,能被密钥扫描工具识别;只会显示一次,复制后马上存好; - 写入 GitHub 时不要贴在聊天或代码里,在终端用
gh secret set CLOUDFLARE_API_TOKEN,它会提示你输入值;账号 ID 同理用gh secret set CLOUDFLARE_ACCOUNT_ID。
万一 Token 泄露:立刻在 Cloudflare 面板里删除或轮换它,再更新 GitHub 里的 secret,并查看账号下近期的部署记录。
上线检查清单
每次发布前后过一遍,比任何经验都可靠:
| 阶段 | 检查项 | 怎么确认 |
|---|---|---|
| 发布前 | 测试和构建全绿 | CI 的 test 任务通过 |
| 发布前 | 迁移向后兼容 | 新迁移只加不删;看一眼 SQL |
| 发布前 | 线上 secret 已设置 | npx wrangler secret list 里有 ADMIN_TOKEN |
| 发布后 | 首页与详情页能打开 | 浏览器和 curl -I |
| 发布后 | 无令牌写接口返回 401 | curl -i -X POST .../api/books |
| 发布后 | 私有书不在公开接口 | 对线上接口的响应过一遍 |
| 发布后 | 新增一本书,摘要和标签生成 | 管理页实测 |
| 发布后 | 没有新的错误日志 | 面板或 wrangler tail |
| 发布后 | 域名与 HTTPS 正常 | 浏览器访问自定义域名 |
回滚:代码、数据、迁移分开想
线上出事时先止血,再查原因。回滚分三层:
代码回滚用 Worker 的版本机制,几秒完成:
npx wrangler deployments list # 最近的部署记录
npx wrangler versions list # 最近的版本
npx wrangler rollback <VERSION_ID> --message "回滚:详情页 500"
不带版本号时,它会回到上一个版本。官方文档强调了几个限制:回滚只切换代码,不会改变绑定的资源,所以 D1 里的数据和表结构不会跟着变;旧代码遇到新结构可能报错;只能回到最近的 100 个版本。面板里也可以操作:Workers & Pages,选 Worker,Deployments,目标版本旁点 Rollback。
数据回滚依靠 D1 的备份。高风险的迁移之前,先导出一份;D1 的 Time Travel 可以把线上库恢复到 30 天内的某个时间点:
npx wrangler d1 export my-shelf-db --remote --output backup-before-0002.sql
npx wrangler d1 time-travel info my-shelf-db # 查看当前书签/时间点
npx wrangler d1 time-travel restore my-shelf-db --timestamp <时间> # 恢复,需你确认
restore 会覆盖线上数据,属于重大操作,必须由你确认后执行。
修复主线也别忘了:用 wrangler rollback 回滚后,如果问题代码还在 main 上,下一次推送又会把坏代码部署回去。所以回滚之后要在仓库里 git revert 或修复,让 main 与线上一致。
基础监控:日志与分析
我们在配置里早就打开了 observability。它的作用:Worker 里的 console.log、console.error 和请求信息会被收集,可以在面板的 Observability 里搜索。免费计划每天 20 万条日志事件,保留 3 天;官方页面还提示,从 2026 年 12 月 1 日起计费方式会切换到 Cloudflare Observability 的定价,上线前看一眼最新说明。
需要实时观察时用 wrangler tail:
npx wrangler tail --status error # 只看出错的请求
npx wrangler tail --search "enrichBook" # 只看包含关键字的日志
记日志有两条纪律:不要记令牌、完整请求头和读者的短评原文;错误要带上能定位的信息,比如书的 id,而不是只写「失败了」。第 4 课的 console.error("enrichBook failed", id, err) 就是这个思路。
流量分析可以用 Cloudflare Web Analytics:面板里进入 Web analytics,Add a site,选择你的主机名。官方文档说明经 Cloudflare 代理的站点默认启用;也可以改为手动贴 JS 片段。具体是否使用 Cookie 的隐私细节,请看官方的隐私说明后再决定要不要开。
还要留意免费额度:Workers 免费计划每天 10 万次请求(静态资源请求免费且不限量),D1 免费计划每天 500 万行读取、10 万行写入,Workers AI 每天 10,000 Neurons。个人书单远用不到,但如果被爬虫打满,这些数字是你判断的依据。
读取 CLAUDE.md、wrangler.jsonc 和 package.json,为书单项目准备自动部署。严格遵守红线:不读取 .dev.vars,不执行任何 --remote 命令,不执行 wrangler deploy,不设置任何 secret;这些都由我手动完成。
任务:
- 创建 .github/workflows/deploy.yml:push 到 main 与 pull_request 触发;test 任务运行 npm ci、vitest、构建;deploy 任务 needs test,仅在 main 的 push 时运行,先用 cloudflare/wrangler-action@v4 执行
d1 migrations apply my-shelf-db --remote,再执行 deploy;整个工作流 permissions 仅 contents: read,并设置 concurrency 与 timeout;checkout 用 v6,setup-node 用 v7,Node 版本满足 Astro 的最低要求; - 对照 Astro 与 Cloudflare 的官方部署文档,检查 wrangler.jsonc 中 main、assets、compatibility_date、observability 是否合理,列出你发现的问题但先不修改;
- 写 docs/DEPLOY.md(简短):首次部署的命令顺序、需要在 GitHub 里设置的两个 secret 名称(CLOUDFLARE_API_TOKEN、CLOUDFLARE_ACCOUNT_ID)与设置方法、API Token 建议的最小权限与 TTL、上线检查清单、三种回滚方式(wrangler rollback、D1 export 与 Time Travel、git revert);
- 完成后运行
git diff --stat,并逐项说明哪些步骤需要我手动执行。
CI 里执行了 `wrangler rollback` 把线上退回上一个版本,但没有改仓库。结果是?
下面哪个做法最符合 API Token 的最小权限原则?
动手任务
动手清单
0/6恭喜,你从一句话的想法出发,走完了规格、骨架、界面、后端、测试与上线。回头看,真正可迁移的是方法:先对齐再动手,给智能体可验证的标准,把危险操作留给自己,让独立的眼睛检查。接下来可以去 Claude Code 精通 路径,把评审和测试进一步交给无头模式与 CI。
参考来源
- Deploy your Astro site to Cloudflare Workers(Astro 官方文档)
- Custom Domains(Cloudflare 官方文档)
- GitHub Actions(Cloudflare Workers 官方文档)
- Workers CI/CD 概览(Cloudflare 官方文档)
- wrangler-action(Cloudflare 官方仓库)
- Create API token(Cloudflare 官方文档)
- API token permissions(Cloudflare 官方文档)
- Rollbacks(Cloudflare 官方文档)
- Wrangler Workers commands(Cloudflare 官方文档)
- Wrangler D1 commands(Cloudflare 官方文档)
- Workers Logs(Cloudflare 官方文档)
- Web Analytics(Cloudflare 官方文档)
- Using secrets in GitHub Actions(GitHub 官方文档)
- Building and testing Node.js(GitHub 官方文档)
工具更新很快,若与官方文档不一致,以官方为准。
学完了吗?
标记完成会记录在本机浏览器,计入你的学习进度和连续天数。