个人站增量备份实战:rdiff-backup 时间点快照与 rsnapshot 硬链接轮转,含恢复演练全流程

备份这件事,做过的站长都懂:真正的考验在"恢复"

几乎所有站长都知道要备份,但大部分人做的备份其实是"半成品"。典型的情况是:写了个定时脚本,每天把网站目录和数据库打包成一个 tar.gz,扔到另一台机器上,任务就算完成了。这套东西在不出事的时候看起来人畜无害,但真到了要恢复的那一天,你会发现它有一堆致命的短板:全量备份占用空间巨大,几十个 G 的站点目录存不了多久就得删旧的;想找"三天前那篇文章被误删前的版本",翻遍压缩包也定位不到;最要命的是,恢复流程从来没演练过,真上手时手忙脚乱,把生产环境的文件覆盖错了。

真正可靠的备份体系,要同时满足三件事:能恢复到任意一个历史时间点、增量存储不浪费空间、恢复流程被定期演练过。这篇讲两件在个人站长圈里被严重低估的工具——rdiff-backup 和 rsnapshot。它们都能做到"每个时间点的快照看起来就像一份完整目录",但底层只存增量,空间效率和恢复体验都远胜每天一个 tar 包。

rdiff-backup:让远端目录拥有"时间机器"

rdiff-backup 的核心能力是"把源目录同步到目标目录,同时把每个历史版本的差异保存在目标目录下的一个特殊子目录里"。用过之后,目标目录看起来就是源目录的一份镜像(这点和 rsync 很像),但它多出来的 rdiff-backup-data 目录里,存放着每次运行之间的增量差异。当你要恢复某个历史时间点的文件时,rdiff-backup 会用镜像加上反向补丁,还原出那一刻的完整内容。

它的最大优点是:备份空间 = 一份完整镜像 + 若干增量,而不是"每个时间点一份完整拷贝"。对于一个每天只改几个文件的网站,一份镜像加上几十天的增量,总占用可能只有全量方案的一个零头。而且恢复某个单文件的历史版本只需要一条命令,不需要在压缩包里翻箱倒柜。

安装很简单,Debian/Ubuntu 下:

apt-get install -y rdiff-backup

第一次做全量备份:

rdiff-backup /var/www/mysite /backup/mysite

之后每次运行同一条命令即可,rdiff-backup 会自动识别出增量部分。要保留最近 30 天的历史版本,清理更早的:

rdiff-backup --remove-older-than 30D --force /backup/mysite

要恢复三天前的某个文件:

rdiff-backup --restore-as-of 3D /backup/mysite/index.php /tmp/restored_index.php

要列出有哪些历史时间点:

rdiff-backup --list-increments /backup/mysite

这几条命令就是 rdiff-backup 的日常全部。它特别适合"单机目录的时间点快照"这个场景,比如网站代码、配置文件、证书目录。

跨机备份:rdiff-backup 走 SSH 直连远端

本地备份挡不住整机故障或磁盘损坏,真正的备份必须落到另一台机器上。rdiff-backup 天生支持通过 SSH 把数据推到远端,前提是远端也装了 rdiff-backup 并且版本兼容。基本写法是"源在本地、目标用 user@host::path 表示远端":

rdiff-backup /var/www/mysite user@backup-host::/backup/mysite

它利用 SSH 做传输通道,增量传输只传变化的部分,带宽占用很小。第一次推全量会比较久,之后每次只传差异,几百兆的站点通常几秒钟就跑完。

这里有几个真实的坑必须提前知道。第一,版本要匹配。rdiff-backup 对两端的版本兼容性比较敏感,尤其是跨大版本(比如 1.x 和 2.x)时,老版本客户端连新版服务端可能直接报协议不兼容。稳妥做法是两端都装同一个版本,或者至少在升级前先测试一次连接。第二,SSH 免密的私钥要有命令限制。为了让定时任务免密推送,你得配 SSH 密钥,但千万别用一把能登录 shell 的全能密钥。在远端 authorized_keys 里给这把密钥加上 command 限制或者 no-pty,把它锁死成只能跑 rdiff-backup,这样即使密钥泄露,攻击者也拿不到一个可交互的 shell。第三,排除目录要写在配置或命令里,别把缓存、日志、临时文件也一起备份了,那会让增量数据无谓地膨胀。

