【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一式を揃えようとするのは覚悟が必要になるので手放しでお薦めはできない」でしょうか。
 もう少し使ってみたうえで問題が起きたらまた記事にしようと思います。

以上!


[ コメントを書く ] ( 124 回表示 )   |  このエントリーのURL  |  $star_image$star_image$star_image$star_image$star_image ( 2.8 / 61 )  |  
【その他】リモートデスクトップでrdpファイルにselectedmonitorsの記述をしてもうまく動かないときの確認点 



  Windowsのリモートデスクトップですが、実はマルチモニター環境で特定の画面だけにまたがって全画面表示する、という機能があります。この機能を使ってとあるVMで作業するときに「1枚の全画面だと微妙に使いにくいけどマルチモニタすべての画面をリモデに使いたくない」という要件を満たすべく2枚の画面だけを使うようにしていました。

https://www.hanselman.com/blog/how-to-remote-desktop-fullscreen-rdp-with-just-some-of-your-multiple-monitors
https://qiita.com/yusuke-sasaki/items/5f70dc266caf13021838

 先に結論を書くと、 RDPで複数画面が使えない!というときは、使いたいディスプレイ同士が0pxで隣接しているかを確認する必要があります。以下、検証のメモです。


 実際のモニターのセットアップはこうなっています。グラボの出力順を考慮していないのでぐちゃぐちゃになっていますが、この中から5,6を使ってRDPを全画面表示する、ということをしていました。




 今までこれは動いていたのですが、モニターのつながっているグラボの出力端子を入れ替えたタイミングで画面をまたがっての表示が動かなくなってしまいました。 mstsc /lで調べてみると、端子が入れ替わったことによってモニターのIDが変わっていたので、IDを確認してrdpファイルに書かれている順番を入れ替えれば動くだろう、と思いました。しかし、若干の移動はあったもののIDは大きく変わっておらず、何をやってもモニタをまたがって全画面表示ができずプライマリモニタだけ全画面で表示される挙動を示しました。

 よくわからないので一度マシンを再起動し、もう一度mstsc /lをすると今度は何故かモニターIDが大きく変わっていた(1,3,10,11,12,13から0,1,2,3,4,5になっていた)のですが、それを直しても動かず、色々検証すると下記設定の赤字0,2の2画面表示と2,4及び5,3の2画面表示は問題なく動き、なぜか2,4,5,や4,5と横一列にしようとしたときに5を含むと動かなくなるということがわかりました。




 検証に使った最低限の要素だけ含めたrdpファイルは以下になります。

## NG ##
full address:s:VM1
use multimon:i:1
selectedmonitors:s:4,5
dynamic resolution:i:0


## OK ##
full address:s:VM1
use multimon:i:1
selectedmonitors:s:0,2
dynamic resolution:i:0


 プライマリモニタがRX7900XTXにつながっていることもあり、最初はRadeon仕草かと思いましたが、 mstsc /lの値をよく見るとmstsc側ID(画像赤文字)の5の起点座標がおかしい事に気が付きました。この場合、0,0から3839,2159が4の値なので5が3840から始まらないといけないのですが、なぜか3853となっており、赤字4と5の間に13ピクセルの隙間があるいうことがわかりました。

 カッコ内部の値はそれぞれプライマリモニタ(この場合は赤字の4)の左上を0,0として右下に向けて座標がプラスになり、プライマリモニタより左にモニタがあればxはマイナスに、プライマリモニタより上にモニタあればyがマイナスになります。

 Windowsの仕様として隣り合うモニタ同士はどこかでくっついていないといけないのですが、赤字5は赤字3とくっついているという扱いになり、4と5は13ピクセル離れている、という認識になっているようです。出力端子を入れ替えたのがちょうど赤字の5にあたるものだったのですが、Windowsで画面の位置を移動したときにそれがズレたようです。わかるかそんなモノ…。

 離れてるのであればマウスの移動もできないのではと思いましたが、ある程度の加速度があると多少の隙間を乗り越えて隣のモニタに行けるようです。逆に、隙間があるととてもゆっくりマウスを動かしてモニタの境界線をまたごうとするとブロックされます。密着しているとマウスカーソルの加速度が1px/sでも境界をまたげるようです。たまに感じた違和感はこれだったのか…。




 ディスプレイのスナッピングとの格闘の末、最終的に一回り小さい赤字の1以外は0pxで整列できたのですが、どうしても最後の1つが完全密着になりませんでした。1の値は1279,ー1440,3840,ー1になるべきなのですが、Windowsのディスプレイ設定は2辺と隣り合うことを想定していないのか、赤字の3の左辺、もしくは4の上辺のどちらかにしかスナップせず、逆にどちらかにスナップすると絶対にもう一方は合わないという動きをしていました。

 ただ、赤字の0,2,4,5の横一列は整列されたので、当初赤字4,5の2画面でリモデが使えないという問題は解決しました。現状、赤字の1,3を使って画面をまたいでリモデをする、ということができませんが、このパターンはないと思うので諦めました…。座標を使ってディスプレイの場所を指定できる方法があればいいんですが…。



 変な環境でディスプレイを出力していてRDPで複数画面が使えない!というときは、使いたいディスプレイ同士が0pxで隣接しているかを確認する必要がある、という、ほんとに使い所のないナレッジを得たのでここに供養します。

追記:
 その後またディスプレイポートの差し替えがありスナッピングと格闘していましたが、それを便利にするDPEditというツールがあるのを知りました。
 DPEdit -A simple Windows command line utility to accurately set the relative position of displays in a dual- or multi-monitor setup -

 dpedit /L でつながっているディスプレイ一覧を取得できます。

