0%

准备博客搬家

今年年底之前会把之前Wordpress上的文章迁移到Hexo上。新文章会在https://blog.panpili.com/这个域名上更新。WP上的老文章也会逐步迁移到新的平台上。

为何搬家

Wordpress的可定制性和多账号编辑能力非常强,还支持评论系统,一般情况下上比任何静态的博客模板(包括Hexo)都好用一些,是个合适的入门选择,但对于性能一般的小主机来说有一点卡,安装和迁移不太方便,需要定期备份数据,还容易被攻击。

技术上,Wordpress用的MySQL保存文章,用PHP渲染页面,所以Wordpress必须得托管在有状态的服务器上,而无法使用无状态的Docker托管服务。去年把Wordpress迁移到在谷歌云的免费实例上,成本一个月大概0.1美刀。一般的小主机一个月的账单普遍在5刀左右,很有一点贵。

作为对比,Hexo用的Nodejs渲染成静态页面,可以用无状态的服务器去托管,比如谷歌云的Cloud Run和AWS Lambda。这两个服务都提供免费的访问次数和CPU运算时间,在低访问量下成本很低。

目前计划的是把Hexo的静态文件塞到Docker里,然后用Nginx做反代,把之前博客的链接逐步映射到新的博客链接上。根据之前部署在Cloud Run的React应用的数据,冷启动大概在100多毫秒,会比Wordpress快不少。

静态页面托管

利用Dockerfile和Cloud Build把博客一键部署到Cloud Run上

Cloud Build和Cloud Run的介绍我写在这篇博客中。这篇博客中我也提到过,Cloud Build默认是不支持一个环境跑多个命令的,如果要多阶段构建的话可以利用/workspace文件夹进行临时储存,也可以利用Dockerfile实现。

我用的多阶段的Dockerfile,第一阶段是编译博客为静态网页,第二阶段把网页拷贝到nginx的docker镜像里,最终大小10m多。nginx的端口默认的是8080,如果要改nginx的配置的话要自己写conf文件,取消第15行注释,以便拷贝到容器中覆盖默认配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Stage 1 - build from source
FROM node:16 AS builder
WORKDIR /usr/src/blog
COPY package.json package-lock.json ./
RUN npm install

ENV PATH="./node_modules/.bin:$PATH"

COPY . ./
RUN npm run build

# Stage 2 - put source to nginx env
FROM nginx:stable-alpine
COPY --from=builder /usr/src/blog/public /usr/share/nginx/html
# COPY default.conf /etc/nginx/conf.d/default.conf
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]

默认的nginx配置,default.conf。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
server {
listen 8080;
listen [::]:8080;
server_name localhost;

#access_log /var/log/nginx/host.access.log main;

location / {
root /usr/share/nginx/html;
index index.html index.htm;
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}

Cloud Build的构建配置,cloudbuild.yaml。里面的镜像链接我用的是Artifact Registry,如果要用Container Registry要改仓库链接为gcr.io等,参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
steps:
# Build the container image
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'us-docker.pkg.dev/YOUR_PROJECT_ID/LOCATION/${_NAME}:${_VERSION}', '.']
# Push the container image to Container Registry
- name: 'gcr.io/cloud-builders/docker'
args: ['push', 'us-docker.pkg.dev/YOUR_PROJECT_ID/LOCATION/${_NAME}:${_VERSION}']
# Deploy container image to Cloud Run
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: gcloud
args: ['run', 'deploy', 'CLOUD_RUN_NAME', '--image',
'us-docker.pkg.dev/YOUR_PROJECT_ID/LOCATION/${_NAME}:${_VERSION}', '--region', 'us-central1']
substitutions:
_NAME: 'pili-blog'
_VERSION: '0.0.3'
images:
- us-docker.pkg.dev/YOUR_PROJECT_ID/LOCATION/${_NAME}:${_VERSION}

然后去Cloud Build的页面创建就行,可以设置为手动或者分支更新触发。构建完成后还需要去Cloud Run的权限界面添加allUsers主体,给予 Cloud Run Invoker角色,不然没法公开访问。设置里的内存可以调整为128M,最大实例个数为1,并发可以改为1000。

支持缓存的构建

我在在Cloud Build中加速npm构建中提供了一种支持缓存的npm构建配置。Hexo的编译步骤时间从40秒降低到20秒,总构建时间从2分15秒降低到了1分53秒。

Firebase Hosting

Firebase是被谷歌收购的APP后端服务,现在还集成了很多谷歌云的服务,比如Cloud Function, Cloud Storage and Cloud Monitoring。其中有一项服务非常适合托管静态服务,那就是Firebase Hosting。每月提供10G的免费储存额度,每天提供360M的免费CDN流量。还支持免费的SSL证书,非常方便。

使用起来比Cloud Build 和Cloud Run简单太多了,几个命令就上传了。

1
2
3
4
5
6
7
8
9
10
11
# 安装
npm install -g firebase-tools

# 登录
firebase login

# 初始化项目,指定上传的文件夹,默认是public
firebase init hosting

# 部署
firebase deploy --only hosting

部署完之后,可以去加入自己的域名。在DNS中加入一个A记录,然后等1个小时证书就会批下来。