rsnapshot:基于硬链接的空间魔法

rsnapshot 是另一个思路。它本身是 rsync 的封装,通过硬链接(hard link)实现"多个完整快照共享未变化文件"的效果。它的工作方式是:第一个快照是真实的全量文件拷贝;之后的快照对未变化的文件建立硬链接指向旧快照里的同一份数据,只对变化的文件做真实拷贝。因为硬链接不额外占用磁盘空间,所以你会在备份目录里看到 daily.0、daily.1、hourly.0 等若干个看起来"都是完整目录"的快照,实际占用却接近增量方案。

它和 rdiff-backup 的区别在于:rdiff-backup 给你一个镜像加一堆补丁,恢复历史版本要经过反向补丁计算;rsnapshot 直接给你一堆看起来就是完整目录的历史快照,你可以直接 ls、cp,恢复体验更直观,甚至不需要 rsnapshot 本身就能从快照里往外拷文件。代价是 rsnapshot 依赖同一个文件系统内的硬链接,跨文件系统的快照会退化成实拷贝,所以备份目录必须和快照目录在同一个分区上。

安装与配置:

apt-get install -y rsnapshot

配置文件在 /etc/rsnapshot.conf,最关键的两行是快照根目录和保留策略:

snapshot_root   /backup/rsnapshot/

retain  hourly  6
retain  daily   7
retain  weekly  4
retain  monthly 6

上面这组策略意味着:保留最近 6 个每小时快照、7 个每天快照、4 个每周快照、6 个每月快照。rsnapshot 的轮转是链式的——每小时第一次运行时把 hourly.0 改名成 hourly.1,依此类推,当达到 hourly 的保留数量后,把最老的 hourly 提升为当天第一个 daily。这套链式保留让你用有限的空间覆盖"近到每小时、远到每月"的多个还原点。

要备份哪些目录,用 backup 指令声明:

backup  /var/www/mysite/       localhost/
backup  /etc/nginx/            localhost/
backup  /root/scripts/         localhost/

远端备份则写成 backup root@remote:/path/ remote-host/ 的形式。注意 rsnapshot 里所有路径末尾的斜杠语义很重要,别手滑。

配置好后手动跑一次测试:

rsnapshot -t hourly

-t 是 test 模式,只打印它将执行什么、不真正执行,非常适合首次验证配置对不对。确认无误后去掉 -t 真跑,再用 cron 或 systemd timer 定时调用。

数据库备份:文件快照替代不了的东西

这里必须强调一个新手最容易犯的错误:直接对 MySQL/PostgreSQL 的数据目录做文件级 rsync 或 rdiff-backup,是极其危险的。因为数据库进程在运行时,数据文件始终处于"内存和磁盘不一致"的状态,你拷出来的文件可能是一个逻辑上破损的中间态,恢复时数据库起不来或者数据损坏。

正确的做法分两层。第一层是逻辑备份:数据库自己的导出工具,MySQL 用 mysqldump --single-transaction(InnoDB 表可以不锁表导出一致性快照),PostgreSQL 用 pg_dump。导出的 SQL 文件再交给 rdiff-backup 或 rsnapshot 一起备份。这样备份文件本身就是可读的、可跨版本恢复的。第二层是物理备份(进阶):用 xtrabackup 之类的工具做热备,或者干脆先停库再对数据目录做文件快照,但个人站一般没必要,逻辑备份加保留多天就足够。

