使用 Azure Storage、CDN 和 DNS 部署 Hexo 静态博客

停更的这小半年,我自己身上发生了很多事情,自认为还算坚强地硬撑了过来。

尽管还在阴影之中,没有完全脱离,但是生活还得继续,折腾反而能让我从痛苦的父进程里 fork 出来,在这个只有我自己存在的子进程里,时间的流速也大大加快,这可是我求之不得的事情。

所以,趁着周末我又来折腾了,这次是把之前托管在 GitHub Pages 的 Hexo 静态博客,迁移到了 Azure Storage 和 CDN,顺便迁移了 DNS。

1 以前的方案

  • 博客的 MD 源文件托管在 GitHub 仓库,借助 GitHub Actions 完成 Hexo 解析,详情可见这篇文章

  • 博客的 HTML 文件托管在 GitHub 仓库,自动部署至 GitHub Pages。毋须多言,从国内访问博客的速度比较慢,而且作为一个中文博客,不能被百度收录,白白流失了不少流量(瞧不上百度,还馋人家流量,有点过分)

  • 博客的自定义 CSS 和 JS 文件,以及图片都托管在 GitHub 仓库,借助 jsDelivr 分发,后来 jsDelivr 在围城内挂掉了,于是迁到了 cdnjs 和 Staticaly。这些文件依赖的免费第三方服务是无法得到保障的,因为免费总是意味着会被滥用,随之而来必然是色情暴力政治宗教,所以迟早会被封禁。当然,一些公共的 CSS 和 JS 文件用这些 CDN 服务还是挺香的。

当然,这些缺点在贫穷的我看来都还可以忍受,但架不住来微软之后,每月都有 $150 的 Azure 额度,本着「不用白不用」的古老智慧,动了这次迁移的念头。

但是说实话,如果不是送的额度,个人用户大概率不会选择 Azure 来搭博客,因为 Azure 主要面向企业用户,主打高可用性,所以 resource 普遍较贵。

Anyway,一个月之后我来补充一下,看看我这小博客能用掉多少钱。

好像没多少钱啊,每天还不到 $0.05,我这果然是个小博客,让我来搞点其他 project 玩玩。

2 现在的方案

  • 博客和图片的 GitHub 仓库都不变,只是 CI/CD 里多加了几步,把 Hexo 解析后的静态文件上传至 Azure Storage,借助 Azure CDN 分发,再用 Azure DNS 绑上自己的域名。

具体来讲:

  1. 创建名为 blog-rg 的 Resource group,用来存放与博客相关的 resource。

  2. 创建名为 progczcomstorage 的 Storage account,创建时可以通过 Redundancy 选择不同的可用性。

    Redundancy 我选择了最低也最便宜的 LRS。说是最低,其实也保证了 11 个 9 的 SLA(文档),对于我这个小博客来说也绰绰有余了,毕竟 repo 还有源文件,即便数据丢了大不了重新上传一遍。

  3. 进入 progczcomstorage -> Overview -> Capabilities,开启 Static website 功能,开启后会自动创建名为 $web 的 container。

  4. 参考 Use GitHub Actions workflow to deploy your static website in Azure Storage 把仓库中的静态文件上传至 $web

    这里面有个坑是,第一次上传完之后,更新静态文件后再次上传会失败,因为默认是不会 overwrite 的。其实,即便加了 overwrite 的 arguments,如果你的更新是删除了某个文件的话,上传之后,原来那个文件依然存在,理论上用户还是可以访问到,这对用户来说非常奇怪。

    所以,我在上传之前,会先把 $web 里面的所有文件清空,这样的做法会增加 cost,但好像也没多少钱,满足一下自己的强迫症也还不错。

    codeauto-hexo.yml
    - name: Login Azure
    uses: azure/login@v1
    with:
    creds: ${{ secrets.AZURE_CREDENTIALS }}

    - name: Clear Azure Storage
    uses: azure/CLI@v1
    with:
    inlineScript: |
    az storage blob delete-batch --account-name progczcomstorage --auth-mode key -s '$web'

    - name: Upload to Azure Storage
    uses: azure/CLI@v1
    with:
    inlineScript: |
    az storage blob upload-batch --account-name progczcomstorage --auth-mode key -d '$web' -s blog-source-ws/public --pattern "[!.]*" --overwrite true

    - name: Logout Azure
    run: |
    az logout
    if: always()
  5. 一切顺利的话,已经可以通过 {StorageAccountName}.z7.web.core.windows.net 访问到博客了。

    这里有个槽点是,目前没有办法关掉 {StorageAccountName}.z7.web.core.windows.net 的 public access,即使关掉 Storage account 的 public access 也不行。这意味着,搜索引擎还是会爬到这个 url 的,很难受。

  6. 进入 progczcomstorage -> Azure CDN,创建名为 progczcom-cdn 的 CDN profile 和名为 progczcom 的 CDN endpoint,指向 {StorageAccountName}.z7.web.core.windows.net。

  7. 一切顺利的话,已经可以通过 {CDNEndpointName}.azureedge.net 访问到博客了。

  8. 参考 Host your domain in Azure DNS 把自定义域名接入 Azure DNS,因为 Azure DNS 可以在新建 record 的时候,通过 Alias record set 直接指向 CDN 的 resource,这会省掉很多麻烦。

  9. 在 DNS 中新建 A record,将 @ 顶级域名或者自己想要的某个子域名,指向 CDN 的 resource。

  10. 在 CDN 的 endpoint 中添加 custom domain。

  11. 一切顺利的话,已经通过 HTTP 协议的 custom domain 访问到博客了。

  12. 给 custom domain 添加证书,这里分成两种情况:

    • 如果你选择的不是 @ 顶级域名,那么直接选择 CDN managed,简单省事,后面的步骤都不用再走了。

    • 如果你选择的是 @ 顶级域名,那么恭喜你,还有一大部分需要折腾,因为 @ 顶级域名的证书不支持 CDN managed,只能使用自己在 Key vault 中存放的证书。

  13. 参考 keyvault-acmebot 安装 acmebot,可以非常方便地申请和管理 Let's Encrypt 的免费证书,并在到期之前自动更新。

  14. 参考 Enable HTTPS with your own certificate 绑定自己的证书。

  15. 一切顺利的话,已经通过 HTTPS 协议的 custom domain 访问到博客了。

3 待改进的地方

  • 需要安装 ImageBot 来压缩图片,节省 CDN 流量,提升加载速度。

  • (咬牙切齿)最好找到办法关掉 {StorageAccountName}.z7.web.core.windows.net 的 public access。