在 ZFS 上安装 Arch Linux

来自 Arch Linux 中文维基

这篇文章详细描述了将 Arch Linux 安装在 ZFS 文件系统上所需的步骤。

警告: 盲目地照搬这篇教程上的操作是行不通的。您有必要花时间了解操作系统的启动以及创建存储池和数据集的过程。下面是一些能帮助到您的链接:

安装[编辑 | 编辑源代码]

要将 Arch Linux 安装在 ZFS 上,您需要从含有 ZFS 内核模块的 archiso 安装介质启动。

开始之前[编辑 | 编辑源代码]

考虑到 ZFS 内核模块不是 Linux 的原生文件系统且 Arch Linux 是一个滚动更新发行版,总会有一段(相对较短的)时间内,外部仓库内的对应一定内核版本的内核模块软件包版本会稍落后于 Arch 仓库中的版本,而有时 ZFS 模块(dkms 变种包)甚至在最新版本的内核上完全无法编译。如果您需要或想要一直使用最新版本的内核的话,将 Arch 安装在 ZFS 上可能不是一个好主意。无论如何,大多数情况下,将 linux 包及其相关变种 linux-* (如果使用dkms的话还包括 linux-*-headers 包)放在 pacman.conf 的 IgnorePkg 一栏,只对其进行手动更新,并在出现明显问题时立即回滚是一个明智的选择。 或者也可以使用 LTS (长期支持)版本的内核,它应至少可以搭配 dkms 正常工作。

在 archiso 安装介质上获取 ZFS 模块[编辑 | 编辑源代码]

有一个脚本可以简单地在 archiso 安装介质上安装并启用 ZFS 模块。它应该适用于任何版本的 archiso 安装介质。

eoli3n/archiso-zfs

在自定义的 archiso 安装介质中启用 ZFS 模块[编辑 | 编辑源代码]

要构建一个自定义的 archiso 安装介质,请参考 ZFS 一文。

对目标磁盘驱动器进行分区[编辑 | 编辑源代码]

要决定 ZFS 应使用哪一种分区图类型,请见 磁盘分区 。 ZFS 支持 GUID 分区图(GPT)和主引导记录(MBR)。

ZFS 管理自己的分区,所以只需创建一个最简单的分区类型即可。创建 ZFS 文件系统的分区类型应为类型 bf00 ,或“ Solaris 根目录分区”。

大于 2TB 的磁盘驱动器需使用 GUID 分区图。在 BIOS/GPT 类型配置上安装 GRUB 引导器还需一个较小(约1~2MiB)的 BIOS 启动分区来存放启动代码的映像。

根据您设备的固件类型以及您所选择的引导方式,系统启动可能需要一个 EFI 系统分区(ESP)。在使用 BIOS 的设备(或一台以 Legacy 模式启动的设备)上不需要 EFI 系统分区。 参考 Arch 引导过程#启动引导器 以获取更多信息。

分区类型[编辑 | 编辑源代码]

这是一个可以用于在 BIOS/MBR 的 ZFS 设备上使用 GRUB 启动引导器部署安装的基本分区类型示例:

Part     Size   Type
----     ----   -------------------------
   1     XXXG   Solaris Root (bf00)

在 BIOS(或一台以 Legacy 模式启动的)设备上使用 GUID 分区图与 GRUB 启动引导器的示例:

Part     Size   Type
----     ----   -------------------------
   1       2M   BIOS boot partition (ef02)
   2     XXXG   Solaris Root (bf00)

这是另一个示例,这次使用的是 GUID 分区图与 UEFI 启动引导器(如 rEFInd ):

Part     Size   Type
----     ----   -------------------------
   1     100M   EFI boot partition (ef00)
   2     XXXG   Solaris Root (bf00)

ZFS 不支持使用交换文件。如果您需要一个交换分区,参考 ZFS#交换卷 来创建一个用于交换空间的 ZVOL。