dpedit /L

Display #5
Device name: \\.\DISPLAY5
Device string: AMD Radeon RX 7900 XTX
Active: 1
Mirroring: 0
Modes pruned: 0
Primary: 0
Removable: 0
VGA compatible: 0
Dimensions: {3840, 2160}
Position: {3840, 0}

Display #6
Device name: \\.\DISPLAY6
Device string: AMD Radeon RX 7900 XTX
Active: 1
Mirroring: 0
Modes pruned: 0
Primary: 4
Removable: 0
VGA compatible: 0
Dimensions: {3840, 2160}
Position: {0, 0}

Display #10
Device name: \\.\DISPLAY10
Device string: NVIDIA Quadro M2000
Active: 1
Mirroring: 0
Modes pruned: 0
Primary: 0
Removable: 0
VGA compatible: 0
Dimensions: {3840, 2160}
Position: {-7680, 0}

Display #11
Device name: \\.\DISPLAY11
Device string: NVIDIA Quadro M2000
Active: 1
Mirroring: 0
Modes pruned: 0
Primary: 0
Removable: 0
VGA compatible: 0
Dimensions: {2560, 1440}
Position: {1280, -1440}

Display #12
Device name: \\.\DISPLAY12
Device string: NVIDIA Quadro M2000
Active: 1
Mirroring: 0
Modes pruned: 0
Primary: 0
Removable: 0
VGA compatible: 0
Dimensions: {3840, 2160}
Position: {-3840, 0}

Display #13
Device name: \\.\DISPLAY13
Device string: NVIDIA Quadro M2000
Active: 1
Mirroring: 0
Modes pruned: 0
Primary: 0
Removable: 0
VGA compatible: 0
Dimensions: {3840, 2160}
Position: {3840, -2160}


 dpedit [ディスプレイ番号] [x座標] [y座標] でディスプレイを一つづつ、もしくは複数指定して配置を指定できます。上記の内容を5,6,10,11,12,13の順で一発で指定しようとすると以下のようになります

dpedit 5 3840 0 6 0 0 10 -7680 0 11 1280 -1440 12 -3840 0 13 3840 -2160


 複雑な配置を行っている場合にピクセルパーフェクトで配置できずにツラミを感じている人は他にもいるようなので、そのうちPowerToysに入るかもしれません
  Fine-tune multi-monitor positioning tool #2652 - microsoft / PowerToys -

 いずれにしても、これで不毛なイラつきがなくなったのでまた一つPCの利用が快適になりました。




[ コメントを書く ] ( 275 回表示 )   |  このエントリーのURL  |  $star_image$star_image$star_image$star_image$star_image ( 2.7 / 268 )  |  
【HW】複数枚のGPUを搭載したWindowsのGPUの扱いについて 
複数GPUを搭載している環境だと、WindowsのバージョンによってセカンダリGPUの扱いが違うというのがあるのでそのメモです。前回の内容に入れようと思った内容だったのですが長くなりすぎたので分けました。

昔は表示しているディスプレイごとに使うGPUが違った


 古のXPからW10の何処かのバージョンまでは、グラボが複数あってそれぞれ画面を出力している場合、その画面を表示しているGPUがその内容を処理していました。
 そのため、ゲーム用に使うGUPは画面を1枚だけつなぎ、それ以外の雑多な内容を表示するためのをGPUを別に搭載することによって一種の負荷分散が出来ました。1080を買った直後くらいまでは「ゲーム用GPUは画面を1枚だけ出すのが一番パフォーマンスが出る」という動きをしていた記憶があります。

すべての処理がプライマリGPU処理に


 それがWindows10のどこかのバージョン(20H2くらい?)からすべての内容はプライマリGPUで処理し、その結果を各ボードのRAMにコピーする、という挙動になっていました。その結果、 利点としては HEVCの動画やウィンドウモードで起動したゲームなどを、低性能なセカンダリGPUにつながっているディスプレイ領域にもっていってもそれなりに動く、という挙動になりました。




 上記はセカンダリGPUとしてつながっているQuadro M2000から出力しているディスプレイにWindowモードで起動したARKを持っていった際のスクショですが、1080でも厳しい内容であっても処理している実態は7900XTXなので余裕で100FPS出ています。

 しかし、欠点としてはどうやってもプライマリGPUの負荷だけが上がるようになり、違うGPUにつながっているディスプレイにブラウザなどを置くと、プライマリGPUで描写→セカンダリGPUのVRAMにコピーという無駄なステップが増えてしまったり、セカンダリGPUのVRAMやビデオデコーダが有効活用できないという問題がありました。

無理やりプログラムが使うGPUを指定する


 プロセスが利用するGPUは、プロセスが立ち上がるときにプライマリになっているディスプレイ(「このディスプレイをメインディスプレイにする」のディスプレイ)がつながっているGPUとなります。
 一時期はブラウザを起動する前にプライマリディスプレイをセカンダリGPU側のディスプレイに変更し、ブラウザの起動が終わったらプライマリディスプレイをもとに戻す、ということを行うことによって強制的にブラウザの処理をセカンダリGPUにさせていた時期もありました。
 ただ、これをするとFirefoxがWebGLをレンダリングできなくなるというような問題があったので微妙なところでした。

