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 IDAccessKey Secret,并且为其最少配置对 DNS 的完全管理权

最好不要把 AccessKey IDAccessKey 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 challengescertificate 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
最后修改:2025 年 06 月 07 日
如果觉得我的文章对你有用,请随意赞赏