在 ZFS 上安装 Arch Linux

出自 ArchWiki

本文详细介绍了在 ZFS 根文件系统上安装 Arch Linux 所需的步骤。

注意: 本指南假定读者对 ZFS 有一定的了解。如果您不熟悉 ZFS,建议首先阅读并理解 ZFS#概念,然后再回到本指南。在现有系统上安装 ZFS 并先熟悉一下命令也可能会有所帮助。

由于 ZFS 内核模块是树外模块(即未包含在主线内核中),并且 Arch Linux 是滚动发布发行版,因此通常会有一段短暂的时间,外部仓库中的内核特定软件包与 Arch 仓库中的软件包不同步。这有时会导致 ZFS 模块(DKMS 软件包)无法使用最新的内核进行编译。如果您始终希望使用最新的内核软件包,那么在 ZFS 上安装 Arch 可能不是理想的选择。

请参阅 ZFS#安装 以获取可能的解决方案。

获取安装介质

要在 ZFS 上安装 Arch Linux,您需要使用带有 ZFS 模块的安装介质。最简单的方法是使用替代 ISO (假设您信任此类 ISO)。您也可以使用来自其他支持 ZFS 的发行版的 ISO,例如 Ubuntu 或 NixOS,或者创建自定义镜像(见下文)。

使用包含 ZFS 模块的非官方 archiso

存在一个非官方的 archiso,可以直接使用,无需手动创建完整镜像或在启动后添加 ZFS 模块。但请注意,它仅包含 linux-lts 内核和 zfs-linux-lts 模块。

请参阅 r-maerz/archlinux-lts-zfs

使用来自其他发行版的 ISO

您也可以选择带有内置 ZFS 模块的 ISO 的发行版,因为大多数发行版都应该打包了 arch-install-scripts。例如,Ubuntu ISO 和 NixOS ISO 都应该可以正常工作。只需记住根据需要更改或跳过安装指南中的某些步骤,例如网络配置。

将 ZFS 模块嵌入到自定义 archiso 中

按照 Archiso 步骤创建功能完善的 Arch Linux Live CD/DVD/USB 镜像。要在镜像中包含 ZFS 支持,您可以从 AUR 构建您选择的 PKGBUILD,或者从非官方用户仓库之一包含预构建的软件包。

使用来自 AUR 的自建 ZFS 软件包

按照正常步骤构建您想要的 ZFS 软件包。如果您不确定,zfs-dkmsAURzfs-utilsAUR 可能会与您可能希望对 Archiso 镜像执行的最广泛的其他修改兼容。继续设置自定义本地仓库在您的新配置文件的 Pacman 配置中包含结果仓库

在要安装的软件包列表中包含构建的软件包。下面的示例假定您只想包含 zfs-dkmsAURzfs-utilsAUR 软件包。

packages.x86_64
...
zfs-dkms
zfs-utils

如果您包含任何 DKMS 软件包,请确保您还包含 ISO 中包含的任何内核的标头 (linux-headers 适用于默认内核)。

使用 archzfs 非官方用户仓库

archzfs 非官方用户仓库添加到您的新 Archiso 配置文件中的 pacman.conf

archzfs-linux 组添加到要安装的软件包列表中(archzfs 仓库仅为 x86_64 架构提供软件包)。

packages.x86_64
...
archzfs-linux
注意: 如果您稍后在运行 modprobe zfs 时遇到问题,您应该在 packages.x86_64 中包含 linux-headers。

完成

无论您从哪里获取 ZFS 软件包,都应最后构建 ISO

选择启动方法

由于您选择使用的 initrd 工具和引导加载器会影响安装过程的后续步骤,因此您应该在继续安装之前决定使用它们的哪些组合。

Initrd 工具

默认情况下,dracut 和 mkinitcpio 都不支持从 ZFS 根目录启动,因为它们不包含 initrd 中必要的内核模块和用户空间工具。您需要使用 dracut 模块或 mkinitcpio 钩子来制作可以从 ZFS 根目录启动的 initrd。您选择使用的 initrd 工具反过来会影响用于指定 ZFS 根目录的内核参数/cmdlines 的语法。

