收起左侧

【实测/避坑】fnOS ZFS ZVOL + IBLOCK iSCSI:写入 125→962 MiB/s

0
回复
72
查看
[ 复制链接 ]

1

主题

0

回帖

0

牛值

系统先锋体验团🛩️

前言

之前我在 fnOS 上把一块只用于 Steam 游戏库的存储空间做成了 iSCSI。旧方案是:

  • 5×6TB 机械硬盘组成 RAID0
  • 2×约 250GB 消费级 NVMe 作为 LVM dm-cache writeback
  • ext4 文件系统上放 10TB FILEIO 镜像文件
  • Windows 通过 100G 网络挂载为 NTFS 游戏盘

读取跑分尚可,但 FILEIO 的同步写入长期只有约 125 MiB/s。最后我把这一块存储空间重建为原生 ZFS,使用 ZVOL + LIO IBLOCK 导出 iSCSI。重启后实测顺序写入约 962 MiB/s,读取约 1.65 GiB/s。

这篇不是“照抄命令即可”的无脑教程,而是把实际迁移过程、踩坑和数据安全边界整理出来,供类似需求参考。

重要警告:本文涉及销毁存储池和重建 iSCSI。我的这个盘只存可重新下载的 Steam 游戏,所以接受清空。任何有重要数据的存储空间都不要照抄,务必先做离线备份。

一、最终拓扑

5×6TB HDD
  **─ ZFS stripe(5 个顶层单盘 vdev,无冗余)
       **─ 10TB thick ZVOL,volblocksize=16K
            **─ LIO /backstores/block(IBLOCK)
                 **─ iSCSI
                      **─ Windows GPT + NTFS 64K

2×消费级 NVMe
  **─ ZFS L2ARC cache(只做读缓存)

关键参数:

  • ZFS:ashift=12compression=lz4atime=off
  • ZVOL:volblocksize=16Ksync=standard、厚置备
  • LIO:IBLOCK,逻辑扇区保持 512B,队列深度 128
  • Windows:GPT、NTFS 64K 分配单元
  • iSCSI:两条 MCS 连接,走专用高速网络

两块消费级 NVMe 只作为 L2ARC。它们损坏或拔掉不会破坏主池数据,因此我没有把它们做成 SLOG 或 special vdev。

二、为什么 FILEIO 写入慢

旧 LUN 是“文件型 LUN”:

iSCSI → LIO FILEIO → 10TB 镜像文件 → ext4 → LVM/dm-cache → md RAID0

同步写入需要穿过文件系统和块层,路径长,O_DSYNC 下的稳定 QD32 顺序写只有约 124.90 MiB/s。

我也测试过 FILEIO 的 aio=true / Async1:短时间写入会明显变快,但在当前 fnOS 内核上出现过 LIO 内核线程长期 D 状态,关机也会卡住。最终已经回滚。

避坑:不要只看 Async FILEIO 的短时跑分。我这台机器上它出现了内核线程卡死,不适合作为稳定方案。

IBLOCK 直接把 ZVOL 块设备交给 LIO:

iSCSI → LIO IBLOCK → ZVOL → ZFS vdev

少了一层大文件和 ext4,同步写路径更直接,这才是本次写入提升的核心。

三、迁移前必须做的事情

我实际执行时先做了以下检查:

  1. Windows 停止 Steam,卸载/脱机 iSCSI 磁盘并断开会话。
  2. NAS 停止对应 iSCSI target,确认没有进程继续访问旧 LUN。
  3. 备份 targetcli 配置、fnOS iSCSI 配置以及系统数据库相关记录。
  4. 用 UUID、序列号和设备拓扑逐个核对目标硬盘。
  5. 再次确认其他重要存储池均为 ONLINE,且不在待删除设备列表中。
  6. 只删除目标存储空间对应的 VG、PV、md 和成员盘。

不要依赖 /dev/sdX 的字母顺序,重启后它可能变化。涉及销毁操作时应使用 /dev/disk/by-id/ 并逐盘核对。

四、ZFS 与 ZVOL 的关键配置

下面仅展示结构,POOLDISK1 等都应替换成自己核对过的 by-id 路径。

1. 创建 ZFS stripe

zpool create -f \
  -o ashift=12 \
  POOL \
  /dev/disk/by-id/DISK1 \
  /dev/disk/by-id/DISK2 \
  /dev/disk/by-id/DISK3 \
  /dev/disk/by-id/DISK4 \
  /dev/disk/by-id/DISK5

zfs set atime=off POOL
zfs set compression=lz4 POOL
zfs set xattr=sa POOL
zfs set acltype=posix POOL

这里是 5 个独立顶层 vdev,也就是条带:性能优先、没有冗余,任何一块硬盘故障都会丢失整个池。风险与原先的 md RAID0 一样,只适用于可重新下载的数据。

2. 创建 ZVOL

zfs create -o mountpoint=none POOL/iscsi

zfs create \
  -V 10T \
  -b 16K \
  -o compression=lz4 \
  -o sync=standard \
  POOL/iscsi/steam

在我使用的 OpenZFS 2.4.1 上,创建时显式加 refreservation=auto 反而会报 ENOSPC;省略它以后,ZVOL 可以正常创建,并自动得到与卷大小对应的 reservation。这个现象可能与版本有关,建议创建后用下面的命令核对:

zfs get volsize,volblocksize,compression,sync,refreservation POOL/iscsi/steam
ls -l /dev/zvol/POOL/iscsi/steam

五、关于“大于 2TB 必须改 4K”的误区

本次迁移参考并感谢社区这篇文章:

〖教程〗ZFS存储空间开 iSCSI UI 正常以及大于 2T 的解决

