跳到正文
码上手 coding.chengyu.dev

上线:域名、自动部署、回滚与监控

用 wrangler 完成首次部署,绑定自定义域名,用 GitHub Actions 实现推送即部署并给 API Token 最小权限,再备好检查清单、回滚方案与基础监控。

约 45 分钟 进阶 Claude CodeCloudflareGitHub Actions 核实于 2026-10-25

学完这一课,你能

  • 能按正确顺序完成首次部署:迁移、构建、部署、设置密钥
  • 能给 Worker 绑定自定义域名,并说清常见的冲突
  • 能写出 GitHub Actions 工作流,并为 Cloudflare API Token 授予最小权限
  • 能用版本回滚和 D1 备份应对线上事故,用日志与分析观察线上运行

本地跑得好好的网站,上线当天常见的三类事故:数据库迁移忘了在线上执行,页面一片 500;密钥没设置,管理页进不去;改了一行代码推上去,线上整个挂掉,却不知道怎么退回去。

这一课是整个路径的收尾:第一次上线怎么做才稳,之后怎样每次都稳,出了事怎么办。

第一次部署:顺序比命令重要

首次部署手动完成,目的是亲眼看每一步的结果,之后再交给自动化。顺序如下,每一步都有原因:

~/projects/my-shelf
# 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。个人书单远用不到,但如果被爬虫打满,这些数字是你判断的依据。

生成 GitHub Actions 部署工作流与上线清单 Claude Code

读取 CLAUDE.md、wrangler.jsonc 和 package.json,为书单项目准备自动部署。严格遵守红线:不读取 .dev.vars,不执行任何 --remote 命令,不执行 wrangler deploy,不设置任何 secret;这些都由我手动完成。

任务:

  1. 创建 .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 的最低要求;
  2. 对照 Astro 与 Cloudflare 的官方部署文档,检查 wrangler.jsonc 中 main、assets、compatibility_date、observability 是否合理,列出你发现的问题但先不修改;
  3. 写 docs/DEPLOY.md(简短):首次部署的命令顺序、需要在 GitHub 里设置的两个 secret 名称(CLOUDFLARE_API_TOKEN、CLOUDFLARE_ACCOUNT_ID)与设置方法、API Token 建议的最小权限与 TTL、上线检查清单、三种回滚方式(wrangler rollback、D1 export 与 Time Travel、git revert);
  4. 完成后运行 git diff --stat,并逐项说明哪些步骤需要我手动执行。
小测

CI 里执行了 `wrangler rollback` 把线上退回上一个版本,但没有改仓库。结果是?

小测

下面哪个做法最符合 API Token 的最小权限原则?

动手任务

动手清单

0/6

恭喜,你从一句话的想法出发,走完了规格、骨架、界面、后端、测试与上线。回头看,真正可迁移的是方法:先对齐再动手,给智能体可验证的标准,把危险操作留给自己,让独立的眼睛检查。接下来可以去 Claude Code 精通 路径,把评审和测试进一步交给无头模式与 CI。

参考来源

工具更新很快,若与官方文档不一致,以官方为准。

学完了吗?

标记完成会记录在本机浏览器,计入你的学习进度和连续天数。

Esc

输入关键词开始搜索,例如「CLAUDE.md」「MCP」「部署」