Skip to content

NFS Server

  • Hostname 与 DNS:见 ACSA hosts
  • 操作系统:Proxmox VE 8 (Debian 12)

登录权限

由于 NFS 没有计算资源,仅作存储用途,因此配置 sshd 只允许管理员登录:

/etc/ssh/sshd_config.d/acsa.conf
HostKey /etc/ssh/ssh_host_ecdsa_key
HostCertificate /etc/ssh/ssh_host_ecdsa_key-cert.pub
TrustedUserCAKeys /etc/ssh/ssh_user_ca
PasswordAuthentication no
ChallengeResponseAuthentication no
AuthenticationMethods publickey
AllowGroups root sudo

Glados

截至 2025 年 1 月使用的 NFS 服务器,现在用作 ACSA-MED,替代不堪重负的群晖。

iDRAC 授权

我们于 2024 年 3 月 13 日从淘宝上花¥60 给这台机器购买了 iDRAC 9 Enterprise License,这样我们终于有远程 KVM 可以用了(没错戴尔把这么基础的功能加钱卖)。 这份授权文件在这,以备不时之需。

为了能让 Telegraf 从 IPMI 获取传感器信息,telegraf 用户被添加进了 sys 组,并且根据 Telegraf 文档创建了相应的 udev 规则:

/etc/udev/rules.d/52-telegraf-ipmi.rules
KERNEL=="ipmi*", MODE="660", GROUP="sys"

ZFS

在之前使用 RAID 卡自带的 RAID 功能时,它只能提供非常基础的管理并且并不适合我们的使用场景。因此我们现在选择将它调到 HBA 模式,使用 ZFS 做为 RAID+LVM+caching 的 all-in-one 的解决方案。

RAID

Zeus

作为集中存储的服务器,其吞吐能力是比较重要的,因此我们使用的方案为 3*RAID-Z2。在这种情况下会损失6块盘的容量,最大故障冗余是每组两块盘。

zpool create rpool -O canmount=off -O xattr=sa -O relatime=on -O compress=zstd raidz2 /dev/sd[a-h] raidz2 /dev/sd[i-p] raidz2 /dev/sd[q-x] cache nvme0n1

Glados

作为医疗数据的存储服务器,一方面其型号限制了硬盘数量,另一方面它不需要很高的 IOPS,因此我们选择直接对全部 8 块硬盘使用 RAID-Z2

FYI(Legacy things)

We choose RAID-10 over RAID-6 for performance reasons (see this article). In ZFS this is accomplished using "mirror vdev group" setup:

zpool create rpool mirror /dev/sdc /dev/sdd mirror /dev/sde /dev/sdf mirror /dev/sdg /dev/sdh

This way /dev/sdc and /dev/sdd are mirrored together as one vdev (RAID-1), and three vdevs like this are added together to form a large pool (RAID-0, with minor differences).

Caching

默认情况下,ZFS 使用系统总内存的一半作为可调替换缓存(Adaptive Replacement Cache, ARC),其统计信息可以通过 arc_summary 命令查看。

ARC 的内存使用可以通过 ZFS 模块的参数 zfs_arc_minzfs_arc_max 进行调整。因为服务器上有很多内存没有使用,我们将 ACR 设置为 4 GB 到 432 GB (Glados 上为 80 GB)。持久化变更直接修改 modprobe config 文件 /etc/zfs.conf

/etc/modprobe.d/zfs.conf
options zfs zfs_dmu_offset_next_sync=1

options zfs zfs_arc_min=4294967296
options zfs zfs_arc_max=85899345920
options zfs zfs_arc_dnode_limit_percent=40
options zfs zfs_arc_lotsfree_percent=5

options zfs zfs_vdev_async_read_max_active=8
options zfs zfs_vdev_async_read_min_active=2
options zfs zfs_vdev_scrub_max_active=5
options zfs zfs_vdev_max_active=20000

options zfs l2arc_noprefetch=0
options zfs l2arc_headroom=8

要在线更改这些值,直接写入到相应的 sys 文件:

echo 463856467968 > /sys/module/zfs/parameters/zfs_arc_min

需要注意并不是所有的参数都可以在线更改,细节请查阅 ZFS 文档。

