系统本地备份

使用rsync及borg进行系统备份

Linux上的备份软件非常多,而且大部分为免费或者开源的,具体可参考Arch Linux Wiki中的对比,或者restic项目所作的总结。如果不在乎GUI,推荐使用rsync或borg,可通过脚本配合systemd timer实现定时备份,并由inotify推送通知。

rsync是Linux上大名鼎鼎的同步工具,已有近25年历史,Linux系统一般已预装。常见的备份软件大多与其有关联:Back in Time、TimeShift 等是直接基于rsync实现;Dropbox、Duplicity 虽未直接使用rsync,也都使用了基于rsync算法的实现,Deja Dup 则是 Duplicity 的图形界面;rclone 宣称是针对商业云存储的rsync工具,足见rsync的影响力。
rsync支持基于数据块的增量式备份,也可以更新整个变动文件。增量式备份需要读取全部源及目标文件以实现对比,默认情况下本地备份时,会直接更新整个变动文件,而在网络备份时,则会采用增量式备份。而配合inotifywait监测目录变动,还可实现文件的实时同步。
rsync的缺点在于它是同步软件,并非针对文件备份的,缺少备份软件的一些常见特性:

  • 只能保留最新版本,不能同时保持多个历史版本。
    --backup-dir, --link-dest等可在一定程度上实现该功能,具体参考下面介绍。
  • 不能自动加密备份文件。

而borg作为专门的备份软件,上述问题都能很好处理。borg属于备份软件中的后起之秀,始于2015年,作为更早的Attic(始于2010)的分支,现在发展的非常不错,在reddit等社区中有较高评价。

rsync

1
2
备份:rsync -avP --delete --exclude={.cache,.apps}  ~/   /media/$USER/backup/rsync/
恢复:rsync -avP --include-from="filter_dotfiles" /media/$USER/backup/rsync/ ~/

参数意义:

  • -a --archive 存档模式:递归拷贝目录,保持文件权限、修改时间等元信息,保持符号链接及特殊文件等。
  • -v --verbose 冗长模式:输出同步过程的详细文件信息。
  • -P --process --partial 显示文件的复制进度,保留传输不完整的文件
  • --include 设置包含名单
  • --exclude 设置忽略名单
  • --delete 删除目标端多余(同步源没有)的文件