还有一个小技巧:数据库导出文件最好带上日期命名并放进备份目录,而不是每次都覆盖同一个文件名。因为 rdiff-backup 是增量保存的,每次覆盖同名文件它依然能保存历史版本;但如果你的备份工具是简单的 rsync 覆盖无版本,命名带日期就能至少保住多天的导出。

空间策略与保留策略:别让备份把磁盘撑爆

备份系统最常见的"自己把自己搞死"的方式,是备份数据无限增长直到把备份盘写满,然后备份静默失败,而你毫不知情。必须显式设定保留策略,并且监控备份盘的使用率。

rdiff-backup 用 --remove-older-than 定期清理;rsnapshot 用 retain 配置自动轮转。两者都要在备份脚本的最后加一步"检查磁盘空间",如果剩余空间低于某个阈值(比如 15%),要么告警、要么自动删最老的快照,绝不能放任写满。

另外,备份完成的判断不能只看脚本有没有报错。要给备份任务加"新鲜度检查":比如每天定时检查最新快照的时间戳,如果超过 26 小时没有新快照,说明备份任务失败或卡住了,立刻发告警。很多站长事后才发现"备份已经三周没成功了",就是因为任务静默失败没人管。

恢复演练:备份的价值只在恢复的那一刻兑现

最容易被忽略、也是最重要的一步:定期把备份真的恢复一次。备份系统有一个残酷的特性——它平时看不出问题,只有在你最需要它的时候才会暴露缺陷。而那时候你往往已经没有试错的余地了。

恢复演练应该这么做:找一个隔离的目录或临时容器,从备份里恢复网站代码、配置和数据库,然后本地起一个测试环境,确认页面能正常打开、数据完整、权限正确。整个流程计时,把步骤写成文档。演练的目的有三个:验证备份数据本身可用、验证你记得恢复步骤(人也会忘)、量化恢复时间(RTO)。

对于 rdiff-backup,演练可以直接用 --restore-as-of 恢复任意时间点;对于 rsnapshot,直接从一个 daily.3 目录里 cp 出来即可。两种工具的恢复都很直观,这也是它们相比"一堆 tar 包"最大的优势——历史版本是"活"的,能直接浏览。

真实复盘:一次误删之后的三十秒

说个真实经历。有一次清理服务器上的临时文件,手一抖把网站的一个核心模板目录整个删了,而且当时还没意识到。几分钟后用户反馈页面错乱,登录服务器一看,目录是空的。

因为之前配好了 rsnapshot(每 6 小时一个快照),我做的第一件事不是慌,而是去看最新的快照:ls /backup/rsnapshot/hourly.0/mysite/。确认模板文件还在,直接把整个目录拷回原位,刷新页面恢复正常。从发现到恢复完成,大概三十秒。如果当时用的是"每天一个 tar 包"的老方案,我得先决定回滚哪一天的包、解压到临时目录、再挑出这个目录覆盖上去,而且会覆盖掉这之间对其它文件做的所有改动——风险和工作量都大得多。

这件事让我彻底明白了一件事:备份的粒度决定了恢复的精度。快照式备份(rdiff-backup / rsnapshot)能让你精确到"恢复到某个目录的某个时间点",而粗粒度的整包备份只能让你"要么全回滚、要么手工挑",两者在应急时的体验天差地别。

总结

对个人站长而言,一套靠谱的备份体系不需要多复杂,但必须满足三个硬指标:有版本、能跨机、练过恢复。rdiff-backup 适合给单机目录做带历史的时间点快照,增量省空间、恢复单文件精确;rsnapshot 用硬链接给出"看起来都是完整目录"的多个快照,恢复直观、可以直接浏览;数据库则必须走自身逻辑导出,绝不能裸拷数据文件。

把保留策略、磁盘空间监控、备份新鲜度告警、定期恢复演练这四件事补齐,你的备份才算真正"能用"。毕竟,没有演练过的备份,只是心理安慰。

Last modification:October 14th, 2026 at 10:30 pm

Leave a Comment