个人站灾备方案:异地备份与恢复演练

一、灾难恢复不是「我有备份」

备份和灾备是两个不同的问题,混淆它们的人非常多。备份回答的是「数据丢了能不能找回来」,灾备回答的是「机房没了业务能不能继续跑」。一个人做站,往往只做了前一半:每天 mysqldump 到同一台机器的 /backup 目录,觉得万无一失。等磁盘坏了、或者误删了整个 /home,才发现备份和数据在同一个盘上。

这篇文章讲的是一个人能真正维护得住的灾备方案,不追求 RTO 五分钟那套企业级指标,目标很朴素:主要数据丢了能在几小时内恢复,站点能在另一台机器上重新跑起来,而且这套东西的日常维护成本控制在每周十分钟以内。

先给结论。个人站的灾备要覆盖四层:数据(数据库和上传文件)、配置(Nginx、PHP、证书、定时任务)、代码(程序文件和主题)、以及环境(操作系统版本、依赖、PHP 扩展)。只备份第一层的占绝大多数,而真正让恢复卡住的往往是后三层——你手上有数据库,但想不起来 PHP 装过哪些扩展,或者证书私钥在哪。

二、先算清楚两件事:RPO 和 RTO

RPO 是最多能接受丢多少数据,RTO 是恢复需要多长时间。这两个数字决定了方案的成本,必须先定下来再动手,否则很容易做成一个又复杂又不解决问题的东西。

对一个内容站,如果每天自动备份一次,RPO 就是 24 小时。这意味着最坏情况丢掉一天的文章和评论。大部分个人站长能接受,如果是交易类或者有大量用户投稿,可能要压到一小时。

RTO 则取决于你的手工操作熟练度。如果你从来没演练过恢复,实际的 RTO 可能是一天而不是你以为的一小时——因为你会卡在「这个报错是什么意思」上。我的经验是:把恢复流程写成脚本并至少演练一次,RTO 能压缩到原来的三分之一。

定完这两个数,方案的骨架就清楚了。24 小时 RPO 意味着每天一次全量备份够用;几小时 RTO 意味着必须有第二台机器或者在云上能快速开一台,并且恢复过程是脚本化的。

三、数据层:全量加增量,别只做一种

数据层要分两类处理:数据库和文件。它们的特性完全不同,混在一起备份会做出又大又慢的东西。

数据库我建议每天一次全量逻辑备份,加每小时的 binlog 增量。全量用 mysqldump,注意几个参数:

mysqldump --single-transaction --quick --default-character-set=utf8mb4 \
  --routines --triggers --events \
  zz1984 > /backup/db/zz1984-$(date +%F).sql

--single-transaction 保证 InnoDB 表在备份期间不加锁,业务不受影响;--quick 是逐行取数据不缓存在内存里,大表必备;后面三个参数分别备份存储过程、触发器和事件,很多人的备份少了这三个,恢复之后定时任务全没了。字符集一定要显式指定 utf8mb4,否则在默认字符集不同的机器上恢复会出现中文乱码。

文件指上传目录、主题文件、配置。这些文件的特点是数量多、单个不大、变化不频繁。用 rsync 做增量最合适,配合硬链接做快照:

rsync -a --delete --link-dest=/backup/files/latest \
  /var/www/html/usr/uploads/ /backup/files/$(date +%F)/

--link-dest 是关键,它让没有变化的文件用硬链接指向上一次的快照,磁盘占用只有变化的部分,但你得到的是每天一份完整可用的目录。恢复的时候直接进对应日期的目录就能拷,不需要先还原增量再还原全量,这一点在紧张的时候非常重要。

注意 --delete 的含义是「让备份和目标目录完全一致」,删掉的文件在备份里也会消失。如果你的需求是「误删了也能找回来」,那应该用快照而不是镜像,硬链接快照正好满足:昨天的目录里还留着那个文件。

四、配置和代码:最容易漏的一层

我做灾备时踩过最深的坑不是数据,是配置。数据库恢复了,站点起不来,因为想不起来当初给 PHP 加过哪个扩展、Nginx 里那条 rewrite 是干什么用的、证书私钥放在哪个不标准的路径下。

解决办法是把配置和代码一起纳入版本管理。我的做法是建一个私有的 git 仓库(自己搭的 Gitea 或者用私有托管都行),把这些东西放进去:Nginx 的 vhost 目录、PHP 的 php.ini 和 pool 配置、certbot 的 renewal 配置(不含私钥)、crontab 的导出、防火墙规则、以及程序本身的主题和插件目录。

私钥和数据库密码不要放进 git。单独用一个加密的归档,用 age 或者 gpg 加密,每周备份一次,放在跟服务器不同的地方。密码管理器里存一份恢复口令,这是唯一的单点,必须妥善保存。

导出系统环境清单这件事很值得做,一条命令就能生成:

dpkg --get-selections > /backup/env/packages.txt
php -m > /backup/env/php-modules.txt
nginx -V 2>&1 > /backup/env/nginx-build.txt
crontab -l > /backup/env/crontab.txt
systemctl list-units --type=service --state=running > /backup/env/services.txt

这五个文件加起来不到一百 KB,但在恢复的时候能省掉几个小时。尤其是 php-modules.txt,重建机器时照着它一个个装扩展,比凭记忆猜靠谱得多。

五、异地:备份必须离开那台机器

前面所有工作都有一个前提:备份不能被一起毁掉。同一台机器上的 /backup 目录,在磁盘损坏、误删目录、勒索软件这三类事故里全军覆没。

个人站成本最低的异地方式是对象存储。把备份目录每天同步上去,设置生命周期规则只保留三十天,费用大概是几块钱一个月。用 rclone 一句话搞定:

rclone sync /backup/ remote:backup-zz1984/ --transfers 4 --checkers 8 \
  --exclude "*.tmp" --log-file /var/log/rclone.log --log-level INFO

sync 跟 rsync -a --delete 语义一致,会让远端和本地完全一致。如果你担心本地被攻击后 sync 一下把远端也删了,可以改用一个版本的策略:远端只增不删,用 rclone copy 加日期目录。这样代价是存储成本上升,但抗风险能力强得多。对个人站来说,我倾向于后者,因为几块钱换一个「删不掉」的副本很值。

还有一层考虑:云端账号本身也是单点。如果对象存储的密钥泄露,攻击者可以把你的备份也删掉。所以至少保留一份完全离线、跟任何账号都不相关的副本,哪怕是一个季度手工拷一次的移动硬盘。这条不是技术问题,是风险意识问题。

另外记得做恢复测试。

没有验证过的备份等于没有备份。最简单的方式是每季度在临时目录里恢复一次数据库和文件,跑一个只读的查询确认数据完整。这个动作二十分钟,能发现很多平时看不见的问题:备份脚本早就失败了但没人收到告警、归档文件损坏、字符集不对导致中文变问号。

六、把恢复流程写成脚本,并且演练

灾难发生的时候人的状态是很差的,凌晨三点、睡眼惺忪、心里慌。这时候靠记忆操作必然出错。所以恢复流程必须是脚本,而且是要照着念就能跑的那种。

我给自己写的恢复脚本分四段,每段都能独立验证。第一段装环境,从 packages.txt 还原软件包,装 PHP 扩展,确认版本跟原来一致。第二段放文件和配置,从最新的快照目录 rsync 回去,恢复 Nginx 配置和证书。第三段导数据库,解压 sql 文件,按原样导入。第四段验证,访问首页、登录后台、检查一条文章详情页、检查上传目录权限。

每一段的结尾都要有一个明确的检查点,比如第一段结束时 php -m | wc -l 应该跟原来一样多,第二段结束时 nginx -t 必须通过。这样出问题时你知道卡在哪一段,而不是面对一个白屏页面发呆。

演练的做法是:在云上临时开一台机器,从零开始按脚本跑一遍,全程计时。我自己的记录是第一次两小时四十分钟,第三次四十分钟,卡点从「不知道装哪个扩展」变成了「DNS 切换等待」。演练完记得把发现的问题补回文档,下次会更快。

演练还有一个隐藏收益:它会逼你发现那些从来没被记录下来的东西。比如某个定时任务依赖了一个手工装的二进制文件,或者证书续期脚本里写死了旧机器的路径。这些问题不演练永远发现不了,等真的出事就成了致命伤。

七、一个人能维护的监控与告警

综上所有流程都有一个共同前提:你知道它在跑。备份没跑、同步失败、磁盘满了导致备份文件写了一半,这些情况如果没有告警,你会在恢复的时候才发现最重要的那份备份是空的。

监控不用做得复杂,三个检查就够。第一,每天备份完成后写一个标记文件,第二天检查这个文件的日期是不是昨天的。第二,异地同步完成后对比本地和远端的文件数量,差太多就是异常。第三,恢复测试的脚本每季度跑一次,结果记录在案。

告警渠道用最简单的就行,一封邮件、或者往自己的机器人 webhook 推一条消息。关键是这个告警不能和服务器本身共命运——如果告警脚本跑在同一台机器上,机器挂了也不会告警。可以放在另一台小机器上做外部探测,或者用第三方的免费监控服务定时访问一个健康检查地址。

还有一条容易忽略的:备份日志本身要留。rclone 的日志、mysqldump 的退出码、磁盘使用量的历史曲线,这些在你排查「为什么这份备份是坏的」时是唯一线索。

八、小结

个人站的灾备不需要多先进的技术,需要的是覆盖完整和反复验证。数据分数据库和文件两类分别处理,配置和代码纳入版本管理,备份一定要放到另一台机器和另一个账号上,恢复流程写成脚本并且真的演练一次。做到这四件事,你的 RTO 大概率能压到一小时内,而日常维护只是每周花十分钟看一眼告警邮件。

Last modification:November 3rd, 2026 at 12:24 pm

Leave a Comment