注意事项:

  • rsync默认基于文件大小及修改时间判断文件变动,通过参数-c可调整为基于checksum。
  • 同步时如果遇到文件冲突,比如上次同步后两边各有不同的更新,rsync并不会有任何提示或针对性处理,默认始终为同步源覆盖同步目标,即使目标端文件修改的更晚。如果使用-u参数,则当目标端文件修改时间更晚时,不会进行覆盖,除此之外不会对冲突内容做更多处理。
  • 参数-b(--backup)可开启备份,为同步过程中被替换或删除的文件创建原始备份,默认文件备份与文件处于同一目录,文件名为原文件名后加~
    --backup-dir=DIR参数可为备份文件指定单独的存储备目录,备份文件将会存储于指定的目录下,并保持目录结构。其中DIR可以使用绝对路径($HOME/delete)或相对路径(../delete)。需注意的是~不会展开为home目录,而是被作为相对路径处理,$HOME可以展开为home目录。
    要实现类似记录历史版本的效果,可将DIR设为 …/$(date --iso-8601=minutes),每次文件同步都会在目标目录旁创建以当前时间命名的更新备份,但要将某个文件恢复到某个历史版本只能手动操作了。
    如果同时使用--delete参数:使用--backup时,默认会保护以~结尾的文件不被删除,若自定义include/exclude规则,需自行加入相关规则;使用--backup-dir=DIR时,若DIR位于目标目录内,则每次同步时,原先的备份目录会被删除,从而会被移入新的备份目录,依次套娃。
  • 增量备份:将每次更新内容(增量)放入新目录
    --compare-dest=DIR:提供对比目录,只有与对比目录不同的文件才复制到目标目录
    --copy-dest=DIR:类似--compare-dest,未修改文件会本地复制到目标目录
    --link-dest=DIR:类似--compare-dest,未修改文件会在目标目录建立本地硬链接
    硬链接要求文件本身及元信息都一致,若后者不一致会本地复制文件并更新元信息。
  • 在传输速度慢时,如通过网络传输的网速慢时,可使用参数-z启用压缩;但如果传输速度快时,如本地备份,则并不需要,压缩和解压需要额外占用CPU。
  • include与exclude同时使用时,位于前面的参数优先级更高。比如想要同步忽略目录的子目录时,include应该置于exclude之前。此外,如果exclude的匹配规则太宽泛,比如'*',此时若include未明确包含目录的子目录,则子目录会被排除在外,使用时需要小心。
  • include/exclude匹配多个规则时
    • 可将匹配规则列表置于{}中,但注意以,分割且不能用空格!目录最好使用引号括起来,对于bash不是必须的,但在zsh中执行时会出错。此外,{}内只有一个匹配规则时,规则后要加,,否则不能按预期执行。
    • 也可以使用include-from/exclude-from/filter,将文件列表或匹配规则放入单独的文件,每行一个文件或规则。当涉及多层子目录时有点复杂,需要逐层目录依次添加,具体可查看man文档,或参考下面的示例。
  • 注意include/exclude路径首尾的/,匹配的目录会随之变化。
    • include/exclude路径开始的/代表同步源,而非系统根目录,文件匹配是以同步源目录为根目录的。如.*代表所有隐藏文件(夹),会递归搜索整个同步源,而/.*仅代表直接位于根目录下的隐藏文件(夹),而不会进行递归匹配,即不含根目录下非隐藏文件夹中的隐藏文件(夹)。因此备份$HOME目录文件,但忽略隐藏的配置文件应--exclude="/.*",注意既不能省略/,也不能用~/.*
    • 如果不确定的话,谨慎起见建议include/exclude的匹配规则都以/起始,即通过相对路径来限制规则匹配的范围,以避免在意料之外引入或移除某些目录下的子文件或目录。
    • include/exclude路径结尾加/时,代表匹配对象为路径,会忽略文件;不加/则会同时匹配文件及路径。还是上面的例子,如要仅忽略$HOME下的隐藏文件夹而不忽略隐藏文件,可以--exclude="/.*/"
  • 注意同步源路径结尾的/,rsync的操作会随之而变。
    • 对于同步源路径:结尾不加/,代表同步的是整个源目录,包含源文件夹本身,会在目标路径下新建与同步源同名的文件夹;如果结尾加/,则代表仅同步源目录下的内容,不含源文件夹,会将源目录下的内容直接复制到目标路径之下。
    • 此外还会影响include/exclude的文件匹配:同步源路径结尾不加/,include/exclude匹配路径的根目录为源目录的上一级目录;结尾加/,include/exclude匹配路径的根目录为源目录本身。
    • 同步目标路径结尾是否加/似乎没有什么影响。
  • 最后,保险起见,可以在实际运行之前通过参数-n(--dry-run) 测试运行,与-v配合可在不实际执行的情况下查看将会执行的操作。

下面是一个用于筛选配置文件的include规则文件示例:+代表包含,-代表排除,注意多层目录要逐层添加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
+ /.config
+ /.config/autostart
+ /.config/fontconfig
+ /.config/mpv
+ /.config/zim
- /.config/*

+ /.local
+ /.local/share
+ /.local/share/applications
+ /.local/share/gnome-shell
+ /.local/share/icons
+ /.local/share/themes
+ /.local/share/Trash
- /.local/share/*
- /.local/*

+ /.mozilla
+ /.nutstore
+ /.ssh

+ /.gitconfig
+ /.tmux.conf
+ /.zsh_history
+ /.zshrc
- /.*

borg