Typecho 是传统的基于 php 的博客系统,该系统主要由两部分组成,一部分是站点文件,另外一部分是数据库
要实现 Typecho 的双机灾备与故障转移,我们需要确保站点文件和数据库的同步与高可用切换能力
架构目标
- 两台服务器:主机(A)+ 备机(B)
- 主机故障时,备机能够自动/手动接管服务
要求具备以下能力:
- Typecho 站点文件的一致性
- 数据库数据的同步
- 域名或负载均衡的快速切换
站点文件的同步策略
使用 inotifywait
搭配 rsync
,首先在主机(A)上执行:
apt update
apt install inotify-tools -y
之后编写一个同步脚本:
#!/bin/bash
WATCH_DIR="/var/www/typecho"
REMOTE="<备机(B)>:/var/www/typecho"
inotifywait -mrq -e modify,create,delete,move "$WATCH_DIR" | while read path action file; do
echo "Detected change: $action $file"
rsync -az --delete "$WATCH_DIR"/ $REMOTE
done
其中的 <备机(B)>
要替换成对于的 IP,并且在主机(A)上配置好密钥验证或者证书验证,确保其能正常通过 rsync
进行连接
之后文件会存入到备机(B)的对应 /var/www/typecho
目录中
数据库同步策略
可以参考我写的这篇文章:
需要注意的是,表同步过去了之后,在备站(B)上的数据库上,也要为 Typecho 配置对应的权限
什么意思呢,你可以打开 Typecho 的配置文件,一般来说在路径 /var/www/typecho
下,文件名叫做 config.inc.php
里面有关于数据库连接的一些配置
比如我这里的 Typecho 博客,就是以 typecho
这个用户去连接数据库的,我们在备站(B)的数据库中也要配置一个 typecho
用户,并且密码也要和配置文件里面一样,同时赋予其读 typecho*
表的权限
首先以 root
身份连接到数据库:
sudo mysql -u root
创建 typecho
用户,这里假设你上面查到的 Typecho 表为 typecho
,密码设置为上面配置文件的密码:
CREATE USER 'typecho'@'localhost' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON typecho.* TO 'typecho'@'localhost';
FLUSH PRIVILEGES;
之后重新使用 typecho
用户登录,看看能否看到表:
配置 Web 服务
不出意外的话,现在你现在在备站(B)配置好 Web 服务,然后在云服务商将 DNS 改到备站(B)的 IP,就可以正常访问了
这个站点除了不能评论和写新文章,和主站(A)完全一样
蛤?你问我为什么只能读不能写?因为数据库是主从设计,备站(B)的数据库只能从主站(A)单向同步,本身不能推送数据到主站(A)的数据库
还有一个就是证书的问题,一般情况下,站点域名都是解析到主站的 IP 上,备用站没办法正常获取到证书(HTTP-01),只能通过 DNS-01 的方法获取证书
我比较喜欢用 Caddy,这里需要自己编译一个新的 Caddy,可以看我写的这篇文章:
因为我的域名是托管在阿里云的,可以使用他们的 DNS 进行证书验证签发,你需要在 RAM 中去创建 AccessKey,得到一对:AccessKey ID
和 AccessKey Secret
,并且为其最少配置对 DNS 的完全管理权
最好不要把 AccessKey ID
和 AccessKey Secret
这种比较敏感的东西直接写到 Caddyfile
中,比较推荐的方法是把变量写进 systemd 的 EnvironmentFile
在 /etc/caddy/
目录下新建一个专门存放环境变量的文件,比如 /etc/caddy/aliyun. Env
:
nano /etc/caddy/aliyun.env
写入:
ALIYUN_ACCESS_KEY_ID=你的AccessKeyID
ALIYUN_ACCESS_KEY_SECRET=你的AccessKeySecret
保存后设置权限:
sudo chmod 600 /etc/caddy/aliyun.env
sudo chown root:root /etc/caddy/aliyun.env
修改 Caddy 的 systemd 单元文件 /etc/systemd/system/caddy.service
(或 /lib/systemd/system/caddy.service
,取决你之前放在哪儿)。在 [Service]
段里,加上一行:
EnvironmentFile=/etc/caddy/aliyun.env
最终的(核心片段)大致长这样:
[Service]
User=caddy
Group=caddy
EnvironmentFile=/etc/caddy/aliyun.env
ExecStart=/usr/local/bin/caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
ExecReload=/usr/local/bin/caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile
Restart=on-failure
重载,并且重启一下:
sudo systemctl daemon-reload
sudo systemctl restart caddy
sudo journalctl -u caddy -f
这时 Caddy 启动日志不再弹出 “AccessKeyID is empty” 的错误,应能看到 solving challenges
、certificate obtained successfully
等正常输出
在两台服务器上都可以进行一样的配置
切流量
切的前提是健康检查失败,Typecho 这个博客系统本身不带有健康检查,得自己重新写一个 php 页面,可以从这个 gist 里面取: Typecho健康检查
之后放到网站根目录(/var/www/typecho
)中,即可:
之后就可以开始设计切流量的脚本了,阿里云给的 Python SDK 还是比较丰富和好用的,为此,我写了一个很好用的脚本,并且可以使用 Docker 进行部署:GitHub - liueic/typecho\_dnstypecho
检验效果
可以在主站直接停掉 MariaDB,这样话 Health Check 就通不过,就可以伪造出站点垮了的效果
systemctl stop mariadb
测试完了不要忘记重新启动数据库:
systemctl start mariadb