Snapper 是一个由 openSUSE 的 Arvin Schnell 开发的工具,用于管理 Btrfs 子卷和 LVM 精简配置(thin-provisioned)卷。它可以创建和比较快照,在快照间回滚,并支持自动按时间序列创建快照。
安装
安装 snapper包 包。或者安装开发版本 snapper-gitAUR。
此外,还可以安装 GUI 前端 snapper-gui-gitAUR、btrfs-assistantAUR 或 snapper-toolsAUR。
建立一个新的配置
在为 btrfs 子卷建立一个 snapper 配置前,这个子卷必须已经存在。否则,你应该在建立 snapper 配置前创建它。
要为位置为 /path/to/subvolume
的 btrfs 子卷创建一个新的 snapper 配置文件,并命名为 config
:
# snapper -c config create-config /path/to/subvolume
这将会:
- 根据
/usr/share/snapper/config-templates/default
处的默认配置模板创建一个配置文件/etc/snapper/configs/config
。 - 在
/path/to/subvolume/.snapshots
处创建一个子卷,用于存储未来该配置文件产生的子卷。子卷的路径将会是/path/to/subvolume/.snapshots/#/snapshot
,#
是子卷序号。 - 将
config
加入到/etc/conf.d/snapper
的SNAPPER_CONFIGS
中。
例如,要为挂载在 /
的子卷创建一个配置文件:
# snapper -c root create-config /
@.snapshots
子卷已经被挂载到 /.snapshots
,这会导致 snapper create-config
无法正常创建配置 [1]。要让 Snapper 使用 @.snapshots
来创建备份,需要进行如下操作:
- 卸载
@.snapshots
子卷并删除已存在的挂载点。
- 创建 Snapper 配置。
- 删除 Snapper 创建的子卷。
- 重新创建
/.snapshots
挂载点,重新挂载@.snapshots
子卷。
此时,配置文件已经激活。如果你的 cron 守护进程已经运行, snapper 将会使用 #自动按时创建快照。否则,你需要使用 systemd 单元文件和定时器。参阅 #启用/停用。
创建快照
自动按时创建快照
一个快照时间线(timeline)由可配置数目的每小时/日/月/年快照组成。当自动按时创建启用时,默认每小时创建一个快照。每天由时间线清理算法清理多余快照。详情请参照 snapper-configs(5) 中的 TIMELINE_*
变量。
启用/停用
如果你拥有一个 cron 守护进程,该特性应该已经自动启用。要停用,编辑你想禁用该特性的子卷对应配置文件为:
TIMELINE_CREATE="no"
如果你没有 cron 守护进程,你可以使用提供的 systemd 单元文件。启动并启用 snapper-timeline.timer
来启用自动按时创建快照。另外,启用并启动 snapper-cleanup.timer
来定期清理老旧快照。
设置快照限制
默认配置将保留 10 个每小时快照,10 个每日快照,10 个每月快照和 10 个每年快照。你可以在配置文件中更改这些限制,特别是在繁忙的子卷,例如 /
上。参阅 #避免影响性能。
这是一份名为 config
的配置文件的示例片段,它将保留 5 个每小时快照,7 个每日快照,不保留每月和每年快照:
/etc/snapper/configs/config
TIMELINE_MIN_AGE="1800" TIMELINE_LIMIT_HOURLY="5" TIMELINE_LIMIT_DAILY="7" TIMELINE_LIMIT_WEEKLY="0" TIMELINE_LIMIT_MONTHLY="0" TIMELINE_LIMIT_YEARLY="0"
更改创建和清理频率
如果你使用提供的 systemd 定时器,你可以修改它们来更改创建和清理频率。
例如,编辑 snapper-timeline.timer
,加入下列配置来设定快照频率为间隔五分钟,而不是一小时:
[Timer] OnCalendar= OnCalendar=*:0/5
在编辑 snapper-cleanup.timer
来每小时运行清理,而不是每天的时候, 你需要更改 OnUnitActiveSec
。 加入:
[Timer] OnUnitActiveSec=1h
参阅 Systemd/定时器和 Systemd#附加配置片段。
手动创建快照
Single 快照
默认情况下 snapper 创建 single 类型的快照,它们与其他快照没有特别关系。
要为一个子卷创建快照:
# snapper -c config create --description desc
以上命令没有对应的清理算法,因此该快照将会一直存储直到 删除 它。
要设置一个清理算法,在 create
后使用 -c
选项,并在 number
,timeline
,pre
或 post
中选择一个参数。 number
使 snapper 定期清理超出配置文件中设置的数量限制的快照。例如,要创建一个使用 number
清理算法的快照:
# snapper -c config create -c number
参阅 #自动按时创建快照 查看 timeline
是如何工作的。参阅 #pre/post 快照 查看 pre
post
如何工作。
pre/post 快照
另一种快照类型是 pre/post 快照。两个快照会在进行重大更改时先后成对创建(例如进行系统更新时)。
如果这种重大更改是/可被单个命令调用,那么 snapper create --command
可以在调用这个命令的同时创建 pre/post 快照。
# snapper -c config create --command cmd
--command
选项能更好的处理输出重定向。pre/post快照也可以被手动创建。
首先创建一个pre快照:
# snapper -c config create -t pre -p
记下被创建快照的序号(在创建post快照时需要用到)。
然后进行想要进行的操作(比如安装一个程序,或是进行升级等)。
最后创建post快照,将下文的 N
替换为pre快照的序号:
# snapper -c config create -t post --pre-number N
启动时快照
要让 snapper 为 root
配置文件创建一个快照,启用 snapper-boot.timer
。通过这种方式创建的快照是 Single 快照。
管理快照
列出配置
列出所有你已经创建的配置:
# snapper list-configs
列出快照
要列出名为 config 的配置文件对应的快照:
# snapper -c config list
还原快照
在还原快照时,部分文件可能会保持原样,原因可能是快照中不包含该文件(例如,该文件位于另一个子卷上),也可能是过滤配置排除了该文件。
过滤配置
部分文件保存着系统的状态信息,例如 /etc/mtab
。这些文件不应该被还原。Arch Linux 提供的 Snapper 的默认配置已经排除了这些文件。Snapper 支持按照用户排除某些文件或文件夹。/etc/snapper/filters/*.txt
和 /usr/share/snapper/filters/*.txt
中的每行指定了要排除的项目。当 Snapper 计算快照之间的差别时,会排除这些文件。注意,这些文件仍会包含在快照中,如果不想让这些文件包含在快照中,使用单独的子卷或者挂载点。
参见 SUSE 的文档Directories That Are Excluded from Snapshots[失效链接 2024-03-03 ⓘ]。
还原快照
如果你使用 Snapper 的默认布局,每个快照是在所指定子卷下的 .snapshots
目录的下层子卷,例如 @home
的快照位于 @home/.snapshots
下。
例如要将某个快照恢复到 home
,首先启动到 Arch Linux 的 LiveCD。
使用UUID将 btrfs 的顶层子卷挂载到/mnt
。
# mount -t btrfs -o subvol=/ /dev/disk/by-uuid/UUID_of_root_volume /mnt # cd /mnt
将目前的子卷移动到其他地方,例如将 @home
移动到@home-backup
:
# mv @home @home-backup
找到你想恢复快照的序号(每个快照都单独显示为一行,所以能很方便的按照时间查找序号):
# grep -r '<date>' /mnt/@home-backup/.snapshots/*/info.xml
... /mnt/@home-backup/.snapshots/number/info.xml: <date>2021-07-26 22:00:00</date> ...
info.xml
记录的时间为 UTC 时间,请务必将其和本地时间的时差计算在内。记住 number
。
使用 number
快照来创建新快照。
# btrfs subvolume snapshot @home-backup/.snapshots/number/snapshot @home
将 .snapshots
移回恢复完成的子卷下,例如@home
。
# mv @home-backup/.snapshots @home/
subvolid
而不是子卷路径,则需要更改 /mnt/@/etc/fstab
中的 subvolid
(假定把@子卷挂载到了/
)。
可以使用 btrfs subvolume list /mnt | grep @home$
来查询新的 subvolid
。
重新启动计算机。
检查计算机运行是否正常。这时可以删除在还原之前创建的快照了——例如@home-backup
。在删除前记得检查是否有需要保留的数据。
删除快照
要删除序号为 N
的快照:
# snapper -c config delete N
可以一次删除多个快照。例如,要删除 root 配置文件的 65 和 70 号快照:
# snapper -c root delete 65 70
要同时删除多个连续的快照,例如删除 65 到 70 号所有快照:
# snapper -c root delete 65-70
要立即释放快照占用的磁盘空间,使用 --sync
选项:
# snapper -c root delete --sync 65
要删除一个 Snapper 配置和其所有快照,包括 .snapshots
子卷:
# snapper -c config delete-config
允许非 root 用户访问
所有配置文件都是由 root 用户创建的,默认情况下,也只有 root 用户可以查看并访问它们。
要让指定用户可以列出某一配置文件的快照,修改 /etc/snapper/configs/config
中的 ALLOW_USERS
项。现在你应该可以以普通用户权限运行 snapper -c configlist
最后,如果你想允许某一用户浏览 .snapshots
目录,但是该目录的拥有者必须为 root。因此,你应该修改包括该用户的组为组拥有者,以 users
为例:
# chmod a+rx .snapshots # chown :users .snapshots
提示与技巧
在进行 pacman 操作时创建快照
下列软件包可以依据 pacman 进行的事务自动创建快照。
- snap-pac — 使用 pacman 钩子使 pacman 自动使用 Snapper 创建#pre/post 快照。类似与 openSUSE 的 YaST 的行为。
- grub-btrfs — 包含了一个名为 grub-btrfsd 的 systemd 单元。可自动查找快照并将其添加到 GRUB 菜单中。
- snap-pac-grub — 在 snap-pac包 创建快照后自动使用 grub-btrfs包 更新 GRUB 启动项。同样使用pacman 钩子。
-
snp — 包装任何 shell 命令以供创建 pre/post 快照(例如
snp pacman -Syu
)相比 Snapper 自身的--command
选项能更好的处理输出重定向(参见 #pre/post 快照)。
- limine-snapper-sync — 在 snap-pac包 创建快照后自动在 Limine 中创建启动项。(参见Limine#Snapper snapshot integration for Btrfs)
启动到只读快照中
使用 grub-btrfs包、snap-pac-grubAUR或limine-snapper-syncAUR的用户应该注意:在默认状态下,Snapper 创建的快照是只读的。而在启动到只读快照时会有一些困难。许多服务,例如显示管理器,都需要一个可写的 /var
目录。在启动到只读快照时,这些服务将无法正常工作。
为了避免这个问题,可以使快照设为可写,或者使用 overlayfs 启动到快照中,这也是开发者认可的方法。这种情况下快照像是一个 Live CD 环境。
要使用 overlayfs 启动快照:
- 确保 grub-btrfs包 已经安装。
- 添加
grub-btrfs-overlayfs
到/etc/mkinitcpio.conf
HOOKS
的末尾。例如:HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block filesystems fsck grub-btrfs-overlayfs)
进一步了解:
- grub-btrfs README (包含了使用 dracut包 而不是 mkinitcpio包 的配置方法)
- Github 上的讨论
在进行 pacman 事务时备份非 btrfs /boot 分区
如果你的 /boot
分区是在一个非 btrfs 文件系统上(例如一个 ESP 分区),Snapper 无法备份这些分区。
在系统备份#快照与_/boot_分区中提供了一个自动在内核更新时将 /boot
复制到你的 btrfs 根目录下的方法。这种方法也能和 snap-pac包 一起使用。
增量备份到外部驱动器
一些工具可以使用 Snapper 进行自动备份。参见 Btrfs#增量备份到外置设备。
建议的文件系统布局
这是一种建议的文件系统布局,可以方便的将挂载到 /
的 @
子卷还原到之前的状态:
子卷 | 挂载点 |
---|---|
@ | / |
@home | /home |
@snapshots | /.snapshots |
@var_log | /var/log |
subvolid=5 | ├── @ -| | contained directories: | ├── /usr | ├── /bin | ├── /.snapshots | ├── ... | ├── @home ├── @snapshots ├── @var_log └── @...
子卷@...
应当挂载到任何应有自己子卷的目录。
如果你想将@
子卷恢复到之前的状态,其他的子卷将不会被影响。例如,当你在将@
恢复到之前的快照时,/home
将不会受到影响,因为/home
挂载到了单独的子卷下。
这个布局能使 Snapper 定期为 /
创建快照,并能让在出现启动问题时更容易还原快照。
在这种情况下,进行初始设置之后,Snapper不需要更多配置。
- 考虑为其他不想包含在
@
快照中的目录创建子卷。例如/var/cache
,/var/spool
,/var/tmp
,/var/lib/machines
(systemd-nspawn),/var/lib/docker
(Docker),/var/lib/postgres
(PostgreSQL),或是其他/var/lib/
下的目录。你可以使用类似上面的平行子卷结构,或是创建下层子卷。但是,pacman 数据库/var/lib/pacman
必须位于挂载到/
的子卷下。 - 你可以为
@home
或是其他子卷单独创建快照或者还原快照。
配置 Snapper 和挂载点
假定@
挂载到了/
下,同时 /.snapshots
未被挂载,同时也不存在同名目录。可以使用如下命令确认:
# umount /.snapshots # rm -r /.snapshots
接下来为/
#建立一个新的配置。Snapper 会自动在@
下创建 .snapshots
子卷。这在建议的布局下是不必要的,可以删除。
# btrfs subvolume delete /.snapshots
删除这个子卷之后,重新创建 /.snapshots
目录。
# mkdir /.snapshots
现在将 @snapshots
子卷挂载到 /.snapshots
目录。例如,当文件系统位于 /dev/sda1
时:
# mount -o subvol=@snapshots /dev/sda1 /.snapshots
要使此配置永久生效,在 fstab 中添加条目。
若 fstab 中已存在这个条目,重新挂载:
# mount -a
将此文件夹权限设为 750
。
这将使 Snapper 创建的快照储存在 @
之外。所以 @
被还原或替换时也不会丢失快照。
还原到之前的快照
要将 /
还原到某个快照的状态,首先启动到 Arch Linux Live CD。
挂载顶层子卷(subvolid=5)。也就是说,不使用任何 subvolid
或 subvol
挂载参数。
找到想还原快照的序号:
# grep -r '<date>' /mnt/@snapshots/*/info.xml
输出类似这样。每个快照都单独显示为一行,所以能很方便的按照时间查找序号。
/mnt/@snapshots/number/info.xml: <date>2021-07-26 22:00:00</date>
info.xml
记录的时间为 UTC 时间,请务必将其和本地时间的时差计算在内。记住number
。
将@
移动到其他位置,例如 /@.broken
来备份当前的系统状态。或是直接删除@
子卷:btrfs subvolume delete /mnt/@
。
为 Snapper 创建的快照创建一个可读写快照:
# btrfs subvolume snapshot /mnt/@snapshots/number/snapshot /mnt/@
将 number
替换为你想还原的快照序号。
subvolid
而不是子卷路径,则需要更改 /mnt/@/etc/fstab
中的 subvolid
(假定把@子卷挂载到了/
)。
可以使用 btrfs subvolume list /mnt | grep @$
来查询新的 subvolid
。另外若在启动加载器中使用了 subvolid
,也需要一并更改。例如 refind_linux.conf
。最后,卸载顶层子卷(subvolid=5),挂载@
到/mnt
以及 ESP分区。chroot到完成还原的根目录下,并重新生成 initramfs。
现在/
已经恢复到了之前快照的状态。重新启动计算机。
/etc/snapper-rollback.conf
来匹配系统的实际布局情况。还原其他子卷到之前的状态
参见#还原快照。
从快照中删除文件
若你想删除指定的文件或文件夹而不想删除整个快照,可以使用 snappersAUR 实现。这个脚本还可用于以 Snapper 目前不支持的其他方式操作过去的快照。
若你不想使用其他脚本,你可以使 快照变为可读写:
# btrfs property set /path/to/.snapshots/<snapshot_num>/snapshot ro false
验证 ro=false
:
# btrfs property get /path/to/.snapshots/<snapshot_num>/snapshot ro=false
现在可以像平常一样修改 /path/to/.snapshots/<snapshot_num>/snapshot
了。你也可以使用 shell 循环批量处理快照。
避免影响性能
在/
这种繁忙的文件系统中,长时间保留大量快照会占用大量硬盘空间,可能影响文件系统的性能。你可以通过以下方式降低影响:
updatedb
默认情况下,updatedb
(参见 locate)也会索引由 snapper 创建的 .snapshots
目录,如果快照较多,这可能会导致运行速度严重的性能影响和大量内存占用。你可以编辑updatedb
,阻止.snapshots
索引该目录:
/etc/updatedb.conf
PRUNENAMES = ".snapshots"
禁用配额
Btrfs 的配额功能(不是后来引入的“简单配额”(simple quota))可能会导致删除快照时占用大量 CPU 来维护配额信息。
要确定配额是否已启用,使用如下命令:
# btrfs qgroup show /
可以使用如下命令禁用配额:
# btrfs quota disable /
计算快照数量
如果禁用配额没有减缓性能问题,那么计算快照数量可能会有用:
# btrfs subvolume list -s / | wc -l
为用户数据和日志创建子卷
如果目录中包含用户数据(如电子邮件或日志),建议将其存储在自己的子卷而不是根子卷 /
中。这样,如果恢复 /
的快照,用户数据和日志不会受到影响。可以为用户数据使用单独的 timeline。不建议为 /var/log
创建日志快照。这可能会导致难以排除故障。
还可以使用#过滤配置在还原过程中跳过某些目录。有关跳过某些目录的示例和原因,请参阅 SLES 文档。
依据磁盘使用量清理快照
疑难解答
日志
Snapper 将所有活动写入到 /var/log/snapper.log
中——在你认为出错时,首先检查该文件。
当你遇到关于每小时/每日/每周快照的问题时,最常见的原因是由于 cronie 服务(或者你使用的其他 cron 守护进程)没有运行。
IO 错误
如果你在试图创建快照时遇到 'IO Error',请确认你试图创建快照的子卷对应的 .snapshots 目录是一个子卷。
另一个可能的原因是 .snapshots 目录的拥有者不是 root。你会在在 /var/log/snapper.log
中找到 Btrfs.cc(openInfosDir):219 - .snapshots must have owner root
。
未被管理的快照导致空间浪费
快照有可能“丢失”,它们仍然存在于磁盘上,但不会被 snapper 管理。这会造成大量磁盘空间浪费。要检查这种情况,请比较:
# snapper -c <config> list
与
# btrfs subvolume list -o <parent subvolume>/.snapshots
第二个命令列出的子卷但未出现在第一个命令的输出中的都是未被 snapper 管理的快照。可以手动删除。