缩略图

2024 年,将网站部署到 Cloudflare Workers

· 技术

一觉醒来,才发现 Cloudflare 推出了一个 Workers 的新功能——Static Assets,我便以最快的速度将网站重新部署回了 Cloudflare Workers。

从 Workers Site 到 Workers Static Assets

Workers Site

将网站部署到 Cloudflare Workers 这一操作得追溯到 2019 年。这一年的九月,Cloudflare 推出了 Workers Site——将你的网站文件存储在 KV Storage,Workers 接到请求后从中取出并返回,以此实现网站托管。

我大概是在 2020 年到 2021 年这个区间尝试将网站部署在 Cloudflare Workers——起码要比我尝试 Cloudflare 在 2021 年推出的 Pages 要早很多。但是因为自己爱折腾(缩),在很长一段的时间里都在动态博客和静态博客之前来回横跳,就算是在用静态博客也是在 Vercel 和 Cloudflare 之间横跳,我并没有过多关注 Cloudflare Workers Site 在这几年间的变化。直到去年 9 月到今年 3 月这个区间,我大致是稳定将网站部署在了 Cloudflare Workers Site。这时,我才发现一些不对劲。

因为贫穷,我一直都是在使用 Cloudflare Free Plan。Free Plan 下的 KV Storage 只给了 1k 操作数(包括写入、删除和列出),而 Wrangler 部署一次网站需要先删除对应 KV Storage 里面的原有网站文件、再上传新的网站文件,也就是说:

  • 我的网站是 SSG 应用,构建出来有 373 个文件;
  • Wrangler 首先要删除 KV Storage 里面的原有网站文件,原有网站文件数量相同,因此我消耗了 373 次 KV 操作数;
  • 随后 Wrangler 要上传新的网站文件,因此我又消耗了 373 次 KV 操作数;
  • 最终我消耗了 746 次 KV 操作数,还收到了 Cloudflare 自动发送的 KV 使用量预警。

......那这样,我一天只需要交一个 Commit 就可以收工了。

Cloudflare 都在 Workers Site 文档 写了一个告示:Use Cloudflare Pages for hosting fullstack applications instead of Workers Sites. 那我干脆用 Cloudflare Pages 部署好了,反正也没差。

时间便来到了前天。我闲来无事又翻了一下 Cloudflare Workers 文档,却发现了一个新东西:Workers Static Assets

Workers Static Assets

是的你没看错,Cloudflare 推出了仍在测试的 Workers Static Assets,并且 Workers Site 文档 也从「Use Cloudflare Pages instead」变成了「Use Workers Static Assets instead」。

相比 Workers Site,Workers Static Assets 将网站文件直接存在了 Cloudflare Network 上(存疑,文档没有明写)、而不是 KV Storage,因此无需担心 KV 操作数会不会一下用光(雾)。并且,Workers Site 需要使用 script 从 KV Storage 中取出网站内容并返回,而 Workers Static Assets 是在 Cloudflare Network 上直接返回网站文件、不涉及到 Workers script,因此不计入 Workers 的 100k usage。

这篇文章发表时,我的网站(主页、博客、档案馆和 Hyperlink Redirect,全部都是 SSG 应用)和由 Nitro 驱动的 API 都部署在了 Cloudflare Workers 上。只有 API 的请求数算入了 Workers usage。

作为更偏执于 Cloudflare Workers 的博主,虽然 Workers Static Assets 和 Pages 对我来说没有太大的区别,但我还是将我的网站部署在了 Cloudflare Workers 上。如果你想了解它们之间的区别,可以看看 Cloudflare 团队的比较:Workers vs. Pages (compatibility matrix)

如何部署

说了这么多废话,该来教教部署了。

Workers Static Assets 的部署和 Workers Site 的部署没有特别大的差异,唯一差异就是在 wrangler.toml 配置文件上。

考虑到真有人没尝试过所以 让我们先从 API Token 的创建开始。

安装 Wrangler CLI

$ pnpm add -g wrangler@latest

你要安装的是 wrangler 而不是 @cloudflare/wrangler @cloudflare/wrangler 是早期的 npm 包,现在它们已经用 wrangler 代替。

创建 API 令牌

访问 仪表盘 => 我的个人资料 => API 令牌,创建一个新的令牌。

方便起见,你可以直接使用「编辑 Cloudflare Workers」模板,但在权限部分仅保留下图列出的权限。

Workers Static Assets 需要用到的权限

如果你创建的这个 API 令牌 仅用于部署网站到 Cloudflare Workers,那么我不建议你再给予它更多的权限。权限越多,风险越大。

下述的账户资源和区域资源选择你的账户区域即可。令牌 TTL 根据你的实际情况来设定时间长度,如果你不想设定 TTL、长期使用这个令牌,请妥善保管好生成的 API 令牌。

添加 wrangler.toml 配置文件

按常理来讲,我们应该先初始化一个 Cloudflare Workers 项目。但是,部署一个 Workers Static Assets 项目只需要一个 wrangler.toml 文件、完全没必要在本地进行初始化。因此我们只需要 wrangler.toml 配置文件就可以啦。

添加配置文件也十分的简单。在你的项目根目录创建一个 wrangler.toml 文件,并写入以下内容:

#:schema node_modules/wrangler/config-schema.json
name = "<TBD>"
compatibility_date = "<TBD>"
assets = { directory = "./public" }

