Featured image of post 将 Ubuntu 从 ext4 分区就地迁移到 btrfs RAID1 上

将 Ubuntu 从 ext4 分区就地迁移到 btrfs RAID1 上

在不备份数据的情况下,将 Ubuntu 系统从 ext4 分区就地迁移到 btrfs RAID1 上,危险行为,请勿模仿……

前言

我的个人服务器使用的是 Ubuntu Server 24.04.2 LTS 操作系统,安装在一条 250 GB 的旧 m.2 NVMe SSD 上。考虑到这条 SSD 已经断断续续服役了 7 年半了,说不定哪天就会挂掉,我决定增加一条 250 GB 的新 m.2 NVMe SSD,使用 btrfs 将两条 SSD 组建成一个 RAID1 分区,并将 Ubuntu 系统迁移到这个 RAID1 分区上。

显然,较为保险的做法是,先备份数据,然后将两块盘组成 RAID1,再将数据恢复到 RAID1 分区上。不过由于这台服务器刚组装好不久,上面只有一些 Git 仓库,没有其他有价值的数据,因此我突发奇想,尝试了另一种更少见更危险的做法:现将原有的 ext4 分区直接转换为 btrfs 分区,再加入第二块硬盘组成 RAID1。现将这个过程记录下来,不供模仿,仅资参考。

过程

准备工作

需要的内容:

  • 一块 SSD,大小不少于原有的 SSD;
  • Live 环境(推荐使用 Ubuntu 桌面版的安装盘);
  • 一台可以在过程中查找资料的设备;
  • 充足的时间和耐心;

转换分区类型

首先确认系统上已经安装了 btrfs-progs 包:

sudo apt install btrfs-progs

然后重启到 Live 环境,并打开终端,确认 Live 环境中也安装了 btrfs-progs 包。由于一会儿还要在新的 SSD 上创建 EFI 分区,这里也把 dosfstoolsmtools 包安装上,以备不测:

sudo apt install btrfs-progs dosfstools mtools

接下来就可以使用 btrfs-convert 命令将 ext4 分区转换为 btrfs 分区了。这里一定要确定好设备的路径,因为 Live 环境下设备路径可能和原来不一样。可以使用自己喜欢的分区工具,无论是 fdiskparted 还是 gparted,或者是 Live 环境的磁盘管理工具,查看具体的分区信息。我这里原有的系统分区在 /dev/nvme1n1p2,因此执行以下命令:

sudo btrfs-convert /dev/nvme1n1p2

稍等片刻,转换成功后,可以在分区工具中看到 /dev/nvme1n1p2 的文件系统类型已经变成了 btrfs。在 /mnt 目录下创建一个挂载点 /mnt/btrfs,然后挂载这个分区,挂载的时候顺便打开压缩选项:

sudo mkdir -p /mnt/btrfs
sudo mount -t btrfs -o compress=zstd:1 /dev/nvme1n1p2 /mnt/btrfs

此时就可以在 /mnt/btrfs 目录下访问原有的系统文件了。此处先确认一下文件都在,权限都正常。然后我们创建几个子卷:

名称挂载点描述
@/根目录
@home/home用户目录
@snapshots-快照目录
cd /mnt/btrfs
sudo btrfs subvolume create @
sudo btrfs subvolume create @home
sudo btrfs subvolume create @snapshots

接下来将文件移动到对应的子卷中,然后删除多余的文件:

cd /mnt/btrfs
sudo rsync -aAXv /mnt/btrfs/ /mnt/btrfs/@/
sudo rsync -aAXv /mnt/btrfs/@/home/ /mnt/btrfs/@home/
sudo rm -rf /mnt/btrfs/@/home /mnt/btrfs/@/@ /mnt/btrfs/@/@home /mnt/btrfs/@/@snapshots
ls | grep -v @ | sudo xargs rm -rf --

此时 /mnt/btrfs 目录下应该只剩下 @@home@snapshots 三个子卷了,这就是我们将要使用的 btrfs 子卷。同时,在 @/ext2_saved 目录下会有一个 image 文件,这是原有的 ext4 分区的备份镜像文件,可以在需要的时候供 btrfs-convert 恢复原来的 ext4 分区;等到我们完成整个迁移过程后,可以删除这个备份文件。

创建 RAID1

接下来开始创建 RAID1。首先对新 SSD 进行分区:需要创建两个分区,一个是 EFI 分区(格式化为FAT32),另一个是 btrfs 所用的分区(不格式化),两个分区的大小都和现有 SSD 上的分区保持一致。这一步同样可以用任何分区工具完成,注意不要看错盘,不要错删了现有的分区!

创建完分区后,就可以把新的分区加入 btrfs 了。我这里新的分区在 /dev/nvme0n1p2,因此执行以下命令:

