坑边闲话:TrueNAS 提供了良好的 ZFS 使用体验,但是频繁使用 Web 界面并非是一个令人舒适的事情。我的原则是,与存储相关的问题尽量在命令行里解决。此外,将 Debian 系统配置为一个理想的存储服务器是很吸引人的,但这依旧需要用户对存储管理的命令行有深入的理解。

本文包括但不限于以下内容;

  • ZFS 打快照、删除快照
  • ZFS 存储吃创建
  • ZFS 文件系统创建与销毁
  • ZFS 文件系统的参数调整

1. 安装 ZFS 相关组件·

1.1 常规安装·

Debian 或者 Ubuntu 等 Linux 发行版不具备 TrueNAS 的网页图形化界面,因此操作 ZFS 时颇为繁琐,为此我们做简单介绍。

1
sudo apt-get install -y zfs-dkms zfsutils-linux nfs4-acl-tools

图 1. ZFS 与 Linux 内核的开源许可是冲突的,安装时会有提示。

OpenZFS 和 Linux 的许可证不兼容。

OpenZFS 根据共同开发和分发许可证(CDDL)授权,而 Linux 内核则根据通用公共许可证第二版(GPL-2)授权。虽然这两种许可证都是自由开源许可证,但它们是限制性的许可证。这种组合导致了问题,因为它阻止了在同一个二进制文件中使用专门在另一许可证下可用的代码片段。

你将以这样的方式构建 OpenZFS,即它们不会被构建成一个单一的大型二进制文件。请注意,将这两种二进制文件分发在同一媒介(如磁盘映像、虚拟设备等)可能会导致侵权

随后会注册两个 Systemd 服务:

1
2
Created symlink /etc/systemd/system/zed.service → /lib/systemd/system/zfs-zed.service.
Created symlink /etc/systemd/system/zfs.target.wants/zfs-zed.service → /lib/systemd/system/zfs-zed.service.

1.2 问题排查·

如果遇到 zfs-load-module.service 服务失败时,大概率是 Linux Kernel 升级了但是系统并没有下载对应的 linux-headers 文件,可通过下列命令安装头文件并重新安装 ZFS DKMS 模块。

1
sudo apt install linux-headers-$(uname -r)

2. 存储池相关·

存储池是最关键的部分,因为它是数据集的底层。存储池与 RAID 类型密切相关,一般存储池将会涉及以下内容:

  • 存储冗余度,
  • 虚设备,如 Slog、L2ARC 等。

如果不能保证底层的可靠,用户侧做再多设置都是错的。接下来将介绍与存储池相关的高频操作。

2.1 创建存储池·

对于磁盘列表,可以使用 nvme-cli 等工具查看硬盘列表。

图 2. 使用 nvme-list 命令查看 NVMe 硬盘列表,Node 列就是磁盘的句柄(盘符),可用来执行 ZFS pool 的创建。

对于 SATA 硬盘,可能需要使用 lsblk 等命令进行查看。

获得了磁盘列表,并成功地将物理磁盘与磁盘句柄(盘符)进行配对之后,即可进行存储池创建:

1
2
$POOL_NAME=tank
sudo zpool create ${POOL_NAME} raidz sda sdb sdc -f

最佳实践

可以看到,创建存储池是一个与硬件相关的命令。为了保证后期的辨识便利性,笔者建议按照如下关键字级联给存储池命名:

  • 磁盘制造商,如
    • DapuStor
    • Intel
    • Kioxia
    • Memblaze
    • Micron
    • Samsung
    • Solidigm
    • WD
  • 磁盘型号,如
    • R5100
    • P5520
    • PM1733
    • SN750
  • RAIDZ 级别,如
    • Mirror
    • Stripe
    • RAID-Z1
    • RAID-Z2
    • RAID-Z3

DapuStor_R5100_RAID-Z1 等。如此一来,一眼便知存储器类型及冗余级别。

2.2 对存储池进行导入和导出·

在创建存储池之后,如果需要将硬盘拔除,则需要先把存储池下线,即 export 导出。

1
2
$POOL_NAME=tank
zpool export ${POOL_NAME}

如果需要恢复存储池,则使用相反的命令 import 导入。

1
2
$POOL_NAME=tank
sudo zpool import ${POOL_NAME}

如果需要对存储池进行重命名,则需要先导出再导入且在导入的时候使用新的名字,同时应该注意,并没有 zpool-rename 这种命令。

1
2
3
4
5
# Rename a zfs pool
$OLD_NAME=old_name
$NEW_NAME=new_name
zpool export ${OLD_NAME}
zpool import ${OLD_NAME} ${NEW_NAME}