Win10から処理するGPUを指定できるようになったが…


 Windows10の21H2くらいからプロセスを処理させるGPUを指定できたのですが、試したところ省電力、高パフォーマンスというくくりは、UDHグラフィックスなどのiGPUを省電力GPU、それ以外のディスクリートGPUは高パフォーマンスGPUというくくりでした。
 なので、両方ともディスクリートであるM2000と1080という組み合わせの場合、どちらかを省エネグラボに指定する、ということは出来ませんでした。
https://forest.watch.impress.co.jp/docs/news/1270962.html

 M2000を省電力、1080を高パフォーマンスと指定できる方法がないかと探したのですが、結局iGPU以外は省電力GPUとして指定することは出来ませんでした。

Win11でいつの間にかプロセスのGPU割当機能が追加された


 7900XTXに換装後も4k複数枚でブラウザを起動したままにしていると相変わらずVRAMを10GBとか消費することがある(ブラウザを閉じてもDWMが4GBくらい抱えたままになる)のは解消しませんでした。メモリに余裕があるとはいえ流石にどうにかしたいです。

 WindowsUpdateを当てたらマシにならないかとWindowsUpdateを当てることにしたのですが、上記のグラフィックスのInsider previewを試すためにDevビルドにしたままなのを忘れていて、久しぶりにUpdateを当てたらWin11のInsider previewが降ってくるという事故を起こしてしまいました。
 意図せずWin11(のInsider preview)になってしまったのですが、ロールバックする前に今の環境をWin11にした場合の挙動を確認することにしました。

 仕事では13世代のi5を使う関係でW11を使っていますが、W10に比べてすべての動作が緩慢なのでW11をメイン機に入れたくないと思っていましたが、使ってみると「あれ…なんか逆に調子いい?」という感じだったので、そのまま利用を続行しました。CPUはRyzen 5 5600XなのでP/Eコアというものはなく、OSはどちらを使っても問題ないはずなのですが…。

 そして、いろいろな設定を巡っているといつの間にかプログラムを処理するGPUを指定できるようになっていました。




 W11も初期のバージョンはこの「特定のGPU」という選択肢がなかったように思いますが、いつの間にか増えていました。確認したバージョンは Insider Preview Build 23612 (Dev Channel)となりますが、ビルド的に多分22H2くらいにも入っている気がします。

 その結果、ブラウザでなにか調べたり動画を見たりするときにはセカンダリGPUのVRAMやビデオデコーダがが使われるようになり、いい感じに動くようになりました。




 もっと前にこれができるようになっていてほしかったですが、ようやく欲しかった機能が実装されました。

まとめ


 プライマリGPUがミドルクラス〜ローエンドのグラボを使っていて、補助GPUを使って少しでもプライマリ側の負荷を下げたい、というときにはこのオプションは有効だと思います。

 現行のミドルクラスならベース性能がかなり上がっているのでその必要もない気はしますが、VRAMが8GBクラスのものだとゲーム用にメモリを空けるためにブラウザ処理用の補助GPUが欲しくなる気もします。
 ちなみに4kだとゲームを起動すると8GB位のメモリはあっという間に使うので、4kでゲームをしたいなら12GB、できれば16GB以上は必要だと感じました。

 動画のHWエンコードソフトから使うGPUを選べないような場合も、強制的に使うGPUを指定することによって負荷分散ができるようになります。1080とM2000を組み合わせたときにはOBSやD3DGearのHEVC録画はGPUを分けたほうがゲームのFPSは安定していました。

 また、プライマリGPUの性能が高いのであれば、 NVS810 のような1スロットで8枚DPをはやせるGPUでもプライマリGPUの性能によって快適に使えるので、ある種のハブとして使うといった柔軟性がかなり高くなったと思います。
 

 こういったカーネル周りに手が入って良くなっている感じはするので、事故で上げてしまったW11はW10に戻さずに使うことにします。

以上


[ コメントを書く ] ( 251 回表示 )   |  このエントリーのURL  |  $star_image$star_image$star_image$star_image$star_image ( 3 / 270 )  |  
【HW】グラボの消費電力を下げるためにFSR/RSRを使う 


 普段使っているモニタを4k*4枚に変更したのですが、ふとゲームをしようと思ったら今まで使っていたGTX1080では処理能力が足りなくなったためグラボを変えようと思っていたところに、じゃんぱらでRadeon RX7900XTXがなぜか特価で出ていたため久々のRadeonにしました。その時に色々検証したのでその時のメモです。

ことの発端


 仕事でモニタを使うときに、置く場所の関係であまり多くのモニタを置くことができなかったので、モニタの中に極限まで情報をぶち込みたいという欲求から27インチの4Kモニタをスケーリング無しで使っていました。目を潰しながら4kを使っているうちに慣れてきたので、家で使っているモニタも4kにしようとすべて置き換えました。
 1枚だけ変えると色味が合わないとかベゼル位置が合わないというような微妙なストレスが嫌なので 4kを複数枚買いましたが、使ってて思うのは個人的には27インチWQHDくらいの解像度がベストだと思います。
 27インチ4kモニタは11インチのFHDモバイルモニタを4枚並べたときとDPI的に同じになるのでスケーリングなしにするとまあまあ厳しいですが、現代のITエンジニアは眼球に可能な限り多くの情報を詰め込む必要があります。

 モニタを変えてからしばらくは忙しくゲームをする暇と気力がなくてオフィス用途でしか使っていませんでしたが、ふとARK: Survival Evolvedを起動してみたところ、すべてEpicの状態で4kネイティブで起動すると20FPSくらいしか出ないという状態でした。
 ゲームしたいときに出来ないというのは問題なのと、4k複数枚の環境だと普通に使っていてもDWMプロセスやFirefoxなどのブラウザがVRAMを消費していって8Gのうちの5GBを消費しているようなことがあったので、いい加減グラボを変えたいという思いが強かったので新しいものを買うことにしました。

