跳转到内容

为应对本站因各种原因无法访问,建议用户安装 arch-wiki-docs-zh-cnCNRepo 或者 arch-wiki-docs-zh-twCNRepo 离线文档包备用,也可尝试使用经由Cloudflare CDN的替代版本aw.lilydjwg.me

在 ZFS 上安装 Arch Linux

来自 Arch Linux 中文维基

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

创建自定义 ISO

[编辑 | 编辑源代码]

要在 ZFS 上安装 Arch Linux,您需要使用带有 ZFS 内核模块安装介质

请按照 archiso#安装 的步骤操作。

接下来,依照 archiso#准备自定义配置文件 来准备一个自定义的配置文件(下文中以 archlive 为例),另请参阅 archiso#ArchZFS_实例

下一步,编辑软件包列表 packages.x86_64 并在其中加入 linux-lts 内核与下列 ZFS 软件包:

本文或本章节的事实准确性存在争议。

原因: 为何需要 libunwind?如果有任何 ZFS 软件包需要它,则它应该已经是相关软件包的依赖项。(在 Talk:在 ZFS 上安装 Arch Linux 中讨论)


packages.x86_64
...
linux-lts
linux-lts-headers
libunwind
zfs-utils
zfs-dkms

请确保从列表中移除 linuxbroadcom-wl (后者会拉取 linux 包),并将 airootfs/etc/mkinitcpio.d/linux.preset 删除。

您还需要编辑 pacman.conf 并在其中加入:

本文或本章节可能需要合并到非官方用户仓库#archzfs

附注: 重复关于 pacman.conf 的相关教程没有意义,写明指向非官方用户仓库#archzfs 的链接即可,否则用户可能重复更改 Server 部分。(在 Talk:在 ZFS 上安装 Arch Linux 中讨论)
...
[archzfs]
SigLevel = TrustAll Optional
Server = https://github.com/archzfs/archzfs/releases/download/experimental

您还需要编辑引导加载器的配置文件,以将 vmlinuz-linuxinitramfs-linux.img 的条目分别改为 vmlinuz-linux-ltsinitramfs-linux-lts.img:

$ sed -i -E 's/(vmlinuz|initramfs)-linux/&-lts/g' archlive/efiboot/loader/entries/*.conf archlive/syslinux/*.cfg archlive/grub/*.cfg

接下来,开始镜像的构建过程:

$ mkarchiso -v -r -w /tmp/archiso-tmp -o isobuild ~/archlive

现在,您的 archlive/isobuild 目录下应该已经出现了archlinux-YYYY.MM.DD.x86_64.iso

请将该文件烧录至您选择使用的安装介质。

注意:
  • 如果在系统更新后,zpool status -v 的输出显示您的存储池需要升级以支持新的特性,您可能需要重新制作上述安装介质。
  • 在实际安装时,您应该使用 linux-ltslinux-lts-headers 来保证最大兼容性。
  • 您应从非官方的 archzfs 仓库而非 AUR 获取 zfs-utilszfs-dkms 的更新。如果您选择使用 AUR 中的版本,则可能出现当前的主线或 zen 内核不被 ZFS 支持的情况,从而导致您的系统无法启动。因此,在实际操作时,建议您使用 linux-lts 内核。

请确保先在虚拟机中测试您的 ISO。只需运行:

# modprobe zfs
# zpool status

如果命令执行失败,则您的 ZFS 模块并未成功构建,您需要重新尝试。

警告:请勿分发您的自定义 ISO 镜像,否则您将违反 GNU 通用公共许可证 (GPL) 与通用开发和发行许可证 (CDDL)。

ZFS 支持 GUID 分区图与主引导记录分区表。要决定使用哪种分区图,请参考分区#选择_GPT_还是_MBR

提示:
  • 虽然您可以为旧设备创建基于主引导记录的可引导 ZFS 根分区,但更建议您使用 GUID 分区图。
  • 推荐您使用单独的 /boot 分区以避免有关引导加载程序的问题,同时保证最好的兼容性。

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

Part     Size   Type
----     ----   -------------------------
   1       1G   EFI system partition (ef00)
   2     XXXG   Solaris Root (bf00)

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

Part     Size   Type
----     ----   -------------------------
   1       2M   BIOS boot partition (ef02)
   2       1G   Linux Partition (8300)
   3     XXXG   Solaris Root (bf00)

您可以选择创建独立的 linux 交换分区或将 zvol 用作交换空间,后者详见 ZFS#交换卷

如果您想要创建一个传统的交换分区,详见 分区#布局示例

若您创建了单独的 boot 分区以及其他非 ZFS 分区,请将其格式化

设置 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
警告: 接下来创建存储池时,如果使用例如 /dev/nvme0n1p2 的一般设备名而不是 /dev/disk/by-id/<device number> 来创建存储池,则有可能会使存储池的导入出现问题。若要在下一部分开始前知晓您设备的正确 by-id 名称,请运行:

ls -lh /dev/disk/by-id/

并记录您将要使用的正确 by-id 名称,而非标准设备名,如:

lrwxrwxrwx 1 root root 9 Aug 12 16:26 ata-ST3000DM001-9YN166_S1F0JKRR -> ../../sdc

使用压缩与 TRIM

[编辑 | 编辑源代码]
# zpool create -f -o ashift=12         \
             -o autotrim=on            \
             -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        \
             zroot /dev/disk/by-id/id-to-partition-partx

使用压缩, TRIM 与原生加密

[编辑 | 编辑源代码]

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

# zpool create -f -o ashift=12         \
             -o autotrim=on            \
             -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
注意:

前述所有选项对 GNU/Linux 系统都有意义,而且它们中的大部分在根文件系统上必须被使用,尤其是 canmountmountpoint,这点与通常的存储设备设置方式不同。xattr, dnodesizenormalization 为可选项。

加密与压缩为可选项,但本教程推荐您启用压缩。为了在数据无损压缩存储的情况下避免性能损失,建议使用 lz4 算法。较为均衡的 zstd 算法是默认压缩选项。

您可以在手册页面 zfsprops(7)找到关于这些选项的详细解释。

-O 之后的参数将控制 ZFS 的行为。 您可以在 zfsprops(7) 手册页面中找到关于这些参数的详细解释。

警告:由于在不使用 spl hostid 与 zpool.cache 文件的情况下导入 ZFS 存储池存在较多问题,且可能会导致存储池损坏,本教程不使用 canmount=on 选项。如果您未设置 spl hostid 并使用 zfs-zed 加载您的存储池,您在跨系统使用存储池的情况下将面临存储池损坏的风险。
警告:
  • 使用 ZFS 时应始终使用设备的 id 名称,否则导入存储池时会发生错误。
  • 除使用 by-id 名称外,也可以考虑使用 by-partuuidby-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选项。

示例请参见 在 ZFS 上安装 Debian Buster # 第三步:安装系统

注意:若要将如 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
注意:systemd-journald 需要您为 /var/log/journal/ 启用 POSIX 访问控制列表,即为该目录所在的数据集设置 acltype=posix,否则 systemd-journald.service 将无法启动。详见 systemd#systemd-tmpfiles-setup.service在系统启动时失败

导出并导入您的存储池

[编辑 | 编辑源代码]

要验证您的设置,将您所有的 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 -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 操作时使用 linux-lts 作为您的内核。同时请确保使用 pacstrap 安装 libunwind 从而为 zfs-utilsAUR 解析依赖项目。
  • 安装指南#生成 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。
注意:出于简洁性以及兼容性考虑,本指南建议您使用 zfs-dkmsAURlinux-lts
  • 创建初始 ramdisk 前,请先编辑 /etc/mkinitcpio.conf,并将 zfs 加至 MODULES:
MODULES=(zfs)

接下来在 HOOKS 中将 zfs 加至 filesystems 前,并将 systemd 切换为 udev。您的 HOOKS 一行看起来应类似如下示例:

HOOKS=(base udev ... block zfs filesystems)
警告:
  • 默认情况下,根文件系统为 ZFS 的情况下,不支持在 initramfs 中使用 systemd。使用 systemd 会导致 root 文件系统无法挂载,从而导致系统无法启动。要解决这个问题,请将 mkinitcpio 使用的所有 systemd HOOK 更换为 mkinitcpio#常用钩子中描述的基于 Busybox 的 hook。如果您仍然想要在 initramfs 中使用 systemd,请阅读下文中关于 mkinitcpio-sd-zfsAUR 的信息。
注意:
  • zfs-utilsAUR 提供了一个开箱即用且适用于基于 systemd 的 initramfs 的 dracut hook。
  • zfs-utilsAUR 提供的 mkinitcpio 的 ZFS hook 仅兼容基于 busybox 的 initramfs 镜像。
  • 对于使用了 ZFS 加密的用户,建议您将 keyboard 移至 autodetect 前,以防止在输入密码时出现问题。
  • mkinitcpio-sd-zfsAUR 提供了与 systemd 兼容的 mkinitcpio initramfs 的 ZFS hook。请注意,"sd-zfs" hook 未经过完整测试,并且可能不兼容启用了加密的文件系统。
  • 将 ZFS 添加至您的内核参数。

现在,请设置您的 引导加载程序。要使得 ZFS 可以启动,您还需要以下的内核参数

root=ZFS=zroot/ROOT/default rw

设置 hostid

[编辑 | 编辑源代码]

为了使您的系统能够顺利启动,您还需要设置 hostid。

注意:
  • 本章节的内容假设您仍处在 arch-chroot 环境中。
  • 虽然 zpool.cache 正待废弃,但zfs-utils 仍需要 zpool.cache 来获得可以启动的系统,故现在这仍是取得可启动的系统的默认以及建议方式。[详见: https://github.com/openzfs/zfs/issues/1035]

对每个您想自动挂载的存储池运行以下命令:

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

当根目录文件系统为 ZFS 时,本机的 hostid 在挂载时将不可见。解决方案有两种。您可以在引导加载程序中将您的 spl hostid 写入 内核参数中,例如 spl.spl_hostid=0x00bab10c。要确定您的 hostid,请运行 hostid

另一种,同时也是推荐的解决方案,是保证 /etc/hostid 中写入了 hostid,并重新生成 initramfs 以将其拷贝至 initramfs 镜像内。要安全地写入 hostid 文件,请使用 zgenhostid

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

# zgenhostid $(hostid)

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

# zgenhostid deadbeef

完成后请勿忘记重新生成 initramfs

配置 ZFS 挂载 (适用于 systemd)

[编辑 | 编辑源代码]
注意:

这一章节仅适用于 systemd,即 Arch Linux 默认使用的 init 系统。

要自动挂载存储池,您需要启用 zfs.target

启用 zfs.target

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

配置 ZFS 挂载 (适用于 OpenRC)

[编辑 | 编辑源代码]
警告:

本章节适用于 OpenRC init 与服务管理系统的用户。OpenRC 仅在 AUR 可用,Arch Linux 对其没有官方支持。根据 OpenRC 页面,当向他人求助时,您应声明自己在使用 OpenRC。

注意:

虽然 Arch Linux 的默认 init 系统是 systemd,本章节仍出于文档完整性的目的提供。

您需要将 zfs-importzfs-mount init 脚本添加至 boot runlevel。

# rc-update add zfs-import boot
# rc-update add zfs-mount boot
注意:

如果您启用了 ZFS 加密并且希望在启动时解锁您的 ZFS 分区,还应将 zfs-load-key 添加至 boot runlevel。

# rc-update add zfs-load-key boot

卸载文件系统并重新启动

[编辑 | 编辑源代码]

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

# umount /mnt/boot 

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

# zfs umount -a
# zfs umount zroot/ROOT/default
# 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
  • 移除 /etc/hostid
  • 重新生成 initramfs

zpool 因为忙碌而拒绝导出

[编辑 | 编辑源代码]

Arch-chroot 会在系统里挂在特定的内核空间文件系统。如果没有卸载这些文件系统,存储池可能会拒绝导出。此时,请重新挂载 ZFS 分区,并运行 findmnt -R /mnt

然后运行对于每个仍然挂载的文件系统,运行 umount -f /path/to/partition

之后您应当能够正常导出您的存储池。

另请参阅

[编辑 | 编辑源代码]