3. 数据集相关·

在完成了底层存储池的构建之后,我们所关心的主要是用户侧的内容,如存储池和文件共享等。

存储池已经

  • 通过 data vdev 提供了基本的存储能力,
  • 通过 Slog 和 L2ARC 提供了读写强化(可选),
  • 通过 Hot spare 盘提供了一定程度的故障自动转移(可选)。

数据集就是建立在存储池之上的抽象的文件系统,也是用户真正可读写的最基本单元。存储池使用 zpool 系列命令进行配置,数据集通过 zfs 命令进行配置。从命令名也可以看出,数据集就是真正的 ZFS 文件系统。

3.1 创建数据集·

1
2
$DATASET_NAME=data
sudo zfs create ${POOL_NAME}/${DATASET_NAME}

3.2 设置数据集的相关属性·

熟悉 C# 编程的朋友应该清楚,通过设置一对 getset 函数,可以用来获取、修改 class 的属性。ZFS 的数据集属性访问与 C# 的语法非常相似。

  • 获取属性使用 zfs get
  • 设置属性使用 zfs set

3.2.1 设置压缩方式·

1
sudo zfs set compression=lz4 ${POOL_NAME}/${DATASET_NAME}

3.2.2 设置权限类型·

1
sudo zfs set acltype=nfsv4 ${POOL_NAME}/${DATASET_NAME}

3.2.3 设置挂载入口·

这是唯一一个特殊的命令,它使用 zfs 控制存储池。在存储池创建之后,原则上需要一个文件系统入口才可被用户访问,mountpoint 即该入口。

1
sudo zfs set mountpoint=${/PATH/OF/MOUNT_POINT}] ${POOL_NAME}

设置了存储池的入口之后,存储池下的数据集也将以对应目录结构被自动挂载。

3.2.4 重命名数据集·

使用 zfs rename 命令可对数据集进行重命名,如

1
2
3
$OLD_DATASET_NAME
$NEW_DATASET_NAME
sudo zfs rename $POOL_NAME/$OLD_DATASET_NAME $POOL_NAME/$NEW_DATASET_NAME

4. 快照相关·

4.1 创建快照·

打快照建议使用标准化命名方式,随意取名字可能会导致后期管理紊乱。

1
sudo zfs snapshot ${POOL_NAME}/${DATASET_NAME}@$(date +"manual-%Y-%m-%d_%H-%M")

4.2 移除快照·

过于老旧的快照会占用很多不必要的空间,及时清理不再需要的快照是个好习惯。

1
sudo zfs destroy ${POOL_NAME}/${DATASET_NAME}@${SNAPSHOT_NAME}

注意

zfs destroy 命令,不仅仅可以用来删除快照,还可以用来移除 dataset,这是一个很危险的操作,一定要谨慎无比地使用!

5. 数据集迁移与备份·

5.1 数据集迁移·

1
sudo zfs send ${POOL_NAME}/${DATASET_NAME}@${SNAPSHOT_NAME} | sudo ${TARGET_POOL}/${TARGET_DATASET}

如果接受数据流的是另一台机器,则可通过网络传输:

1
sudo zfs send ${POOL_NAME}/${DATASET_NAME}@${SNAPSHOT_NAME} | ssh -p ${PORT} ${USERNAME}@${HOSTNAME} sudo zfs recv ${TARGET_POOL}/${TARGET_DATASET}

5.2 查看快照与数据集之间的差异·

1
sudo zfs diff -F ${POOL_NAME}/${DATASET_NAME}@${OLD_SNAPSHOT} ${POOL_NAME}/${DATASET_NAME}@${NEW_SNAPSHOT} | while read -r line; do echo -e "$line"; done

最佳实践

由于 ZFS 的 zfs diff 命令输出中文字符、日文字符时,会使用八进制编码(Ocatl Encoding),所以打印到屏幕上是 \dddd 的样子。通过上述命令可以逐行转移为可识别的中日文字。

SuperUser 参考

总结·

ZFS 因为优秀的特性使得它在一众文件系统中独领风骚很多年,至今它都是最优秀的文件系统之一,可以说在开源方案中,ZFS 是最优秀的之一。然而,因为 Linux Kernel 和 OpenZFS 的开源许可冲突,导致两者至今不能合并,这或许也是一个开源届的损失。但是我认为,这依旧是社会契约的一种合理表现。或许在未来的某一天我们能够看到 OpenZFS 正式融入 Linux Kernel.

除此之外,本文不涉及 boot from ZFS,毕竟这还不是一个非常完善的操作。