Nova Kwok's Awesome Blog

使用 Google Cloud Platform 的 Storage 托管静态站点并通过 Google CDN 加速

在之前的博文 《使用 GitHub Pages 托管静态网站》 讲到了在 GitHub Pages 上托管自己的静态博客,诚然,GitHub 给开发者们提供了一个优秀的托管环境,但是如果想要对大陆地区访问速度更加快一些的话,我们可以考虑将站点内容放在 Google Cloud Platform 的 Storage 中,并且使用 Google CDN 进行全网加速(主要是因为国内大部分线路可以不绕路使用到香港边缘节点).

本文假设:

获取 SSL 证书

除非希望托管一个 HTTP 的网站,否则一个证书是必不可少的,Google 不会帮你自动完成这一步。

有多种方式可以获取一个 SSL 证书,如果目前手头没有的话,我们通过 Let’s Encrypt 申请一个好了,首先获取 certbot:

$ git clone https://github.com/certbot/certbot.git
$ cd certbot

由于我自己的一些原因(在迁移前域名解析到 GitHub Pages 上的,不能通过改变解析的方式验证,否则会造成博客访问中断),这里我使用了 DNS 的方式进行获取:

$ ./certbot-auto certonly --manual  --preferred-challenges=dns  --email [email protected] --server https://acme-v02.api.letsencrypt.org/directory --agree-tos -d nova.moe

题外话:如果上述域名写成 “*.nova.moe” 的话即可获得一个 Wildcard 证书,不过呢,那个证书无法被用于 nova.moe 这个裸域,只能被用于 nova.moe 的二级域名。

之后会看到一个修改域名 TXT 记录的要求,类似如下:

Please deploy a DNS TXT record under the name
_acme-challenge.nova.moe with the following value:

J50GNXkhGmKCfn-0LQJcknVGtPEAQ_U_WajcLXgqWqo

此时我们只需要做一个 _acme-challenge 的 TXT 解析,内容为上述的 J50G...qo 即可。

如果没有遇到的问题的话我们就可以看到生成好的证书信息,类似如下:

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/nova.moe/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/nova.moe/privkey.pem

此时通过任何你喜欢的方式把 fullchain.pemprivkey.pem 下载到自己本地。

创建 Storage 桶,配置权限,导入文件

创建 Storage 桶

新建一个 Storage 容器,名称就是你希望的域名(比如我这里就是 nova.moe

配置权限

由于是对外提供内容的网站,我们需要把 Public Access 设置为 Public 并且为网站配置优化,具体方法如下:

导入文件

注意,这里不要参考 《挂载 Google Storage 到 VPS 文件系统》,挂载到本地目录后上传,因为这样会导致文件的 meta 信息错误,导致本来该渲染为图片的地方变成了 octec-stream,本来该是网页的地方变成了 octec-stream ,本来… 然后访问任何页面都是弹出一个下载框了。

正确方法是使用 gsutil 来上传,语法如下:

$ gsutil cp -r dir gs://my-bucket

其中 dir 就是源目录,假设我的博客放在 /var/www/nova.moe/ 目录下 ,my-bucket 是目标 Storage 桶地址,比如我的就是 gs://nova.moe,整理一下就是:

$ gsutil /var/www/nova.moe/* -r dir gs://nova.moe

可能有同学想到这里如果用 gsutil rsync 的话会不会更好一些,毕竟有增量同步之类的。

不是这样的,这样做的话即不能保留 meta 信息,也不会增量同步,相关描述如下:

Note that by default, the gsutil rsync command does not copy the ACLs of objects being synchronized and instead will use the default bucket ACL (see gsutil help defacl).

The gsutil rsync command copies changed files in their entirety and does not employ the rsync delta-transfer algorithm to transfer portions of a changed file. This is because cloud objects are immutable and no facility exists to read partial cloud object checksums or perform partial overwrites.

如果不幸已经上传了一些文件,想要快速全部删除掉(当然,不删除桶)的话,使用:

$ gsutil rm gs://nova.moe/**

具体的 gsutil 相关指令见参考链接。

设置 Load Balancer

创建一个 HTTP/HTTPS 的 Load Balancer,Backend 创建一个 Backend Bucket,选择刚刚创建的 Storage 桶并勾选 Enable Cloud CDN:

Frontend 那一块选择 HTTPS:

然后导入 SSL 证书,其中 Public Key 和 Chain 全部上传 fullchain.pem, Private Key 就上传 privkey.pem

创建好了之后有一个 Overview,类似如下:

延迟对比

GitHub Pages

Google Cloud Platform + Google CDN

已知问题 / 缺陷

这样子做的话,每次更新站点的同步也是一个问题,尤其是对于像我这样的 Hexo 用户而言,本地不想安装 SDK,传完文件后手动上服务器 gsutil cp 会很麻烦。

Google Cloud Platform 的 Load Balancer 并不能做 Force SSL,也就是说如果在 HTTPS 只选择了 443 端口的话,用户未添加 https:// 前缀的情况下访问的返回会是 404,如果同时也添加了 80 端口的话,直接访问也不会自动跳转到 https 上面。

一个比较大众化的解决方案是开一个 Compute instance 监听 80 端口专门用来做 SSL 重定向,但这样便失去了便捷性同时也会导致价格无意义地升高(无脑猜测 Google 团队到现在还不提供这个功能是有一定动机的,关于这个的 issue tracker:Redirect all HTTP traffic to HTTPS when using the HTTP(S) Load Balancer 从 15 年到现在还没有一个正式的答复),另一个思路是将域名加入 Preload List,但是现在的网站结构似乎并不能上 List,目前我正在寻找一个更加可靠的解决方案并不定期更新本文,相关更新会优先在 Twitter 上通知,欢迎关注。

2018-08-16 更新:最终我还是选择了新建一个 Compute instance 的方式解决(可以参考:利用 GCE 建立一个 Anycast 网络,超快的香港节点,Google Cloud CDN),Nginx 配置中稍微需要注意一下,Google CDN 会给后端传一个 X-Forwarded-Proto ,鉴于 Google CDN 的 SSL 只到边缘服务器就截止了(其实还是一个 Half-baked SSL),所以后端 Nginx 还是监听在 80 端口的,如果需要 SSL 重定向的话,需要加入以下内容:

if ($http_x_forwarded_proto = "http") {
    return 301 https://$host$request_uri;
}

参考链接

  1. Adding a Cloud Storage Bucket to Content-based Load Balancing
  2. Generate Wildcard SSL certificate using Let’s Encrypt/Certbot
  3. Making Data Public
  4. Hosting a Static Website
  5. cp - Copy files and objects

#Chinese #Google