以下是选项

zfs hook

当使用默认的基于 busybox 的 initrd 时,zfs hook 是唯一的选择。

要配置 zfs hook,只需在您的 mkinitcpio.conf(5) 中在 filesystems hook 之前添加 zfs

可能的内核参数语法为

  • root=zfs,它使用 bootfs 属性确定根文件系统
  • root=ZFS=<pool/dataset>,它使用池或数据集作为根目录。当指定池时,根文件系统基于 mountpoint 属性确定
  • zfs=auto:与 root=zfs 效果相同
  • zfs=<pool/dataset>:与 root=ZFS=<pool/dataset> 效果相同

此外,可以设置以下内核参数来调整 initrd 的行为

  • zfs_force=1 使 zpool import 命令使用 -f 标志
  • zfs_wait=<seconds> 等待设备显示后再运行 zpool import

sd-zfs hook

zfs hook 与基于 systemd 的 initrd 不兼容。相反,您应该使用 sd-zfs hook。

有 2 种选择:一种随 zfs-utils-poscatarchlinuxcn 发行,另一种随 mkinitcpio-sd-zfsAUR[失效链接: 软件包未找到] 发行。前者正在积极维护,而后者似乎已被放弃。

zfs-utils-poscat

要配置此 hook,只需将其添加到 mkinitcpio.confHOOKS 数组中的任何位置。典型的配置可能如下所示

HOOKS=(systemd sd-zfs autodetect microcode modconf kms keyboard sd-vconsole block filesystems fsck)

支持的 cmdline 格式为

  • root=zfs,它在 initrd 中导入所有池,搜索第一个设置了 bootfs 属性的池,然后将 bootfs 挂载为根目录。
  • root=zfs:poolname,它仅导入指定的池,然后将池的 bootfs 挂载为根目录。
  • root=zfs:poolname/dataset,它仅导入指定的池,然后将指定的数据集挂载为根目录。
注意: sd-zfs 不支持 zfs 原生加密
mkinitcpio-sd-zfs

有关配置的文档,请参阅 github 仓库

zfs module

如果相反您想使用 dracut 进行 initrd,那么您应该使用 zfs-utilsAUR 附带的 zfs dracut 模块。查看文档 https://openzfs.github.io/openzfs-docs/man/master/7/dracut.zfs.7.html 了解如何配置 zfs 模块。

引导加载器