提示:#安装并配置启动引导加载器 中列出了支持 ZFS 的启动引导器。
警告: GRUB 启动引导器的一些 BUG (bug #42861, zfsonlinux/grub/issues/5) 使得要将其安装在 ZFS 分区上较为复杂,解决方法见 #安装并配置启动引导加载器

示例 parted 命令[编辑 | 编辑源代码]

这是适用于上述第二种使用 GUID 分区图和 BIOS/Legacy 启动情况的一些示例命令,其中为 GRUB 创建了一个(稍大于)1MB 的 BIOS 启动分区:

# parted /dev/sdx
(parted)mklabel gpt
(parted)mkpart non-fs 0% 2
(parted)mkpart primary 2 100%
(parted)set 1 bios_grub on
(parted)set 2 boot on
(parted)quit

您也可以把上述命令变为如下的单行命令:

# parted --script /dev/sdx mklabel gpt mkpart non-fs 0% 2 mkpart primary 2 100% set 1 bios_grub on set 2 boot on

如果您需要创建一个 EFI 系统分区,那就应将其标记为启动分区而非根分区。

格式化目标磁盘驱动器[编辑 | 编辑源代码]

如果您已为启动引导分区以及非 ZFS 文件系统创建了合适分区,那么将其格式化。不要对刚创建的 Solaris 根分区以及您的 BIOS 启动分区做任何操作。Solaris 根分区将由 ZFS 管理,而 BIOS 启动分区则会由您的启动引导器管理。

设置 ZFS 文件系统[编辑 | 编辑源代码]

警告: 不要在您的数据集名称中使用 '-' (见此 "特性")

首先,确认 ZFS 内核模块已经被加载,

# modprobe zfs

创建根存储池[编辑 | 编辑源代码]

创建存储池并设置好数据集的默认选项。存储池上新创建的任何数据集都会保留这个存储池创建时使用 -O 设定的选项。默认选项在 在 ZFS 上安装 Debian Buster. 第二步: 磁盘格式化 中有详细说明。

注意: 物理扇区大小为512字节的磁盘驱动器应使用 -o ashift=9 参数,而物理扇区大小为4096字节的磁盘驱动器应使用 -o ashift=12 参数。要获得每个 SCSI/SATA 磁盘驱动器的物理扇区大小,可以运行 lsblk -S -o NAME,PHY-SEC。如果想查看所有设备的物理扇区大小,可从命令中删去 -S。若使用 NVMe 驱动器,使用 nvme id-ns /dev/nvmeXnY -H | grep "LBA Format" 来获取正在使用的逻辑块地址。大部分 NVMe 驱动器使用512字节的逻辑块大小,见 OpenZFS: NVMe low level formatting 以将其大小改为4096字节。
警告: 应始终注意现代的大部分设备的物理扇区大小为4096字节,但有些设备会将自己的物理扇区大小报告为512字节,尤其是固态驱动器(SSD)。即使在报告扇区大小为512字节、实际物理扇区大小为4096字节的设备上选择 ashift=9一定会导致严重的性能下降。在物理扇区大小为512字节的设备上选择 ashift=12 不会导致性能下降,但可能使磁盘可用容量减少。如果您不确定的话,对于现代设备,应使用 ashift=12,或者您可以搜索您设备对应的正确值。对于有关的讨论,参见 OpenZFS issue #967 ;对设置较高的 ashift 值可能出现的问题,见 OpenZFS issue #2497
# zpool create -f -o ashift=12         \
             -O acltype=posixacl       \
             -O relatime=on            \
             -O xattr=sa               \
             -O dnodesize=legacy       \
             -O normalization=formD    \
             -O mountpoint=none        \
             -O canmount=off           \
             -O devices=off            \
             -R /mnt                   \
             zroot /dev/disk/by-id/id-to-partition-partx

压缩与原生加密[编辑 | 编辑源代码]

以下命令创建的存储池会在所有数据集上默认启用压缩与原生加密:

# zpool create -f -o ashift=12         \
             -O acltype=posixacl       \
             -O relatime=on            \
             -O xattr=sa               \
             -O dnodesize=legacy       \
             -O normalization=formD    \
             -O mountpoint=none        \
             -O canmount=off           \
             -O devices=off            \
             -R /mnt                   \
             -O compression=lz4        \
             -O encryption=aes-256-gcm \
             -O keyformat=passphrase   \
             -O keylocation=prompt     \
             zroot /dev/disk/by-id/id-to-partition-partx
警告:
  • 使用 ZFS 时应始终使用设备的 by-id 名称,否则导入存储池时会发生错误。
  • 除使用 by-id 名称外,也可以考虑使用 by-partuuid 或 by-uuid名称,因为即使一个内置磁驱动器被移入USB移动硬盘盒,这些名称的值也不会发生改变,反之亦然。(这仅当 ZFS 在磁盘上的某个分区中才有效,若 ZFS 占据整个磁盘则无效)
  • GRUB 启动引导器用户应注意的是 zpool-create 命令一般会默认启用所有功能,而其中某些是 GRUB 启动引导器不支持的功能。见: ZFS#GRUB-compatible pool creation 来创建与 GRUB 兼容的存储池。

创建您的数据集[编辑 | 编辑源代码]

ZFS 使用数据集的概念来管理您的存储,而非使用传统的磁盘分区。与磁盘分区不同,数据集没有固定的大小, 每个数据集也可以有各自不同的属性,例如压缩。普通的 ZFS 数据集由 ZFS 自动挂载,而传统的数据集则需由 fstab 或 使用 mount 命令挂载。

ZFS 最实用的功能之一便是启动环境。启动环境使您可以创建系统的可引导快照,您也可以通过简单地重启到某启动环境来将整个系统回滚到那个快照。这使得系统更新变得更加安全,对软件开发与测试来讲也十分有用。要使用如 beadm, zectlAUR (用于 systemd-boot), or zedenvAUR (用于 GRUB) 等的启动环境管理器来管理启动环境,您的数据集必须有正确的配置。其关键是将您存放数据的目录 (如 /home) 与系统数据分别放在相互独立的不同数据集中,且不要在存储池根目录中存放数据,因为放在存储池根目录的数据以后将不能被移动。

您总是至少应该为您的根目录创建一个数据集,且多数情况下您也会想要把 /home 存放在一个单独的数据集中。您也可以自行选择是否要无视启动环境而始终保留完整的日志文件。如果您使用的某些软件会在 /home 之外存放数据 (如数据库服务器),您应整理数据集的结构,使得这些软件的数据目录与根目录数据集分离开来。

以下的示例命令会创建一个只分根目录数据集与 /home 数据集的最基本可用于启动环境使用的配置。数据集使用其所在的存储池在创建时设定的默认选项。

# zfs create -o mountpoint=none zroot/data
# zfs create -o mountpoint=none zroot/ROOT
# zfs create -o mountpoint=/ -o canmount=noauto zroot/ROOT/default
# zfs create -o mountpoint=/home zroot/data/home

创建根目录数据集时您也可以不指定挂载点,毕竟无论如何 GRUB 启动引导器会将其挂载至 / 。这也使得您可以通过克隆旧版数据集并将其放入 GRUB 启动菜单来直接从旧版的根目录启动。这种情况下,您可以使用以下命令来创建您的根目录数据集:

# zfs create -o mountpoint=/roots/default zroot/ROOT/default

您可以将 /root 存储在您的 zroot/data/home 数据集中。

# zfs create -o mountpoint=/root zroot/data/home/root

对于存放某些特殊目录的数据集,您需要对其启用某些特定选项:

特殊目录所需的选项
目录 数据集选项 详细信息
/ canmount=noauto
/var/log/journal acltype=posixacl systemd#systemd-tmpfiles-setup.service在系统启动时失败

系统数据集[编辑 | 编辑源代码]

为系统目录创建数据集时,使用canmount=off选项。

示例请参见 Debian-Buster-Root-on-ZFS#step-3-system-installation

注意: 若要将如 zroot/var/log 的数据集挂载至 /var/log,应考虑使用 zfs-mount-generator 而非 zfs-mount.service。这会修复文件系统挂载顺序,在 这里 有详细介绍。
# zfs create -o mountpoint=/var -o canmount=off     zroot/var
# zfs create                                        zroot/var/log
# zfs create -o mountpoint=/var/lib -o canmount=off zroot/var/lib
# zfs create                                        zroot/var/lib/libvirt
# zfs create                                        zroot/var/lib/docker

导出并导入您的存储池[编辑 | 编辑源代码]

要验证您的设置,将您所有的 ZFS 存储池先导出后再重新导入。

警告: 不要跳过这一步,否则您再导入存储池时必须使用 -f 参数。这会使导入的存储池卸载。
注意: 如果您使用交换分区的话,这一步可能会失败。您需要先使用 swapoff 命令将其关闭。
# zpool export zroot
# zpool import -d /dev/disk/by-id -R /mnt zroot -N
注意: -d 并不是设备的实际 ID,而是包含着软链接的 /dev/by-id 目录。

如果这个命令执行失败并且您被要求使用数字 ID 来导入某个存储池,运行 zpool import 来找到您存储池的 ID,然后使用类似下方的命令导入存储池:

# zpool import 9876543212345678910 (您的设备的 ID) -R /mnt zroot

如果您启用了原生加密选项,先加载 ZFS 密钥。

# zfs load-key zroot

由于根目录数据集使用 canmount=noauto 参数,您需要先将其手动挂载,然后再挂载其他数据集。

# zfs mount zroot/ROOT/default
# zfs mount -a

现在 ZFS 文件系统已准备完毕以待使用。

配置根目录文件系统[编辑 | 编辑源代码]

如果您使用了传统类型的数据集,则您需要将其写入 /etc/fstab

为根目录所在的子文件系统设置 bootfs(启动文件系统),以便启动引导加载器找到操作系统。

# zpool set bootfs=zroot/ROOT/default zroot

如果您还没有 /etc/zfs/zpool.cache,请手动创建:

# zpool set cachefile=/etc/zfs/zpool.cache zroot

切记要将 zpool.cache 文件放入您的新系统中。稍后 ZFS 守护进程启动时需要这个文件。

# mkdir -p /mnt/etc/zfs
# cp /etc/zfs/zpool.cache /mnt/etc/zfs/zpool.cache

安装并配置 Arch Linux[编辑 | 编辑源代码]

按照 安装指南 安装系统。若有涉及 ZFSonLinux 所需的特殊操作,将会在此列出。

  • 首先使用 mount 命令挂载所有的传统类型数据集以及非 ZFS 的引导或系统分区。
  • 安装基本系统。
  • 安装指南#生成 fstab 文件 中所描述的方式对 ZFS 来说绝非必要。通常 ZFS 会自行挂载自己的分区,所以除非用户使用了传统型数据集,fstab 文件中不需要任何有关 ZFS 的部分。 要为文件系统生成 fstab,运行:
# genfstab -U -p /mnt >> /mnt/etc/fstab
# arch-chroot /mnt
  • 编辑 /etc/fstab
注意:
  • 如果您选择为系统目录创建传统数据集,将其在 fstab 中保留。
  • 将除交换空间与 EFI 系统分区以外的所有非传统型数据集注释掉。可以使用较简单的 /dev/zvol/zroot/swap 来取代交换空间的 UUID。
  • 在更新 ramdisk 并启用 ZFS 支持前,您需要在 /etc/pacman.conf 中加入 Arch ZFS 仓库,将其 签名 并在 arch-chroot 环境下 安装 zfs-linuxAUR (如果您使用 LTS 长期支持版内核则安装 zfs-linux-ltsAUR )。
  • 创建初始 ramdisk 前,先编辑 /etc/mkinitcpio.conf,并将 zfs 加至 MODULES:
MODULES=(zfs)

接下来在 HOOKS 中将 zfs 加至 filesystems 前。同时也应把 keyboard hook 移动至 zfs 前,这样如果出现问题,您仍可在终端中输入。若您不使用 Ext3 或 Ext4,您也可以移除 fsck。您的 HOOKS 一行看起来应类似如下示例:

HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block zfs filesystems)
  • 若在 initrd 中使用 systemd hook,您应安装 mkinitcpio-sd-zfsAUR 并将 sd-zfs hook 而非 zfs hook 放至 systemd hook 后面。应注意的是此 hook 和默认的 zfs hook 相比使用了不同的内核参数,可在其 项目主页 找到更多信息。