グラボ選定


 最初は4080を買おうと思っていたのですが、じゃんぱらで7900XTXが特売されていたのでVRAMが24GBと多いという利点からこれも候補に上がりました。色々比較すると、

・純粋な3D処理能力は4080よりも高い場面も結構ある
・すべてのタイトルでDLSSなどの機械学習が使える訳では無い
・7900XTXの特価は12万だったが4080はどうしても18万くらいになる
・RTX5000シリーズが先送りになったのでRTX4000シリーズの価格が下る要素がなくなってしまった
・VRAMが多いので雑に使っても心の余裕がある


 となり、過去散々ATiのグラボで色々踏んだ事があってもまあいいかなと思えたのでポチりました。

 VRAMがそんなに必要になるか、と思うかもしれないですが、3ヶ月位(下手すると1年)再起動せずにブラウザのタブやウィンドウを大量に開いたまま使っているとVRAMの使用量が肥大化していき、VRAMが足りなくなるとブラウザやOSの挙動がおかしくなり、すべてのブラウザを閉じるという必要が出てきます。
 ブラウザをすべて閉じても今度はWindowsのDWMがVRAMを握ったままになり、その状態でゲームをするとスワップのような挙動になる事があり、解消には結局再起動が必要になってストレスだったりします。

 使い方が悪いのでは??というのはそうなんですが、原状どうにもならないのでこの使い方だとスペックによるパワーで押し切るしかないです。また、4kという解像度が1枚でFHDディスプレイ4枚分の表示面積があるので、それを複数枚となると相応のスペックが必要になります。
 27インチで4kの解像度でゲームをする必要があるのかと言われると、別に画質を求めているわけではないのですが、フルスクリーンの解像度変更によって開いているウィンドウがめちゃくちゃになるのが嫌なので、どうしてもネイティブの解像度で出したくなります。

 ただ、こういった変な要求がなければ現在では性能や値段、発熱や消費電力の扱いやすさから4070あたりが妥当だと思います。また、自分は今のところ常用デスクトップ機で機械学習をさせるつもりはないのでRadeonも候補になりましたが、普段機ででお絵かきAIも動かしたいというような要望があると必然的にGeforceが候補になるかと思います。


わかっていたけど高い消費電力


 グラボを換装した感想ですが、4kでもARKなど今までやってきたゲームがコマ落ちせずかなり快適に動くので1080との性能差を実感しました。ただ、それに比例して消費電力が増えたので、 AMD Software: Adrenalin EditionというAMD謹製のGPUのモニタリング、チューニングをするツールから消費電力を監視し、これを下げる方向にチューニングする方法を模索することにしました。




フレームレート制限で消費電力を下げる


 ARKを遊んでいるとGPUだけで350Wくらい行ってしまうので、どうにかこれを下げられないかとアレコレしていると、当然ながら負荷が下がればそれに伴って消費電力も下がることがわかりました。60Hzのディスプレイなので、それ以上のフレームレートは無駄になってしまうため、まずはフレームレートの固定を行いました。




 HL2やL4D2などでゲームからV-Syncを有効にすると明らかな入力ラグが出るので今まではティアリング上等で200FPSなどのフレームレートを出していましたが、FreeSync対応モニタなのが有効なのかドライバが賢くなったのかわかりませんが、Adrenalineからフレームレートの制限をかけると気になるラグは出ませんでした。
 80-90FPS出ていて350W消費していたのが60固定にすることによって300Wくらいに落ちたので、もう少し下げられないか試してみました

RSRなどのアップスケーリングを試す


 Adrenalineの設定にRadeon Super Resolution(RSR)という項目があったのでこれについて調べてみると、ゲームの解像度をネイティブ以下に設定したフルスクリーンゲームを起動した際にドライバ側でネイティブまでアップスケーリングして出力する、という機能だとわかり、試しにゲームから解像度をWQHDにしてRSRを有効にしてみるとかなり効くことがわかりました。
 具体的にはRSRを使うことによりフレームレート制限と合わさりGPU負荷が80%まで下がり、消費電力が一気に180W程度にまで落ちました。ただ、ゲーム自体はネイティ解像度以下で出力されるため、開いているウィンドウがぶっ飛ぶ問題は起きてしまいました。

 よく見てみるとAdrenalineの説明にFSRをドライバレベルで実行するのがRSRである、という文言があったのでこのFSRについても調べてみましたが、FSRとは FidelityFX Super Resolutionの略であり、こちらは出力するテクスチャの解像度を内部的に落としたあと、グラボでアンチエイリアシングなどの処理をしたあとにアップスケーリングをかけるという技術になるようです。
 ただ、FSRについてはゲーム側の対応が必要になるようなので、そもそもARKなどは対応していないのでは?と思いましたが、ゲームのオプションにあるResolution scaleというのがアップスケーリング前に描写するテクスチャの画像になるようです。しらなかった…




 FSRについては思いっきり解像度を下げると流石にテクスチャの不鮮明さが目についてしますが、27インチ4kであれば7-8割位のスケーリングならネイティブと遜色ないレベルになりました。
 60FPSの制限の中では画質と消費電力のトレードオフになりますが、違和感のないところでWQHDのRSRと同じ180Wかそれよりも若干高い200W程度で落ち着きました。もとの350Wに比べたらだいぶマシになりました。

