跳到主要内容

FreeBSD ZFS

ZFS是最好的文件系统

什么是 ZFS

zroot 数据存储

FreeBSD安装过程支持直接构建ZFS作为操作系统root存储,默认命名为 zroot zpool。我由于设备PCIe插槽有限(用于GPU)并且资金捉襟见肘,所以最终只在 一块NVMe 上使用默认的 zroot 存储池构建和使用ZFS。

备注

FreeBSD installer过程中如果选择自动ZFS,会将整个磁盘数据抹掉然后分配3个分区:

  • 分区1是 efi 分区(FAT32)
  • 分区2是 freebsd-swap 分区
  • 分区3是 freebsd-zfs 分区,也就是除了上述2个分区外,所有剩余磁盘空间都分配给 zroot zpool

由于我计划构建一个FreeBSD和Linux(LFS)并存的系统,所以我在安装过程中采用了手工划分ZFS步骤

在FreeBSD安装过程中手工配置ZFS,以便限制默认 zroot zpool 使用 256G 空间,剩余磁盘空间后续用于Linux以及数据存储 zdata zpool:

  • 当FreeBSD Installer安装过程到了 Partition 分区步骤时,不能选择 Auto ZFS ,而是要选择 shell 选项,此时进入控制台

  • 检查系统中有哪些磁盘

camcontrol devlist

可以看到第二行显示是 铠侠KIOXIA EXCERIA G2 NVMe SSD存储 ,识别为 nda0 , 将在 nda0 上安装FreeBSD:

<AHCI SGPIO Enclosure 2.00 0001>   at scbus8 target 0 lun 0 (ses0,pass0)
<KIOXIA-EXCERIA G2 SSD ECFA17.1> at scbus9 target 0 lun 1 (pass1,nda0)
<SanDisk' Cruzer Fit 1.00> at scbus10 target 0 lun 0 (da0,pass3)
  • 创建一个全新的分区表( 警告,会摧毁磁盘上所有数据 ):
# 如果这里出现报错提示nda0 busy,则可能是磁盘中原本有数据,需要使用 -F 参数
gpart destroy nda0

# 创建gpt分区表
gpart create -s gpt nda0
  • 创建包含启动代码的分区(bootcade partition) 我的实践是创建UEFI Boot
gpart add -a 4k -s 260M -t efi nda0
# Create a FAT32 partition
newfs_msdos -F 32 -c 1 /dev/nda0p1
mount -t msdosfs -o longnames /dev/nda0p1 /mnt
mkdir -p /mnt/EFI/BOOT
cp /boot/loader.efi /mnt/EFI/BOOT/BOOTX64.efi
umount /mnt
  • 创建分区:

    • -a <number> 控制对齐
    • -s <size> 设置分区大小,如果没有指定分区大小,就会将磁盘剩余空间全部用掉(我设置指定256GB)
gpart add -a 1m -s 2G -t freebsd-swap -l swap0 nda0
gpart add -a 1m -s 256G -t freebsd-zfs -l disk0 nda0
  • 创建ZFS Pool:
gpart show -p nda0

# 挂载tmpfs
mount -t tmpfs tmpfs /mnt

# 创建ZFS存储池
zpool create -o altroot=/mnt zroot nda0p3
  • 现在需要依次创建完整的ZFS文件系统,以便安装程序能够正确分布目录:
# 设置默认的lz4压缩,默认配置通常适合
zfs set compress=on zroot

# 创建Boot环境的层次结构
zfs create -o mountpoint=none zroot/ROOT
zfs create -o mountpoint=none zroot/ROOT/default
mount -t zfs zroot/ROOT/default /mnt

# 创建剩余文件系统
zfs create -o mountpoint=/tmp -o exec=on -o setuid=off zroot/tmp
zfs create -o canmount=off -o mountpoint=/usr zroot/usr
zfs create zroot/usr/home
zfs create -o exec=off -o setuid=off zroot/usr/src
zfs create zroot/usr/obj
zfs create -o mountpoint=/usr/ports -o setuid=off zroot/usr/ports
zfs create -o exec=off -o setuid=off zroot/usr/ports/distfiles
zfs create -o exec=off -o setuid=off zroot/usr/ports/packages
zfs create -o canmount=off -o mountpoint=/var zroot/var
zfs create -o exec=off -o setuid=off zroot/var/audit
zfs create -o exec=off -o setuid=off zroot/var/crash
zfs create -o exec=off -o setuid=off zroot/var/log
zfs create -o atime=on -o exec=off -o setuid=off zroot/var/mail
zfs create -o exec=on -o setuid=off zroot/var/tmp

# 链接和权限设置
ln -s /usr/home /mnt/home
chmod 1777 /mnt/var/tmp
chmod 1777 /mnt/tmp
  • 配置启动环境