# Workers Logs
# Docs: https://developers.cloudflare.com/workers/observability/logs/workers-logs/
# Configuration: https://developers.cloudflare.com/workers/observability/logs/workers-logs/#enable-workers-logs
[observability]
enabled = true

这是一个 Workers Static Assets 项目的默认 wrangler.toml 配置文件的内容。让我们来分析一下要怎么修改:

  • name:项目的名称;
  • compatibility_date:项目兼容性日期,确保在该日期之后发布的任何新特性或更改不会影响到现有的应用程序行为;
  • assets:网站的静态资源(缩),需要绑定到输出目录;
    • 比如我的网站框架是 Nuxt、SSG 输出目录是 .output/public,那么我就写 { directory = "./output/public" } 即可。

下面有一个 observability 控制 Workers Log 的启用与否,请根据你的实际情况来启用或禁用(缩)。


等等,我在上面提到了「我也将由 Nitro 驱动的 API 部署在了 Cloudflare Workers 上」,并且这个 API 是 Serverless 模式。这是怎么做到的呢?Nitro 作为 Nuxt 的 Server Engine,一定程度上可以代表 SSR 模式下的 Nuxt,那么我要如何部署 SSR 模式下的 Nuxt 项目呢?

还是很简单,小小修改一下上面的 wrangler.toml 配置文件:

#:schema node_modules/wrangler/config-schema.json
name = "<TBD>"
main = ".output/server/index.mjs" # 添加这一行
compatibility_date = "<TBD>"
assets = { directory = "./output/public", binding = "ASSETS" } # 这一行添加 bingding

# Workers Logs
# Docs: https://developers.cloudflare.com/workers/observability/logs/workers-logs/
# Configuration: https://developers.cloudflare.com/workers/observability/logs/workers-logs/#enable-workers-logs
[observability]
enabled = true

来看看我们改动了一些什么:

  • 因为是 SSR,我们需要指定一个 main、路径指向 Nitro 生成的 Server entry,让 Workers 执行它;
  • 我们还需要将 .output/public 下的静态文件进行 binding,以便 Workers 能够访问它们。

通过 GitHub Actions + wrangler-action 部署

享受到 CI / CD 的好处之后,我不喜欢在本地手动执行执行 pnpm generatewrangler deploy 部署项目,而是让 GitHub Actions 一次性帮我搞定。

在项目根目录下创建 .github/workflows 目录,并在这个目录下新建一个 YAML 文件(我起名叫 deploy.yml)。这个 YAML 文件将作为部署项目的 GitHub Actions 配置文件。

劝你先别急着推上去,不然有红色惊喜。(缩)

name: Deploy site

on:
  push:
    # 请注意你的 branch 名称是 main 还是 master
    branches:
      - main
jobs:
  CI:
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      # 我的博客使用到了 pnpm 和 Node.js 20,故需要先使用对应的 Action 配置当前环境。
      - name: Prepare pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 9
      - name: Prepare Node.js 20
        uses: actions/setup-node@v4
        with:
          node-version: 20
      # 运行 pnpm install 安装依赖
      # 我没有配置 node_modules 的缓存,后续会考虑
      - name: Install dependencies
        run: pnpm install
      # 运行 pnpm generate,以 SSG 模式构建 Nuxt 应用
      - name: Generate site
        run: pnpm generate
      # 部署到 Cloudflare Workers
      - name: Deploy to Cloudflare Workers
        uses: cloudflare/wrangler-action@v3
        with:
          accountId: ${{ secrets.CF_ACCOUNT_ID }}
          apiToken: ${{ secrets.CF_API_TOKEN }}
          command: deploy

如果此前你已经在使用 GitHub Actions,相信你修改后的配置也能一次运行,这里就不多废话了。

使用 wrangler-action 必须要提供一个 apiToken 值,内容是在上面创建的、仅能操作 Workers 的 API 令牌。为了安全起见,我将 API 令牌存在了 GitHub 仓库的 Repository secrets 里,并在配置文件中进行引用。你可以在仓库的 Settings => Secrets and variables (在侧边栏) => Actions 中添加。注意一下上面的 Label 是 Secrets 还是 Variables,不要添加错了。

accountId 不是一个必须要提供的值。但是,如果你的账户存在多个区域(有人邀请你管理他的 Cloudflare 区域,包括域名和 Workers 这类),你必须要提供这个值。对应的值可以在你的 域名概述 中找到。


配置推上去了,但是 wrangler-action 吐出了一个报错:

assets.bucket is a required field

bucket 并不是一个必填值。出现这个报错是因为 wrangler-action 默认会提供一个 3.1x 版本的 wrangler 进行部署,所以导致了 assets.bucket is a required field 这个报错,版本 3.78.10 的 wrangler 解决了这个问题= =。行吧,那就在 wrangler-action 的配置中多指定一个 wranglerVersion 值:

- name: Deploy to Cloudflare Workers
  uses: cloudflare/wrangler-action@v3
  with:
    accountId: ${{ secrets.CF_ACCOUNT_ID }}
    apiToken: ${{ secrets.CF_API_TOKEN }}
    command: deploy
    wranglerVersion: "3.78.10"

或者你跟我一样想把 wrangler 安装在项目中、每次 pnpm update --latest 时也能更新 wrangler 版本,那就执行 pnpm add -D wrangler 即可。wrangler-action 会读取你的项目中是否有 wrangler,若有则使用它来执行部署任务。

当 wrangler-action 顺利跑完,你的项目也成功部署在 Cloudflare Workers 上啦。记得去配置域名到 Workers 项目上哦(缩)。

Loading Artalk...