ついでにDLSSなどについても調べてみる


 AMDのグラボなのでNVIDIAの技術であるDLSSは使えないのですが、こちらについても調べてみたところ、やっていることとしてはFSRと同じ低解像度テクスチャのアップスケーリングだとわかりました。
 違いとしては、FSRはスケーリング処理をシェーダーとして扱うため特殊なコアが必要ないという特徴がありますが、DLSSはその処理をTensorコアなどの専用プロセッサで行うので対応したグラボが必要になる、ということみたいです。
 事実、FSRは1080でも利用できたようなので、実はこれで誤魔化せばグラボを買わなくても耐えられたのでは…という思いはありますが、FSRを行うにもグラボ側の余裕がないと効果がないようなので多分意味はあったと信じます。まあ、VRAMが足りない問題は出ていたので…

 DLSSの進化系であるDLSS-FG(Frame Generation)は、実際のレンダリングフレームの間にTensorで(描写済みのテクスチャからいわば画像として)作成したフレームを挟むことによりフレームレートを上げる処理になるようです。同じような技術がFSR3でも実装されるようです。
 ただ、補完フレームを挟む必要があるので入力から出力までのバッファが必要になり、入力のレイテンシ低減にはならず、むしろ増える方向になるようです。FSR/DLSSは実際の描写が軽くなるためこれを有効にすることによって入力レイテンシが下がるらしいです。



 フレームレートと入力遅延が一致しないというのはなかなか難しいですが、GPUの性能が足りずそもそもコマ落ちするレベルでレイテンシが上がっているようなときにはDLSS-FGのレイテンシのほうがマシになる可能性はあります。

まとめ


 昔は「ドライバのフィルタ処理で正しくテクスチャを描写してない!手抜きだ!チートだ!」 みたいなことを言っていた時期がありましたが、今となっては「クソ真面目に描写するのは馬鹿らしいからAI使って補完しようぜ!」みたいなことを言い始めるあたり時代を感じます。まあ前者についてはただの言いがかりですが。

 フレームレートを増やす技術というのは言い換えれば同じフレームレートなら負荷が下がる技術ということになるので、極端な高フレームレートディスプレイを使っていないのであれば消費電力を下げる恩恵があるというのを実感しました。
それなら下位のモデルでもいいのではないかと思うかもしれませんが、これらの技術が (RSRは原理的に使えそうではありますが) すべての場面で使えるわけではないというのと、ベーストルクがあるからこそできる余裕というのもあるので、グラボを買うなら個人的にはある程度の上位モデルをおすすめしたいです。

 ただ、11月ごろに7900XTXを買ったのですが、2月に4080Superが15万くらいで出るらしいというのを見て「それならそっちにしたわ…」みたいな気持ちはあります…。まあ、こんなことでもないとRadeonを買うこともなかったと思いますし、使ってみたら改めて知ることも多く、AI以外の用途なら悪くないというのがわかったので良しとします。
 7900XTXも出始めの頃のドライバの出来は相変わらず散々だったようなので、他人におすすめするときは「Geforce買っておけ」と言いますが…。

 FSPカウンタなどで高いフレームレートを見ると気持ちがいいですが、逆に消費電力を減らす方に振ってみるのも面白いと思いました。

以上




余談:過去のATiで踏んだもの


 3870を2枚でCrossFireしていたときが一番ひどかったです。CFを使うとどのドライバでも何かしらおかしかったので、その出来の悪いドライバから一番マシなのはどれかを探すのが大変でした。ジャンク漁りかな?
 どれも出来が悪いので、毎月出るCatalystドライバに今抱えている問題が直らないかという僅かな願いをかけて更新するのですが、ひどいものだと入れたら最後、青くなって戻ってこれなくなるということが多々ありました。月刊Catalyst!今月号の付属はきれいな青画面つき!!!みたいな感じでした。
 まあ、そのあとGTX295を使いましたが 結局nVidiaでもデュアルチップはうまく動かない事が多く、その後使ったHD4890は安定していました。


余談:買ったモニタ


 モニタについてはそこまでこだわりがないのですが、1枚だけ変えると色味が合わないとかベゼル位置が合わないというような微妙なストレスが嫌なので、使っていた4枚をすべて交換する前提で安いものを探していました。するとAliで162ドルのものが見つかりました。 (今は絶版になってますが)




 モバイルモニタなどの中国製モニタをいくつか試した経験から、今の中国製のモニタのパネルはそれなりに品質が高いという経験則と、ベゼルに変な文字が入っていないのと、何故かこれだけ大きいにも関わらず送料が無料だったので 、試しに1枚買って1ヶ月検証したのですが、そこそこ良かったので計4枚買いました

 まあ買った時期によって(もともとできるとは書いていなかった)入力のPiPができるリビジョンとできないリビジョンが混ざっていたとか、 旧リビジョンのモニタについてきたACが公称消費電力が45Wに対して12V/3.33A=40Wという微妙に出力の足りないACで「大丈夫かこれ」とか思っていたら案の定1ヶ月でACが壊れたとか、解像度をWQHDにすると144Hz駆動にする裏オプションが存在するとか、大陸しぐさ満載でしたが、鍛えられてくるとむしろそれが楽しくなってきます。 色んな意味でシビレますね。

 ちなみにACについては汎用的な2.2/5.5mm径のACなので在庫が家にくさるほどあり、12V/5Aのものに交換して無事に動きました。それを口実に紛争を開始して部分的な返金を受けられたので良しとします。向こうも知ってか後期リビジョンではACが最初から12V/5Aのものになっていました。色については初期は黄色みが強いですがOSDで調整すればいい感じの発色になりました。