注意:
  • sd-zfs 暂不支持原生加密,见 dasJ/sd-zfs/issues/4
  • 如果您的 /usr 存放在单独的数据集上,并按照下面的指示进行了操作,您需要确认您的 zfs hook 后启用了 usr hook ,否则您的系统将无法启动。
  • 当您生成 initramfs 时, zpool.cache 文件会被复制到 initrd 中。如果您之前还未生成或需要重新生成 zpool.cache,记得在完成后重新生成 initramfs。
  • 您也能使用 legacy 挂载点来让 fstab 挂载它。

安装并配置启动引导加载器[编辑 | 编辑源代码]

原则上讲,只要内核与 initrd 在一个非 ZFS 分区上,那么启动引导器的配置就与平时没有什么差别。只要内核有 ZFS 模块、生成 initrd 时正确地使用了 zfs hook 且内核的命令行里添加了 zfs 参数,系统就可以启动。比如,大部分情况下, zfs=zroot/ROOT/default rw 就足够了。如果您的根目录文件系统参数是按照本篇内容设定好的,您甚至也可以只用 zfs=zroot rw 。您用不到 root 参数,因为 ZFS 会挂载根目录。至于如何设定内核参数,详见您所使用的启动引导加载器的帮助文档。

但是,如果您需要从 ZFS 上加载内核映像与/或 initrd,您就需要使用一个可以读取 ZFS 的启动引导器,并将其正确配置。

