用 rsync 做同步
目标
亲手确认 rsync 的核心选项。尤其是末尾斜杠规则、--delete 的风险与保护措施、--link-dest 多代备份,这三项要亲自制作。
为什么重要
rsync 是用得最多、也最容易用错的工具。-a 不包含 ACL、扩展属性和硬链接;源路径末尾的一个斜杠会改变目录深度;源目录为空时 --delete 会把目标清空——这三点都曾造成过真实的事故。相反,--link-dest 是 rsync 最优美的功能。每一代看起来都像完整快照,而磁盘只为变更的部分占用空间。
步骤
- 在
/root/sync/src之下创建以下内容:app/main.py、app/util.py、conf/app.yaml、logs/app.log、logs/error.log。每个文件中随便写入至少一行内容。 - 对把
/root/sync/src/同步到/root/sync/dst1/的操作执行 dry-run,同时显示变更明细,并把结果保存到/root/sync/dryrun.txt。 - 通过 SSH 真正执行同样的同步。目标是
root@127.0.0.1:/root/sync/dst1/,端口为 2222,密钥为/root/.ssh/id_labhub。完成后/root/sync/dst1/app/main.py必须存在。 - 验证末尾斜杠的差异。把
/root/sync/src(没有斜杠)发送到/root/sync/dst2/时,必须生成/root/sync/dst2/src/app/main.py。 - 编写
/root/sync/backup.sh脚本。它要以set -euo pipefail开头,包含在源目录为空时以退出码 1 中止的检查,并使用--delete把/root/sync/src/同步到/root/sync/dst3/。源路径要以第一个参数接收,没有参数时使用/root/sync/src/——评分器会把空目录作为第一个参数传入,确认保护措施真的起作用。运行脚本,创建/root/sync/dst3。 - 排除
logs/之下的内容,把/root/sync/src/同步到/root/sync/dst4/。/root/sync/dst4/logs不得存在。 - 创建多代备份。先完整同步到
/root/sync/gen/daily.1/,再用--link-dest创建/root/sync/gen/daily.0/。未变更的文件在两代之间必须共享同一个 inode。 - 把
/root/sync/final.txt写成下面这 3 行。PENDING=<3번과 같은 동기화를 다시 dry-run 했을 때 전송 예정 파일 수>/DST4_HAS_LOGS=no/SHARED_INODE=yes(第一项的占位符为再次对与第 3 步相同的同步执行 dry-run 时预定传输的文件数)PENDING必须为 0。
参考
rsync -ain <원본> <대상>(占位符依次为源路径、目标路径)是 dry-run + itemize 的组合。- 指定 SSH 用
-e 'ssh -p 2222 -i /root/.ssh/id_labhub'。 - 确认 inode 共享,用
stat -c '%i %h' <파일>(占位符为文件名)比较两代的文件即可。 - 常见错误 1:在第 3 步去掉源路径末尾的斜杠,会生成
dst1/src/,导致检查失败。 - 常见错误 2:在第 7 步,如果给
--link-dest传入相对路径,它会以目标目录为基准来解释,链接就建不起来。请使用绝对路径。
创建源目录树
在 /root/sync/src 之下创建以下内容:app/main.py、app/util.py、conf/app.yaml、logs/app.log、logs/error.log。每个文件中随便写入至少一行内容。
把子目录和日志文件混在一起,后面步骤中的排除规则才有意义。
先做模拟
对把 /root/sync/src/ 同步到 /root/sync/dst1/ 的操作执行 dry-run,同时显示变更明细,并把结果保存到 /root/sync/dryrun.txt。
请同时使用两个选项:一个只显示将会改变什么而不做实际变更,另一个逐行显示变更内容。
通过 SSH 实际同步
通过 SSH 真正执行同样的同步。目标是 root@127.0.0.1:/root/sync/dst1/,端口为 2222,密钥为 /root/.ssh/id_labhub。完成后 /root/sync/dst1/app/main.py 必须存在。
rsync 有一个选项,可以整体指定远程 shell 命令。请把端口和密钥放进去。
验证末尾斜杠的差异
验证末尾斜杠的差异。把 /root/sync/src(没有斜杠)发送到 /root/sync/dst2/ 时,必须生成 /root/sync/dst2/src/app/main.py。
把同一个源只改变斜杠的有无,分别发送到两个目标,比较结果的结构。
带保护的 --delete
编写 /root/sync/backup.sh 脚本。它要以 set -euo pipefail 开头,包含在源目录为空时以退出码 1 中止的检查,并使用 --delete 把 /root/sync/src/ 同步到 /root/sync/dst3/。源路径要以第一个参数接收,没有参数时使用 /root/sync/src/——评分器会把空目录作为第一个参数传入,确认保护措施真的起作用。运行脚本,创建 /root/sync/dst3。
请在脚本开头加入源目录为空时立即中止的检查。同时加上 set -euo pipefail。
应用排除规则
排除 logs/ 之下的内容,把 /root/sync/src/ 同步到 /root/sync/dst4/。/root/sync/dst4/logs 不得存在。
排除模式可以指定多次,也可以从文件中读取。目标中不能有日志文件。
硬链接多代备份
创建多代备份。先完整同步到 /root/sync/gen/daily.1/,再用 --link-dest 创建 /root/sync/gen/daily.0/。未变更的文件在两代之间必须共享同一个 inode。
--link-dest 如果给的是相对路径,会以目标为基准来解释。绝对路径更安全。用 stat 的链接数来确认。
证明同步已完成
把 /root/sync/final.txt 写成下面这 3 行。
PENDING=<3번과 같은 동기화를 다시 dry-run 했을 때 전송 예정 파일 수> / DST4_HAS_LOGS=no / SHARED_INODE=yes(第一项的占位符为再次对与第 3 步相同的同步执行 dry-run 时预定传输的文件数)
PENDING 必须为 0。
如果同步正确,用 dry-run 再次运行同一条命令时,应当没有需要传输的内容。