这个托管也有缺点,那就是不支持Nginx的参数正则匹配,只支持路径重定向。

谷歌的CDN在大陆的速度其实很不错,100ms左右,比Cloudflare快一倍。

Cloud Storage or AWS S3

这两个对象储存也支持挂静态网站。用域名为名称创建储存桶,然后在DNS加入CNAME记录。

这个方法缺点挺多的:
1.只支持HTTP。可以挂一个CDN来支持HTTPS。
2.贵,因为储存桶不仅有流量费,还有读取和写入产生的请求费用,非常不建议拿来挂静态网站。
3.不支持地址重定向。

Github Pages

其实Github Pages也能挂博客,但是要求免费用户的仓库设为公共。也支持自定义域名,理论上开个Cloudflare CDN墙内也能用。

Cloudflare Pages

不收储存和流量费,免费版限制一个月构建(部署)的次数为500,对于博客来说够用了。文件数量和文件大小都有限制。和Firebase Hosting的部署思路不同,Cloudflare Pages走的类似Cloud Run的基于代码仓库的构建部署方式,不支持直接上传静态文件。

墙内的云服务商

看了下腾讯和阿里云的,基本没有免费额度,价格不便宜,域名需要备案,没意思。

媒体托管

腾讯云COS

去年年底的时候把WP服务器的图片托管到了腾讯云COS,并送了一个CDN域名。送了半年的流量包,基本上没花钱。6月份没有流量包了,预估的月费3毛左右。

今年5月份取消赠送CDN域名,只能挂备案过的域名才可以开境内加速,不推荐。

谷歌云Cloud Storage

流量太贵,CDN更贵。

亚马逊S3

一个月提供100G的免费出站流量(不包括中国大陆),100G之后流量费为0.09刀每G,比腾讯云稍贵。

以域名为名称新建一个储存桶,比如xxx.example.com,方便后面自定义域名和挂CDN。

储存桶提供两种访问模式,Restful和静态网页。推荐使用Restful,因为支持HTTPS,而静态网页不支持。

防盗链设置

原理上通过检测referer中发送的地址来判断是否盗链。开启后在本地调试的图片也访问不了,因为发送的referer是localhost,可以临时关掉用于调试。

这个方法能够干掉绝大部分盗链,但也有绕过的办法,比如用curl加上这个标头,或者用修改过的浏览器。绝对不可以用防盗链来屏蔽对敏感内容的访问!

储存桶配置如下,其中允许在三个网页下展示储存桶的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"Version": "2012-10-17",
"Id": "http referer policy example",
"Statement": [
{
"Sid": "Allow get requests originating from www.example.com and example.com.",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::xxx.example.com/*",
"Condition": {
"StringLike": {
"aws:Referer": [
"https://panpili.com/*",
"https://blog.panpili.com/*",
"https://www.panpili.com/*",
]
}
}
}
]
}

CDN设置

推荐给S3挂载CDN,因为到大陆的流量并不免费。

Cloudflare

我现在就给S3挂的Cloudflare,速度还凑合。在DNS中加入一个xxx.example.com的CNAME映射,地址为xxx.s3.bucket-location.amazonaws.com。SSL设置开到完全,不要开完全(严格),Cloudflare不认S3的证书。

AWS Cloudfront

AWS自家的CDN,一个月提供1000G的免费流量,速度和Cloudflare差不太多,大陆不备案的话用新加坡的CDN节点,200ms的水平。S3到Cloudfront不收流量费。同时在Certificate Manager中提供免费的SSL证书,证书支持通配符。产品缺点在本身不带防火墙,也不支持防盗链,由AWS WAF单独提供支持,一个月起步价就6刀

Cloudflare R2

Cloudflare内测中的对象储存服务,不收流量费,只收储存费和请求费。一个月送10G免费储存空间和不少的免费读写操作。不知道是否支持防盗链,可以作为一个备选。

把Wordpress文章链接映射到新博客平台

以前Wordpress的文章用的数字编号的链接,比如https://panpili.com/?p=1421,如果我想要跳转到新的链接,https://blog.panpili.com/2022/cloud/service-comparison/。利用Nginx的if正则匹配,实现这个功能并不难。

把下面的规则加到location / {}里,放到跳转到index.html或者php的上面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
location / {
# redirect example for panpili.com/?p=1421
# regular expression, ~ case sensitive, ~* not case sensitive
# ^/ start with /
# \? \= for special character
# return 301 url, redirect to the url
if ($request_uri ~ ^/\?p\=1421$)
{
return 301 https://blog.panpili.com/2022/cloud/service-comparison/;
}

# html example
index index.html index.htm;
}

可以逐步迁移旧文章到新的博客平台了,开心。

迁移进度

6月10号

静态网页托管到了Firebase Hosting,放弃Cloud Run。媒体托管到了AWS S3,放弃腾讯云COS。迁移了几篇老博客的文章并重定向了链接,过程有一点点麻烦,主要在迁移图片上比较花时间。

8月30号

一口气把剩下几篇给迁移了,Wordpress正式被废弃,博客域名也迁入blog.panpili.com。