考虑到以上情况,配置您启动引导加载器的过程应该还算直接。下面有一些例子,不过也不要被其所局限。

使用 EFISTUB[编辑 | 编辑源代码]

如果您使用 EFISTUB 启动引导器,您可以手动向您的 UEFI 启动菜单中加入一个条目:

# efibootmgr --create --disk your_esp_disk --part your_esp_partition_number --label "Arch Linux (ZFS)" --loader /vmlinuz-linux --unicode 'zfs=zroot/ROOT/default rw initrd=\initramfs-linux.img'

使用统一内核映像 (Unified Kernel Image, UKI)[编辑 | 编辑源代码]

使用 统一内核映像 (UKI) 就会比较直接。您只需要确保您在 /etc/cmdline.d 中的某个地方加入了 zfs 参数即可。除了配置 mkinitcpio 模块和 hook 的过程需要依照本文来做,其他部分与使用一个普通统一内核映像没有区别。例如:

/etc/cmdline.d/root.conf
zfs=zroot/ROOT/default rw

设置 zfs=zroot 大概率就够用了(未经过测试),毕竟 ZFS 应该可以自动挂载根目录。只要记得每次作出更改时重新运行一遍 mkinitcpio 即可。

使用 GRUB[编辑 | 编辑源代码]

如果您使用 GRUB 启动引导器,您可以将您的 /boot 目录保存在 ZFS 存储池上。请详细阅读 Debian-Buster-Root-on-ZFS#step-3-system-installation