但我实测后有两点不同结论:

  1. 大于 2TB 的磁盘并不要求 iSCSI 逻辑扇区改成 4096B。 Windows 使用 GPT 时,512B 逻辑扇区一样支持 10TB。
  2. 现代 OpenZFS 的 ZVOL 默认 volblocksize 也不应假定为 4096B。 我这个版本实际使用 16K,并在创建时明确指定。

所以我保持 LIO 的逻辑块大小为 512B,ZVOL 的 volblocksize=16K。这两个“块大小”不是同一个概念,不要混为一谈,更不要对系统配置 JSON 做不加区分的全局替换。

六、创建 IBLOCK iSCSI

结构示例:

targetcli /backstores/block create \
  name=steam \
  dev=/dev/zvol/POOL/iscsi/steam

targetcli /iscsi create iqn.YYYY-MM.example:nas.steam
targetcli /iscsi/iqn.YYYY-MM.example:nas.steam/tpg1/luns create \
  /backstores/block/steam

随后应按自己的环境配置 initiator ACL、CHAP、portal 和 MCS。不要把真实 IQN、密码或内网地址直接复制到公开帖子或脚本。

我保留的安全基线是:

  • block_size=512
  • queue_depth=128
  • is_nonrot=1
  • emulate_write_cache=0
  • sync=standard

没有把 ZFS 的 sync 关闭,也没有用消费级 NVMe 做 SLOG。对游戏库可以偏性能,但我仍希望一次普通掉电不至于把风险无限放大。

fnOS 的内部数据库和 iSCSI JSON 会随版本变化。我的做法是先完整备份,只精确更新对应存储空间和单个 LUN 的记录,并增加 systemd 服务在启动时等待 ZVOL 出现后恢复 target 参数。不建议未经核对直接复制 SQL,也不要全局替换配置文件。能通过 UI 完成的部分优先使用 UI。

七、实测结果

测试环境是 100G 专用网络;数值是 MiB/s。

场景 顺序读 1M QD32 随机读 64K QD32 顺序写 1M QD32
旧 FILEIO(稳定基线) 1530.95 1545.85 124.90
新 ZVOL + IBLOCK,重启后 1653.60 1714.30 961.54

写入从约 125 MiB/s 提升到约 962 MiB/s,约 7.7 倍。

为了区分缓存影响,我还做了几组测试:

测试状态 顺序读 随机读 64K
HDD 冷数据、ARC 仅 metadata 751.46 67.99
L2ARC 部分预热、ARC data 关闭 1466.00 1438.88
正常热缓存状态 1653.60 1714.30

这说明高随机读取明显依赖 L2ARC;机械盘本身不可能提供 1.4 GiB/s 的 64K 随机读取。

另外,8GiB 不可压缩数据的 WriteThrough 填充约为 278.52 MiB/s。上面的 961.54 MiB/s 是 QD32 短时基准,不应理解成任意数据量下都能一直保持近 1 GiB/s。Steam 下载、解压和补丁更新还会受到客户端 CPU、网络源、NTFS 元数据和游戏文件结构影响。

八、L2ARC 的取舍

加入读缓存示例:

zpool add POOL cache \
  /dev/disk/by-id/NVME1 \
  /dev/disk/by-id/NVME2

我用计数器验证过 L2ARC hit/read 会随重复读取增长,说明缓存确实命中,而不是只看了一次漂亮跑分。

注意:

  • L2ARC 主要改善重复读取,首次冷读仍由 HDD 决定。
  • L2ARC 不负责提高同步写入。
  • 消费级、无断 电保护的 NVMe 不适合贸然拿来做 SLOG。
  • special vdev 是池结构的一部分,损坏可能导致整个池不可用,不能把它当成“可随时拔掉的缓存”。

九、重启验证

真正迁移完成的标准不是“当时能挂载”,而是 NAS 和 Windows 都重启后仍能自动恢复。

我最终确认:

  • 三个存储池全部 ONLINE,读/写/校验错误为 0。
  • 只重建了 Steam 所在的目标池,另外两个重要池未改动。
  • ZVOL 自动出现,LIO 只保留一个 IBLOCK backstore,没有遗留 FILEIO。
  • iSCSI target 和两条 MCS 连接自动恢复。
  • Windows 10TB GPT/NTFS 盘符正常,文件系统无告警。
  • NAS 内核日志没有新的存储、ZFS 或 iSCSI 相关错误。
  • 重启后的跑分与迁移后预期一致。

十、结论

如果 iSCSI 只是大容量文件系统上的 FILEIO 镜像,且同步写入明显偏低,迁移到 ZFS ZVOL + LIO IBLOCK 确实可能带来很大提升。本次真正有效的改动是缩短写入路径,而不是简单地把 writethrough 改成不安全的缓存模式。

我的建议是:

  1. 重要数据优先考虑 mirror 或 RAIDZ,不要使用本文的五盘 stripe。
  2. ZVOL 根据负载选择 volblocksize,Steam/NTFS 场景可从 16K 开始实测。
  3. 逻辑扇区 512B 配合 GPT 可以正常使用 10TB,不要为了“大于 2TB”盲改 4K。
  4. 消费级 NVMe 适合做可丢失的 L2ARC,不要未经评估就做 SLOG/special vdev。
  5. 每次只改一个变量,改前后做同一套测试,并进行真实重启验证。
  6. 所有内部配置变更先备份,精确修改,避免全局替换。

以上是我这套“Steam 游戏库专用、高性能优先、允许重下”的实测经验。不同硬盘、内存、网络和 fnOS/OpenZFS 版本会有差异,欢迎大家补充更稳妥的做法。

收藏
送赞
分享
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则