由于导入 ZFS 池、挂载根文件系统和 pivot_root 到新根目录的任务都由 UKI 或 vmlinuz+initrd 处理,因此对您可以使用的引导加载器没有任何要求。实际上,即使是 EFI 启动存根 也应该足够了,前提是内核参数根据您用于 initrd 的工具进行了正确配置(请参阅上面的#Initrd 工具部分)

注意: 除了 grub2 之外的所有引导加载器都无法从 ZFS 文件系统中读取文件,这意味着除非您使用 grub2,否则您需要将 UKI 或 vmlinuz+initrd 放在引导加载器可读取的单独文件系统上。当使用 UEFI 启动时,您可以重用您的 ESP 来存储它们(实际上建议将您的 UKI 放在 ESP 中),只需记住为您的 ESP 保留足够的空间

使用 GRUB2

Grub2 能够读取 ZFS 文件系统,前提是池的创建仅启用了有限的功能集(请参阅 ZFS#GRUB 兼容的池创建),因此在使用 Grub2 时可以将 UKI/initrd 放在 ZFS 根目录上。

警告: 考虑到 UKI/initrd 应该可以从 ZFS 根目录完全重建(请参阅#支持完整系统回滚的布局),将 UKI/initrd 放在 ZFS 根目录上是否能实现太多功能值得怀疑。此外,Grub2 的 ZFS 实现完全独立于 OpenZFS 实现,其可靠性尚不为人所知。

分区目标驱动器

分区类似于其他文件系统。有关使用什么布局以及如何分区磁盘,请参阅前面提到的分区页面或安装指南

注意: ZFS 不支持交换文件,并且使用 zvol 进行交换存在明显的缺点:系统可能会在高内存压力下死锁,并且无法休眠到 zvol 交换分区。因此,建议使用单独的交换分区。

支持完整系统回滚的布局

为了能够使用 ZFS 快照所有内容,您需要重建 UKI 或 vmlinuz+initrd(以便您可以回滚您的完整系统状态),您可以使用以下分区布局

  • 不要在 /boot 上挂载任何内容,这样 vmlinuz 将放置在您的根目录上,这是一个 ZFS 文件系统。
  • 如果您使用 UKI,请将 ESP 挂载到 /efi 并将 UKI 目标指向 /efi/EFI/Linux/<image 名称>.efi
  • 如果您使用 vmlinuz+initrd,请将 ESP(UEFI) 或启动分区(BIOS) 挂载到 /efi 并将 initrd 目标指向 /efi/<initrd 名称>.img。设置一个 pacman 钩子,将 vmlinuz 从 /boot/vmlinuz-* 自动复制到 /efi/

要执行回滚,只需回滚您的 ZFS 根文件系统,然后重新生成您的 UKI 或重新生成 initrd,然后手动将 vmlinuz 从 /boot/vmlinuz-* 复制到 /efi/

设置 ZFS 文件系统

在 Live CD 上启用 ZED

由于本指南假定使用 zfs-mount-generator(8),我们需要在首次启动到我们的系统之前生成 zfs-list 缓存。这需要

  1. 在 Live CD 上启用 zfs-zed.service
  2. 使用 touch 为您打算创建的每个池在 /etc/zfs/zfs-list.cache/<池名称> 创建空文件

创建根池

有关详细信息,请参阅 ZFS#创建 ZFS 池。例如,以下命令在分区 /dev/nvme0n1p2 上创建一个名为 rpool 的根池,并将 altroot 属性设置为 /mnt

# zpool create \
         -O acltype=posixacl    \
         -O relatime=on         \
         -O dnodesize=auto      \
         -O normalization=formD \
         -O compression=zstd    \
         -O mountpoint=/        \
         -R /mnt                \
         rpool /dev/nvme0n1p2
提示: 使用 altroot 属性,该属性在池创建或导入期间通过 -R 标志设置,以临时向挂载点添加前缀,以避免遮蔽 live cd 环境

创建文件系统

有关详细信息,请参阅 ZFS#创建数据集。以下是在选择数据集选项和布局时的一些注意事项

  1. 大多数属性都从父数据集继承到子数据集,除非显式覆盖。
  2. 子级的 mountpoint 属性的默认值为 <父级的挂载点>/<子级的名称>

安装和配置 Arch Linux

按照 安装指南#安装 中的安装步骤,在重启之前完成操作。您可能应该使用 linux-lts 内核而不是 linux

安装 ZFS

按照 ZFS#安装 安装 ZFS。

配置 ZFS

按照 ZFS#配置 配置 ZFS 相关服务。但请注意

  1. 我们正处于 chroot 环境中,所以不要尝试启动 systemd 服务,只需启用它们。
  2. 跳过 ZFS#zfs-mount-generator 中的所有步骤,除了启用 zfs-zed.service。我们稍后会填充缓存。

设置 initrd

请参阅 #Initrd 工具 以配置您选择的 initrd 生成器。不要忘记通过 mkinitcpio -Pdracut --regenerate-all 重新生成 initrd。

填充 zfs-list 缓存

现在退出 chroot。复制 /etc/zfs/zfs-list.cache/mnt/etc/zfs/zfs-list.cache

# cp -r /etc/zfs/zfs-list.cache /mnt/etc/zfs/

这提供了 zfs-mount-generator 所需的缓存。

卸载、导出和重启

卸载所有已挂载的文件系统(假设 altroot/mnt

# umount -R /mnt

导出所有池

# zpool export -a

重启

# reboot