服务器迁移的长征

三年之期已到,龙王归位!之前买了三年的VPS要过期了,续费太贵续不起,周末从上海搬到了杭州,分享一下过程中遇到的坑,纯水文、无干货,建议绕道。

从 ubuntu16 到 ubuntu22,跨越6年的长征。。。

背景

本站源站之前长期部署在腾讯云上,最早使用腾讯云 1 元的学生服务器;毕业后使用最低配的服务器,续了几次45元每月的(太贵了),后来在叠加各种优惠券后,390元续费了3年(性价比极高)。

现在终于到期了,一个月36元还是有些小贵的,之前人和机器都在上海,现在人在杭州,想买个杭州的机器,最终决定新买一个。

作为腾讯云的老用户,在腾讯云逛了一圈,没有杭州机房,1c1g的机器全部售罄,新买2c2g的比较贵,直接续费1c1g最优惠半价,是18元每月。

杭州机房只有阿里云才有,价格也合理,0.2c1g的机器,5年600元,10元每月。

开整!搬家!

迁移前资源分析

域名资源:

  • '@':指向源站。
  • www:使用腾讯云CDN,挂在 www CNAME下。

源站资源:

  • 使用 apache2
  • 对于根域名的请求,全部 301 到 www域名
  • 对于 http 请求,全部 301 到 https
  • 使用hexo生成静态资源
  • 对于 /comment 路径下的访问,使用 wsgi 接管,作为评论服务,使用 django
  • 对于 /webhook 路径下的访问,使用 wsgi 接管,作为github的webhook,使用 flask
  • 为兼容远古时期的其他人引用的我的 wordpress 资源,进行 301 rewrite
  • 为兼容访问量巨大的文章,301到微信文章,进行 301 rewrite

初步尝试

虽然是ubuntu16到ubuntu22,我在web方面完完全全是小白,就想复用之前的架构,不折腾了。

  1. 安装 apache2(成功)
  2. 安装 hexo 并且生成静态资源文件(成功)
  3. 启用 https 配置(失败)
    1. 一开始 https 好好的,从某一时刻突然就不行了
    2. 从远程用域名访问 https 时,链接会被RST掉,后端甚至收不动内容
    3. 从本地用域名访问 https 时,没有任何问题
    4. 从远程用ip访问 https 时,没有任何问题
    5. 从本地用ip访问 https 时,没有任何问题
    6. 排查数小时后,请教陈源大佬后秒解,缺少ICP备案,腾讯云的备案和阿里云的备案是不通用的
    7. 临时解决方案,只用ip访问
  4. 对接 /comment 下的 wsgi + django(巨大的失败
  5. 对接 /webhook 下的 wsgi + flask(成功)

迁移评论功能时,卡了非常非常非常久,表现非常奇怪,请教了很多大佬,至今没有解决。

apache配置:

1
2
3
4
5
WSGIDaemonProcess example.com python-path=/var/www/blog_comment
WSGIScriptAlias /comment /var/www/blog_comment/blog_comment/wsgi.py
<Location /comment>
WSGIProcessGroup example.com
</Location>

在执行 django 代码时,如果不涉及数据库的读写操作,完全没问题,服务器可以正确返回。如果涉及到数据库的读写,处理函数可以正确执行结束并返回,客户端可以收到返回的字节,但服务器不会结束该请求,客户端在等待后续数据,导致客户端卡死,服务端卡死。

查了很久很久,从来没听说有其他人遇到这个现象,尝试了大约 6 小时,认为是框架 bug 无解

迁移到nginx

跟 ap 大佬请教的时候,对话如下:

我:我问个小白的问题,nginx 和 apache 谁先进?
ap:现在几乎所有地方都建议用nginx+uwsgi了。。已经是事实上的工业标准
我:对不起,我还活在 2013 年
ap: nginx比apache晚10年出,先进很多,uwsgi也比apache内置的好很多,易用性上也是
我:好吧,今天搞了一天白搞了,我从 ubuntu16 迁移到 ubuntu22,把这个包袱也干了吧

最终,服务器的架构如下:

根域名挂CDN,由CDN将全部请求301到www域名

"内容分发网络" - "缓存配置" - "访问URL重写配置"。

优点:在某些特殊情况下,源站的http服务无法直接被访问,通过CDN可以访问到;使用CDN保护源站真实IP。
缺点:域名的CNAME记录和A/AAAA记录是冲突的,需要换个地方记录源站IP。

CDN配置,将http请求全部301到https

优点:省的服务器自己配了。

nginx基础配置

  1. 同时开启 http 和 https 服务,服务静态资源

uwsgi高级配置

uwsgi可以将python的web程序暴露给指定的 unix domain socket 或者 http socket,我不愿意在本地开放端口,因此在一个公共可访问的位置放了一个 socket。

这个 socket 需要同时被 www-data 和 python进程对应的uid 读写,所以我设置为了 664,uid=ubuntu,gid=www-data。

uwsgi是pip安装的用户态的进程,重启后服务器不会感知到它的变化,因此需要配置开机启动,这里我使用了官网介绍的 systemd 服务,https://uwsgi-docs-zh.readthedocs.io/zh_CN/latest/Systemd.html

/home/ubuntu/blog_comment/uwsgi.ini 里,配置

1
chmod-socket=664

,在 /etc/systemd/system/uwsgi.service 里,配置

1
2
3
[Service]
User=ubuntu
Group=www-data

执行 sudo systemctl restart uwsgi 可以执行重启服务,执行 sudo systemctl enable uwsgi 可以加入开机自启动。

nginx高级配置

  1. 对接 /comment 下的 uwsgi + django(成功)
  2. 重写代码,将 /webhook 的逻辑合入到 /comment 中(成功)
  3. 编写rewrite规则

uwsgi 正常的情况下,将 nginx 对接到本地的 socket 就行,注意 socket 的 rwx 权限。

1
2
3
4
5
location /comment {
rewrite ^/comment/(.*) /$1 break;
uwsgi_pass unix:///var/www/sock/sock;
include uwsgi_params;
}

因为我django没考虑 /comment 这个前缀,因此需要rewrite把这个前缀去掉,此时评论功能就恢复正常了。

最后就差兼容 wordpress 和 微信公众号的重定向了。

原先:apache2

1
2
3
4
RewriteEngine On
RewriteCond %{QUERY_STRING} ^p=([0-9]+)$
RewriteRule ^/$ https://leadroyal.cn/p/%1? [R=301]
RewriteRule ^/p/1228$ https://mp.weixin.qq.com/s/1f-XpG9IDoNLXQmVSXO1Lw? [R=301]

现在:nginx

1
2
3
4
5
6
location / {
if ($arg_p) {
rewrite ^/(.*) /p/$arg_p? permanent;
}
rewrite ^/p/1228$ https://mp.weixin.qq.com/s/1f-XpG9IDoNLXQmVSXO1Lw? permanent;
}

对比下来,nginx确实优秀很多。

结束

累死我了,核心在于错误地使用了 apache2,水一篇流水账吧,apache http server 赶紧给爷死!