在 ZFS 上安装 Arch Linux
这篇文章详细描述了将 Arch Linux 安装在 ZFS 文件系统上所需的步骤。
由于 ZFS 不是 Linux 的原生文件系统(例如:不包含在主线内核之内)且 Arch Linux 是一个滚动更新发行版,总有些时候外部仓库内的对应一定内核版本的内核模块软件包版本会稍落后于 Arch 仓库中的版本,这有时会导致 ZFS 模块(或其 dkms 变种包)在最新版本的内核上无法编译。如果您想要一直使用最新版本的内核的话,将 Arch 安装在 ZFS 上可能并不是很理想。
可能的解决方案见 ZFS#安装。
安装
要在 ZFS 上安装 Arch Linux,您需要使用带有 ZFS 内核模块的安装介质。您可以使用现有的 Arch Linux; 或者,如果您没有现有的 Arch Linux 系统,您也可以使用启用了“共享文件夹”的虚拟机软件,如 VirtualBox 或 VMWare。
在自定义 archiso 安装介质中嵌入 ZFS 模块
要构建自定义的ISO, 安装 archiso包。依照 archiso#准备自定义配置文件 来准备一个自定义的配置文件(下文中以 archlive 为例)。
首先,编辑 软件包列表 packages.x86_64 并在其中加入 linux-lts包 内核与下列 ZFS 软件包:
packages.x86_64
... linux-lts linux-lts-headers libunwind zfs-utils zfs-dkms
请确保从列表中移除 linux 与 broadcom-wl (后者会拉取 linux包 包) 。
您还需要编辑 pacman.conf 并在其中加入:
... [archzfs] SigLevel = TrustAll Optional Server = https://github.com/archzfs/archzfs/releases/download/experimental
您还需要编辑以下文件来将 vmlinuz-linux 与 initramfs-linux.img 的条目改为 vmlinuz-linux-lts 和 initramfs-linux-lts.img:
archlive/airootfs/etc/mkinitcpio.d/linux.preset archlive/efiboot/loader/entries/01-archiso-linux.conf archlive/efiboot/loader/entries/02-archiso-speech-linux.conf archlive/syslinux/archiso_pxe-linux.cfg archlive/syslinux/archiso_sys-linux.cfg archlive/grub/loopback.cfg archlive/grub/grub.cfg
接下来我们要创建 isobuild 与 work 两个目录来开始构建 ISO 镜像:
# mkdir isobuild # mkarchiso -v -r -w /tmp/archiso-tmp -o isobuild ~/archlive
You should now have an archlinux-YYYY.MM.DD.x86_64.iso in the archlive/isobuild directory.
Burn this file to your installation media of choice.
- You may wish to recreate this media occasionally if the
zpool status -voutput shows, after an update, that the zpool needs to be updated to support newer features. - You should use the linux-lts包 and linux-lts-headers包 for the actual installation for best compatibility. You also should use the unofficial archzfs repository for binary packages for zfs-utils and zfs-dkms updates, not the AUR packages. If you do choose to use the AUR packages, there will be times when a new mainline and zen Linux kernel, do not support ZFS, and may break you system. Due to this, the linux-lts kernel is strongly recommended for the actual installation section.
Be sure to test your new ISO with a virtual machine and test the ISO. Simply run:
# modprobe zfs # zpool status
If it fails, then your zfs module did not build correctly, and you will have to try again.
To build a custom ISO, install archiso包. Follow archiso#Prepare a custom profile to prepare a custom profile (named archlive in the following example).
First, edit the package list file packages.x86_64 to add the linux-lts包 kernel and the following ZFS packages:
packages.x86_64
... linux-lts linux-lts-headers libunwind zfs-utils zfs-dkms
Make sure to remove linux and broadcom-wl (which would pull in the linux包 package) from the list.
You will also need to edit pacman.conf and append:
... [archzfs] SigLevel = TrustAll Optional Server = https://github.com/archzfs/archzfs/releases/download/experimental
You will also need to edit the following files to change vmlinuz-linux and initramfs-linux.img entries to vmlinuz-linux-lts and initramfs-linux-lts.img:
archlive/airootfs/etc/mkinitcpio.d/linux.preset archlive/efiboot/loader/entries/01-archiso-linux.conf archlive/efiboot/loader/entries/02-archiso-speech-linux.conf archlive/syslinux/archiso_pxe-linux.cfg archlive/syslinux/archiso_sys-linux.cfg archlive/grub/loopback.cfg archlive/grub/grub.cfg
Now we will make an isobuild and work pair of directories to start the build process:
# mkdir isobuild # mkarchiso -v -r -w /tmp/archiso-tmp -o isobuild ~/archlive
You should now have an archlinux-YYYY.MM.DD.x86_64.iso in the archlive/isobuild directory.
Burn this file to your installation media of choice.
- You may wish to recreate this media occasionally if the
zpool status -voutput shows, after an update, that the zpool needs to be updated to support newer features. - You should use the linux-lts包 and linux-lts-headers包 for the actual installation for best compatibility. You also should use the unofficial archzfs repository for binary packages for zfs-utils and zfs-dkms updates, not the AUR packages. If you do choose to use the AUR packages, there will be times when a new mainline and zen Linux kernel, do not support ZFS, and may break you system. Due to this, the linux-lts kernel is strongly recommended for the actual installation section.
Be sure to test your new ISO with a virtual machine and test the ISO. Simply run:
# modprobe zfs # zpool status
If it fails, then your zfs module did not build correctly, and you will have to try again.
对目标磁盘驱动器进行分区
ZFS 支持 GUID 分区图与主启动记录分区表。要决定使用哪种分区图类型,请参考分区#选择 GPT 还是 MBR。
ZFS 会管理自己的分区,所以只需创建一个最简单的分区类型即可。创建 ZFS 文件系统的分区类型应为类型 bf00 ,或“ Solaris 根目录分区”。
分区类型
Although for some legacy machines you, in the past, with MBR based partitioning methods, could create a zfs bootable root partition, it is not recommended to use ZFS in this manner due usage of GPT partitioning differences. It is recommended to use a separate /boot partition to avoid issues with bootloaders and ensure best compatibility.
在 BIOS(或一台以 Legacy 模式启动的)设备上使用 GUID 分区图与 GRUB 启动引导器的示例:
Part Size Type ---- ---- ------------------------- 1 2M BIOS boot partition (ef02) 2 1G Linux Partition (8300) 3 XXXG Solaris Root (bf00)
You may also use this method of partitioning with Grub and rEFInd. This method is the most recommended by the author for best compatibility with all systems.
Part Size Type ---- ---- ------------------------- 1 1G EFI System Partition (ef00) 2 XXXG Solaris Root (bf00)
You can choose to separate Linux swap partition, or using a zvol, see ZFS#Swap volume, as swap.
If you wish to create a traditional swap partition, see Partitioning#Example layouts.
格式化目标磁盘驱动器
如果您已为启动引导分区以及非 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 。
ls -lh /dev/disk/by-id/
and take note of the correct by-id value to use rather than the standard device naming scheme such as:
lrwxrwxrwx 1 root root 9 Aug 12 16:26 ata-ST3000DM001-9YN166_S1F0JKRR -> ../../sdc
# zpool create -f -o ashift=12 \
-O acltype=posixacl \
-O relatime=on \
-O xattr=sa \
-O dnodesize=auto \
-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=auto \
-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
The options after -O control ZFS behavior.
A detailed explanation of them can be found in the zfsprops(7) man page.
- 使用 ZFS 时应始终使用设备的 by-id 名称,否则导入存储池时会发生错误。
- 除使用 by-id 名称外,也可以考虑使用 by-partuuid 或 by-uuid名称,因为即使一个内置磁驱动器被移入USB移动硬盘盒,这些名称的值也不会发生改变,反之亦然。(这仅当 ZFS 在磁盘上的某个分区中才有效,若 ZFS 占据整个磁盘则无效)
创建您的数据集
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=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/log/journal -o acltype=posixacl zroot/var/log/journal # 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 的引导或系统分区。
- 安装基本系统。
pacstrap portion. Make sure to also add libunwind包 to pacstrap to resolve a dependency for zfs-utilsAUR.-
安装指南#生成 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-dkmsAUR 和 zfs-utilsAUR。
- 创建初始 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)
base udev at the beginning of the HOOKS array instead of systemd. 安装并配置启动引导加载器
- Add ZFS to your kernel command line
You can now set up your boot loader. You also need to add a kernel parameter to make ZFS bootable:
root=ZFS=zroot/ROOT/default rw
配置 systemd ZFS 挂载
要确保您的系统能够正常地重新启动,您需要启用 zfs.target 以便自动挂载存储池与生成 hostid。
- 这一节内容的前提是假设您仍在
arch-chroot环境中。 - Usage of zpool.cache is still required by zfs-utils to achieve a bootable system, although zpool.cache has been considered a candidate for deprecation [See: https://github.com/openzfs/zfs/issues/1035], however at this time, it is still the default and recommended method to achieving a bootable system.
分别为每个您想要自动挂载的存储池执行:
# 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 # zfs umount zroot/ROOT/default # 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。
Zpool refuses to export saying it's busy
Arch-chroot will mount specific kernelspace file systems in the system. If these are not unmounted, the zpool may refuse to dismount properly. If this happens, remount the ZFS partition and run findmnt -R /mnt
Then run umount -f /path/to/partition against the partition still mounted.
This should allow the zpool to export.