利用BTRFS快照实现快速Linux环境切换

对于喜欢折腾的人来说,经常会遇到环境玩崩的情况,在Linux下更是一件常事。而如果每次都要重装系统,其实又非常不方便,也没必要。

在BTRFS文件系统逐步稳定并可用的今天,如果我们将根文件系统迁移到了BTRFS,或许,我们可以借助BTRFS快照功能,实现Linux环境的无痛切换与还原?

思路

为了方便,我们假设一个非常无聊的例子:

  • 希望在一个分区内,安装一个Debian系统
  • 在Debian系统上,分别安装配置GNOME和KDE两个桌面环境
  • 实现两个桌面环境的根文件系统互不干扰,可按需分别启动

需要说明的是,这套配置里,我们假设包括/usr, /etc等路径均没有分配独立的挂载点,否则那就太麻烦啦!/home是否独立挂载取决于环境隔离的程度。

仔细一想,想要实现上述功能的思路是非常简单的:

  1. 在BTRFS文件系统上安装一个基础操作系统
  2. 对基础操作系统进行快照
  3. 根据基础快照,创建两组新的读写快照(子卷),在两个子卷内分别安装GNOME和KDE环境,并按需切换

其实,以Debian为例,如果在安装系统时指定了根文件系统为BTRFS文件系统,Debian安装程序会贴心地为我们创建一个@rootfs子卷,而后每次启动电脑时,实际上都是将@rootfs子卷挂载成/节点。因此,以上步骤的前两步都已被自动完成。

似乎,我们只要解决第三步就行?但麻烦的也在这第三步。

GRUB的麻烦

回忆一下,Linux是如何确定自动挂载文件系统配置的?第一个想到的一定是/etc/fstab文件。

还是以Debian的为例,默认情况下,如果设置根文件系统为BTRFS,在/etc/fstab文件里面,我们可以找到类似于以下定义:

1
2
# / was on /dev/sda1 during installation
UUID=12345678-1234-1234-5678-123456789012 / btrfs defaults,subvol=@rootfs 0 0

显然,这一句的意思是,将硬盘上的@rootfs子卷,挂载到/路径上。

那么接下来,我们似乎只要创建一个@rootfs的快照子卷,然后修改上面的fstab文件,把subvol=后面替换成新子卷名称,就可以实现快速切换了?

一开始我也是这么认为的,直到我安装完KDE桌面重启后,对着仍然黑麻麻的屏幕两眼发黑,我才意识到不对劲。

让我们瞄一眼/boot/grub/grub.cfg文件:

1
2
3
4
5
6
7
8
9
menuentry 'Debian GNU/Linux' --class debian --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-12345678-1234-1234-5678-123456789012' {
# ......
insmod btrfs
# ......
echo 'Loading Linux 6.1.0-30-amd64 ...'
linux /@rootfs/boot/vmlinuz-6.1.0-30-amd64 root=UUID=12345678-1234-1234-5678-123456789012 ro rootflags=subvol=@rootfs quiet
echo 'Loading initial ramdisk ...'
initrd /@rootfs/boot/initrd.img-6.1.0-30-amd64
}

好吧……事实上根文件系统的子卷名称,是写入到了GRUB配置文件里的。在GRUB引导时,会按照/boot/grub/grub.cfg的配置查找子卷,然后加载该子卷中的系统。

而要修改GRUB配置,似乎就有点麻烦了。如果手动修改配置,需要在每次安装完驱动或更新内核后再手动调整一遍配置,极其麻烦且容易疏漏。而如果希望自动修改,又暂时没有好主意。

重命名子卷

修改GRUB配置的路子走不通,系统默认引导@rootfs子卷的行为有点难修改。既然如此,为什么我们不通过替换@rootfs子卷的方式,狸猫换太子呢!

安装基本系统

首先正常安装Debian基本系统,在配置分区的时候,为/挂载点分配一个BTRFS文件系统。

进行基础配置

安装完系统后,重启进入基本系统,进行必要的配置,安装一些在不同环境下都用得上的包。也就是准备一个基本系统的过程啦!

拍摄基础快照

重启进LiveCD,挂载BTRFS分区整个卷:

1
2
mount /dev/sda1 /mnt
cd /mnt

我们给系统子卷@rootfs创建一个快照@sysbase(名字可以随意起),用于记录此刻基本系统的状态:

1
btrfs subvolume snapshot @rootfs @sysbase

如果希望基本系统内容不变,可以增加一个选项-r,以创建一个只读快照。

配置环境1

接着,重启电脑,按需要配置第一个环境,例如安装GNOME桌面等。

配置环境2

之后,如果我们想要从基本系统开始重新配置一个新环境,我们需要再次进入LiveCD,挂载BTRFS分区完整卷到/mnt下。

这回,我们需要把环境1,也就是现在的@rootfs先移到一边,也就是重命名一下。

1
mv @rootfs @gnome-env

现在,我们的硬盘上似乎只有两个子卷了:

1
2
3
/mnt/
├── @gnome-env
└── @sysbase

我们现在想让系统回到基本系统,也就是回到@sysbase的状态。怎么做呢?我们可以,给@sysbase再创建一个快照,让这个快照成为新的@rootfs

1
btrfs subvolume snapshot @sysbase @rootfs

接着重启电脑,我们就回到了基本系统状态,此时我们对系统的修改,就变成了我们的环境2。

还要更多环境!

如果还需要更多环境,只需要像配置环境2时一样,将当前@rootfs重命名一下,再从基本系统,或者任何一个其他快照,创建一个新的@rootfs快照,重启电脑,就可以创建新环境了。

而如果我们希望在环境之间切换,也非常简单,同样地,将当前@rootfs重命名为其他名字,再将想要切换的快照重命名成@rootfs,或者创建一个新@rootfs快照都行。

如果我们玩坏了环境,也不用完全重装系统,只需要将不想要的环境子卷删除,btrfs subvolume delete @broken-env,再选择一个好的环境为@rootfs即可。非常灵活!