[ コメントを書く ] ( 453 回表示 )   |  このエントリーのURL  |  $star_image$star_image$star_image$star_image$star_image ( 3 / 269 )  |  
【Monitoring】 ZabbixからESXiホストに刺さってるMegaRAIDの状態をLLDで動的に取得して監視する 
 個人でESXiを動かしていると、何かとLSIのRAID情報を取得したいと思うことがありますが、LSAではなくて既存のZabbixからデータを取得したいと思うことがあります。ありますよね(同調圧力)。
 何かいい方法がないかと見ていたらstorcliがjsonでデータを出力できることに気がついたので、ZabbixのLLDと組み合わせていい感じにデータを監視する方法を作りました。先にテンプレートのJSONを置いておきます。

Template_LSI-storcli.json


 storcliが動けばLinuxであっても情報を取得できるので、agentのsystem.run形式で作ったものも同梱しておきます。storcliの実行にroot権限が必要になるので、storcliにスティッキービットをつけたりsudoを許可したりagent自体をrootでうごかしたりしていい感じに対応してください。
 system.runは悪だ!!!!!という方は、最終的にjsonが取れればいいので、userparameterにしたりrootのcronでstorcliを実行して結果をテキストデータとして出力してログ監視するなど、linuxであればやりようはいくつもあるのでいい感じに工夫してください。
 まあ、linuxでエージェントが入っているようであれば/var/log/kernelなどをmegaraid_sasといったようなキーワードで監視していれば大体のイベントが取得できますが…。

 ESXi用のテンプレートはSSHで接続してstorcliの実行結果を取得するので、Zabbix-agentが入っていないLinuxを監視したい場合にも使えます。

前提条件


 利用するにあたり、以下の前提条件が必要になります。

Zabbixのバージョンが6系であること
監視対象上でstorcliが動かせること
作成したLDに対して名前がついていること
ssh接続が有効になっていること
ESXiホストの/etc/ssh/sshd_configのPasswordAuthentication が yesになっていること


 注意点としては、LLDとJSONのクエリの関係で、作成した論理ディスクに対して名前が必要になります。デフォルトではついていないので、storcliで監視対象全てで被らない一意な名前をつけてあげてください

/opt/lsi/storcli/storcli  /call/vall show all #一覧を確認
/opt/lsi/storcli/storcli  /c0/v0 set name=myhost01-boot #/c0/v0に対して名前を付ける


 ESXi側のSSHに関しては共通鍵認証でもいいですが、テンプレートの変更が必要なのでそのへんの設定は今回は省きます。デフォルトのkeyboard-interactiveになっていても動きそうな気がしますがテストしてません。
 また、テンプレートの形式がZabbix6形式になっているので4系では入らない気がします。LLDのためにJSONpathとカスタムスクリプトでJSを使っているので、これを元に一から作る場合であっても、Zabbix側のカスタムスクリプトでJSの拡張がされていないと動かない気がします。

 いずれにしても、4系の環境がもうないのでテストしてないですが、JSONpathは4系でも使えたはずなのでなにかのヒントにはなるかと思います。

利用方法


 まず、Zabbixに監視するためのホストを作ります。ESXiの場合はSSHで接続するので、インターフェースは参照しないので127.0.0.1のままでOKです。ホスト名だけ入れてください。



 次に、マクロに色々仕組みます。利用しているマクロは以下になります

