通过 VPN 使用 rsync:哪一侧才是真值来源?
隧道只提供路径,不提供合并策略。先选定权威副本,预览单向复制,再单独决定删除是否随行。
两台服务器、一条 VPN、一个必须在两边都存在的目录,以及一条容易执行却难以撤销的命令。在输入之前,先确定哪一份副本是权威的,因为之后每一个 rsync 问题都取决于这个答案。单向副本、共享工作目录和带版本控制的归档,对修改和删除需要不同的规则。隧道只负责提供路径,对它们没有任何意见。
在输入命令之前先确定哪一份副本胜出
用一句话写下方向,包括两侧的主机名和路径,并取得数据所有者的同意。在本演练中,源是 /srv/publish/,目标是 fileserver.example 上只被允许写入 /srv/mirror/ 的账户。
复制之前先确认传输事实:SSH 主机密钥已验证,目标账户只能写入副本区域而不能写别处,两台主机拥有兼容的 rsync 版本。这些前提可以防止一个小小的计划失误演变成权限事故。它们还会告诉你,出问题时目标日志里会出现哪个账户。
预览复制并阅读逐项列表
先用预览标志运行复制,并保留输出。逐项变更会告诉你每条路径将会发生什么,这比事后得到的完成摘要有用得多。把输出留在变更记录里,下一次运行才有可比较的对象。
rsync -rt --dry-run --itemize-changes -e ssh -- /srv/publish/ [email protected]:/srv/mirror/
末尾的斜杠很关键。在源一侧,它表示该目录的内容,而省略它会额外增加一层以源命名的目录。 rsync 手册记录了两种形式以及保留元数据的标志,而你需要哪一组选项取决于数据,而不是习惯。
把每一行与你想要的目标对照着读。去掉预览标志会真正执行复制并可能覆盖文件;若没有删除选项,目标上已经存在的多余文件就原地保留。只有当你确实决定可以接受留下过时文件时,这才是安全的默认值。如果答案是否,那么该做的是下面那个删除决定,而不是往这条命令里再撒一个标志。
然后再决定删除是否随行
把目标变成镜像是第二个决定,而不是第一个决定的细化。单独预览它,只加上删除标志、其他都不改,这样列表里的差异就完全属于那个决定。
rsync -rt --delete --dry-run --itemize-changes -e ssh -- /srv/publish/ [email protected]:/srv/mirror/
删除会在同步范围内移除接收侧的多余文件,正因如此,错误的源路径、未挂载的目录或排除范围超出预期的过滤器,都可能让一次破坏性运行看起来像干净的镜像。预览列表是这种差异在代价变高之前唯一可见的地方。在数据足以支持的地方保留一份独立的恢复副本,并记住镜像是忠实地复制意外删除,而不是保存历史。
预览也锁不住任何东西。人们会在试运行和真实运行之间编辑文件,文件系统也有自己的脾气,所以除非两棵树都安静,你批准的那次运行和实际执行的那次并不是同一个操作。
两个单向作业并不构成双向同步
在两个方向上运行同一条命令,是这种场景下丢失数据最常见的方式。每个方向都把另一侧当作目标,乐于覆盖更新的内容,而删除则按计划触发的顺序镜像过去。
如果用户必须编辑两份副本,那就是另一个产品决定:冲突处理、删除跟踪,以及为这种模型设计的工具。用计划顺序来近似这种做法,正是共享目录最终让每份文档都出现两个谁也合并不了的版本的原因。请明确地做决定,而不是靠两条 cron 条目和侥幸去近似。
先把策略写下来,再做自动化
计划任务应该带上方向、路径、元数据策略和已批准的删除决定。把这五项事实写进任务文件本身,而不是写进下一位管理员可能永远不读的 wiki 页面。防止运行重叠,因为两份副本同时写同一棵树会产生谁都没想要的状态。
当源是活动数据库或其他多文件数据集时,先做与应用一致的导出,或做一次协调过的快照。递归遍历文件系统不是事务,而变化中的数据集副本也不是一致的。
记录完成与失败,并检查目标上的数据集,而不只是退出状态。故障后恢复 VPN 只恢复连通性;它并不说明上一次复制是否完成,也不说明冲突的编辑是否得到解决。