在 ZFS 上安装 Arch Linux
这篇文章详细描述了将 Arch Linux 安装在 ZFS 文件系统上所需的步骤。
由于 ZFS 不是 Linux 的原生文件系统(例如:不包含在主线内核之内)且 Arch Linux 是一个滚动更新发行版,总有些时候外部仓库内的对应一定内核版本的内核模块软件包版本会稍落后于 Arch 仓库中的版本,这有时会导致 ZFS 模块(或其 dkms 变种包)在最新版本的内核上无法编译。如果您想要一直使用最新版本的内核的话,将 Arch 安装在 ZFS 上可能并不是很理想。
可能的解决方案见 ZFS#安装。
安装[编辑 | 编辑源代码]
要在 ZFS 上安装 Arch Linux,您需要使用带有 ZFS 内核模块的安装介质。您可以选择在官方 ISO 中添加相应内核模块或创建一个自定义的安装镜像。
在 archiso 安装介质上获取 ZFS 模块[编辑 | 编辑源代码]
有一个脚本可以简单地在 archiso 安装介质上安装并启用 ZFS 模块。它应该适用于任何版本的 archiso 安装介质。
在自定义的 archiso 安装介质中启用 ZFS 模块[编辑 | 编辑源代码]
要构建一个自定义的 archiso 安装介质,请参考 ZFS#Create an Archiso image with ZFS support。
对目标磁盘驱动器进行分区[编辑 | 编辑源代码]
ZFS 支持 GUID 分区图与主启动记录分区表。要决定使用哪种分区图类型,请参考分区#选择 GPT 还是 MBR。
ZFS 会管理自己的分区,所以只需创建一个最简单的分区类型即可。创建 ZFS 文件系统的分区类型应为类型 bf00
,或“ Solaris 根目录分区”。
分区类型[编辑 | 编辑源代码]
这是一个可以用于在 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)
要在使用 BIOS 启动模式并使用 GUID 分区图或主启动记录的计算机上使用 Syslinux 与 zfsbootmenuAUR :
Part Size Type ---- ---- ------------------------- 1 512M XBOOTLDR partition (ea00) 2 XXXG Solaris Root (bf00)
这是另一个示例,这次使用的是 GUID 分区图与 UEFI 启动引导器(如 rEFInd ):
Part Size Type ---- ---- ------------------------- 1 1G EFI System Partition (ef00) 2 XXXG Solaris Root (bf00)
ZFS 不支持使用交换文件。如果您需要一个交换分区,参考 ZFS#交换卷来创建一个用于交换空间的 ZVOL。
示例 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. 第二步: 磁盘格式化中有详细说明。
-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字节。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
参数。这会使导入的存储池卸载。# 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
- 依照安装指南#chroot 到新安装的系统中的方法将根目录切换至新安装的系统内。
# 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 的手册也提供了手动配置该软件的详细方法。您也可以阅读 GRUB 和 GRUB/方法与技巧作为补充。
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-probe 、 grub-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[编辑 | 编辑源代码]
systemd-boot 仅支持 UEFI 启动且无法读取 ZFS 存储池:您必须将您的 /boot
目录保存在一个单独的 VFAT 或 ext4 分区上。
按照 systemd-boot#安装 EFI 启动管理器的教程将 systemd-boot 安装在您的 EFI 系统分区上。
创建一个启动项:
esp/loader/entries/arch.conf
title Arch Linux linux /vmlinuz-linux initrd /initramfs-linux.img options zfs=zroot/ROOT/default rw
使用 rEFInd[编辑 | 编辑源代码]
要在 rEFInd 启动管理器中使用 EFISTUB,refind_linux.conf
中针对 ZFS 设定的内核参数应该包含 zfs=bootfs
或 zfs=zroot
以便系统从 ZFS 启动。无需额外设定 root
与 rootfstype
参数。
使用 ZFSBootMenu[编辑 | 编辑源代码]
安装 zfebootmenuAUR。
对于 UEFI[编辑 | 编辑源代码]
安装 efibootmgr包。若使用 ZFSBootMenu,您的 /boot
目录必须存放在您的根文件系统中。更多信息请见启动环境与您:孰轻孰重及 UEFI 启动。
如果您的 ESP 还未挂载,请将其挂载。然后,配置 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 --create --disk your_esp_disk --part your_esp_partition_number --label "ZFSBootMenu" --loader '\EFI\zbm\vmlinuz-linux.EFI' --unicode
对于 BIOS[编辑 | 编辑源代码]
与 UEFI 相同,如果您使用 ZFSBootMenu, /boot
目录必须存放在 zroot
存储池内。
但您还需要需要一个 syslinux
目录而非 efi
目录,因为 ZFSBootMenu 依赖 Syslinux。
将您的分区挂在至 syslinux
目录。
安装 syslinux包 和 gptfdisk包 来获得 GUID 分区图支持。
拷贝 syslinux
目录中的所有 .c32
文件:
# cp /usr/lib/syslinux/bios/*.c32 /your_location/syslinux
然后安装启动引导加载器:
# extlinux --install /your_location/syslinux
在这之后,继续依照您的分区图类型写入 Syslinux 引导代码 (boot code) :
- 在 #使用主启动记录的磁盘上应该安装
mbr.bin
, - 在 #使用 GUID 分区图的磁盘上应安装
gptmbr.bin
配置 ZFSBootMenu 使用的内核启动参数:
# zfs set org.zfsbootmenu:commandline="rw" zroot/ROOT
编辑以下文件来设置 ZFSBootMenu 的映像生成:
/etc/zfsbootmenu/config.yaml
Global: ManageImages: true BootMountPoint: path_to_your_syslinux_folder 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_syslinux_folder/zfsbootmenu Versions: false Enabled: true Kernel: CommandLine: ro quiet loglevel=0
然后生成 ZFSBootMenu 映像:
# generate-zbm
编辑以下的文件并将其内容替换为:
/boot/syslinux/syslinux.cfg
UI menu.c32 PROMPT 0 MENU TITLE ZFSBootMenu TIMEOUT 50 DEFAULT zfsbootmenu LABEL zfsbootmenu MENU LABEL ZFSBootMenu KERNEL /zfsbootmenu/vmlinuz-linux-bootmenu INITRD /zfsbootmenu/initramfs-bootmenu.img APPEND zfsbootmenu quiet # The following for the backup entry LABEL zfsbootmenu-backup MENU LABEL ZFSBootMenu (Backup) KERNEL /zfsbootmenu/vmlinuz-linux-bootmenu-backup INITRD /zfsbootmenu/initramfs-bootmenu-backup.img APPEND zfsbootmenu quiet
配置 systemd ZFS 挂载[编辑 | 编辑源代码]
要确保您的系统能够正常地重新启动,您需要启用 zfs.target
以便自动挂载存储池与生成 hostid。
arch-chroot
环境中。分别为每个您想要自动挂载的存储池执行:
# zpool set cachefile=/etc/zfs/zpool.cache pool(存储池名)
启用 zfs.target
要在系统启动时自动挂载 ZFS 存储池,您需要启用 zfs-import-cache.service
、zfs-mount.service
与 zfs-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
现在,重新启动系统。
从 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
- 移除
/etc/hostid
。 - 重新生成 initramfs。