【HW】 Proxmox VEでInfiniband+NFS over RDMAを使う 



 かなり長いことInfinibandを使っていますが、Infinibandで試そうと思いつつ全く試していなかったことがあります。それがNFS over RDMAです。ESXiでIB+NFSoRDMAに対応していれば試していたのですが、これが非対応だったこととSRPが速すぎてそれ以上追求するモチベーションがわかなかったことが理由でした。しかし、最近PVEを検証する機会があり、その中でどうもIB+NFS+RDMAがいけそうだというのがわかったものの、ほかを探しても一切の情報がないので検証してみました。

 PVEに行く前にそもそもNFSoRDMAってどう使うの?という疑問があり色々ググりましたが、NFSサーバ側には svcrdmaモジュールが必要で、クライアント側にはxprtrdmaが必要になるみたいですが、Debian12ではrpcrdma.koのエイリアスとしてすでにInboxでドライバをもっていました。
https://enterprise-support.nvidia.com/s/article/howto-configure-nfs-over-rdma--roce-x

root@debian:~# modinfo svcrdma
filename:       /lib/modules/6.1.0-28-amd64/kernel/net/sunrpc/xprtrdma/rpcrdma.ko
alias:          rpcrdma6
alias:          xprtrdma
alias:          svcrdma
license:        Dual BSD/GPL
description:    RPC/RDMA Transport
author:         Open Grid Computing and Network Appliance, Inc.
depends:        sunrpc,ib_core,rdma_cm
retpoline:      Y
intree:         Y
name:           rpcrdma
vermagic:       6.1.0-28-amd64 SMP preempt mod_unload modversions
sig_id:         PKCS#7
signer:         Debian Secure Boot CA
sig_key:        32:A0:28:7F:84:1A:03:6F:A3:93:C1:E0:65:C4:3A:E6:B2:42:26:43
sig_hashalgo:   sha256
signature:      15:83:F8:C8:E4:FE:CB:21:AA:92:43:39:E8:FC:5F:61:0C:D9:3E:F9:
                28:1D:9A:1C:35:25:58:08:0C:DB:8E:D5:07:36:CD:E4:D5:1E:30:2A:
                61:FE:20:AB:DF:98:C0:55:C5:F6:6E:B8:8B:32:42:D4:D5:9A:58:44:
                E5:EB:5A:FB:B6:06:E9:1E:B2:0A:F6:D1:FC:64:B1:F9:68:DF:55:06:
                D3:70:DB:13:35:99:FF:2B:6C:42:8C:D0:1D:C7:D6:3A:BC:55:04:08:
                0A:7D:1D:6A:EF:D6:70:32:BB:A4:B5:D3:2A:62:2E:1A:1C:F9:D4:B4:
                B6:87:4E:FF:F8:C3:96:3E:09:2A:86:4C:41:F3:2B:9E:AA:AE:07:0D:
                98:A4:EA:D4:5F:5F:E3:CC:DF:42:1F:79:FF:F7:B6:77:DF:09:BF:38:
                74:6F:1B:18:D0:48:BF:61:02:DC:25:7A:CE:6D:91:79:06:4B:A2:02:
                28:85:A4:D3:7A:21:79:80:31:6C:98:B9:6F:E2:94:BA:CB:AB:A9:03:
                6A:A8:17:3F:26:4B:91:42:E0:5C:53:6E:B2:D3:99:63:24:04:FD:32:
                3A:FF:2D:48:A5:B5:D4:89:B9:DD:D5:FC:FA:15:5E:1D:35:06:C0:46:
                F6:C7:CE:0F:B7:4E:1F:38:F2:5E:A5:46:8E:1C:7C:9C