您可以在 GRUB#BIOS 系统GRUB#UEFI 系统 找到安装 GRUB 的方法。GRUB 的 手册 也提供了手动配置该软件的详细方法。您也可以阅读 GRUBGRUB/方法与技巧 作为补充。

BUG: 无法检测根存储池[编辑 | 编辑源代码]

一个已知的 BUG 会导致 grub-mkconfig 无法检测根存储池并将其正确写入 /boot/grub/grub.cfg 中。这个 BUG 仍未修复,但有两种解决方案:

  • 解决方案 A :编辑 /etc/grub.d/10_linux 中检测 rpool 的代码。将
rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || true`
替换为
rpool=`zdb -l ${GRUB_DEVICE} | grep " name:" | cut -d\' -f2`
这样 grub-mkconfig 应该就能够检测到正确的存储池名并将正确路径写入 /boot/grub/grub.cfg
  • 解决方案 B :如果上述方法仍不起作用,您可以将正确路径硬编码至 /etc/grub.d/10_linux 内。将
linux   ${rel_dirname}/${basename} root=${linux_root_device_thisversion} rw ${args}
替换为
linux   ${rel_dirname}/${basename} root=ZFS=zroot/ROOT/default rw ${args}

错误:无法获取 …… 的规范路径(error: failed to get canonical path of)[编辑 | 编辑源代码]

grub-mkconfig 无法正确地为 ZFS 上的系统生成相应条目。

# grub-mkconfig -o /boot/grub/grub.cfg
/usr/bin/grub-probe: error: failed to get canonical path of `/dev/bus-Your_Disk_ID-part#'
grub-install: error: failed to get canonical path of `/dev/bus-Your_Disk_ID-part#'

要解决这个问题,您必须设置 ZPOOL_VDEV_NAME_PATH=1 环境变量。例如:

# ZPOOL_VDEV_NAME_PATH=1 grub-mkconfig -o /boot/grub/grub.cfg

错误:未知的文件系统(error: unknown filesystem)[编辑 | 编辑源代码]