sudo btrfs device add /dev/nvme0n1p2 /mnt/btrfs

接下来手动触发同步:

sudo btrfs balance start -dconvert=raid1 -mconvert=raid1 /mnt/btrfs

等待同步完成后,执行以下命令查看 RAID1 的状态:

sudo btrfs filesystem df /mnt/btrfs
sudo btrfs filesystem show /mnt/btrfs

如果一切正常,应该能看到类似以下的输出(记录下这里的UUID,后面会用上):

Data, RAID1: total=23.00GiB, used=21.39GiB
System, RAID1: total=32.00MiB, used=16.00KiB
Metadata, RAID1: total=2.00GiB, used=690.62MiB
GlobalReserve, single: total=270.81MiB, used=0.00B

Label: none  uuid: ab2bbc1e-c476-4283-aea0-40ace705cbf9
    Total devices 2 FS bytes used 22.06GiB
    devid    1 size 230.00GiB used 25.03GiB path /dev/nvme0n1p2
    devid    2 size 230.00GiB used 25.03GiB path /dev/nvme1n1p2

最后,我们可以压缩一下已经存在的文件。这一步并非必须,但建议压缩一下:

sudo btrfs filesystem defragment -r -v -czstd /mnt/btrfs/@/
sudo btrfs filesystem defragment -r -v -czstd /mnt/btrfs/@home/

至此,我们的 btrfs RAID1 已经创建完成,并且原有的系统文件已经迁移到新的 btrfs 分区上。不过现在我们还不能启动原有的系统,因为 GRUB 和 fstab 还没有更新。

更新系统和引导配置

接下来,我们要通过 chroot 回到原有的系统中,更新系统和引导配置。首先卸载之前挂载的 btrfs 分区:

cd /mnt
sudo umount /mnt/btrfs

然后将两个子卷重新挂载到 /mnt/old 目录下,恢复原有的目录结构。这里使用上一步中看到的 UUID 来挂载(如果没有记录下来也没有关系,直接挂载 /dev/nvme1n1p2 之类的设备路径也可以):

sudo mkdir -p /mnt/old
sudo mount -t btrfs -o noatime,compress=zstd:1,subvol=@ /dev/disk/by-uuid/ab2bbc1e-c476-4283-aea0-40ace705cbf9 /mnt/old
sudo mkdir -p /mnt/old/home
sudo mount -t btrfs -o noatime,compress=zstd:1,subvol=@home /dev/disk/by-uuid/ab2bbc1e-c476-4283-aea0-40ace705cbf9 /mnt/old/home

接下来挂载其他必要的文件系统,然后 chroot 进入原有的系统:

cd /mnt/old
sudo mount -o bind /dev dev
sudo mount -o bind /sys sys
sudo mount -o bind /proc proc
sudo chroot .

首先修改 /etc/fstab 文件,首先将原有的 ext4 分区的配置删除,改为 btrfs 子卷的配置;然后将新的 SSD 的 EFI 分区添加到 /etc/fstab 中:

UUID=ab2bbc1e-c476-4283-aea0-40ace705cbf9 / btrfs defaults,compress=zstd:1,subvol=@ 0 1
UUID=ab2bbc1e-c476-4283-aea0-40ace705cbf9 /home btrfs defaults,compress=zstd:1,subvol=@home 0 2

UUID=<uuid of the new SSD EFI partition> /boot/efi2 vfat defaults 0 1

然后,在两个 EFI 分区上安装 GRUB:

mkdir -p /boot/efi
mkdir -p /boot/efi2
mount /dev/nvme1n1p1 /boot/efi
mount /dev/nvme0n1p1 /boot/efi2
mount -t efivarfs none /sys/firmware/efi/efivars
grub-install --target=x86_64-efi --efi-directory=/boot/efi --recheck
grub-install --target=x86_64-efi --efi-directory=/boot/efi2 --recheck

最后,更新 GRUB 配置:

update-initramfs -u
update-grub

这样,我们的系统就已经准备好了。

重启

重启系统。如无意外,应该可以顺利回到原有的系统中,并且通过 mount 可以看到,//home 都已经是 btrfs 分区了:

/dev/nvme0n1p2 on / type btrfs (rw,relatime,compress=zstd:1,ssd,discard=async,space_cache=v2,subvolid=257,subvol=/@)
/dev/nvme0n1p2 on /home type btrfs (rw,relatime,compress=zstd:1,ssd,discard=async,space_cache=v2,subvolid=258,subvol=/@home)

再次重启,尝试通过 BIOS 的启动菜单选择两个不同的 SSD 启动,两个EFI 分区也都应该可以正常启动系统。

结语

欢迎来到 btrfs!关于快照等等进阶功能,网上也不乏相关的教程,就留待未来再叙吧。

参考资料