root@debian:~# modinfo xprtrdma
filename:       /lib/modules/6.1.0-28-amd64/kernel/net/sunrpc/xprtrdma/rpcrdma.ko
alias:          rpcrdma6
alias:          xprtrdma
alias:          svcrdma
license:        Dual BSD/GPL
description:    RPC/RDMA Transport
author:         Open Grid Computing and Network Appliance, Inc.
depends:        sunrpc,ib_core,rdma_cm
retpoline:      Y
intree:         Y
name:           rpcrdma
vermagic:       6.1.0-28-amd64 SMP preempt mod_unload modversions
sig_id:         PKCS#7
signer:         Debian Secure Boot CA
sig_key:        32:A0:28:7F:84:1A:03:6F:A3:93:C1:E0:65:C4:3A:E6:B2:42:26:43
sig_hashalgo:   sha256
signature:      15:83:F8:C8:E4:FE:CB:21:AA:92:43:39:E8:FC:5F:61:0C:D9:3E:F9:
                28:1D:9A:1C:35:25:58:08:0C:DB:8E:D5:07:36:CD:E4:D5:1E:30:2A:
                61:FE:20:AB:DF:98:C0:55:C5:F6:6E:B8:8B:32:42:D4:D5:9A:58:44:
                E5:EB:5A:FB:B6:06:E9:1E:B2:0A:F6:D1:FC:64:B1:F9:68:DF:55:06:
                D3:70:DB:13:35:99:FF:2B:6C:42:8C:D0:1D:C7:D6:3A:BC:55:04:08:
                0A:7D:1D:6A:EF:D6:70:32:BB:A4:B5:D3:2A:62:2E:1A:1C:F9:D4:B4:
                B6:87:4E:FF:F8:C3:96:3E:09:2A:86:4C:41:F3:2B:9E:AA:AE:07:0D:
                98:A4:EA:D4:5F:5F:E3:CC:DF:42:1F:79:FF:F7:B6:77:DF:09:BF:38:
                74:6F:1B:18:D0:48:BF:61:02:DC:25:7A:CE:6D:91:79:06:4B:A2:02:
                28:85:A4:D3:7A:21:79:80:31:6C:98:B9:6F:E2:94:BA:CB:AB:A9:03:
                6A:A8:17:3F:26:4B:91:42:E0:5C:53:6E:B2:D3:99:63:24:04:FD:32:
                3A:FF:2D:48:A5:B5:D4:89:B9:DD:D5:FC:FA:15:5E:1D:35:06:C0:46:
                F6:C7:CE:0F:B7:4E:1F:38:F2:5E:A5:46:8E:1C:7C:9C[



 もう一つNFS関連で試そうと思って試していなかったこととしてNFSのセッショントランク/マルチパス(pNFSと勘違いしていた)もあったので、どうせならこれも試してみようと思い今回以下のものを試してみました。

・IPoIB NFS
・IPoIB NFS マルチパス
・NFSoRDMAマルチパス

検証環境について




 今回の検証環境は以下となります

NFSサーバ
OS周り
root@debian:~# uname -a
Linux debian 6.1.0-28-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.119-1 (2024-11-22) x86_64 GNU/Linux
root@debian:~# lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 12 (bookworm)
Release:        12
Codename:       bookworm

ハードウェア周り
X11SPW-TF
CPU:Intel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz 1ソケット
RAM:DDR4 2400 64GB LRDIMM *4 (256GB)
IB HCA;MCX354A-QCBT (QDR Dual port IB HCA@PCIE 3.0 x8)


PVE01/02
OS周り
root@pve01:~# uname -a
Linux pve01 6.8.12-4-pve #1 SMP PREEMPT_DYNAMIC PMX 6.8.12-4 (2024-11-06T15:04Z) x86_64 GNU/Linux
root@pve01:~# pveversion
pve-manager/8.3.1/fb48e850ef9dde27 (running kernel: 6.8.12-4-pve)

ハードウェア周り
JGINYUE B650I NIGHT DEVIL
CPU:16 x AMD Ryzen 7 7700 8-Core Processor (1 Socket)
PVE01 RAM:DDR5 4800 16GB*2 (32GB)
PVE02 RAM:DDR5 4800 8GB*2 (16GB)
IB HCA:MCB194A-FCAT (Connect-IB FDR Dual port HCA@PCIE 3.0x16)


スイッチ
Mellanox SX6036


 今回はIBの最大性能を見たいため、NFSサーバはtmpfsなどのオンメモリなファイルシステムを使っても余裕のあるマシンということでLRDIMMが使えるマシンにしました。シングルスレッドの性能的に若干不安がありますが、RDMAが有効ならCPUの性能がなくても十分な性能を発揮するはずです。HCAについては本来はこちらも MCB194A-FCATを使いたかったのですが、拡張スロットにx16がない(エッジレスx8スロットもない)のとそもそも在庫がないのでCX354Aを使っています。

 QDRが40Gbpsの8b/10b税を払うと32Gbpsとなり、2ポート合計の実効が64GbpsなのでPCIE3.0 x8の帯域ぴったりになりますが、オーバーヘッドを考えると若干足りてない気がします。そのため、とりあえず効果があるかを確認するためにシングルポートQDR以上の帯域が出れば良いことにします。
 ちなみにFDRでリンクしたい場合、ケーブルもFDR対応のものを使わないとQDRになってしまうようです。手持ちのケーブルだと直結してもFDRにはなりませんでした。以前無駄にハマりました。


 そこに接続するマシンはシングルスレッドの性能がほしいため、デスクトップ向けのCPUを搭載したマシンを使っています。今回はAliExpressで何故か安く出ていて買ってしまったRyzen 7 7700を搭載したマシンを2台用意しています。マザボも同じくAliで安く出ていた JGINYUE B650I NIGHT DEVIL というITXの板を利用しています。2台とも石、板、箱がAE調達というAEビルドマシンです。板のメーカーについては全く未知ですが、なにげにあとから9000系の対応BIOSを出しているなど、ある程度やる気はあるようで、今のところ素直に動いています。

前提条件


 OSのインストールなどが終わり普通に使える状態で、どこかでOpenSMなどのサブネットマネージャが動いていて、リンクが上がっていることとします

root@pve01:~# apt install ibutils
root@pve01:~# ibstat
CA 'mlx5_0'
        CA type: MT4113
        Number of ports: 2
        Firmware version: 10.14.2066
        Hardware version: 0
        Node GUID: 0x5849560e5cbc0401
        System image GUID: 0x5849560e5cbc0401
        Port 1:
                State: Active
                Physical state: LinkUp
                Rate: 40
                Base lid: 19
                LMC: 0
                SM lid: 1
                Capability mask: 0x26516848
                Port GUID: 0x5849560e5cbc0401
                Link layer: InfiniBand
        Port 2:
                State: Active
                Physical state: LinkUp
                Rate: 40
                Base lid: 20
                LMC: 0
                SM lid: 1
                Capability mask: 0x26516848
                Port GUID: 0x5849560e5cbc0409
                Link layer: InfiniBand


NFSサーバの準備



 そのままではipoibが使えないので、ib_ipoibモジュールを読み込みibp~というリンクが生えていることを確認します。検証環境のためIP体系が変ですが、通常は192.168.1.0/24、192.168.2.0/24~とかで大丈夫です。

root@debian:~# ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether ac:1f:6b:bd:03:30 brd ff:ff:ff:ff:ff:ff
    altname enp25s0f0
3: eno2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether ac:1f:6b:bd:03:31 brd ff:ff:ff:ff:ff:ff
    altname enp25s0f1
root@debian:~# modprobe ib_ipoib
root@debian:~# ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether ac:1f:6b:bd:03:30 brd ff:ff:ff:ff:ff:ff
    altname enp25s0f0
3: eno2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether ac:1f:6b:bd:03:31 brd ff:ff:ff:ff:ff:ff
    altname enp25s0f1
4: ibp179s0: <BROADCAST,MULTICAST> mtu 4092 qdisc noop state DOWN mode DEFAULT group default qlen 256
    link/infiniband 80:00:02:08:fe:80:00:00:00:00:00:00:e4:1d:2d:03:00:49:2c:61 brd 00:ff:ff:ff:ff:12:40:1b:ff:ff:00:00:00:00:00:00:ff:ff:ff:ff
5: ibp179s0d1: <BROADCAST,MULTICAST> mtu 4092 qdisc noop state DOWN mode DEFAULT group default qlen 256
    link/infiniband 80:00:02:09:fe:80:00:00:00:00:00:00:e4:1d:2d:03:00:49:2c:62 brd 00:ff:ff:ff:ff:12:40:1b:ff:ff:00:00:00:00:00:00:ff:ff:ff:ff


 まずNFSサーバ側に生えたIFにIPを割り振ります。例として 10.111.1.200/24と 10.111.2.200/24を割り振ります。IBはVLANなどの概念はないので同一スイッチの同一サブネット上に2つのIP系ができることになります。IBにもPartitionというものがあるものの、あくまでマルチパスを貼るための都合上の2つのIP体系なので問題ないです。

root@debian:~# ip a add dev ibp179s0 10.111.1.200/24
root@debian:~# ip a add dev ibp179s0d1 10.111.2.200/24
root@debian:~# ip l se up ibp179s0
root@debian:~# ip l se up ibp179s0d1


次にtmpfsでNFSベンチマーク用のオンメモリディレクトリを作成します。今回はRAM領域を240GB作成しています。

root@debian:~# mkdir /exports
root@debian:~# mount -t tmpfs -o size=240G tmpfs /exports/
root@debian:~# mount|grep exports
tmpfs on /exports type tmpfs (rw,relatime,size=251658240k,inode64)


RAM上のFIOベンチマーク


 NFSでマウントされる前に、まずはサーバ上での上限を確認します。

root@debian:~# cd /exports/
root@debian:/exports# fio --name=fiotest --ioengine=io_uring --iodepth=1 --time_based --runtime=10 --rw=randread --bs=8k --size=1G --numjobs=1 --group_reporting
   bw (  MiB/s): min= 3063, max= 3208, per=100.00%, avg=3187.67, stdev=31.30, samples=19
   iops        : min=392128, max=410708, avg=408021.58, stdev=4006.29, samples=19

root@debian:/exports# fio --name=fiotest --ioengine=io_uring --iodepth=8 --time_based --runtime=10 --rw=randread --bs=8k --size=1G --numjobs=8 --group_reporting
   bw (  MiB/s): min=18787, max=22317, per=100.00%, avg=22060.57, stdev=119.99, samples=152
   iops        : min=2404778, max=2856698, avg=2823753.89, stdev=15358.64, samples=152

root@debian:/exports# fio --name=fiotest --ioengine=io_uring --iodepth=1 --time_based --runtime=10 --rw=randwrite --bs=8k --size=1G --numjobs=1 --group_reporting
   bw (  MiB/s): min= 1726, max= 3838, per=100.00%, avg=2622.56, stdev=1016.40, samples=19
   iops        : min=221000, max=491292, avg=335687.05, stdev=130099.60, samples=19

root@debian:/exports# fio --name=fiotest --ioengine=io_uring --iodepth=8 --time_based --runtime=10 --rw=randwrite --bs=8k --size=1G --numjobs=8 --group_reporting
   bw (  MiB/s): min=11517, max=16850, per=100.00%, avg=14898.87, stdev=168.53, samples=152
   iops        : min=1474194, max=2156870, avg=1907055.58, stdev=21572.48, samples=152


 RAM上ではO_DIRECTがないので(当然)direct=1を外していますが、Q1T1だとRandRead408K IOPS、RandWrite335K IOPSで、Q8T8だとRandRead2.8M IOPS、RandWrite1.9M IOPSでした。


exportsの設定


 作成した/exportをNFSの領域に設定します。検証なので*で雑に設定しています。マルチパスを使いたいのでNFS4のオプションである fsid=0にて、/exportsの下をNFSクライアントから見た/(ルートディレクトリ)とします。その下にNFSクライアントからみた/nfs1となるnfs1ディレクトリを作成します。

root@debian:~# echo '/exports *(fsid=0,rw,insecure,no_root_squash,no_subtree_check)' >> /etc/exports
root@debian:~# mkdir /exports/nfs1
root@debian:~# exportfs -avr
exporting *:/exports



PVE側のNFSの設定


 ipoibを読み込ませる以外は普通と同じです。ipoibを読み込ませるとNICがリンク一覧に出てくるので、そこからIPを設定します。ipコマンドから同じように設定することもできますが、ipコマンドの設定だとWebUIから設定を変更したタイミングで揮発するのでWebUIを正としたほうが無難です。再起動時にipoibを読み込ませたい場合は/etc/modulesに追記しておきます

root@pve01:~# modprobe ib_ipoib
root@pve01:~# echo ib_ipoib>>/etc/modules


 IFが生えたらIPとサブネットだけ設定します。





 その後、NFSの設定をします。ただのNFSであればvers=3でよいですが、このあとマルチパスを使いたいので4.2を指定します。PVE特有の話ではなくLinuxでの話になりますが、fsid=0を指定するとNFS3と4でルートディレクトリの始まりが変わるので注意が必要です。(ちなみに上記設定でv3を使う場合は/export/nfs1になります。)





IPoIB上のNFSのベンチマーク


 VMを経由せず、まずはマウントしたクライアントから直接fioを実行して性能を確認します

# read
root@pve01:/mnt/pve/rdma# fio --name=fiotest --ioengine=io_uring --iodepth=1 --time_based --runtime=10 --rw=randread --bs=8k --size=1G --numjobs=1 --group_reporting
   bw (  KiB/s): min=69104, max=89968, per=100.00%, avg=84469.05, stdev=3966.55, samples=19
   iops        : min= 8638, max=11246, avg=10558.63, stdev=495.82, samples=19
root@pve01:/mnt/pve/rdma# fio --name=fiotest --ioengine=io_uring --iodepth=8 --time_based --runtime=10 --rw=randread --bs=8k --size=1G --numjobs=8 --group_reporting
   bw (  KiB/s): min=135712, max=190992, per=100.00%, avg=165444.00, stdev=1502.60, samples=160
   iops        : min=16964, max=23874, avg=20680.50, stdev=187.83, samples=160
root@pve01:/mnt/pve/rdma#  fio --name=fiotest --ioengine=io_uring --iodepth=8 --time_based --runtime=10 --rw=randread --bs=1M --size=1G --numjobs=8 --group_reporting
   bw (  KiB/s): min=305152, max=432128, per=100.00%, avg=371507.20, stdev=3970.09, samples=160
   iops        : min=  298, max=  422, avg=362.80, stdev= 3.88, samples=160

#write
root@pve01:/mnt/pve/rdma# fio --name=fiotest --ioengine=io_uring --iodepth=1 --time_based --runtime=10 --rw=randwrite --bs=8k --size=1G --numjobs=1 --group_reporting
   bw (  KiB/s): min=   16, max=1170816, per=100.00%, avg=559240.53, stdev=486898.18, samples=15
   iops        : min=    2, max=146352, avg=69905.07, stdev=60862.27, samples=15
root@pve01:/mnt/pve/rdma# fio --name=fiotest --ioengine=io_uring --iodepth=8 --time_based --runtime=10 --rw=randwrite --bs=8k --size=1G --numjobs=8 --group_reporting
   bw (  MiB/s): min=   82, max=13513, per=95.40%, avg=3327.49, stdev=630.16, samples=155
   iops        : min=10592, max=1729712, avg=425919.08, stdev=80659.95, samples=155
root@pve01:/mnt/pve/rdma# fio --name=fiotest --ioengine=io_uring --iodepth=8 --time_based --runtime=10 --rw=randwrite --bs=1M --size=1G --numjobs=8 --group_reporting
   bw (  MiB/s): min=  316, max=16376, per=95.07%, avg=3116.83, stdev=768.83, samples=156
   iops        : min=  316, max=16376, avg=3116.83, stdev=768.83, samples=156


 表にまとめると以下です。

                                                                                                                                                                                                                                                                                                                               
IOPS avgBW avg (MiByte/s)BW avg in Gbps
IPoIB single randreadQ1T1 8k1055880.50.675283105
IPoIB single randreadQ8T8 8k20680161.51.354760515
IPoIB single randreadQ8T8 1M362354.32.972084523
 
IPoIB single randwriteQ1T1 8k69905546.14.581019921
IPoIB single randwriteQ8T8 8k4259193327.427.91226091
IPoIB single randwriteQ8T8 1M31163116.826.14561965



 Readに対してWriteおかしくない?????????と思いましたが、何回試してもこの数字でした。atopの通信帯域を見ていましたが、こちらはなぜか16Gbps以上カウントされませんでした。async writeみたいな動きをしていますが、それならreadももっと上がってもいいような気がします。thinking_faceみたいになりながら色々見ましたが何もわからん…IPoIB特有の動きでしょうか…

マルチパスを試す


 NFSのマルチパスについて探し方が悪いのかLinuxでの情報がほんとに見つかりませんでしたが、動きを見ているとmax_connectを指定したあと違うIPの同じExportsを違うディレクトリにマウントすると有効化されるようでした。
https://thinksystem.lenovofiles.com/storage/help/index.jsp?topic=%2Fontap_nfs-trunking%2Fclient-mount-task.html&cp=1_14_3_5_3_1_3

 NFSサーバ側で/export/nfs1と/export/nfs2を作り、PVEからマウントします。先程と同様/exportにfsid0が指定されているのでPVEの設定に落とすと以下になります。ストレージの設定をWebUIから変えてもoptions以下の内容は変更されないので、contentをISOやContainerなどに変更しても都度optionsの書き直しは不要でした。

nfs: rdma
        export /nfs1
        path /mnt/pve/rdma
        server 10.111.1.200
        content images
        options vers=4.2,max_connect=2
        prune-backups keep-all=1

nfs: rdma2
        export /nfs1
        path /mnt/pve/rdma2
        server 10.111.2.200
        content images
        options vers=4.2,max_connect=2
        prune-backups keep-all=1


その後、データストアを無効化し、アンマウントしたあとに再度有効化すると新しい設定が入るようでした。すぐに入らないので少し待つ必要がありました

root@pve01:~# pvesm set rdma2 --disable 1
root@pve01:~# pvesm set rdma --disable 1
root@pve01:~# umount /mnt/pve/rdma
root@pve01:~# umount /mnt/pve/rdma2
root@pve01:~# pvesm set rdma2 --disable 0
root@pve01:~# pvesm set rdma --disable 0



 成功すると違うマウントを指定したはずなのに同じIPでマウントされます。この状態が本当にあっているのかわかりませんが、少なくともatopなどでNICの利用状況を見ていると一つのディレクトリに対する操作を行ったときにそれぞれのNICで通信が発生していました。なんか…違う気がする…


root@pve01:~# mount|grep nfs
10.111.2.200:/nfs1 on /mnt/pve/rdma2 type nfs4 (rw,relatime,vers=4.2,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,max_connect=2,timeo=600,retrans=2,sec=sys,clientaddr=10.111.2.201,local_lock=none,addr=10.111.2.200)
10.111.2.200:/nfs1 on /mnt/pve/rdma type nfs4 (rw,relatime,vers=4.2,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,max_connect=2,timeo=600,retrans=2,sec=sys,clientaddr=10.111.2.201,local_lock=none,addr=10.111.2.200


 とりあえずベンチを回して差が出たか確認します。

### nfs multipath
# read
root@pve01:/mnt/pve/rdma# fio --name=fiotest --ioengine=io_uring --iodepth=1 --time_based --runtime=10 --rw=randread --bs=8k --size=1G --numjobs=1 --group_reporting
   bw (  KiB/s): min=70656, max=86496, per=100.00%, avg=82204.63, stdev=3661.31, samples=19
   iops        : min= 8832, max=10812, avg=10275.58, stdev=457.66, samples=19
root@pve01:/mnt/pve/rdma# fio --name=fiotest --ioengine=io_uring --iodepth=8 --time_based --runtime=10 --rw=randread --bs=8k --size=1G --numjobs=8 --group_reporting
   bw (  KiB/s): min=249520, max=474544, per=99.54%, avg=393720.42, stdev=5737.21, samples=152
   iops        : min=31190, max=59318, avg=49215.05, stdev=717.15, samples=152
root@pve01:/mnt/pve/rdma# fio --name=fiotest --ioengine=io_uring --iodepth=1 --time_based --runtime=10 --rw=randread --bs=1M --size=1G --numjobs=8 --group_reporting
   bw (  KiB/s): min=550912, max=1323008, per=100.00%, avg=808895.33, stdev=20944.35, samples=158
   iops        : min=  538, max= 1292, avg=789.94, stdev=20.45, samples=158

#write
root@pve01:/mnt/pve/rdma# fio --name=fiotest --ioengine=io_uring --iodepth=1 --time_based --runtime=10 --rw=randwrite --bs=8k --size=1G --numjobs=1 --group_reporting
   bw (  KiB/s): min=   32, max=1161296, per=100.00%, avg=616809.41, stdev=469939.92, samples=17
   iops        : min=    4, max=145162, avg=77101.18, stdev=58742.49, samples=17
root@pve01:/mnt/pve/rdma# fio --name=fiotest --ioengine=io_uring --iodepth=8 --time_based --runtime=10 --rw=randwrite --bs=8k --size=1G --numjobs=8 --group_reporting
   bw (  MiB/s): min=  256, max=12827, per=100.00%, avg=4207.57, stdev=578.61, samples=149
   iops        : min=32824, max=1641910, avg=538569.22, stdev=74061.61, samples=149
root@pve01:/mnt/pve/rdma# fio --name=fiotest --ioengine=io_uring --iodepth=1 --time_based --runtime=10 --rw=randwrite --bs=1M --size=1G --numjobs=8 --group_reporting
   bw (  MiB/s): min=  484, max=19754, per=98.44%, avg=4232.42, stdev=820.85, samples=156
   iops        : min=  484, max=19754, avg=4232.42, stdev=820.85, samples=156


 シングルとの差分を表にまとめるとこうなります

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
IOPS avgBW avg (MiByte/s)BW avg in GbpsMultipath_efficiency (%)
IPoIB single randreadQ1T1 8k1055880.50.675283105
IPoIB con=2 randreadQ1T1 8k1027580.30.67360538399.7515528
IPoIB single randreadQ8T8 8k20680161.51.354760515
IPoIB con=2 randreadQ8T8 8k49215384.53.225420545238.0804954
IPoIB single randreadQ8T8 1M362354.32.972084523
IPoIB  con=2 randreadQ8T8 1M789789.96.626163039222.9466554
 
IPoIB single randwriteQ1T1 8k69905546.14.581019921
IPoIB con=2 randwriteQ1T1 8k77101602.35.052459803110.2911555
IPoIB single randwriteQ8T8 8k4259193327.427.91226091
IPoIB con=2 randwriteQ8T8 8k5385694207.635.29591544126.4530865
IPoIB single randwriteQ8T8 1M31163116.826.14561965
IPoIB con=2 randwriteQ8T8 1M42324232.435.50395296135.7931211


 相変わらず書き込みの速度だけはぶっ壊れていますが、パスが増えたことによって複数スレッドでの読み込みの速度の増加が確認できたので、マルチパスの効果はあるようです。しかしそれ以上にやはりIPoIBはなにかある気がします。もともと大して期待はしていませんでしたが、それにしても読み込みが合計80Gbpsに対して実測6Gbpsというのは悲しいです。
 ただ、効果があるのは確かなので、イーサネットの場合には意味があると思います。

NFSoRDMAを検証する


 IPoIBの挙動が謎すぎて時間を取られてしまいましたがようやくメインディッシュです。サーバ側に svcrdmaを、クライアント側に xprtrdmaを読み込ませ、RDMAを有効化していきます。

root@debian:~# modprobe svcrdma
root@debian:~# systemctl restart nfs-kernel-server



 このままだとproto=rdmaを指定してRDMAで接続をしようとしたときに接続拒否されるので、portlistにrdmaの標準ポートである20049を追加します。

root@debian:~# cat /proc/fs/nfsd/portlist
tcp 2049
tcp 2049
root@debian:~# echo rdma 20049 > /proc/fs/nfsd/portlist
root@debian:~# cat /proc/fs/nfsd/portlist
rdma 20049
rdma 20049
tcp 2049
tcp 2049


 オンザフライでの変更ではなく、設定ファイルで変更する場合は/etc/nfs.confにてrdma=yを有効にしたあとにサービスの再起動が必要になります。

# /etc/nfs.conf
[nfsd]
# debug=0
# threads=8
# host=
# port=0
# grace-time=90
# lease-time=90
# udp=n
# tcp=y
# vers3=y
# vers4=y
# vers4.0=y
# vers4.1=y
# vers4.2=y
rdma=y
rdma-port=20049

root@debian:~# systemctl restart nfs-kernel-server
root@debian:~# cat /proc/fs/nfsd/portlist
rdma 20049
rdma 20049
tcp 2049
tcp 2049



 次に、PVE側の設定を行います。事前にコマンドラインからmountコマンドでproto=rdmaで接続できることは確認したので、sotrage.confに合う設定を入れます。今回はRDMAの最大速度を見たいのでいきなりマルチパスで行います


#/etc/pve/storage.cfg 
nfs: rdma
        export /nfs1
        path /mnt/pve/rdma
        server 10.111.1.200
        content images
        options vers=4.2,max_connect=2,proto=rdma
        prune-backups keep-all=1

nfs: rdma2
        export /nfs1
        path /mnt/pve/rdma2
        server 10.111.2.200
        content images
        options vers=4.2,max_connect=2,proto=rdma
        prune-backups keep-all=1

root@pve01:~# modprobe xprtrdma
root@pve01:~# echo "xprtrdma" >> /etc/modules

root@pve01:~# pvesm set rdma2 --disable 1
root@pve01:~# pvesm set rdma --disable 1
root@pve01:~# umount /mnt/pve/rdma
root@pve01:~# umount /mnt/pve/rdma2
root@pve01:~# pvesm set rdma2 --disable 0
root@pve01:~# pvesm set rdma --disable 0


 しばらく待ち、NFSのマウントオプションにproto=rdmaがある状態でNFSがマウントされたかを確認します。

root@pve01:~# mount|grep rdma
10.111.1.200:/nfs1 on /mnt/pve/rdma type nfs4 (rw,relatime,vers=4.2,rsize=1048576,wsize=1048576,namlen=255,hard,proto=rdma,max_connect=2,port=20049,timeo=600,retrans=2,sec=sys,clientaddr=10.111.1.201,local_lock=none,addr=10.111.1.200)
10.111.1.200:/nfs1 on /mnt/pve/rdma2 type nfs4 (rw,relatime,vers=4.2,rsize=1048576,wsize=1048576,namlen=255,hard,proto=rdma,max_connect=2,port=20049,timeo=600,retrans=2,sec=sys,clientaddr=10.111.1.201,local_lock=none,addr=10.111.1.200)



 まずはPVE01で直接IOを発行してベンチマークをかけます

### nfs over RDMA
#read
root@pve01:/mnt/pve/rdma# fio --name=fiotest --ioengine=io_uring --iodepth=1 --time_based --runtime=10 --rw=randread --bs=8k --size=1G --numjobs=1 --group_reporting
   bw (  KiB/s): min=106624, max=131440, per=99.95%, avg=128103.58, stdev=5284.67, samples=19
   iops        : min=13328, max=16430, avg=16012.95, stdev=660.58, samples=19
root@pve01:/mnt/pve/rdma# fio --name=fiotest --ioengine=io_uring --iodepth=8 --time_based --runtime=10 --rw=randread --bs=8k --size=1G --numjobs=8 --group_reporting
   bw (  MiB/s): min= 1092, max= 1991, per=99.85%, avg=1828.98, stdev=30.13, samples=152
   iops        : min=139790, max=254956, avg=234109.58, stdev=3856.49, samples=152
root@pve01:/mnt/pve/rdma# fio --name=fiotest --ioengine=io_uring --iodepth=1 --time_based --runtime=10 --rw=randread --bs=1M --size=1G --numjobs=8 --group_reporting
   bw (  MiB/s): min= 2964, max= 3824, per=99.77%, avg=3633.37, stdev=33.98, samples=152
   iops        : min= 2964, max= 3824, avg=3633.37, stdev=33.98, samples=152



#write
root@pve01:/mnt/pve/rdma# fio --name=fiotest --ioengine=io_uring --iodepth=1 --time_based --runtime=10 --rw=randwrite --bs=8k --size=1G --numjobs=1 --group_reporting
   bw (  KiB/s): min=245120, max=1139216, per=98.00%, avg=782675.37, stdev=331891.63, samples=19
   iops        : min=30640, max=142402, avg=97834.42, stdev=41486.45, samples=19
root@pve01:/mnt/pve/rdma# fio --name=fiotest --ioengine=io_uring --iodepth=8 --time_based --runtime=10 --rw=randwrite --bs=8k --size=1G --numjobs=8 --group_reporting
   bw (  MiB/s): min=  903, max=13740, per=100.00%, avg=5349.23, stdev=563.43, samples=158
   iops        : min=115650, max=1758800, avg=684700.85, stdev=72119.49, samples=158
root@pve01:/mnt/pve/rdma# fio --name=fiotest --ioengine=io_uring --iodepth=1 --time_based --runtime=10 --rw=randwrite --bs=1M --size=1G --numjobs=8 --group_reporting
   bw (  MiB/s): min= 2088, max=16382, per=100.00%, avg=6476.16, stdev=562.35, samples=158
   iops        : min= 2088, max=16382, avg=6476.16, stdev=562.35, samples=158



 IPoIBマルチパスとの比較を表にまとめます
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
IOPS avgBW avg (MiByte/s)BW avg in GbpsRDMA_efficiency (%)
IPoIB single randreadQ1T1 8k1055880.50.675283105
IPoIB con=2 randreadQ1T1 8k1027580.30.673605383
RDMA con=2 randwriteQ1T1 8k16012128.1031.074606107159.5305106
IPoIB single randreadQ8T8 8k20680161.51.354760515
IPoIB con=2 randreadQ8T8 8k49215384.53.225420545
RDMA con=2 randwriteQ8T8 8k2341091828.9815.34259992475.6775033
IPoIB single randreadQ8T8 1M362354.32.972084523
IPoIB  con=2 randreadQ8T8 1M789789.96.626163039
RDMA con=2 randwriteQ8T8 1M36333633.330.47833671459.9696164
 
IPoIB single randwriteQ1T1 8k69905546.14.581019921
IPoIB con=2 randwriteQ1T1 8k77101602.35.052459803
RDMA con=2  randreadQ1T1 8k97834782.6756.565555332129.9477005
IPoIB single randwriteQ8T8 8k4259193327.427.91226091
IPoIB con=2 randwriteQ8T8 8k5385694207.635.29591544
RDMA con=2  randreadQ8T8 8k6847005349.2344.87260427127.1325696
IPoIB single randwriteQ8T8 1M31163116.826.14561965
IPoIB con=2 randwriteQ8T8 1M42324232.435.50395296
RDMA con=2  randreadQ8T8 1M6476647654.32463836153.0101125


 ReadQ1T1がもう一声欲しいですが、サーバ側のatopを見ていると1コアのCPU使用率が張り付くことがあり、1セッションのIOはシングルスレッドの性能に依存するようで、サーバ側の限界かもしれません。それ以外は圧倒的に速いです。特に弱かったReadが増強され、Q8T8においては200k IOPSを達成できました。Writeの速度に関しては54Gbpsを記録しました。NFSでこれだけの性能が出たら十分すぎます。

VMを立てて検証してみる


 RDMAなストレージが十分に速いことが確認できたら、その上でWindowsVMを立てて実際に速いのか試してみます。Windowsのセットアップの注意点としては、VirtIOを使わないと性能が出ないためSCSIコントローラにVirtIO SCSI Singleを選択する必要があります。また、それに伴いOSインストール時にドライバの読み込みが必要になるので、virtio-driverの入ったISOをマウントするために2つ目のCDROMが必要になります。SATAでセットアップしてから後でVirtIOに変更することもできますが、トラブルを防ぐため正攻法でセットアップしました。

 上記の設定をしてインストールを進めていく中で、まず速度の差を実感しました。Win11のセットアップは時間がかかりますが、明らかに速いです。もっとも、これはRyzen 7 7700が速いという気もしますが、IOがネックになっていないというのも大きいと思います。

 なんやかんやしてWindowsのセットアップが終わったら、いつものCDMをまわします。以下がその結果で、それぞれMiB/sとIOPSです。





 Q1T1については4kで12k IOPSが出ているのでfioのテスト結果と一致しますが、fioで8Q8Tを実行したときには234k IOPSが出ているのに対してVM上では174k IOPSと、いい線いっていますがもう少し出てもいいかと思いました。

 帯域についてはQDRの理論上限値32Gbpsで、MB/sに直すと4000MB/sとなり、マルチパスがうまく使えてない気もします。ただ、perfqueryやatopのIBリンクの情報を見ていると、両ポートでパケットの送受信は行われているようです。複数ディスクでのIOが発生したらまた違うのかもしれません。

 Writeについては値がぶっ壊れることもなかったので、fioのテスト方法が正しくなかったか、上記のマルチパスがうまく使えていないという問題がある気がします。

 virtioの上限についてはPVEホストにtmpfsを作成してVMからRAM上に作成したドライブにベンチマークをした限りはもっと高い値が出たので、何かしらチューニングがあるのかもしれないですが、ぱっと検証した限りはUnsafe WBを使う以外の方法ではこれが限界でした。

 ちなみに今回初めてRyzen 7 7700をVMホストとして使いましたが、やっぱりレスポンスがいいので PassMark PerformanceTest をまわしてシングルスレッドの性能を見てみましたが、VMでも4k超えとかなり調子が良いことがわかりました。





 ちなみにPassmarkのシングルスレッドの数値と個人的なWindowsを使う感覚としては
 ~700 なにするのもつらい
 1000~1500 もたつくがIOが十分に速ければコア数によってはなんとか使える
 1500~2400 IOが速ければそれなりに使える
 2400~3000 はやい
 3000~4000 目に見えて速い
 4000~    新世界
 という感じです。 

 AMDのCPUの場合、95度に張り付くまでカツカツを攻めるという動きをするので、ついているCPUクーラーと外気温によって大きくスコアが変わるのですが、同じ状態でWindowsをベアインストールして試していたときは4100-4200くらいだったと思います。

 なので、多少の仮想化税はありますが、それでも十分速いと思います。ちなみにこのITXの筐体にはAXP90-X47 FULLというFull copperなクーラーを使っています。もともと先に組んだPVE02の筐体に組み込もうとしたのですがクーラーの高さが僅かに高く、箱に入り切らずSHURIKEN 3を買うことに…。

マイグレーションを試してみる


 もう一つ気になることとして、PVEでライブマイグレーションをするために正しく共有ストレージとして認識されているか、RAM転送でIBを使うように設定してうまくいくのか、というのがあったので試してみました。ようやくPVE02の出番です。

 基本的には01と同じ設定を行い、01側でクラスターを作成して02を参加させました。クラスターを作成するときに、裏LANがあるとそれを追加できるので、IPoIBのインターフェースも追加します。画像では0,1がIBで2がEtherですが、これは数値が高いほうが優先されるという動きになるので、これで作ったあとに間違いに気が付き10.111.1.201を20、10.111.2.201を10、172.20.1.51を1に変えました。





 参加した段階でproto=rdmaが書かれたstorage.cfgが02側にも配布されるので、xprtrdmaが読み込まれていれば参加した時点でNFSのRDMAマウントができていました。

 ここでハマったのが何故か1G Etherを使ってRAMの転送を行ってしまいIBが使われず困ったのですが、Datacenter→OptionsにMigration settingsという項目があり、どのIPを使うか指定する必要がありました。これをIBの裏LANアドレスを指定することにより、IPoIBですがIBが使われるようになりました。




2024-12-08 04:38:18 use dedicated network address for sending migration traffic (10.111.1.202)
2024-12-08 04:38:18 starting migration of VM 101 to node 'pve02' (10.111.1.202)
2024-12-08 04:38:18 starting VM 101 on remote node 'pve02'
2024-12-08 04:38:19 start remote tunnel
2024-12-08 04:38:20 ssh tunnel ver 1
2024-12-08 04:38:20 starting online/live migration on unix:/run/qemu-server/101.migrate
2024-12-08 04:38:20 set migration capabilities
2024-12-08 04:38:20 migration downtime limit: 100 ms
2024-12-08 04:38:20 migration cachesize: 1.0 GiB
2024-12-08 04:38:20 set migration parameters
2024-12-08 04:38:20 start migrate command to unix:/run/qemu-server/101.migrate
2024-12-08 04:38:21 migration active, transferred 656.7 MiB of 8.0 GiB VM-state, 813.8 MiB/s
2024-12-08 04:38:22 migration active, transferred 1.4 GiB of 8.0 GiB VM-state, 801.0 MiB/s
2024-12-08 04:38:23 migration active, transferred 2.1 GiB of 8.0 GiB VM-state, 803.5 MiB/s
2024-12-08 04:38:24 migration active, transferred 2.9 GiB of 8.0 GiB VM-state, 778.9 MiB/s
2024-12-08 04:38:25 migration active, transferred 3.6 GiB of 8.0 GiB VM-state, 1.0 GiB/s
2024-12-08 04:38:26 migration active, transferred 4.3 GiB of 8.0 GiB VM-state, 783.7 MiB/s
2024-12-08 04:38:27 migration active, transferred 5.2 GiB of 8.0 GiB VM-state, 856.8 MiB/s
2024-12-08 04:38:27 xbzrle: send updates to 36484 pages in 27.2 MiB encoded memory, overflow 2013
2024-12-08 04:38:27 average migration speed: 1.1 GiB/s - downtime 86 ms
2024-12-08 04:38:27 migration status: completed
2024-12-08 04:38:30 migration finished successfully (duration 00:00:12)
TASK OK



 ストレージは正しく共有と認識されたので、RAMだけの転送となりました。8GBのRAMを割り当てたWindowsをマイグレしたところ大体10GbpsくらいでRAMの転送がされ、12秒で終わりました。マイグレ中もあえてリモデから色々やっていましたが、一瞬マウスカーソルにラグのようなものが出たと思ったら終わっていました。はやい。



検証した感想


 NFSのMPIOやRDMAなどを初めて設定しましたが、かなり良い結果がでたとおもいます。PVE自体は触り始めて1ヶ月経っていないのでお作法的な部分がまだよくわかっていないですが、中身がDebianという15年の付き合いのあるOSなので個人的には非常に取り掛かりやすいです。

 PVEはVMwareとは違ってハードウェアの対応の広さや、困ってもLinux的に色々とどうにかできるのが良いと思いました。それ以外にもHAレプリケーションやVMwareでいうVDPがより使いやすいPBSとして提供されているなど、非常によくできていると感じました。

 PVE自体にvSAN的なCephもあり、今更ストレージとコンピュートノードを分けるなや!!時代はHCIやで!!! という思想も感じますが、小さいマシンだとIOスロット数の問題やOSD自体のIOコンピュートコストが馬鹿にできないこともあり、できればIOを分けてホストはVM/Containerの処理に専念させたい、ということもあります。

 合計100コア以上のXeonSP/EPYCと数TBのRAMと有り余るSSDと100GbEを搭載したマシンが10台以上ある、というような構成ならともかく、家で使うときにはSPoFができることを承知で ストレージはストレージマシンにまとめ、 IBと10Gと起動用SATA DOMに相当するものを積んでおけばとりあえず快適に使える、というのは便利です。
 ノードごとにNVMEストレージを揃えたくないという気持ちがあったので、IBさえ繋いでおけばSATA SSDより速い大容量な集約ストレージが使えることになり、これを解決できたのは個人的にかなり大きいです。

 また、特にWindowsなどの検証をするとIOの速さが利用度の快適さに直結するので、IOの速さというのは譲れない要件でもあります。ストレージマシンが死ぬと地獄を見るというのは回避できませんが、クラスター型ストレージが死なないかと言われるとまたそれも微妙なところで、メタデータに問題が起きるとこれも地獄になります。
 そういった面で問題が起きたときにシンプルさが強さになることもあります。

 共有ブロックストレージ+クラスター型ファイルシステムではなく、NFSというファイルベースストレージなのもシンプルでよいです。色々トラブルを踏むとシンプルさに帰結する気がします。
 代償としてNFSは細かいIOが苦手、というのがありますが、今回のRDMAの結果からすると十分実用域だと思います。

 ただ、散々IBについて話してきましたが、今からお家でInfinibandをおすすめできるか、というと、微妙…むしろやめといたほうが無難、と言わざるを得ないです。

 以前も書いたように、IB自体はL2で使う限りはスイッチやHCA含め、帯域に対しての消費電力がかなり低いのでいい面があるのですが、IBはIBでありイーサネットではないので、利用できる用途がかなり限られます。自分もほぼストレージ用インターフェースとしてしか使っていません。

 やはり用途が限られるのとIB特有の必要な知識があまり表面に出てこないので、IBまわりは本当に需要がなく、国内外で捨て値で売られています。今回検証に使ったスイッチのSX6036なんて3000円で買っています。元が底値で買っているので、仮にこれから一から揃えるとしても、万が一手放すときには再販価値はない、という前提になります。

 まあ16/32Gb FC-SANを始めたいと思うのであればそれよりも安価ですが、FC-SANはドライバさえあれば確実にブロックデバイスとして認識できるというメリットもあります。あまりにもコアなメリットですが特定のときに刺さります。

 しかし、再販価値がない前提で物が底値で手に入るというのは、わかっているオタクからすると二束三文で高速IOを手に入れられるというメリットでもあります。「もうIBやめたい」と思いながらも物が安いので抜け出せず、なんだかんだ物が増えてしまいました。

 その中で「一部のSASケーブルはIBのQSFPケーブルとしても使える(※ただしQDRのみ)」というしょうもない知識を手に入れたので、ケーブルなどもかなり安く手に入れられるようになりました。一度嵌ると抜け出せない沼です。



 総評としては、「確かにIBとRDMAはかなり高速でこれに依存すればノードの構成が楽になるものの、これを目当てに新規でIB一式を揃えようとするのは覚悟が必要になるので手放しでお薦めはできない」でしょうか。
 もう少し使ってみたうえで問題が起きたらまた記事にしようと思います。

以上!


コメントを書く
必要事項とコメントを入力して下さい。









タグの挿入