{$IP} 接続先IP
{$PASS} SSHの接続パスワード
{$STORCLI} Storcliの配置先 ESXiデフォルトは/opt/lsi/storcli/storcliのはず
{$USER} SSHの接続ユーザ




 今思うと{$IP}はホスト作るときにIPで作って{#HOST.IP}でも良かったのでは??という気がしますが、作ってしまったのでそのままにします。また、PASSについてはデバッグが終わったあとであればSecret textに変更可能ですが、最初はクリアテキストでデータが取れるかを見たほうがいいです。

 作成したホストに今回のテンプレートをリンクさせ、LD Data from StorcliExecute nowで実行します。正しく設定されていれば、LLDが発動してアイテムが増えるはずです。LDが増えない場合、上記の制限事項にある手順のstorcli /cx/vx set name=uniquename で名前をつけたか確認してください。Latest dataでデータが取得できているのにLDが増えない場合おそらくこれが原因です。
 同じくPD Data from Storcliを実行すると、刺さっている物理ディスクの一覧が取得できると思います。

 Latest dataが取れない場合、ZabbixからESXiにSSHが届くか、storcliのパスが合っているかなどを確認してください。

制限事項


 物理ディスクの監視ですが、ディスク、HBAのファームウェアのバージョンによっては取得できない項目があります。BBM Error countShield State Countなどはある程度のクラスのディスクでないと値を持っていないので、ディスクによっては取得できないかもしれません。その場合は取得不可のマークが付きますが、影響がないのでそのままにするかLLDで作成されたアイテムから監視の無効化をしてください。




 また、物理ディスクの監視のために実行しているstorcli /call/eall/sall show all jで応答するJSONがかなり大きいため、搭載ディスク本数が24本などの巨大なマシンの場合はうまく取れないかもしれません。

 Zabbix6系ではTEXT型アイテムの場合、history_textテーブルに格納され、使っているDBがMariaDBであればmediumtext型で格納されるため1回のデータ取得で16MBまでは入りますが、使っているDBの種類によっては入り切らない可能性があります。参考までに、ディスクを8本搭載したマシンでstorcli /call/eall/sall show all jを実行すると22KBのJSONが出来上がります。

 調べた限り、Zabbixは1つのセッションで1GBくらいのデータを流せるようなので、おそらくSSHからのデータ取得で問題になることは少ないかと思いますが、場合によってはタイムアウトの時間を伸ばす必要があるかもしれません。 

 storcli /call/eall/sall show all jの結果については依存アイテムを分解するために実行しているだけなので、Zabbixが6系であればヒストリを「Do not keep history」にしてもLLD分解後のデータ取得は可能です。
 もともとデバッグのために1時間しか保持していませんが、コマンド実行に問題がなさそうであればstorcliの履歴については取得しない設定でも問題ないです。

→テンプレートの微調整の際にデフォルトではstorcliの実行結果を保存しないようにしました。storcliのコマンド実行結果を眺めたい場合は PD Data from Storcli のアイテムヒストリをnoneから適宜変更してください


技術的内容(うらみつらみ)


 今回、JSONとLLDを使うにあたってかなり多くの問題にあたりました。まず1つめはstorcliの返すJSONの作りが悪すぎること、そして2つめはZabbix側のJSONの処理がイケてないことです。両方じゃねーか!!!

ZabbixのJSONの扱いが微妙


 ZabbixでJSONを扱うときに2つの問題がおきます。まず1つ目は、LLDでJSONを扱う場合、見つかったキーが["LLDNAME"]とダブルクオートとブラケットに囲まれた状態で取得します。中身だけ見つけてくれ…と、せめてどうにか値に対してtrimやiregsubなどが使えないか試したのですが、どうやらここをどうにかする方法はないようです。

 そのため、アイテム名を整形するためにTemperture of {{#MR_PD_NAME}.iregsub("\[\"(.*)\"\]", \1)}というような正規表現をところどころで書く必要がありイケてないです。
 LLDで見つかったアイテムのキーについてもmrstat.pd.temp.["[\"/c0/e252/s0:WDC WD1005FBYZ-01YCBB1:WD-WMC6M0F4M8WN\"]"]というような状態で入ってしまうのですが、どうせこれは他では使わないしもういいや…と諦めました。

 2つ目は、LLDを実行するときにJSONから複数の値が見つかった場合、それぞれ[{"key": "value"},{"key": "value"}]で返答する必要があり、["value","value"]だとイテレータが動いてくれないという点です。
https://www.zabbix.com/forum/zabbix-help/419391-utilizing-jsonpath-to-setup-an-lld-macros
 これもハマりましたが、上記のスレッドのカスタムスクリプトに助けられました。なおしておいてくれ~~~~~

storcliのJSON構造がカス


 storcliのJSONの構造で苦労したのは物理ディスクの応答内容がカスなことです。一例としては以下のようになります

{
  "Controllers": [
    {
      "Response Data": {
        "Drive /c0/e252/s0": [
          {
            "DG": 0,
            "DID": 14,
            "EID:Slt": "252:0",
            "Intf": "SATA",
            "Med": "HDD",
            "Model": "WDC WD1005FBYZ-01YCBB2",
            "PI": "N",
            "SED": "N",
            "SeSz": "512B",
            "Size": "931.000 GB",
            "Sp": "U",
            "State": "Onln",
            "Type": "-"
          }
        ],
        "Drive /c0/e252/s0 - Detailed Information": {
          "Drive /c0/e252/s0 Device attributes": {
            "Coerced size": "931.000 GB [0x74600000 Sectors]",
            "Connector Name": "Port 0 - 7 x1",
            "Device Speed": "6.0Gb/s",
            "Firmware Revision": "RR07    ",
            "Link Speed": "6.0Gb/s",
            "Logical Sector Size": "512B",
            "Manufacturer Id": "ATA     ",
            "Model Number": "WDC WD1005FBYZ-01YCBB2",
            "NAND Vendor": "NA",
            "NCQ setting": "Enabled",
            "Non Coerced size": "931.012 GB [0x74606db0 Sectors]",
            "Physical Sector Size": "512B",
            "Raw size": "931.512 GB [0x74706db0 Sectors]",
            "SN": "WD-WMC6M0J95THT",
            "WWN": "50014EE0AF2FC003",
            "Write Cache": "N/A"
          },
          "Drive /c0/e252/s0 Policies/Settings": {
            "Certified": "No",
            "Commissioned Spare": "No",
            "Connected Port Number": "7(path0) ",
            "Cryptographic Erase Capable": "No",
            "Drive position": "DriveGroup:0, Span:0, Row:0",
            "Emergency Spare": "No",
            "Enclosure position": "1",
            "FDE Type": "None",
            "Last Predictive Failure Event Sequence Number": 0,
            "Locked": "No",
            "Multipath": "No",
            "Needs EKM Attention": "No",
            "PI Eligible": "No",
            "Port Information": [
              {
                "Linkspeed": "6.0Gb/s",
                "Port": 0,
                "SAS address": "0x4433221107000000",
                "Status": "Active"
              }
            ],
            "SED Capable": "No",
            "SED Enabled": "No",
            "Sanitize Support": "Not supported",
            "Secured": "No",
            "Sequence Number": 2,
            "Successful diagnostics completion on": "N/A",
            "Wide Port Capable": "No"
          },
          "Drive /c0/e252/s0 State": {
            "BBM Error Count": 0,
            "Drive Temperature": " 32C (89.60 F)",
            "Media Error Count": 0,
            "Other Error Count": 0,
            "Predictive Failure Count": 0,
            "S.M.A.R.T alert flagged by drive": "No",
            "Shield Counter": 0
          },
          "Inquiry Data": "7a 42 ff 3f 37 c8 10 00 00 00 00 00 3f 00 00 00 00 00 00 00 20 20 20 20 57 20 2d 44 4d 57 36 43 30 4d 39 4a 54 35 54 48 00 00 00 00 00 00 52 52 37 30 20 20 20 20 44 57 20 43 44 57 30 31 35 30 42 46 5a 59 30 2d 59 31 42 43 32 42 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 10 80 00 40 00 2f 01 40 00 00 00 00 07 00 ff 3f 10 00 3f 00 10 fc fb 00 00 5d ff ff ff 0f 00 00 07 00 "
        }
      }
    }
  ]
}


 各情報について、なぜ変動する値がキーになっているのか、微妙に違うキーに値を入れるのやめろ、と言いたくなります。例えば、ディスクの温度とシリアルを取りたい場合のデータの位置は以下になります。

json.Controllers[0]["Response Data"]["Drive /c0/e252/s0 - Detailed Information"]["Drive /c0/e252/s0 State"]["Drive Temperature"] = " 32C (89.60 F)";
json.Controllers[0]["Response Data"]["Drive /c0/e252/s0 - Detailed Information"]["Drive /c0/e252/s0 Device attributes"].SN = "WD-WMC6M0J95THT";


 なぜ以下のようにしなかったのか…

json.Controllers[0]["Response Data"]["PhysDrive"][0]["Path"]= "/c0/e252/s0";
json.Controllers[0]["Response Data"]["PhysDrive"][0]["SN"] = "WD-WMC6M0J95THT";
json.Controllers[0]["Response Data"]["PhysDrive"][0]["Detailed Information"]["State"]["Drive Temperature Celsius"] = 32


 もともとディスクのシリアルと型番を元にクエリを投げて温度を取ろうと思い、上記のような構造であればJSONだけでクエリが完結したのですが、このような構造のためLLDのキーに色々仕込む必要がありました。
 内容としてはカスタムスクリプトに正規表現でパスを取り出すものを仕組み、LLDの名前として取得して、LLDのアイテムプロトタイプで正規表現で取り出す、という内容です。

 LLDのカスタムスクリプト

var array = JSON.parse(value)
var drives = []
for (var a in array) {
for (var ar in array[a]){
if ( ar.indexOf("Detailed Information") != -1){
prefix=ar.replace(/(Drive\s\/[\w\/]+)\s.*/,"$1");
path=ar.replace(/Drive\s(\/[\w\/]+)\s.*/,"$1");
detail=prefix+" Device attributes";
drive=path+":"+array[a][ar][detail]["Model Number"].trim()+":"+array[a][ar][detail].SN
drives.push(drive)
}
}
}

var len = drives.length;
var x = 0
output = "{ \"data\" :["
for (; x < len - 1; x++){
output += "{\"Name\": \"" + drives[x] + "\"},"
}
output += "{\"Name\": \"" + drives[x] + "\"}"
output += "]}"
return output


 上記のカスタムスクリプトでこのような名前が取れるので、これをLLDで見つかったディスクとして認識させます。




 そして、LLDのアイテムプロトタイプにて、JSONpathでデータを取得する際に.iregsub("\[\"([0-9a-z\/]+):.*\"\]", \1)}で一番最初のスロットナンバーの情報を切り出してJSONのキーに埋め込んでいます。

$.Controllers..["Response Data"]..["Drive {{#MR_PD_NAME}.iregsub("\[\"([0-9a-z\/]+):.*\"\]", \1)} State"]["Drive Temperature"]


 イケてねえ…。まあイケてないものをイケてない方法でどうにかしようとしているので汚くなるのは必然ですが…。

まとめ


 Zabbixは仕事でもプライベートでも使うことが多く、いろいろな意味で一番柔軟性が高い監視方法だと感じています。特定のログを検知しても水曜日の6-7時だけは検知を除外する、というような要求でも、Zabbixであればそれなりに複雑なトリガーの条件式で対応できますが、他の監視方法だとかなりキツイ、もしくはそんな方法はない、という事なりがちで、良くも悪くも使い込めば使い込むほど他のものが使えなくなります。

 Zabbixは以前の仕事でそれなりに使っていたつもりだったのですが、今回やった内容は全く知らず、「え、そんなことできたの…」という発見もかなりありました。Zabbixはパズルですが、どうにかする方法はあるのでみんなで沼に沈みましょう。

 おそらく、今回の方法ももう少しスマートな方法がある気がしますが、とりあえず動いたので参考にはなるかと思います。

追記:
 その後実際に役に立ってしまったので動作については問題ないと思います。また、storcliのデータについてはデフォルトではヒストリを取らない設定に変更し、ディスクのステータスの追加のデータを取るように変更しました。何が起きてるかを知りたいときには手動でヒストリの取得を有効にしてください。
 また、コントローラーが複数存在する場合に、2台目以降のコントローラーのデータが上手くパースできていない問題も修正しました。




[ コメントを書く ] ( 414 回表示 )   |  このエントリーのURL  |  $star_image$star_image$star_image$star_image$star_image ( 3 / 440 )  |  

| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 進む> 最後へ>>