github编辑

构建以 ZFS 为根文件系统的 openSUSE Leap

概览

注意事项

  • 本教程使用整块物理磁盘。

  • 本教程不适用于双系统。

  • 备份你的数据。一切既有数据都将丢失。

  • 这不是 openSUSE 官方教程。如果后续 openSUSE 新增了 根文件系统的 ZFS 支持,本文档将进行更新。此外,openSUSE 的默认系统安装器 YaST2 不支持 ZFSarrow-up-right。本教程将在不使用 YaST2 的情况下,通过 zypper 设置系统的方法,基于社区中人员经验所撰写的 openSUSE 安装方法。有关更多信息,请查看外部链接。

系统要求

在内存少于 2 GiB 的计算机运行 ZFS 时,存在严重的性能瓶颈。对于基础工作负载,建议至少使用 4 GiB 内存以获得正常性能。如果你希望使用去重功能,将需要大量的内存arrow-up-right。启用去重是一项永久性更改,无法轻易还原。

加密

本指南支持三种不同的加密方案:未加密、ZFS 原生加密、以及 LUKS。无论选择哪一种,都能完全使用一切 ZFS 特性。

未加密当然不会加密任何内容。在没有进行任何加密的情况下,该方案自然具有最佳性能。

ZFS 原生加密会对根池中的数据和大多数元数据进行加密。它不会加密数据集或快照的名称或属性。启动池完全不加密,但其中只包含 bootloader、kernel 和 initrd。(除非你在 /etc/fstab 中放置了密码,否则 initrd 不太可能包含敏感数据。)系统在未在控制台输入口令的情况下无法启动。性能良好。由于加密是在 ZFS 内部完成的,即使使用了多个磁盘(mirror 或 raidz 拓扑),数据也只需要加密一次。

LUKS 会加密几乎所有内容。唯一未加密的数据是 bootloader、kernel 和 initrd。系统在未在控制台输入口令的情况下无法启动。性能良好,但 LUKS 位于 ZFS 底层,因此如果使用了多个磁盘(mirror 或 raidz 拓扑),数据需要在每个磁盘上各加密一次。

注意

  • 你可以使用非官方脚本 LroZarrow-up-right(Linux Root On Zfs),它基于本手册并自动化了大多数步骤。

第 1 步:准备安装环境

  1. 启动 openSUSE Live CD。如果出现提示时,请使用用户名 linux 登录,空密码。根据需要将系统连接到互联网(例如加入你的 WiFi 网络)。打开终端。

  2. 检查你的 openSUSE Leap 发行版:

  3. 设置更新软件仓库:

  4. 可选:在 Live CD 环境中安装并启动 OpenSSH 服务器: 如果你有第二台系统,使用 SSH 访问目标系统会更加方便:

    提示:

    你可以使用 ip addr show scope global | grep inet 查找你的 IP 地址。然后,在你的主机上,通过 ssh user@IP 进行连接。不要忘记通过 passwd 为用户设置密码。

  5. 禁用自动挂载: 如果磁盘之前被使用过(在相同偏移位置存在分区),在未禁用的情况下,先前的文件系统(例如 ESP)会被自动挂载:

  6. 切换为 root:

  7. 在 Live CD 环境中安装 ZFS:

第 2 步:格式化磁盘

  1. 设置磁盘变量:

    在 ZFS 中始终使用长别名 /dev/disk/by-id/*。直接使用设备节点 /dev/sd* 可能导致偶发的导入失败,尤其是在系统中存在多个存储池时。

    提示:

    • ls -la /dev/disk/by-id 会列出别名。

    • 你是在虚拟机中操作吗?如果虚拟磁盘在 /dev/disk/by-id 中缺失,使用 KVM virtio 时可用 /dev/vda;否则,请参考故障排查arrow-up-right部分。

  2. 如果重复使用磁盘,根据需要清理: 如果磁盘之前用于 MD 阵列:

    清除分区表:

    如果收到内核仍在使用旧分区表的提示,可以请求内核重新加载分区信息:

    如果新分区仍未显示,可重启并重新开始(此步骤可跳过)。

  3. 分区你的磁盘: 如果需要传统(BIOS)启动,运行:

    如果需要 UEFI 启动(现在或将来使用),运行:

    为启动池创建分区:

    选择以下之一:

    • 未加密或 ZFS 原生加密:

    • LUKS:

    提示:

    • 如果创建镜像或 raidz 拓扑,请对将加入池的所有磁盘重复分区命令。

  4. 创建启动池:

    对启动池无需自定义选项。

    GRUB 不支持所有的 zpool 功能。请参见 grub-core/fs/zfs/zfs.carrow-up-right 中的 spa_feature_names。 此步骤为 /boot 创建单独的启动池,其功能仅限于 GRUB 支持的功能,从而能够让根池使用任意功能。注意 GRUB 以只读方式打开池,因此所有只读兼容功能均被 GRUB“支持”。

    提示:

    • 如果创建镜像拓扑,请使用:

    • 对于 raidz 拓扑,将上面命令中的 mirror 替换为 raidzraidz2raidz3,并列出其他磁盘的分区。

    • 池名称可自定义。若更改,新名称必须保持一致使用。bpool 命名约定来源于本教程。

    功能说明:

    • allocation_classes 功能安全可用。但除非使用它(如 special vdev),否则启用它无意义。几乎不会有人在启动池使用该功能。如果关注启动池速度,将整个池放在更快的磁盘上比使用 special vdev 更合理。

    • project_quota 功能经过测试,安全可用。对启动池而言几乎无实际意义。

    • resilver_defer 功能安全,但启动池容量较小,通常无需使用。

    • spacemap_v2 功能经过测试,安全可用。启动池容量较小,实际影响不大。

    • 作为只读兼容功能,userobj_accounting 理论上兼容,但在实践中 GRUB 可能报错“invalid dnode type”。此功能对 /boot 无关紧要。

  5. 创建根池: 选择以下方案之一:

    • 未加密:

    • ZFS 原生加密:

    • LUKS:

备注:

  • 这里推荐使用 ashift=12,因为如今许多硬盘的物理扇区为 4 KiB(或更大),即使它们显示为 512 B 的逻辑扇区。此外,未来更换的硬盘可能是 4 KiB 物理扇区(此时 ashift=12 是可取的)或 4 KiB 逻辑扇区(此时 ashift=12 是必须的)。

  • 设置 -O acltype=posixacl 可全局启用 POSIX ACL。如果不想启用此选项,可删除它,但随后需要在 /var/logzfs create 中添加 -o acltype=posixacl(注意小写“o”),因为 journald 需要 ACLarrow-up-right

  • 设置 normalization=formD 可消除一些与 UTF-8 文件名规范化相关的极端情况。它还隐含 utf8only=on,意味着只允许 UTF-8 文件名。如果你希望支持非 UTF-8 文件名,请不要使用此选项。关于强制仅使用 UTF-8 文件名可能存在的问题,请参见 The problems with enforced UTF-8 only filenamesarrow-up-right

  • recordsize 未设置(保持默认 128 KiB)。如果想调整(例如 -O recordsize=1M),请参见这些arrow-up-right 博客arrow-up-right 文章arrow-up-right 帖子arrow-up-right

  • 设置 relatime=on 在经典 POSIX atime 行为(性能影响显著)与 atime=off(完全禁用 atime 更新来获得最佳性能)之间提供折中。自 Linux 2.6.30 起,relatime 成为其他文件系统的默认设置。更多信息请参见 RedHat 文档arrow-up-right

  • 设置 xattr=sa 大幅提高扩展属性的性能arrow-up-right。在 ZFS 内部,扩展属性用于实现 POSIX ACL。扩展属性也可被用户空间应用使用,一些桌面 GUI 应用会使用它们arrow-up-rightSamba 可使用它们存储 Windows ACL 和 DOS 属性;Samba Active Directory 域控制器需要它们arrow-up-right。注意 xattr=saLinux 特定arrow-up-right 的。如果将 xattr=sa 池迁移到 ZFS-on-Linux 以外的 OpenZFS 实现,扩展属性将无法读取(数据仍可用)。如果扩展属性的可移植性对你重要,请省略上面的 -O xattr=sa。即便不希望整个池使用 xattr=sa,在 /var/log 上使用通常是安全的。

  • 确保包括驱动路径的 -part4 部分。如果忘记,将指定整个磁盘,ZFS 会重新分区,你将丢失启动器分区。

  • ZFS 原生加密 arrow-up-right 默认使用 aes-256-gcm

  • 对于 LUKS,选择的密钥大小为 512 位。但 XTS 模式需要两个密钥,因此 LUKS 密钥被一分为二。因此 -s 512 即表示 AES-256。

  • 你的口令很可能是最薄弱环节。请谨慎选择。参考 cryptsetup FAQ 第 5 节arrow-up-right 获取指导。

提示:

  • 如果你创建镜像拓扑,请使用以下命令创建池:

  • 对于 raidz 拓扑,将上面命令中的 mirror 替换为 raidzraidz2raidz3,并列出其他磁盘的分区。

  • 使用 LUKS 配合镜像或 raidz 拓扑时,请使用 /dev/mapper/luks1/dev/mapper/luks2 等,这些需要通过 cryptsetup 创建。

  • 可自定义存储池的名称。如果更改,新名称必须保持一致。在可以自动安装到 ZFS 的系统中,根池默认命名为 rpool

  • 如果想使用 grub 引导加载器,必须为根池设置:

    适用于 grub 2.04 和 Leap 15.3。不要对该池执行 zpool upgrade,否则将失去使用 grub2-install 命令的能力。

第 3 步:系统安装

  1. 创建用于容器的文件系统数据集:

    在 Solaris 系统中,根文件系统会被克隆,并通过 pkg image-updatebeadm 对后缀进行递增以应对重大系统更改。类似功能已在 Ubuntu 20.04 中通过 zsys 工具实现,尽管其数据集布局更复杂。即使没有此类工具,仍可手动创建 rpool/ROOT 和 bpool/BOOT 容器的克隆。不过,为简化操作,本教程假设 /boot 使用单一文件系统。

  2. 为根文件系统和启动文件系统创建数据集:

    对于 ZFS,通常不需要使用挂载命令(mountzfs mount)。此处因 canmount=noauto 属于例外情况。

  3. 创建其他数据集:

    以下数据集为可选,取决于你的偏好及软件选择。 如果希望这些数据集不被快照:

    如果系统使用 /opt

    如果系统使用 /srv

    如果系统使用 /usr/local

    如果系统将安装游戏:

    如果系统将存储本地邮件在 /var/mail

    如果系统将使用 Snap 包:

    如果系统将使用 Flatpak 包:

    如果系统使用 /var/www

    如果系统使用 GNOME:

    如果系统使用 Docker(自行管理数据集和快照):

    如果系统使用 NFS(锁定):

    /run 挂载 tmpfs:

    后续推荐使用 tmpfs,但如果希望为 /tmp 创建独立数据集:

    此数据集布局的主要目标是将操作系统与用户数据分离,从而可回滚根文件系统而不影响用户数据。

    如果不做额外操作,/tmp 将作为根文件系统的一部分存储。或者,可以如上创建独立的 /tmp 数据集,将 /tmp 数据排除在根文件系统快照之外。同时,可对 rpool/tmp 设置配额以限制最大使用空间。否则,可在后续使用 tmpfs(内存文件系统)。

  4. 复制 zpool.cache

第 4 步:安装系统

  1. 将软件源添加到 chroot 目录:

  2. 生成软件源索引:

    你会收到指纹异常提示,输入 a 表示始终信任并继续:

  3. 使用 zypper 安装 openSUSE Leap: 如果只安装基本的 pattern,zypper 会安装 busybox-grep,从而屏蔽默认内核包。因此建议新手安装 enhanced_base pattern。但 enhanced_base 包含的冗余包可能会在服务器环境中带来困扰。你需要根据用途选择:

    1. 使用 zypper 安装 openSUSE Leap 的基础包(推荐服务器环境):

    2. 使用 zypper 安装 openSUSE Leap 的增强基础包(推荐桌面环境):

  4. 在 chroot 中安装 openSUSE zypper 包管理系统:

  5. 推荐:在 chroot 中安装 openSUSE YaST2 系统:

    这将方便初学者配置网络及其他系统设置。

如需安装桌面环境,请参见 openSUSE wikiarrow-up-right

第 5 步:系统配置

  1. 配置主机名: 将 主机名 替换为你希望使用的主机名:

    添加一行:

    或者,如果系统在 DNS 中有完整域名:

    提示:

    如果觉得 vi 难用,可以使用 nano

  2. 复制网络信息:

    后续将使用 YaST2 重新配置网络。

  3. 将 LiveCD 环境的虚拟文件系统绑定到新系统并进入 chroot:

    注意:

    这里使用的是 --rbind 而非 --bind

  4. 配置基本系统环境:

    即使你偏好非英语系统语言,也要确保 en_US.UTF-8 可用:

    输出中必须包含以下语言:

    • C

    • C.utf8

    • en_US.utf8

    • POSIX

    locale -a 输出中找到所需语言,然后执行:

  5. 可选:重新安装以增强稳定性: 安装后可能需要重新安装某些软件包以修复小问题。openSUSE 没有类似 dpkg-reconfigure 的命令,可用 zypper install -f 作为替代:

  6. 安装内核:

    注意:

    如果安装了 base pattern,需要卸载 busybox-grep 才能安装 kernel-default 包。

  7. 在 chroot 环境中为新系统安装 ZFS:

    如果系统使用 UEFI 和安全启动,自 openSUSE Leap 15.2 起,内核要求所有内核模块必须进行签名。filesystems 项目中的 ZFS 内核模块已签名,但不是系统首次启动时自动注册的官方 openSUSE 密钥。为了确保系统信任该签名密钥,请安装:

    下次启动时,MOK 会提示你注册新密钥。

  8. 仅对 LUKS 安装:配置 /etc/crypttab

    使用 initramfs 是为了解决 cryptsetup 不支持 ZFSarrow-up-right 的问题。

    提示:

    如果创建 mirror 或 raidz 拓扑,为 luks2 等磁盘重复添加 /etc/crypttab 条目。

  9. 仅对 LUKS 安装:修正 ZFS 的 cryptsetup 命名:

  1. 推荐:生成并设置 hostid:

    检查生成的 hostid 是否与系统 hostid 匹配:

  2. 安装 GRUB: 选择一种启动方式:

    • 安装 GRUB 用于 legacy(BIOS)启动:

      如果处理器是 32 位,使用 grub2-i386-pc

    • 安装 GRUB 用于 UEFI 启动:

      注意:

      • 对于 4Kn 驱动器,-s 1 是为满足 FAT32 最小簇大小(512 MiB 分区)要求。512 B 扇区的驱动器也可正常使用。

      • 对于 mirror 或 raidz 拓扑,这一步只在第一块磁盘上安装 GRUB,其他磁盘稍后处理。

  3. 可选:移除 os-prober:

    仅在双系统环境中需要 os-prober,否则可避免 update-bootloader 报错。

  4. 设置 root 密码:

  5. 启用 bpool 导入:

    确保始终能导入 bpool,无论是否存在 /etc/zfs/zpool.cache,是否启用 zfs-import-scan.service

    内容如下:

  6. 可选(但推荐):将 /tmp 挂载为 tmpfs

    如果之前创建了 /tmp 数据集,则跳过此步,否则可以通过启用 tmp.mount 单元将 /tmp 放在 tmpfs(内存文件系统)上:


第 6 步:内核安装

  1. 将 zfs 模块添加到 dracut:

  2. LiveCD 的内核版本可能与新系统不同。获取新系统内核版本:

  3. 刷新内核文件:

  4. 刷新 initrd 文件:

    注意:

    在某些设备中,dracut 可能无法识别 LUKS 分区,会出现报错“Failure occurred during following action: configuring encrypted DM device X VOLUME_CRYPTSETUP_FAILED” 。 解决方法:检查 cryptsetup 的安装情况。详细信息arrow-up-right

    注意:

    即使已将 zfs 配置添加到系统模块 /etc/modules.d,若 dracut 未识别,还需强制添加:

第 7 步:安装 Grub2

  1. 验证 ZFS 启动文件系统是否被识别:

    输出必须为 zfs

  2. 如果 grub2-probe 命令出现问题,可执行:

    然后返回执行 grub2-probe 步骤。

  3. 可选(但强烈建议):便于调试 GRUB:

    • 删除 GRUB_CMDLINE_LINUX_DEFAULT 中的 quiet

    • 取消注释:GRUB_TERMINAL=console

    • 保存并退出

    系统重启两次并确认一切正常后,可根据需要恢复这些更改。

  4. 更新启动配置:

    注意:

    如果出现 os-prober 错误可忽略。

    注意:

    如果安装 grub2 时出现问题,可考虑使用 systemd-boot。

    注意:

    如果命令无输出,可使用经典的 grub.cfg 生成:

  5. 检查 /boot/grub2/grub.cfg 中是否包含 root=ZFS=rpool/ROOT/susemenuentry,如:

    如果没有,修改 /etc/default/grub

    然后重复上一步。

  6. 安装启动加载器:

    1. 对于 legacy(BIOS)启动,将 GRUB 安装到 MBR:

      注意

      这里是将 GRUB 安装到整个磁盘,而非单个分区。

      如果创建了 mirror 或 raidz 拓扑,需要对池中每块磁盘重复执行该命令。

    2. 对于 UEFI 启动,将 GRUB 安装到 ESP 分区:

      不需要指定磁盘。如果创建了 mirror 或 raidz 拓扑,额外磁盘稍后处理。

第 8 步:Systemd-Boot 安装

警告:

这将破坏你的 YaST2 启动加载器配置。仅在无法通过 grub2 修复问题时才使用此方法。本步骤是为某些情况下 grub2 无法识别 rpool 池而提供的替代方案。

  1. 安装 systemd-boot:

    注意: 如果上一步提示“Failed to get machine id: No medium found”,需要先执行:

    然后重复安装 systemd-boot。

  2. 配置启动加载器:

  3. 创建启动项:

  4. 复制内核和 initrd 文件到 EFI 分区:

  5. 更新 systemd-boot 变量:

第 9 步:文件系统配置

  1. 修复文件系统挂载顺序:

    激活 zfs-mount-generator,让 systemd 识别各个挂载点,这对 /var/log/var/tmp 等目录至关重要。rsyslog.service 会依赖 var-log.mount,使用 systemd PrivateTmp 特性的服务会自动依赖 var-tmp.mount

  2. 验证 zed 是否更新了缓存,确保以下文件非空:

  3. 如果文件为空,强制更新缓存并重新检查:

    如果仍为空,先停止 zed

    然后重新启动 zed 并重试。

  4. 修正缓存路径,去掉前缀 /mnt

第 10 步:首次启动

  1. 可选:安装 SSH 服务:

  2. 可选:对初始安装创建快照:

    以后在每次升级前,你可能希望先创建快照,并在适当的时候删除旧快照(包括这个初始快照)以节省空间。

  3. 退出 chroot 环境,返回 LiveCD 环境:

  4. 在 LiveCD 环境中执行以下命令卸载所有文件系统:

  5. 重启系统:

    等待新安装的系统正常启动,并以 root 登录。

  6. 创建用户账户: 将 用户名 替换为你希望的用户名:

  7. 镜像安装 GRUB(多磁盘安装时):

    如果系统安装在多个磁盘上,需要在其他磁盘上重复安装 GRUB。

    • 对于 legacy(BIOS)启动:先确认当前是否为 EFI 模式:

      输出必须包含 legacy_boot。然后重新配置 GRUB:

      按回车直到出现设备选择界面,使用空格键选择池中的所有磁盘(不是分区)。

    • 对于 UEFI 启动:

      对第二块及后续磁盘(将 debian-2 改为 -3 等):

第 11 步(可选):配置 Swap

注意

在内存压力极高的系统上,无论 swap 空间剩余多少,使用 zvol 作为 swap 设备都可能导致系统锁死。上游已有相关 bug 报告arrow-up-right

  1. 创建用于 swap 的 zvol(卷数据集):

    你可以根据需求调整大小(这里为 4G)。

    压缩算法使用 zle,因为这是开销最小的算法。由于本指南推荐 ashift=12(磁盘上 4 KiB 块),4 KiB 页面大小情况下,没有压缩算法能减少 I/O。唯一例外是全零页面,会被 ZFS 丢弃,但必须启用某种压缩才能实现这一行为。

  2. 配置 swap 设备:

    注意

    在配置文件中始终使用完整 /dev/zvol 别名,切勿使用短设备名 /dev/zdX

    RESUME=none 用于禁用休眠恢复。由于 zvol 在启动时尚未导入(池未导入),恢复脚本无法找到 swap,如果不禁用,启动过程会停滞约 30 秒等待 swap zvol 出现。

  3. 启用 swap 设备:

第 12 步:最终清理

  1. 等待系统正常启动,使用你创建的用户账号登录。确保系统(包括网络)工作正常。

  2. 可选:删除初始安装的快照:

  3. 可选:禁用 root 密码:

  4. 可选(强烈建议):禁用 root SSH 登录:

    如果之前安装过 SSH,请撤销临时修改:

  5. 可选:重新启用图形启动过程:

    如果你希望使用图形启动,并且使用了 LUKS,这可以让提示界面更美观。

    注意

    如果出现 osprober 错误,可忽略。

  6. 可选:仅针对 LUKS 安装,备份 LUKS 头:

    将备份妥善保存(如云端存储)。该备份受 LUKS 密码保护,但你也可选择额外加密。

    提示

    如果创建了镜像或 raidz 拓扑,请对每个 LUKS 卷重复此操作(如 luks2 等)。

故障排除

使用 Live CD 进行救援

请参考 第 1 步:准备安装环境arrow-up-right

对于 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/330arrow-up-right

大多数 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

外部链接

最后更新于