ZFS 同样支持添加额外的高速存储(比如 SSD)作为cache(写 cache / 写 buffer 是另一个问题),叫做 Level-2 ARC (L2ARC)。添加一个 cache 设备的命令为 zpool add rpool cache /dev/nvme1

写 cache(buffer) 叫做 "separate log" 设备(SLOG)。它不需要很大,因此我们直接使用系统磁盘的 LVM 一部分空间。

Compression

ZFS 支持在 volume 上和 dataset 上的透明压缩,我们在 NFS 共享的 home 目录上使用中等程度的压缩。

zfs set compression=zstd rpool/home

根据简单的测试,Zstd level 6 在提供不错的压缩率的同时操作仍然是 I/O 瓶颈。更高的压缩等级在特定的重度写入下可能变为 CPU 瓶颈,且压缩率提升较小,因此我们选择 Zstd-6 作为 home 目录的压缩等级。

根据该幻灯片第七页的描述,低 Zstd 等级对压缩率的影响很小,但性能差距很大,因此我们使用默认压缩等级(compression=zstd,等价于 level 3)来压缩数据。

常见的 compression=on 设置使用了一种较旧的算法 LZ4,尽管速度仍然很快,但已不再被认为是现代算法。

Snapshots

ZFS 支持即时快照,我们使用这一功能来防止数据意外丢失。快照是只读的,可以通过每个 dataset 根目录下的隐藏 .zfs 目录访问。例如:

ibug@snode0:~$ ls /staff/ibug/.zfs/snapshot/
20240408  20240412  20240415  20240419  20240422  20240426  20240429

这些快照目录下的文件可以复制回原始位置,并且它们是只读的。

我们每周为整个主目录创建两次快照(在每周一和周五的凌晨 5:17),并保留 7 个快照。这是通过 cron 和自定义脚本完成的。

#!/bin/sh

DATASET=rpool/home
DATE=$(date +%Y%m%d)

SNAPSHOT="$DATASET@$DATE"
if zfs list "$SNAPSHOT" >/dev/null 2>&1; then
  echo "Already taken snapshot today"
  exit 0
fi

zfs snapshot -r "$SNAPSHOT"

# retain latest snapshots
RETENTION=7
zfs list -t snapshot "$DATASET" |
  tail -n +2 | head -n -$RETENTION | awk '{print $1}' |
  xargs -rn 1 zfs destroy -rv

Extra: MegaCli

过时信息

MegaCli is most useful when using the disk controller in RAID mode. In HBA mode, we can access individual disks directly, and smartctl provides more details on disks.

Download the ZIP from here. There's an RPM package under Linux directory. Use rpm2cpio to convert it to a CPIO archive, then cpio -idv < package.cpio to extract.

The package contains 3 files under /opt/MegaRAID/MegaCli/. Other than the bundled files, libncurses5 is required (install from apt). You can symlink MegaCli64 to /usr/local/sbin for typing less.

Usage:

  • All information: MegaCli64 -AdpAllInfo -aAll
  • Physical disk information: MegaCli64 -PdList -aAll
  • Logical disk and physical disk information: MegaCli64 -LdPdInfo -aAll

NFS Tuning

为了实现更好的读写性能,我们将 NFS server 的线程数调整至 256(默认为16)。

/etc/default/nfs-kernel-server
RPCNFSDCOUNT=256

Networking

Proxmox VE 使用 Debian 的 ifupdown 配置系统,但是提供了一个改进版本 ifupdown2

我们还使用 NFS 作为“网关”来为其他主机提供互联网访问,因此需要将默认的 main 路由规则从优先级 32766 移动到优先级 2,这就是在接口 lo 上进行设置的原因。

auto lo
iface lo inet loopback
        up ip rule add table main pref 2 || true
        up ip rule delete table main pref 32766 || true

auto eno1
iface eno1 inet manual

auto eno2
iface eno2 inet manual

auto vmbr0
iface vmbr0 inet static
        address 222.195.72.127/24
        gateway 222.195.72.254
        bridge-ports eno1 eno2
        bridge-stp off
        bridge-fd 0
iface vmbr0 inet6 static
        address 2001:da8:d800:112::127/64
        gateway 2001:da8:d800:112::1

Because IB needs opensmd to work correctly, so we add a pre-up line to start the service.

因为 IB 需要 opensmd 才能正常工作,需要把 opensmd.service 设置为开机启动。

