构建以 ZFS 为根文件系统的 openSUSE Leap
概览
注意事项
本教程使用整块物理磁盘。
本教程不适用于双系统。
备份你的数据。一切既有数据都将丢失。
这不是 openSUSE 官方教程。如果后续 openSUSE 新增了 根文件系统的 ZFS 支持,本文档将进行更新。此外,openSUSE 的默认系统安装器 YaST2 不支持 ZFS。本教程将在不使用 YaST2 的情况下,通过 zypper 设置系统的方法,基于社区中人员经验所撰写的 openSUSE 安装方法。有关更多信息,请查看外部链接。
系统要求
在提供 4 KiB 逻辑扇区(“4Kn”)的驱动器上进行的安装仅在使用 UEFI 启动时可行。这并非 ZFS 独有的问题。在 4Kn 磁盘上使用传统(BIOS)启动时,GRUB 不能也不会工作。
在内存少于 2 GiB 的计算机运行 ZFS 时,存在严重的性能瓶颈。对于基础工作负载,建议至少使用 4 GiB 内存以获得正常性能。如果你希望使用去重功能,将需要大量的内存。启用去重是一项永久性更改,无法轻易还原。
加密
本指南支持三种不同的加密方案:未加密、ZFS 原生加密、以及 LUKS。无论选择哪一种,都能完全使用一切 ZFS 特性。
未加密当然不会加密任何内容。在没有进行任何加密的情况下,该方案自然具有最佳性能。
ZFS 原生加密会对根池中的数据和大多数元数据进行加密。它不会加密数据集或快照的名称或属性。启动池完全不加密,但其中只包含 bootloader、kernel 和 initrd。(除非你在 /etc/fstab 中放置了密码,否则 initrd 不太可能包含敏感数据。)系统在未在控制台输入口令的情况下无法启动。性能良好。由于加密是在 ZFS 内部完成的,即使使用了多个磁盘(mirror 或 raidz 拓扑),数据也只需要加密一次。
LUKS 会加密几乎所有内容。唯一未加密的数据是 bootloader、kernel 和 initrd。系统在未在控制台输入口令的情况下无法启动。性能良好,但 LUKS 位于 ZFS 底层,因此如果使用了多个磁盘(mirror 或 raidz 拓扑),数据需要在每个磁盘上各加密一次。
注意
你可以使用非官方脚本 LroZ(Linux Root On Zfs),它基于本手册并自动化了大多数步骤。
第 1 步:准备安装环境
启动 openSUSE Live CD。如果出现提示时,请使用用户名
linux登录,空密码。根据需要将系统连接到互联网(例如加入你的 WiFi 网络)。打开终端。检查你的 openSUSE Leap 发行版:
设置更新软件仓库:
可选:在 Live CD 环境中安装并启动 OpenSSH 服务器: 如果你有第二台系统,使用 SSH 访问目标系统会更加方便:
提示:
你可以使用
ip addr show scope global | grep inet查找你的 IP 地址。然后,在你的主机上,通过ssh user@IP进行连接。不要忘记通过passwd为用户设置密码。禁用自动挂载: 如果磁盘之前被使用过(在相同偏移位置存在分区),在未禁用的情况下,先前的文件系统(例如 ESP)会被自动挂载:
切换为 root:
在 Live CD 环境中安装 ZFS:
第 2 步:格式化磁盘
设置磁盘变量:
在 ZFS 中始终使用长别名
/dev/disk/by-id/*。直接使用设备节点/dev/sd*可能导致偶发的导入失败,尤其是在系统中存在多个存储池时。提示:
ls -la /dev/disk/by-id会列出别名。你是在虚拟机中操作吗?如果虚拟磁盘在
/dev/disk/by-id中缺失,使用 KVM virtio 时可用/dev/vda;否则,请参考故障排查部分。
如果重复使用磁盘,根据需要清理: 如果磁盘之前用于 MD 阵列:
清除分区表:
如果收到内核仍在使用旧分区表的提示,可以请求内核重新加载分区信息:
如果新分区仍未显示,可重启并重新开始(此步骤可跳过)。
分区你的磁盘: 如果需要传统(BIOS)启动,运行:
如果需要 UEFI 启动(现在或将来使用),运行:
为启动池创建分区:
选择以下之一:
未加密或 ZFS 原生加密:
LUKS:
提示:
如果创建镜像或 raidz 拓扑,请对将加入池的所有磁盘重复分区命令。
创建启动池:
对启动池无需自定义选项。
GRUB 不支持所有的 zpool 功能。请参见 grub-core/fs/zfs/zfs.c 中的
spa_feature_names。 此步骤为/boot创建单独的启动池,其功能仅限于 GRUB 支持的功能,从而能够让根池使用任意功能。注意 GRUB 以只读方式打开池,因此所有只读兼容功能均被 GRUB“支持”。提示:
如果创建镜像拓扑,请使用:
对于 raidz 拓扑,将上面命令中的
mirror替换为raidz、raidz2或raidz3,并列出其他磁盘的分区。池名称可自定义。若更改,新名称必须保持一致使用。
bpool命名约定来源于本教程。
功能说明:
allocation_classes功能安全可用。但除非使用它(如specialvdev),否则启用它无意义。几乎不会有人在启动池使用该功能。如果关注启动池速度,将整个池放在更快的磁盘上比使用specialvdev 更合理。project_quota功能经过测试,安全可用。对启动池而言几乎无实际意义。resilver_defer功能安全,但启动池容量较小,通常无需使用。spacemap_v2功能经过测试,安全可用。启动池容量较小,实际影响不大。作为只读兼容功能,
userobj_accounting理论上兼容,但在实践中 GRUB 可能报错“invalid dnode type”。此功能对/boot无关紧要。
创建根池: 选择以下方案之一:
未加密:
ZFS 原生加密:
LUKS:
备注:
这里推荐使用
ashift=12,因为如今许多硬盘的物理扇区为 4 KiB(或更大),即使它们显示为 512 B 的逻辑扇区。此外,未来更换的硬盘可能是 4 KiB 物理扇区(此时ashift=12是可取的)或 4 KiB 逻辑扇区(此时ashift=12是必须的)。设置
-O acltype=posixacl可全局启用 POSIX ACL。如果不想启用此选项,可删除它,但随后需要在/var/log的zfs create中添加-o acltype=posixacl(注意小写“o”),因为 journald 需要 ACL。设置
normalization=formD可消除一些与 UTF-8 文件名规范化相关的极端情况。它还隐含utf8only=on,意味着只允许 UTF-8 文件名。如果你希望支持非 UTF-8 文件名,请不要使用此选项。关于强制仅使用 UTF-8 文件名可能存在的问题,请参见 The problems with enforced UTF-8 only filenames。设置
relatime=on在经典 POSIXatime行为(性能影响显著)与atime=off(完全禁用 atime 更新来获得最佳性能)之间提供折中。自 Linux 2.6.30 起,relatime成为其他文件系统的默认设置。更多信息请参见 RedHat 文档。设置
xattr=sa大幅提高扩展属性的性能。在 ZFS 内部,扩展属性用于实现 POSIX ACL。扩展属性也可被用户空间应用使用,一些桌面 GUI 应用会使用它们;Samba 可使用它们存储 Windows ACL 和 DOS 属性;Samba Active Directory 域控制器需要它们。注意xattr=sa是 Linux 特定 的。如果将xattr=sa池迁移到 ZFS-on-Linux 以外的 OpenZFS 实现,扩展属性将无法读取(数据仍可用)。如果扩展属性的可移植性对你重要,请省略上面的-O xattr=sa。即便不希望整个池使用xattr=sa,在/var/log上使用通常是安全的。确保包括驱动路径的
-part4部分。如果忘记,将指定整个磁盘,ZFS 会重新分区,你将丢失启动器分区。ZFS 原生加密 现 默认使用
aes-256-gcm。对于 LUKS,选择的密钥大小为 512 位。但 XTS 模式需要两个密钥,因此 LUKS 密钥被一分为二。因此
-s 512即表示 AES-256。你的口令很可能是最薄弱环节。请谨慎选择。参考 cryptsetup FAQ 第 5 节 获取指导。
提示:
如果你创建镜像拓扑,请使用以下命令创建池:
对于 raidz 拓扑,将上面命令中的
mirror替换为raidz、raidz2或raidz3,并列出其他磁盘的分区。使用 LUKS 配合镜像或 raidz 拓扑时,请使用
/dev/mapper/luks1、/dev/mapper/luks2等,这些需要通过cryptsetup创建。可自定义存储池的名称。如果更改,新名称必须保持一致。在可以自动安装到 ZFS 的系统中,根池默认命名为
rpool。如果想使用 grub 引导加载器,必须为根池设置:
适用于 grub 2.04 和 Leap 15.3。不要对该池执行 zpool upgrade,否则将失去使用 grub2-install 命令的能力。
第 3 步:系统安装
创建用于容器的文件系统数据集:
在 Solaris 系统中,根文件系统会被克隆,并通过
pkg image-update或beadm对后缀进行递增以应对重大系统更改。类似功能已在 Ubuntu 20.04 中通过zsys工具实现,尽管其数据集布局更复杂。即使没有此类工具,仍可手动创建 rpool/ROOT 和 bpool/BOOT 容器的克隆。不过,为简化操作,本教程假设/boot使用单一文件系统。为根文件系统和启动文件系统创建数据集:
对于 ZFS,通常不需要使用挂载命令(
mount或zfs mount)。此处因canmount=noauto属于例外情况。创建其他数据集:
以下数据集为可选,取决于你的偏好及软件选择。 如果希望这些数据集不被快照:
如果系统使用
/opt:如果系统使用
/srv:如果系统使用
/usr/local:如果系统将安装游戏:
如果系统将存储本地邮件在
/var/mail:如果系统将使用 Snap 包:
如果系统将使用 Flatpak 包:
如果系统使用
/var/www:如果系统使用 GNOME:
如果系统使用 Docker(自行管理数据集和快照):
如果系统使用 NFS(锁定):
在
/run挂载 tmpfs:后续推荐使用 tmpfs,但如果希望为
/tmp创建独立数据集:此数据集布局的主要目标是将操作系统与用户数据分离,从而可回滚根文件系统而不影响用户数据。
如果不做额外操作,
/tmp将作为根文件系统的一部分存储。或者,可以如上创建独立的/tmp数据集,将/tmp数据排除在根文件系统快照之外。同时,可对rpool/tmp设置配额以限制最大使用空间。否则,可在后续使用 tmpfs(内存文件系统)。复制
zpool.cache:
第 4 步:安装系统
将软件源添加到 chroot 目录:
生成软件源索引:
你会收到指纹异常提示,输入
a表示始终信任并继续:使用 zypper 安装 openSUSE Leap: 如果只安装基本的 pattern,zypper 会安装 busybox-grep,从而屏蔽默认内核包。因此建议新手安装 enhanced_base pattern。但 enhanced_base 包含的冗余包可能会在服务器环境中带来困扰。你需要根据用途选择:
使用 zypper 安装 openSUSE Leap 的基础包(推荐服务器环境):
使用 zypper 安装 openSUSE Leap 的增强基础包(推荐桌面环境):
在 chroot 中安装 openSUSE zypper 包管理系统:
推荐:在 chroot 中安装 openSUSE YaST2 系统:
这将方便初学者配置网络及其他系统设置。
如需安装桌面环境,请参见 openSUSE wiki。
第 5 步:系统配置
配置主机名: 将
主机名替换为你希望使用的主机名:添加一行:
或者,如果系统在 DNS 中有完整域名:
提示:
如果觉得
vi难用,可以使用nano。复制网络信息:
后续将使用 YaST2 重新配置网络。
将 LiveCD 环境的虚拟文件系统绑定到新系统并进入 chroot:
注意:
这里使用的是
--rbind而非--bind。配置基本系统环境:
即使你偏好非英语系统语言,也要确保
en_US.UTF-8可用:输出中必须包含以下语言:
C
C.utf8
en_US.utf8
POSIX
从
locale -a输出中找到所需语言,然后执行:可选:重新安装以增强稳定性: 安装后可能需要重新安装某些软件包以修复小问题。openSUSE 没有类似
dpkg-reconfigure的命令,可用zypper install -f作为替代:安装内核:
注意:
如果安装了 base pattern,需要卸载 busybox-grep 才能安装 kernel-default 包。
在 chroot 环境中为新系统安装 ZFS:
如果系统使用 UEFI 和安全启动,自 openSUSE Leap 15.2 起,内核要求所有内核模块必须进行签名。
filesystems项目中的 ZFS 内核模块已签名,但不是系统首次启动时自动注册的官方 openSUSE 密钥。为了确保系统信任该签名密钥,请安装:下次启动时,MOK 会提示你注册新密钥。
仅对 LUKS 安装:配置
/etc/crypttab:使用
initramfs是为了解决 cryptsetup 不支持 ZFS 的问题。提示:
如果创建 mirror 或 raidz 拓扑,为
luks2等磁盘重复添加/etc/crypttab条目。仅对 LUKS 安装:修正 ZFS 的 cryptsetup 命名:
推荐:生成并设置 hostid:
检查生成的 hostid 是否与系统 hostid 匹配:
安装 GRUB: 选择一种启动方式:
安装 GRUB 用于 legacy(BIOS)启动:
如果处理器是 32 位,使用
grub2-i386-pc。安装 GRUB 用于 UEFI 启动:
注意:
对于 4Kn 驱动器,
-s 1是为满足 FAT32 最小簇大小(512 MiB 分区)要求。512 B 扇区的驱动器也可正常使用。对于 mirror 或 raidz 拓扑,这一步只在第一块磁盘上安装 GRUB,其他磁盘稍后处理。
可选:移除 os-prober:
仅在双系统环境中需要 os-prober,否则可避免 update-bootloader 报错。
设置 root 密码:
启用 bpool 导入:
确保始终能导入
bpool,无论是否存在/etc/zfs/zpool.cache,是否启用zfs-import-scan.service。内容如下:
可选(但推荐):将
/tmp挂载为 tmpfs如果之前创建了
/tmp数据集,则跳过此步,否则可以通过启用tmp.mount单元将/tmp放在 tmpfs(内存文件系统)上:
第 6 步:内核安装
将 zfs 模块添加到 dracut:
LiveCD 的内核版本可能与新系统不同。获取新系统内核版本:
刷新内核文件:
刷新 initrd 文件:
注意:
在某些设备中,dracut 可能无法识别 LUKS 分区,会出现报错“Failure occurred during following action: configuring encrypted DM device X VOLUME_CRYPTSETUP_FAILED” 。 解决方法:检查 cryptsetup 的安装情况。详细信息
注意:
即使已将 zfs 配置添加到系统模块
/etc/modules.d,若 dracut 未识别,还需强制添加:
第 7 步:安装 Grub2
验证 ZFS 启动文件系统是否被识别:
输出必须为
zfs。如果
grub2-probe命令出现问题,可执行:然后返回执行
grub2-probe步骤。可选(但强烈建议):便于调试 GRUB:
删除
GRUB_CMDLINE_LINUX_DEFAULT中的quiet取消注释:
GRUB_TERMINAL=console保存并退出
系统重启两次并确认一切正常后,可根据需要恢复这些更改。
更新启动配置:
注意:
如果出现
os-prober错误可忽略。注意:
如果安装 grub2 时出现问题,可考虑使用 systemd-boot。
注意:
如果命令无输出,可使用经典的
grub.cfg生成:检查
/boot/grub2/grub.cfg中是否包含root=ZFS=rpool/ROOT/suse的menuentry,如:如果没有,修改
/etc/default/grub:然后重复上一步。
安装启动加载器:
对于 legacy(BIOS)启动,将 GRUB 安装到 MBR:
注意
这里是将 GRUB 安装到整个磁盘,而非单个分区。
如果创建了 mirror 或 raidz 拓扑,需要对池中每块磁盘重复执行该命令。
对于 UEFI 启动,将 GRUB 安装到 ESP 分区:
不需要指定磁盘。如果创建了 mirror 或 raidz 拓扑,额外磁盘稍后处理。
第 8 步:Systemd-Boot 安装
警告:
这将破坏你的 YaST2 启动加载器配置。仅在无法通过 grub2 修复问题时才使用此方法。本步骤是为某些情况下 grub2 无法识别 rpool 池而提供的替代方案。
安装 systemd-boot:
注意: 如果上一步提示“Failed to get machine id: No medium found”,需要先执行:
然后重复安装 systemd-boot。
配置启动加载器:
创建启动项:
复制内核和 initrd 文件到 EFI 分区:
更新 systemd-boot 变量:
第 9 步:文件系统配置
修复文件系统挂载顺序:
激活
zfs-mount-generator,让 systemd 识别各个挂载点,这对/var/log、/var/tmp等目录至关重要。rsyslog.service会依赖var-log.mount,使用 systemdPrivateTmp特性的服务会自动依赖var-tmp.mount。验证
zed是否更新了缓存,确保以下文件非空:如果文件为空,强制更新缓存并重新检查:
如果仍为空,先停止
zed:然后重新启动
zed并重试。修正缓存路径,去掉前缀
/mnt:
第 10 步:首次启动
可选:安装 SSH 服务:
可选:对初始安装创建快照:
以后在每次升级前,你可能希望先创建快照,并在适当的时候删除旧快照(包括这个初始快照)以节省空间。
退出
chroot环境,返回 LiveCD 环境:在 LiveCD 环境中执行以下命令卸载所有文件系统:
重启系统:
等待新安装的系统正常启动,并以 root 登录。
创建用户账户: 将
用户名替换为你希望的用户名:镜像安装 GRUB(多磁盘安装时):
如果系统安装在多个磁盘上,需要在其他磁盘上重复安装 GRUB。
对于 legacy(BIOS)启动:先确认当前是否为 EFI 模式:
输出必须包含
legacy_boot。然后重新配置 GRUB:按回车直到出现设备选择界面,使用空格键选择池中的所有磁盘(不是分区)。
对于 UEFI 启动:
对第二块及后续磁盘(将 debian-2 改为 -3 等):
第 11 步(可选):配置 Swap
注意:
在内存压力极高的系统上,无论 swap 空间剩余多少,使用 zvol 作为 swap 设备都可能导致系统锁死。上游已有相关 bug 报告。
创建用于 swap 的 zvol(卷数据集):
你可以根据需求调整大小(这里为
4G)。压缩算法使用
zle,因为这是开销最小的算法。由于本指南推荐ashift=12(磁盘上 4 KiB 块),4 KiB 页面大小情况下,没有压缩算法能减少 I/O。唯一例外是全零页面,会被 ZFS 丢弃,但必须启用某种压缩才能实现这一行为。配置 swap 设备:
注意:
在配置文件中始终使用完整
/dev/zvol别名,切勿使用短设备名/dev/zdX。RESUME=none用于禁用休眠恢复。由于 zvol 在启动时尚未导入(池未导入),恢复脚本无法找到 swap,如果不禁用,启动过程会停滞约 30 秒等待 swap zvol 出现。启用 swap 设备:
第 12 步:最终清理
等待系统正常启动,使用你创建的用户账号登录。确保系统(包括网络)工作正常。
可选:删除初始安装的快照:
可选:禁用 root 密码:
可选(强烈建议):禁用 root SSH 登录:
如果之前安装过 SSH,请撤销临时修改:
可选:重新启用图形启动过程:
如果你希望使用图形启动,并且使用了 LUKS,这可以让提示界面更美观。
注意:
如果出现
osprober错误,可忽略。可选:仅针对 LUKS 安装,备份 LUKS 头:
将备份妥善保存(如云端存储)。该备份受 LUKS 密码保护,但你也可选择额外加密。
提示:
如果创建了镜像或 raidz 拓扑,请对每个 LUKS 卷重复此操作(如
luks2等)。
故障排除
使用 Live CD 进行救援
请参考 第 1 步:准备安装环境。
对于 LUKS 安装,首先解锁磁盘:
正确挂载所有文件系统:
如有需要,可以进入已安装的系统环境进行 chroot:
进行必要的修复操作。
完成后,进行清理:
Areca
需要 arcsas blob 驱动的系统应将其添加到 /etc/initramfs-tools/modules 文件中,然后运行:
如果内核日志中出现类似以下信息:
请升级或降级 Areca 驱动。在出现此错误的系统上 ZoL 将不稳定。
MPT2SAS
本教程中大多数问题报告都涉及 mpt2sas 硬件,这类硬件会延迟异步初始化驱动器,例如某些 IBM M1015 或已刷回参考 LSI 固件的 OEM 卡。
基本问题是,这些控制器上的磁盘在 Linux 内核启动时不可见,而 ZoL 不支持热插拔池成员。详见:https://github.com/zfsonlinux/zfs/issues/330。
大多数 LSI 卡完全兼容 ZoL。如果你的 LSI 卡存在此问题,可在 /etc/default/zfs 中设置:
系统将在导入池之前等待 X 秒,确保所有磁盘都已出现。
QEMU/KVM/XEN
为每个虚拟磁盘设置唯一序列号(使用 libvirt 或 qemu,例如:-drive if=none,id=disk1,file=disk1.qcow2,serial=1234567890)。
要在虚拟机中使用 UEFI(而不仅仅是 BIOS 启动),请在宿主机上执行:
取消对以下行的注释:
重启 libvirt 服务:
VMware
在虚拟机的
.vmx文件或 vSphere 配置中设置:
这样可以确保在客机中创建别名 /dev/disk。
外部链接
最后更新于