• #cloudflare
  • #blog

Migrate My Blog to Cloudflare Workers

去年十一月,我想把自己拍的乱七八糟的照片放到公开的网站上,做一个作品集(portfolio)网站。我首先想到的是我 Creative Cloud 订阅里的 Adobe Portfolio,不过试了以后才发现这玩意儿的的访问速度实在是太慢了。程序员思维的我就蹦出来想:为什么不自己搭一个呢?当时正对 Cloudflare 非常上头,而我又懒的研究国内各种云平台的 CDN 服务,就脑子一热订阅了 Cloudflare Workers。但可惜,之后因为工作太忙,我就没怎么用它,只是把网站搭起来并且跑通了 GitHub Actions 后就不管了。

转眼已经到了 2021 年,并且已经过去了两个月,我想着也该翻新一下自己的博客。就在某个凌晨花了一个小时把整个站点用 TypeScript 重写了——打算以此当作新的开始。我心想:既然都重写了,不如尝试把博客也转移到 Cloudflare Workers 上呗?于是就此开始折腾。

在 Apple Silicon 上安装 Cloudflare Wrangler

如果你使用的不是基于 ARM 的计算机,可以跳过这部分,直接从下一节开始读起。

这是最简单的一步,也可能是最复杂的一步。

Cloudflare 已经把整套工具链做成一个 CLI 工具了,名叫 Wrangler。官方推荐的安装方法是 npm,但很不幸,你只会得到一行错误:Error: Unsupported platform: Darwin arm64。好在 Wrangler 是 Rust 实现的,所以我们可以用 cargo 来安装,执行 cargo install wrangler 后,Cargo 就会下载并且编译 Wrangler,这过程中并不会发生任何问题。难道这就结束了吗?并没有。当你随便想用 Wrangler 做点事儿的时候,就会遇到下面的错误。

$ wrangler init --site
⬇️  Installing cargo-generate v0.5.0...
Error: could not download `cargo-generate`
no prebuilt cargo-generate binaries are available for this platform

这种问题一看就不是那种「因为用了某些平台上的某种技术而导致无法在你的平台上运行」的错误。而且,因为 cargo generate 这个命令在几个月之前就支持 ARM 平台了,我感觉这个问题一定有 workaround。经过一番搜寻,我发现果然如此,引用相关 issue #1685里的一句话。

Which could potentially be that wrangler is a bit overeager in its convenience-added approach. Rather than expect to download a pre-built binary and fail otherwise, it could first check for an existing binary on the system, and fall back to attempting to download. Or, if cargo is installed, cargo install <wasm-pack|generate|etc> could be invoked to build these utils from source.

说白了就是 Wrangler 压根就没有考虑到这种情况,所以只会从某个地方下载预先编译好的 cargo-generatewasm-pack,这就导致了:虽然 Wrangler 本身可以通过 cargo install 在 Apple Silicon 上运行,但下载这两个工具链的时候出错,因为 wrangler 的开发团队压根就没有考虑过 ARM 平台上的运行体验。

明明可以在我这里跑起来的东西,因为开发人员没有考虑过这种情况,我们难道就要因此放弃吗?不,于是我克隆了一份 Wrangler 的代码,全文搜索错误信息,找到报错的位置。从下面的代码我们可以看到,Wrangler 在运行生成命令时会执行 install::install_cargo_generate() 函数,这个函数会下载 cargo-generate 并返回路径,再把需要执行的命令行参数拼接起来。

pub fn run_generate(name: &str, template: &str) -> Result<(), failure::Error> {
	let binary_path = install::install_cargo_generate()?;

	let args = ["generate", "--git", template, "--name", name, "--force"];

	let command = command(binary_path, &args);
	let command_name = format!("{:?}", command);
	commands::run(command, &command_name)
}

我看了下 install::install_cargo_generate() 的实现,它会根据操作系统和平台请求 https://workers.cloudflare.com/get-binary/{0}/{1}/v{2}/{3}.tar.gz 上下载 cargo-generatewasm-pack。这就令人哭笑不得了,因为它既不是从 Rust 相关的源下载,而且代码中本身也没有提供检测 ARM 平台的代码。我就没有时间贡献 PR 了,就直接把上面代码的 binary_path 替换为我本机的 Cargo 的路径。最后,构建一份 release,并把构建好的程序链接到我自己的 bin 文件夹下,Wrangler 就能正常使用了。

配置 Wrangler 项目

在 Wrangler 安装完后,我们就可以干正事了。其实只需要按照官方文档中的教程一步一步来就好。但我这里还是把每一步摘抄出来,因为这些步骤散布在很多篇文章里。

第一步,在 Gatsby 里用 Wrangler 新建项目。用 cd 进入项目,执行下面的命令。

$ wrangler init --site
🔧   Creating project called `workers-site`...
✨   Done! New project created ./blog/workers-site
✨  Successfully scaffolded workers site
✨  Succesfully created a `wrangler.toml`

第二步,编辑 wrangler.toml。把 account_id 改成执行 wrangler whoami 所得到的 Account ID。把 bucket 改成 "./public" 也就是 Gatsby 构建的目标文件夹。其余的字段不用改。

第三步,部署到 Workers 上。只要运行下面的命令就行了。

$ wrangler publish
✨  Built successfully, built project size is 13 KiB.
🌀  Created namespace for Workers Site "__blog-workers_sites_assets"
✨  Success
🌀  Uploading site files
✨  Successfully published your script to
 https://blog.luyu.workers.dev

给 Cloudflare Workers 添加域名

这可能是会坑到刚用 Cloudflare Workers 的一步,当你把域名添加到 Cloudflare 后,你可能以为只要把 Workers 指派到这个域名的路由下就行了,其实不然,你需要保证 Cloudflare 的 DNS 记录中你指派的路由是被 Cloudflare 代理的。

举个例子,在我的 wrangler.toml 中,route 字段的值是 "luyu.blog/*",那我就需要保证在 Cloudflare 的 DNS 面板中,有 luyu.blog 的域名记录,并且开启了代理。如果你没有一个可以写进 DNS 记录的 IP 地址,可以参考这个官方社区的问答,使用 192.2.0.1 这个地址。

配置 GitHub Actions

要配置 GitHub Actions,我们需要先在 Cloudflare 上取得 API 令牌。为了保证安全性,创建令牌时,可以限制令牌的作用域和账户。我们可以在创建令牌的时候只选择和 Workers 有关的权限。

(未完待续)

© 2000-2021 Copyright Luyu Cheng. All Rights Reserved.

Built with Gatsby, React, Node.js, and many other open source tools.