systemctl enable --now opensmd.service

solution

添加 pre-up 启动服务

auto ibp175s0
iface ibp175s0 inet static
        pre-up systemctl start opensmd.service
        address 10.1.13.1/24
        mtu 2044

VXLAN

由于 InfiniBand 无法进行桥接,为了将网关虚拟机连接到同一广播域的集群中,我们在 ib 接口上创建了一个 VXLAN 接口,以便可以将其桥接到 vmbr8 上。

要添加 VXLAN 接口,我们需要将以下配置添加到 /etc/network/interfaces 文件中。

/etc/network/interfaces
auto vxlan0
iface vxlan0
    pre-up ip link add $IFACE type vxlan id 1 group 239.1.1.1 dev ibp175s0 || true
    post-down ip link delete $IFACE || true
    mtu 1500

然后将 vxlan0 添加到 vmbr8bridge-ports 列表中,即:

-        bridge-ports none
+        bridge-ports vxlan0

Mounting NFS over RDMA

nfs-kernel-server 的默认配置不会启用 RDMA,且 RDMA 连接无法使用和 TCP/UDP 相同的端口,所以我们将 2050 端口分配给 NFS over RDMA。

要启用 NFS over RDMA,我们需要更改 /etc/nfs.conf 并取消注释下面两行:

rdma=y
rdma-port=2050

在 NFS 客户端上,加载 xprtrdma 内核模块并把挂载选项 proto=rdma,port=2050 添加到 NFS 挂载点。

Error: could not insert 'rpcrdma': invalid argument

Just install the mlnx-nfsrdma-dkms package. No reboot needed.

Routing service for other hosts

Legacy things, since we use VXLAN to provide internet for the cluster now.

See /etc/wireguard/wg0.conf for details.

iptables fails randomly

For unknown reasons, Proxmox VE switches back to iptables-legacy randomly, which causes the routing service to fail. A temporary fix is to switch back to iptables-nft and restart the service.

update-alternatives --set iptables /usr/sbin/iptables-nft
update-alternatives --set ip6tables /usr/sbin/ip6tables-nft
systemctl restart iptables

We've deployed a permanent fix

By inserting the update-alternatives commands before the iptables service starts, we can ensure that the correct iptables is used.

systemctl edit iptables.service
[Service]
ExecStartPre=-/usr/bin/update-alternatives --set iptables /usr/sbin/iptables-nft
ExecStartPre=-/usr/bin/update-alternatives --set ip6tables /usr/sbin/ip6tables-nft

We also mask pve-firewall to prevent it from interfering with our setup.

systemctl mask pve-firewall.service

Records

2024-03-24 SSD replacement

  • Poweroff the server
  • Insert the new Intel Optane 900p 208G, with its half-height bracket pre-installed
  • Power on the server, but boot PXE instead. Select Arch Linux to get a shell
    • e2fsck -f /dev/pve/root
    • resize2fs -p /dev/pve/root 14G
    • lvreduce -L 16G pve/root
    • resize2fs -p /dev/pve/root
  • Now reboot into the OS and prepare the new disk
    • fdisk /dev/nvme0n1
      • g to create a new GPT partition table
      • n, Enter, Enter, +100M to create a 100M EFI partition
      • t, uefi to set the partition type
      • n, Enter, Enter, Enter to create a new partition with the rest of the space
      • t, Enter, lvm to set the partition type to LVM
      • w save and exit
    • Configure the new EFI system partition:
      • mkfs.vfat /dev/nvme0n1p1
      • vim /etc/fstab and change the UUID of /boot/efi to the new one
      • umount /boot/efi, mount /boot/efi
      • grub-install --target=x86_64-efi to install GRUB
    • Migrate the root partition to the new disk:
      • pvcreate /dev/nvme0n1p2
      • vgextend pve /dev/nvme0n1p2
      • pvmove /dev/sdb2 /dev/nvme0n1p2 - this one takes some time, but with SSD it should be fast
      • vgreduce pve /dev/sdb2
      • (Optional) pvremove /dev/sdb2
      • (Optional) blkdiscard -f /dev/sdb
  • Pull out the old disk from the server
  • Insert the two 18 TB WD Gold disks
  • Add them to the ZFS pool:
    • zpool add rpool mirror sda sdb