zpool set bootfs=zroot/ROOT/default zroot
  • 创建 /tmp/bsdinstall_etc/fstab :
cat << EOF > /tmp/bsdinstall_etc/fstab
# Device Mountpoint FStype Options Dump Pass#
/dev/gpt/swap0 none swap sw 0 0
EOF
  • 现在退出Shell,此时 bsdinstall 就会继续完成安装(如果上述ZFS分区没有错误的话)

  • 在最后安装步骤,当安装程序提示时候要补充命令,一定要回答 yes ,并完成以下命令(也就是设置系统默认启动时加载ZFS):

sysrc zfs_enable="YES"
echo 'zfs_load="YES"' >> /boot/loader.conf
提示

compression=lz4 启用轻量级lz4压缩能够提升存储利用率,同时因为压缩数据也提高了存储IO性能(但消耗CPU计算资源)

zdata 数据存储

备注

由于我的 组装服务器 只有一个PCIe插槽,通过拆分能够安装3个PCIe设备,我需要将宝贵的PCIe接口用于机器学习的GPU设备,所以最终我取消了 zdata 的多NVMe存储,而改为仅使用一块NVMe的 zroot 数据存储。

当生产环境中使用ZFS,建议构建 RAIDZ 确保数据安全

  • 使用 geom 检查磁盘列表
geom disk list

可以看到主机安装了1块NVMe磁盘,设备名是 ndaX :

Geom name: nda0
Providers:
1. Name: nda0
Mediasize: 2000398934016 (1.8T)
Sectorsize: 512
Mode: r2w2e4
descr: KIOXIA-EXCERIA G2 SSD
lunid: 00000000000000008ce38e030091c40e
ident: Y39B70RTK7AS
rotationrate: 0
fwsectors: 0
fwheads: 0

gpart 划分分区

  • 如上文所述,我在安装过程中手工配置ZFS zroot zpool,所以当前为操作系统分配的 zroot 存储池只占用部分存储空间,使用 gpart 检查磁盘分区:
gpart show nda0

当前有3个分区:

=>        40  3907029088  nda0  GPT  (1.8T)
40 532480 1 efi (260M)
532520 2008 - free - (1.0M)
534528 4194304 2 freebsd-swap (2.0G)
4728832 536870912 3 freebsd-zfs (256G)
541599744 3365429384 - free - (1.6T)
  • 在上述3个分区之后,再创建一个(分区4)分区( -t linux-data 表示是Linux分区),分配 256G ( -s )并设置以 1M 大小进行对齐( -a 1M )
gpart add -t linux-data -a 1M -s 256G nda0
  • 剩余磁盘空间再创建一个分区
gpart add -t freebsd-zfs -a 1M nda0
  • 最终 gpart show nda0 显示如下(添加了2个分区)
=>        40  3907029088  nda0  GPT  (1.8T)
40 532480 1 efi (260M)
532520 2008 - free - (1.0M)
534528 4194304 2 freebsd-swap (2.0G)
4728832 536870912 3 freebsd-zfs (256G)
541599744 536870912 4 linux-data (256G)
1078470656 2828558336 5 freebsd-zfs (1.3T)
3907028992 136 - free - (68K)

创建 stripe 模式 ZFS pool zdata

注意
  • 构建的是 stripe 模式,是为了追求实验环境容量最大化, 生产环境不可使用!!!
  • 生产环境请使用 RAIDZ-5
  • 使用上述 gpart 划分好的分区2(操作系统所在盘是分区4)来构建一个容量最大化的 stripe ZFS 存储池:
zpool create -f -o ashift=12 zdata /dev/nda0p5

# 启用压缩
zfs set compression=lz4 zdata
提示
  • -o ashift=12ashift 属性设置为 12 ,以对应 4KiB (4096字节)块大小。通常对于HDD和SSD,能够获得较好的性能和兼容性。底层是 512字节 一个扇区,所以 2^12 = 4096 就能够对齐和整块读写磁盘。如果没有指定这个 ashift 参数,ZFS会自动检测 ashift ,如果检测失败就会默认使用 ashift=9 ,这会导致性能损失。这个 ashift 参数一旦设置,不能修改
  • 这里使用了 diskid 来标记磁盘,以避免搞错磁盘
  • 创建zfs卷集
# 存储文档的卷集,其他卷集主要在Jails中使用
zfs create zdata/docs

为后续 vm-bhyve运行虚拟化 准备,构建独立的dataset:

zfs create zdata/vms

# 为 vm-bhyve 提供独立的dataset
zfs create zdata/vms/.config
zfs create zdata/vms/.img
zfs create zdata/vms/.iso
zfs create zdata/vms/.templates

进一步阅读