{"componentChunkName":"component---src-templates-post-tsx","path":"/migrate-my-blog-to-cloudflare-workers","result":{"data":{"markdownRemark":{"html":"<p>去年十一月，我想把自己拍的乱七八糟的照片放到公开的网站上，做一个<ruby>作品集<rp>(</rp><rt>portfolio</rt><rp>)</rp></ruby>网站。我首先想到的是我 Creative Cloud 订阅里的 Adobe Portfolio，不过试了以后才发现这玩意儿的的访问速度实在是太慢了。程序员思维的我就蹦出来想：为什么不自己搭一个呢？当时正对 Cloudflare 非常上头，而我又懒的研究国内各种云平台的 CDN 服务，就脑子一热订阅了 Cloudflare Workers。但可惜，之后因为工作太忙，我就没怎么用它，只是把网站搭起来并且跑通了 GitHub Actions 后就不管了。</p>\n<p>转眼已经到了 2021 年，并且已经过去了两个月，我想着也该翻新一下自己的博客。就在某个凌晨花了一个小时把整个站点用 TypeScript 重写了——打算以此当作新的开始。我心想：既然都重写了，不如尝试把博客也转移到 Cloudflare Workers 上呗？于是就此开始折腾。</p>\n<h2>在 Apple Silicon 上安装 Cloudflare Wrangler</h2>\n<div class=\"custom-block warning\"><div class=\"custom-block-body\"><p>如果你使用的不是基于 ARM 的计算机，可以跳过这部分，直接从下一节开始读起。</p></div></div>\n<p>这是最简单的一步，也可能是最复杂的一步。</p>\n<p>Cloudflare 已经把整套工具链做成一个 CLI 工具了，名叫 <a href=\"https://github.com/cloudflare/wrangler\" target=\"_blank\" rel=\"noopener noreferrer\">Wrangler</a>。官方推荐的安装方法是 npm，但很不幸，你只会得到一行错误：<code class=\"language-text\">Error: Unsupported platform: Darwin arm64</code>。好在 Wrangler 是 Rust 实现的，所以我们可以用 cargo 来安装，执行 <code class=\"language-text\">cargo install wrangler</code> 后，Cargo 就会下载并且编译 Wrangler，这过程中并不会发生任何问题。<em>难道这就结束了吗？</em>并没有。当你随便想用 Wrangler 做点事儿的时候，就会遇到下面的错误。</p>\n<div class=\"gatsby-highlight\" data-language=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">$ wrangler init --site\n⬇️  Installing cargo-generate v0.5.0<span class=\"token punctuation\">..</span>.\nError: could not download <span class=\"token variable\"><span class=\"token variable\">`</span>cargo-generate<span class=\"token variable\">`</span></span>\nno prebuilt cargo-generate binaries are available <span class=\"token keyword\">for</span> this platform</code></pre></div>\n<p>这种问题一看就不是那种「因为用了某些平台上的某种技术而导致无法在你的平台上运行」的错误。而且，因为 cargo generate 这个命令在几个月之前就支持 ARM 平台了，我感觉这个问题一定有 workaround。经过一番搜寻，我发现果然如此，引用<a href=\"https://github.com/cloudflare/wrangler/issues/1685\" target=\"_blank\" rel=\"noopener noreferrer\">相关 issue #1685</a>里的一句话。</p>\n<blockquote>\n<p>Which could potentially be that <code class=\"language-text\">wrangler</code> 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 <code class=\"language-text\">cargo</code> is installed, <code class=\"language-text\">cargo install &lt;wasm-pack|generate|etc></code> could be invoked to build these utils from source.</p>\n</blockquote>\n<p>说白了就是 Wrangler 压根就没有考虑到这种情况，所以只会从某个地方下载预先编译好的 <a href=\"https://github.com/cargo-generate/cargo-generate\" target=\"_blank\" rel=\"noopener noreferrer\">cargo-generate</a> 和 <a href=\"https://github.com/rustwasm/wasm-pack\" target=\"_blank\" rel=\"noopener noreferrer\">wasm-pack</a>，这就导致了：虽然 Wrangler 本身可以通过 cargo install 在 Apple Silicon 上运行，但下载这两个工具链的时候出错，因为 wrangler 的开发团队压根就没有考虑过 ARM 平台上的运行体验。</p>\n<p>明明可以在我这里跑起来的东西，因为开发人员没有考虑过这种情况，我们难道就要因此放弃吗？不，于是我克隆了一份 Wrangler 的代码，全文搜索错误信息，找到报错的位置。从下面的代码我们可以看到，Wrangler 在运行生成命令时会执行 <code class=\"language-text\">install::install_cargo_generate()</code> 函数，这个函数会下载 <a href=\"https://github.com/cargo-generate/cargo-generate\" target=\"_blank\" rel=\"noopener noreferrer\">cargo-generate</a> 并返回路径，再把需要执行的命令行参数拼接起来。</p>\n<div class=\"gatsby-highlight\" data-language=\"rust\"><pre class=\"language-rust\"><code class=\"language-rust\"><span class=\"token keyword\">pub</span> <span class=\"token keyword\">fn</span> <span class=\"token function-definition function\">run_generate</span><span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">:</span> <span class=\"token operator\">&amp;</span><span class=\"token keyword\">str</span><span class=\"token punctuation\">,</span> template<span class=\"token punctuation\">:</span> <span class=\"token operator\">&amp;</span><span class=\"token keyword\">str</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">-></span> <span class=\"token class-name\">Result</span><span class=\"token operator\">&lt;</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token namespace\">failure<span class=\"token punctuation\">::</span></span><span class=\"token class-name\">Error</span><span class=\"token operator\">></span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token keyword\">let</span> binary_path <span class=\"token operator\">=</span> <span class=\"token namespace\">install<span class=\"token punctuation\">::</span></span><span class=\"token function\">install_cargo_generate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">?</span><span class=\"token punctuation\">;</span>\n\n\t<span class=\"token keyword\">let</span> args <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"generate\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"--git\"</span><span class=\"token punctuation\">,</span> template<span class=\"token punctuation\">,</span> <span class=\"token string\">\"--name\"</span><span class=\"token punctuation\">,</span> name<span class=\"token punctuation\">,</span> <span class=\"token string\">\"--force\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n\t<span class=\"token keyword\">let</span> command <span class=\"token operator\">=</span> <span class=\"token function\">command</span><span class=\"token punctuation\">(</span>binary_path<span class=\"token punctuation\">,</span> <span class=\"token operator\">&amp;</span>args<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\t<span class=\"token keyword\">let</span> command_name <span class=\"token operator\">=</span> <span class=\"token macro property\">format!</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"{:?}\"</span><span class=\"token punctuation\">,</span> command<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\t<span class=\"token namespace\">commands<span class=\"token punctuation\">::</span></span><span class=\"token function\">run</span><span class=\"token punctuation\">(</span>command<span class=\"token punctuation\">,</span> <span class=\"token operator\">&amp;</span>command_name<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>我看了下 <code class=\"language-text\">install::install_cargo_generate()</code> 的实现，它会根据操作系统和平台请求 <code class=\"language-text\">https://workers.cloudflare.com/get-binary/{0}/{1}/v{2}/{3}.tar.gz</code> 上下载 <a href=\"https://github.com/cargo-generate/cargo-generate\" target=\"_blank\" rel=\"noopener noreferrer\">cargo-generate</a> 和 <a href=\"https://github.com/rustwasm/wasm-pack\" target=\"_blank\" rel=\"noopener noreferrer\">wasm-pack</a>。这就令人哭笑不得了，因为它既不是从 Rust 相关的源下载，而且代码中本身也没有提供检测 ARM 平台的代码。我就没有时间贡献 PR 了，就直接把上面代码的 <code class=\"language-text\">binary_path</code> 替换为我本机的 Cargo 的路径。最后，构建一份 release，并把构建好的程序链接到我自己的 bin 文件夹下，Wrangler 就能正常使用了。</p>\n<h2>配置 Wrangler 项目</h2>\n<p>在 Wrangler 安装完后，我们就可以干正事了。其实只需要按照官方文档中的教程一步一步来就好。但我这里还是把每一步摘抄出来，因为这些步骤散布在很多篇文章里。</p>\n<p>第一步，在 Gatsby 里用 Wrangler 新建项目。用 <code class=\"language-text\">cd</code> 进入项目，执行下面的命令。</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\">$ wrangler init --site\n🔧   Creating project called <span class=\"token variable\"><span class=\"token variable\">`</span>workers-site<span class=\"token variable\">`</span></span><span class=\"token punctuation\">..</span>.\n✨   Done<span class=\"token operator\">!</span> New project created ./blog/workers-site\n✨  Successfully scaffolded workers site\n✨  Succesfully created a <span class=\"token variable\"><span class=\"token variable\">`</span>wrangler.toml<span class=\"token variable\">`</span></span></code></pre></div>\n<p>第二步，编辑 wrangler.toml。把 <code class=\"language-text\">account_id</code> 改成执行 <code class=\"language-text\">wrangler whoami</code> 所得到的 Account ID。把 <code class=\"language-text\">bucket</code> 改成 <code class=\"language-text\">\"./public\"</code> 也就是 Gatsby 构建的目标文件夹。其余的字段不用改。</p>\n<p>第三步，部署到 Workers 上。只要运行下面的命令就行了。</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\">$ wrangler publish\n✨  Built successfully, built project size is <span class=\"token number\">13</span> KiB.\n🌀  Created namespace <span class=\"token keyword\">for</span> Workers Site <span class=\"token string\">\"__blog-workers_sites_assets\"</span>\n✨  Success\n🌀  Uploading site files\n✨  Successfully published your script to\n https://blog.luyu.workers.dev</code></pre></div>\n<h2>给 Cloudflare Workers 添加域名</h2>\n<p>这可能是会坑到刚用 Cloudflare Workers 的一步，当你把域名添加到 Cloudflare 后，你可能以为只要把 Workers 指派到这个域名的路由下就行了，其实不然，你需要保证 Cloudflare 的 DNS 记录中你指派的路由是被 Cloudflare 代理的。</p>\n<p>举个例子，在我的 wrangler.toml 中，<code class=\"language-text\">route</code> 字段的值是 <code class=\"language-text\">\"luyu.blog/*\"</code>，那我就需要保证在 Cloudflare 的 DNS 面板中，有 luyu.blog 的域名记录，并且开启了代理。如果你没有一个可以写进 DNS 记录的 IP 地址，可以参考<a href=\"https://community.cloudflare.com/t/setup-workers-on-personal-domain/88012\" target=\"_blank\" rel=\"noopener noreferrer\">这个官方社区的问答</a>，使用 192.2.0.1 这个地址。</p>\n<h2>配置 GitHub Actions</h2>\n<p>要配置 GitHub Actions，我们需要先在 Cloudflare 上取得 API 令牌。为了保证安全性，创建令牌时，可以限制令牌的作用域和账户。我们可以在创建令牌的时候只选择和 Workers 有关的权限。</p>\n<p>（未完待续）</p>","frontmatter":{"title":"Migrate My Blog to Cloudflare Workers","date":"February 22, 2021","tags":["cloudflare","blog"],"language":"zh-Hans"}},"site":{"siteMetadata":{"title":"Luyu Cheng"}}},"pageContext":{"id":"3608ce38-2441-5dc1-86ed-e872063e47f5"}},"staticQueryHashes":["3649515864","63159454","684267432"]}