GRUB 的命令行工具,如 grub-probegrub-install 等在无法检测文件系统时可能会因 unknown filesystem 错误而执行失败。这可能是因为使用了 GRUB 不支持的文件系统,或使用了 GRUB 不支持的 ZFS 参数(见 ZFS#GRUB-compatible pool creation 来为启动所用的 ZFS 存储池设定合适的参数)。

要排除这个故障,首先应找出 GRUB 无法辨认的是哪个文件系统(可以在您的怀疑对象上运行 grub-probe,例如 grub-probe /grub-probe /boot)。下面是一个示例:

# grub-probe /boot
zfs

# grub-probe /
grub-probe: error: unknown filesystem.

找到出问题的文件系统后,运行 grub-probe -vvvv / 并浏览输出内容中 GRUB 尝试辨认的文件系统。下面这个例子中,GRUB 尝试辨认了 ZFS , 但出现了如下输出:

grub-probe -vvvv /
(...)
grub-core/kern/fs.c:56: Detecting zfs...
grub-core/osdep/hostdisk.c:420: opening the device `/dev/sda4' in open_device()
grub-core/fs/zfs/zfs.c:1199: label ok 0
grub-core/osdep/hostdisk.c:399: reusing open device `/dev/sda4'
grub-core/fs/zfs/zfs.c:1014: check 2 passed
grub-core/fs/zfs/zfs.c:1025: check 3 passed
grub-core/fs/zfs/zfs.c:1032: check 4 passed
grub-core/fs/zfs/zfs.c:1042: check 6 passed
grub-core/fs/zfs/zfs.c:1050: check 7 passed
grub-core/fs/zfs/zfs.c:1061: check 8 passed
grub-core/fs/zfs/zfs.c:1071: check 9 passed
grub-core/fs/zfs/zfs.c:1093: check 11 passed
grub-core/fs/zfs/zfs.c:1119: check 10 passed
grub-core/fs/zfs/zfs.c:1135: str=com.delphix:hole_birth
grub-core/fs/zfs/zfs.c:1135: str=com.delphix:embedded_data
grub-core/fs/zfs/zfs.c:1144: check 12 passed (feature flags)
grub-core/fs/zfs/zfs.c:1884: zio_read: E 0: size 4096/4096
(...)
grub-core/osdep/hostdisk.c:399: reusing open device `/dev/sda4'
grub-core/fs/zfs/zfs.c:2117: zap: name = com.delphix:extensible_dataset, value = 18, cd = 0
grub-core/fs/zfs/zfs.c:2117: zap: name = com.datto:bookmark_v2, value = 0, cd = 0
grub-core/fs/zfs/zfs.c:2117: zap: name = com.datto:encryption, value = c, cd = 0 # <------------------ (检测到启用了原生加密功能)
grub-core/kern/fs.c:78: zfs detection failed.  # <---------------------------------------------------- (检测失败)
grub-core/kern/fs.c:56: Detecting xfs...
grub-core/fs/xfs.c:931: Reading sb
grub-core/fs/xfs.c:270: Validating superblock
grub-core/kern/fs.c:78: xfs detection failed.
grub-core/kern/fs.c:56: Detecting ufs2...
(...)
grub-core/kern/fs.c:56: Detecting affs...
grub-core/kern/fs.c:78: affs detection failed.
grub-probe: error: unknown filesystem.

这说明在检测到 com.datto:encryption 功能前,程序都在正常运行。由于 GRUB 不支持 ZFS 的原生加密功能(截止2021年8月),ZFS 检测就失败了。这时可以创建另一个兼容 GRUB 的存储池用来引导加密的存储池(截止2021年8月,这也是 OpenZFS 官方推荐的做法,参见 有关的 OpenZFS 项目页面)。

在一个兼容 GRUB 的存储池上成功运行 grub-probe 的输出应该类似下方:

grub-probe -vvvv /boot
(...)
grub-core/osdep/hostdisk.c:399: reusing open device `/dev/sda3'
grub-core/fs/zfs/zfs.c:2117: zap: name = com.delphix:extensible_dataset, value = 0, cd = 0
grub-core/fs/zfs/zfs.c:2117: zap: name = com.delphix:embedded_data, value = 1, cd = 0
grub-core/fs/zfs/zfs.c:2117: zap: name = com.delphix:hole_birth, value = 1, cd = 0
grub-core/fs/zfs/zfs.c:2117: zap: name = org.open-zfs:large_blocks, value = 0, cd = 0
grub-core/fs/zfs/zfs.c:2117: zap: name = org.illumos:lz4_compress, value = 1, cd = 0
grub-core/fs/zfs/zfs.c:2117: zap: name = , value = 0, cd = 0
grub-core/fs/zfs/zfs.c:2117: zap: name = , value = 0, cd = 0
grub-core/fs/zfs/zfs.c:3285: alive
(...)
grub-core/fs/zfs/zfs.c:1906: endian = 1
grub-core/fs/zfs/zfs.c:597: dva=8, 20008
grub-core/fs/zfs/zfs.c:2697: alive
zfs

从 ZFS 上引导您的内核与 initrd[编辑 | 编辑源代码]

如果您的内核与 initrd 保存在单独的使用 ext4 或 vfat 等文件系统的非 ZFS /boot 分区上,您可以跳过这一步。

如果您的内核与 initrd 在 ZFS 数据集上保存,GRUB 使用的内核与 initrd 路径应该是如下格式:

/dataset(数据集)/@/actual(实际)/path(路径)

下面是当 Arch 安装在根数据集上时的例子:

/boot/grub/grub.cfg
set timeout=5
set default=0

menuentry "Arch Linux" {
    search -u UUID
    linux /@/boot/vmlinuz-linux zfs=zroot rw
    initrd /@/boot/initramfs-linux.img
}

下面是当 Arch 安装在某个子数据集上时的情况:

/boot/grub/grub.cfg
set timeout=5
set default=0

menuentry "Arch Linux" {
    search -u UUID
    linux /ROOT/default/@/boot/vmlinuz-linux zfs=zroot/ROOT/default rw
    initrd /ROOT/default/@/boot/initramfs-linux.img
}

从单独的引导分区启动您的内核与 initrd[编辑 | 编辑源代码]

这是当您的内核与 initrd 保存在一个单独的非 ZFS 分区上且 Arch 安装在某个子数据集上时的情况:

/boot/grub/grub.cfg
set timeout=5
set default=0

menuentry "Arch Linux" {
    search -u UUID
    linux /vmlinuz-linux zfs=zroot/ROOT/default rw
    initrd /initramfs-linux.img
}

只使用 systemd-boot 进行 EFI 启动[编辑 | 编辑源代码]

systemd-boot 无法读取 ZFS 存储池,所以您必须将您的 /boot 目录保存在一个单独的 VFAT 或 ext4 分区上。

注意: 这种情况下要使用 zectlAUR 来管理您的启动环境,请按照 zectl/docs/plugins/systemdboot.md 的指示操作。

按照 systemd-boot#安装 EFI 启动管理器 的教程将启动引导器安装在 esp 分区上。

创建一个启动项:

/efi/loader/entries/archlinux.conf
title           Arch Linux
linux           vmlinuz-linux
initrd          intel-ucode.img
initrd          initramfs-linux.img
options         zfs=zroot/ROOT/default rw

使用 rEFInd 进行 UEFI 启动[编辑 | 编辑源代码]

为 UEFI 启动管理器使用 EFISTUBrEFIndrefind_linux.conf 中针对 ZFS 设定的内核参数应该包含 zfs=bootfszfs=zroot 以便系统从 ZFS 启动。无需额外设定 rootrootfstype 参数。

使用 ZFSBootMenu 进行 UEFI 启动[编辑 | 编辑源代码]

若使用 ZFSBootMenu,您的 /boot 目录必须存放在您的根文件系统中。更多信息请见 Boot Environments and You: A Primer

您可以选择 #生成 ZFSBootMenu 映像 或使用已构建好的 #ZFSBootMenu EFI 二进制文件

生成 ZFSBootMenu 映像[编辑 | 编辑源代码]

/etc/zfsbootmenu/config.yaml
Global:
  ManageImages: true
  BootMountPoint: path_to_your_ESP ( ESP 分区挂载点) 
  DracutConfDir: /etc/zfsbootmenu/dracut.conf.d
  PreHooksDir: /etc/zfsbootmenu/generate-zbm.pre.d
  PostHooksDir: /etc/zfsbootmenu/generate-zbm.post.d
  InitCPIO: true
  InitCPIOConfig: /etc/zfsbootmenu/mkinitcpio.conf
Components:
  ImageDir: path_to_your_ESP ( ESP 分区挂载点) /EFI/zbm
  Versions: 3
  Enabled: false
  syslinux:
    Config: /boot/syslinux/syslinux.cfg
    Enabled: false
EFI:
  ImageDir: path_to_your_ESP ( ESP 分区挂载点) /EFI/zbm
  Versions: false
  Enabled: true
Kernel:
  CommandLine: ro quiet loglevel=0
  • 生成 ZFSBootMenu 映像:
# generate-zbm
  • 设置启动时的 zfs 命令行参数:
# zfs set org.zfsbootmenu:commandline="rw" zroot/ROOT
  • 向 EFI 启动管理器中加入 ZFSBootMenu 条目:
# efibootmgr -c -d your_esp_disk -p your_esp_partition_number -L "ZFSBootMenu" -l '\EFI\zbm\vmlinuz-linux.EFI'

ZFSBootMenu EFI 二进制文件[编辑 | 编辑源代码]

# zfs set org.zfsbootmenu:commandline="rw" zroot/ROOT
  • 在 EFI 启动管理器中加入 ZFSBootMenu 条目:
# efibootmgr -c -d your_esp_disk -p your_esp_partition_number -L "ZFSBootMenu" -l '\EFI\zbm\zfsbootmenu-release-vmlinuz-x86_64.EFI'

配置 systemd ZFS 挂载[编辑 | 编辑源代码]

要确保您的系统能够正常地重新启动,您需要启用 zfs.target 以便自动挂载存储池与生成 hostid。

注意: 这一节内容的前提是假设您仍在 arch-chroot 环境中。

分别为每个您想要自动挂载的存储池执行:

# zpool set cachefile=/etc/zfs/zpool.cache pool(存储池名)

启用 zfs.target

要在系统启动时自动挂载 ZFS 存储池,您需要 启用 zfs-import-cache.servicezfs-mount.servicezfs-import.target

当根目录文件系统为 ZFS 时,挂载根文件系统的过程中设备的 hostid 将不可用。对此有两个解决方案。 您可以将您的 spl hostid 写入启动引导器的启动 内核参数 中。例如向内核参数中加入 spl.spl_hostid=0x00bab10c。要获取您的 hostid 数字,运行 hostid 命令。

另一个(建议的)解决方案是在 /etc/hostid 内写入一个 hostid ,然后重新生成 initramfs,这个过程中您的 hostid 将会被复制到 initramfs 映像中。要安全写入 hostid 文件,您需要运行 zgenhostid 命令。

要使用 libc 生成的 hostid(建议的操作):

# zgenhostid $(hostid)

要使用自定义的 hostid,运行以下命令。注意,hostid 必须是8个字符的十六进制数字。

# zgenhostid deadbeef

要让 zgenhostid 工具生成一个 hostid :

# zgenhostid

完成后别忘了 重新生成 initramfs

卸载文件系统并重新启动[编辑 | 编辑源代码]

我们离成功不远了!如果您使用传统类型的启动引导分区,先运行:

# umount /mnt/boot 

如果未使用传统的单独引导分区,应直接运行:

# zfs umount -a
# zpool export zroot

现在,重新启动系统。

警告: 如果您没有正确将存储池导出,重新启动后 ZFS 将拒绝在 ramdisk 环境中导入存储池,从而使您卡在 busybox 终端界面。

从 USB 存储设备加载密钥[编辑 | 编辑源代码]

可以将密钥存储在 USB 存储设备上并在启动时加载:

在 USB 存储介质的初始字节处存储密钥:

# dd if=your_password_file (您的密钥文件) bs=32 count=1 of=/dev/disk/by-id/usb_stick (USB 存储设备)

要创建 ZFS 分区,您可以使用上文所述的输入密钥的方式,或直接使用 dd 命令配合管道写入 USB 存储设备中存储的密钥:

# dd if=/dev/disk/by-id/usb_stick bs=32 count=1 | zfs create -o encryption=on -o keyformat=passphrase zroot/ROOT

下一步就要更改 zfs hook。zfs 默认会询问密钥。您需要将获取密钥的方式改为通过从您存放着密钥的 USB 设备中 dd 来获取。要达成这个目的,将 /usr/lib/initcpio/hooks/zfs 中的以下行:

# ! eval zfs load-key "${encryptionroot}"; do

改为:

# ! eval dd if=/dev/disk/by-id/usb_stick bs=32 count=1 | zfs load-key "${encryptionroot}"; do

您刚刚更改了您的 zfs hook,所以不要忘记 重新生成 initramfs。现在 zfs 应该能在启动时从您的 USB 设备中加载密钥。

故障排除[编辑 | 编辑源代码]

系统因 "无法导入 zroot :存储池不存在(cannot import zroot: no such pool available)" 而无法启动[编辑 | 编辑源代码]

您可以尝试以下步骤,看看是否有帮助。

  • 使用 archzfs 仓库 中提供的内核模块,不要使用 dkms 版本。您可以在成功启动后再改为使用 dkms 变种。
  • 移除 /etc/zfs/zpool.cache 并运行:
# zpool set cachefile=none zroot

另请参阅[编辑 | 编辑源代码]