目次
- 概要
- 環境
- Mellanoxドライバのインストール
- RoCEv2サポートの確認
- カーネルモジュールのロード
- ネットワーク設定
- nvme-cliのインストール
- ターゲット側の設定
- Initiator側の設定
- fioを用いてベンチマーク
- EthernetSwitchを挟む
- 複数のInitiatorからIO負荷をかける
- まとめ
概要
近年、NVMe over Fabric (=NVMeoF) という言葉をよく聞くようになりました。
この Fabric 自体は NVMe のプロトコルをネットワーク上に流すことでリモートホスト上からターゲットホストの NVMe Disk に直接IO操作を行うものです。
同じような目的を持ったプロトコルとしては iSCSI などがあげられます。
しかし、SCSI 自体が近年目覚ましく進化したフラッシュストレージ (=SSD) の性能に追いつけていないのが現状です。
この問題に対処するため、より効率的なプロトコルとして新たに NVMe プロトコルが策定されました。
NVMe over Fabric は効率的な NVMe プロトコルを利用することにより iSCSI よりも高速にIOを裁くことができるメリットがあり、注目されています。
NVMe over Fabric は通信に使用するプロトコルとして Infiniband や Ethernet 、さらには FibreChannel を選択することができます。
加えて、RDMA(=Remote Direct Memory Access) などの技術を用いることでより高速・低遅延を実現しています。
RDMA 自体は Infiniband の技術です。
しかし、RDMA を Ethernet 上で利用するための技術として、RoCE (RDMA over Converged Ethernet) があります。
この RoCE は v1 と v2 が存在し、RoCEv1 は Link Layer のプロトコルであるため異なるネットワークセグメント間において接続することができませんでした。
しかし RoCEv2 では Internet Layer のプロトコルとなったため、ルーティングが可能となり、異なるネットワークセグメント間においても使用することができるようになりました。
まとめると、低遅延でかつより高速にリモートホスト上の NVMe にアクセスするための技術として、NVMe over RDMA があり、これを Ethernet 上で利用するために RoCEv2を用いる必要があるということです。
また、RoCE 自体は途中経路上のスイッチにも設定が必要です。
このため、RDMA を用いずに TCP で直接接続するプロトコルとして、NVMe over TCP が存在します。
しかし、Mellanox (NVIDIA) の ConnectX-4 以降においてはスイッチの設定を不要とする Zero Touch RoCE が使用でき、より簡単に環境構築が可能となりました。
今回はそんな ConnectX-4 を用いながら、実際に NVMe over RDMA (RoCEv2) な環境を構築し、軽くパフォーマンスなどを見ていきます。
(NVMe over TCPについては後日検証します。)
環境
今回の検証に使用した環境です。
マシンは2台用意し、それぞれをターゲット(Server)側、Initiator(Client)側としています。
ターゲット側
詳細 | |
---|---|
Machine | HPE ProLiant DL380 Gen10 |
CPU | Intel Xeon Silver 4208 x2 |
Memory | DDR4 ECC RDIMM 128GB (16GB x8 2400MHz) |
NIC | Mellanox ConnectX-4 100Gb(Ethernet) MCX415A-CCAT |
NVMe | Intel DC P4600 1.6TB U.2 x2 |
OS | Ubuntu Server 22.04 LTS |
Initiator側
詳細 | |
---|---|
Machine | Fujitsu Primergy RX2540 M4 |
CPU | Intel Xeon Gold 6148 x1 |
Memory | DDR4 ECC RDIMM 128GB (32GB x4 2666MHz) |
NIC | Mellanox ConnectX-4 100Gb(Ethernet) MCX415A-CCAT |
OS | Ubuntu Server 22.04 LTS |
Mellanoxドライバのインストール
事前に、RDMAに対応した Mellanox ドライバである OFED をインストールしておく必要があります。
ドライバは公式サイトからダウンロードできます。
# tar -xzvf MLNX_OFED_LINUX-5.6-2.0.9.0-ubuntu22.04-x86_64.tgz
# cd MLNX_OFED_LINUX-5.6-2.0.9.0-ubuntu22.04-x86_64
# ./mlnxofedinstall --with-nvmf
# /etc/init.d/openibd restart
インストール完了後、念のために initramfs を更新しておきます。
しなかった場合、この後に行うカーネルモジュールの読み込みでエラーになる場合があります。
# update-initramfs -u
# reboot
RoCEv2サポートの確認
念のため、公式サイトを参考にNICがRoCEv2に対応していることを確認しておきます。
ネットワークインタフェース名を確認しておきます。
# cat /sys/class/infiniband/mlx5_0/ports/1/gid_attrs/ndevs/0
ens5np0
# cat /sys/class/infiniband/mlx5_0/ports/1/gid_attrs/ndevs/1
ens5np0
今回は 0 と 1 が該当するものでした。
続いて、確認した情報をもとにサポートしているかどうかを確認します。
# cat /sys/class/infiniband/mlx5_0/ports/1/gid_attrs/types/0
IB/RoCE v1
# cat /sys/class/infiniband/mlx5_0/ports/1/gid_attrs/types/1
RoCE v2
RoCE v2をサポートしていることが確認できました。
カーネルモジュールのロード
ターゲット側、Initiator側の両方でカーネルモジュールを読み込んでおきます。
# modprobe mlx5_core
# lsmod | grep mlx
mlx5_ib 434176 0
ib_uverbs 135168 6 rdma_ucm,mlx5_ib
ib_core 417792 8 rdma_cm,ib_ipoib,iw_cm,ib_umad,rdma_ucm,ib_uverbs,mlx5_ib,ib_cm
mlx5_core 1888256 1 mlx5_ib
mlxdevm 172032 1 mlx5_core
mlxfw 32768 1 mlx5_core
psample 20480 1 mlx5_core
tls 106496 2 bonding,mlx5_core
mlx_compat 69632 11 rdma_cm,ib_ipoib,mlxdevm,iw_cm,ib_umad,ib_core,rdma_ucm,ib_uverbs,mlx5_ib,ib_cm,mlx5_core
pci_hyperv_intf 16384 1 mlx5_core
# modprobe nvmet
# modprobe nvmet-rdma
# modprobe nvme-rdma
# lsmod | grep nvme
nvme_rdma 40960 0
nvme_fabrics 24576 1 nvme_rdma
nvmet_rdma 57344 0
nvmet 135168 1 nvmet_rdma
rdma_cm 122880 3 nvme_rdma,nvmet_rdma,rdma_ucm
ib_core 417792 10 rdma_cm,ib_ipoib,nvme_rdma,nvmet_rdma,iw_cm,ib_umad,rdma_ucm,ib_uverbs,mlx5_ib,ib_cm
nvme 49152 2 nvmet,nvmet_rdma
nvme_core 122880 4 nvmet,nvme,nvme_rdma,nvme_fabrics
mlx_compat 69632 17 rdma_cm,ib_ipoib,mlxdevm,nvmet,nvme,nvme_rdma,nvmet_rdma,iw_cm,nvme_core,nvme_fabrics,ib_umad,ib_core,rdma_ucm,ib_uverbs,mlx5_ib,ib_cm,mlx5_core
ネットワーク設定
この段階でネットワーク設定を行っておきます。
手順については省略します。
なお、今回は次のような構成にしています。
ターゲット側 10.0.0.1/24 ---------- 10.0.0.2/24 Initiator側
nvme-cliのインストール
ターゲット側とInitiator側の両方に nvme-cli をインストールしておきます。
# apt install nvme-cli
ターゲット側の設定
今回は、ターゲット側のソフトウェアとして SPDK (=Storage Performance Development Kit) を用います。
git から clone してビルドします。
root@target:~# git clone https://github.com/spdk/spdk
root@target:~# cd spdk/
root@target:~/spdk# git submodule update --init
root@target:~/spdk# scripts/pkgdep.sh --rdma
root@target:~/spdk# ./configure --with-rdma --enable-debug
root@target:~/spdk# make
テストを実施しておきます。All unit tests passed
の表示を確認できればOKです。
root@target:~/spdk# ./test/unit/unittest.sh
<省略>
=====================
All unit tests passed
=====================
WARN: lcov not installed or SPDK built without coverage!
WARN: neither valgrind nor ASAN is enabled!
テストをパスしたことが確認出来たら、NVMeドライバの変更を行います。
変更には、SPDK 内の setup.sh
を用います。
先に現在の状態を確認します。
root@dev0003:~/spdk# scripts/setup.sh status
Hugepages
node hugesize free / total
node0 1048576kB 0 / 0
node0 2048kB 0 / 0
node1 1048576kB 0 / 0
node1 2048kB 0 / 0
Type BDF Vendor Device NUMA Driver Device Block devices
I/OAT 0000:00:04.0 8086 2021 0 ioatdma - -
I/OAT 0000:00:04.1 8086 2021 0 ioatdma - -
I/OAT 0000:00:04.2 8086 2021 0 ioatdma - -
I/OAT 0000:00:04.3 8086 2021 0 ioatdma - -
I/OAT 0000:00:04.4 8086 2021 0 ioatdma - -
I/OAT 0000:00:04.5 8086 2021 0 ioatdma - -
I/OAT 0000:00:04.6 8086 2021 0 ioatdma - -
I/OAT 0000:00:04.7 8086 2021 0 ioatdma - -
NVMe 0000:39:00.0 8086 0a54 0 nvme nvme0 nvme0n1
NVMe 0000:3a:00.0 8086 0a54 0 nvme nvme1 nvme1n1
I/OAT 0000:80:04.0 8086 2021 1 ioatdma - -
I/OAT 0000:80:04.1 8086 2021 1 ioatdma - -
I/OAT 0000:80:04.2 8086 2021 1 ioatdma - -
I/OAT 0000:80:04.3 8086 2021 1 ioatdma - -
I/OAT 0000:80:04.4 8086 2021 1 ioatdma - -
I/OAT 0000:80:04.5 8086 2021 1 ioatdma - -
I/OAT 0000:80:04.6 8086 2021 1 ioatdma - -
I/OAT 0000:80:04.7 8086 2021 1 ioatdma - -
現在はカーネル上の ioatdma や nvme で制御が行われていることがわかります。
続いて、念のため NVMe をフォーマットして初期化しておきます。
検証環境では、以前ブロックデバイスとして使用していたディスクを使用したためか、この後の操作で Active devices: data@nvme0n1, so not binding PCI dev
といったような表示となりドライバの変更を行うことが出来ませんでした。
フォーマットは次のコマンドで実施できます。
(使用するすべてのNVMeに対して実施しました。)
root@target:~# nvme format /dev/nvme0n1
先ほどの setup.sh
をオプションなしで実行することで uio_pci_generic
に変更します。
root@target:~/spdk# scripts/setup.sh
0000:39:00.0 (8086 0a54): nvme -> uio_pci_generic
0000:3a:00.0 (8086 0a54): nvme -> uio_pci_generic
0000:00:04.7 (8086 2021): ioatdma -> uio_pci_generic
0000:00:04.6 (8086 2021): ioatdma -> uio_pci_generic
0000:00:04.5 (8086 2021): ioatdma -> uio_pci_generic
0000:00:04.4 (8086 2021): ioatdma -> uio_pci_generic
0000:00:04.3 (8086 2021): ioatdma -> uio_pci_generic
0000:00:04.2 (8086 2021): ioatdma -> uio_pci_generic
0000:00:04.1 (8086 2021): ioatdma -> uio_pci_generic
0000:00:04.0 (8086 2021): ioatdma -> uio_pci_generic
0000:80:04.7 (8086 2021): ioatdma -> uio_pci_generic
0000:80:04.6 (8086 2021): ioatdma -> uio_pci_generic
0000:80:04.5 (8086 2021): ioatdma -> uio_pci_generic
0000:80:04.4 (8086 2021): ioatdma -> uio_pci_generic
0000:80:04.3 (8086 2021): ioatdma -> uio_pci_generic
0000:80:04.2 (8086 2021): ioatdma -> uio_pci_generic
0000:80:04.1 (8086 2021): ioatdma -> uio_pci_generic
0000:80:04.0 (8086 2021): ioatdma -> uio_pci_generic
変更が行われたか確認しておきます。uio_pci_generic
になっていればOKです。
root@target:~/spdk# scripts/setup.sh status
Hugepages
node hugesize free / total
node0 1048576kB 0 / 0
node0 2048kB 1024 / 1024
node1 1048576kB 0 / 0
node1 2048kB 0 / 0
Type BDF Vendor Device NUMA Driver Device Block devices
I/OAT 0000:00:04.0 8086 2021 0 uio_pci_generic - -
I/OAT 0000:00:04.1 8086 2021 0 uio_pci_generic - -
I/OAT 0000:00:04.2 8086 2021 0 uio_pci_generic - -
I/OAT 0000:00:04.3 8086 2021 0 uio_pci_generic - -
I/OAT 0000:00:04.4 8086 2021 0 uio_pci_generic - -
I/OAT 0000:00:04.5 8086 2021 0 uio_pci_generic - -
I/OAT 0000:00:04.6 8086 2021 0 uio_pci_generic - -
I/OAT 0000:00:04.7 8086 2021 0 uio_pci_generic - -
NVMe 0000:39:00.0 8086 0a54 0 uio_pci_generic - -
NVMe 0000:3a:00.0 8086 0a54 0 uio_pci_generic - -
I/OAT 0000:80:04.0 8086 2021 1 uio_pci_generic - -
I/OAT 0000:80:04.1 8086 2021 1 uio_pci_generic - -
I/OAT 0000:80:04.2 8086 2021 1 uio_pci_generic - -
I/OAT 0000:80:04.3 8086 2021 1 uio_pci_generic - -
I/OAT 0000:80:04.4 8086 2021 1 uio_pci_generic - -
I/OAT 0000:80:04.5 8086 2021 1 uio_pci_generic - -
I/OAT 0000:80:04.6 8086 2021 1 uio_pci_generic - -
I/OAT 0000:80:04.7 8086 2021 1 uio_pci_generic - -
続いて、ターゲットプログラムを起動します。
なお、この後の作業では Shell が最低2つ必要になりますので、事前に用意しておくことをおすすめします。
root@target:~/spdk# ./build/bin/nvmf_tgt
起動したら、もう一方の Shell で設定を投入していきます。
まず初めに、RDMA の設定を投入します。
次のコマンドは、I/O ユニットサイズを 8192byte 、最大 I/O ユニットサイズを 131072 、カプセル内のデータサイズを 8192byte としています。
root@dev0003:~/spdk# scripts/rpc.py nvmf_create_transport -t RDMA -u 8192 -i 131072 -c 8192
続いて、ブロックデバイスの設定を行います。
SPDK では Malloc を使用して RAM ディスクを作成して試すこともできます。
ここでは、両方の手順について記述しておきます。
Malloc を使用する場合
root@target:~/spdk# scripts/rpc.py bdev_malloc_create -b Malloc0 512 512
Malloc0
root@target:~/spdk# scripts/rpc.py nvmf_create_subsystem nqn.2016-06.io.spdk:cnode1 -a -s SPDK00000000000001 -d SPDK_Controller1
root@target:~/spdk# scripts/rpc.py nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 Malloc0
root@target:~/spdk# scripts/rpc.py nvmf_subsystem_add_listener nqn.2016-06.io.spdk:cnode1 -t rdma -a 10.0.0.1 -s 4420
NVMe を使用する場合bdev_nvme_attach_controller
実施時の -a
オプションはPCIeのIDを指定します。
このIDは setup.sh status
で確認でき、今回の例では 0000:39:00.0
または 0000:3a:00.0
です。
root@target:~/spdk# scripts/rpc.py bdev_nvme_attach_controller -b NVMe0 -a 0000:39:00.0 -t pcie
root@target:~/spdk# scripts/rpc.py bdev_nvme_get_controllers
[
{
"name": "nvme0",
"ctrlrs": [
{
"state": "enabled",
"trid": {
"trtype": "PCIe",
"traddr": "39:00.0"
},
"cntlid": 0,
"host": {
"nqn": "nqn.2014-08.org.nvmexpress:uuid:e00e3bdd-93c6-4f3b-83be-0f9965b91e97",
"addr": "",
"svcid": ""
}
}
]
}
]
root@target:~/spdk# scripts/rpc.py nvmf_create_subsystem nqn.2016-06.io.spdk:cnode1 -a -s SPDK00000000000001 -d SPDK_Controller1
<ここで名前を確認しておく>
root@target:~/spdk# scripts/rpc.py bdev_get_bdevs | grep "name"
<確認した名前を使用>
root@target:~/spdk# scripts/rpc.py nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 NVMe0n1
root@target:~/spdk# scripts/rpc.py nvmf_subsystem_add_listener nqn.2016-06.io.spdk:cnode1 -t rdma -a 10.0.0.1 -s 4420
双方において、listen するアドレスとして 10.0.0.1
を使用し、ポート番号は 4420
を使用していますが、それぞれを環境に合わせて変更してください。
(なお、本来であればポート番号はRoCEv2用にIANAで予約された 4791
を使用するべきだと思います。)
ここからの手順は NVMe を使用した場合の例です。手順は同様ですが、表示が若干異なる場合があります。
ここまでの設定は nvmf_tgt
を停止すると初期化されてしまうため、エクスポートしておきます。
root@target:~/spdk# scripts/rpc.py save_config > setting.json
エクスポートした設定は nvmf_tgt
を起動する際に読み込ませることができます。
root@target:~/spdk# ./build/bin/nvmf_tgt -c setting.json
Initiator側の設定
Initiator 側で Discover を実施して検出できるか確認します。
root@initiator:~# nvme discover -t rdma -a 10.0.0.1 -s 4420
Discovery Log Number of Records 1, Generation counter 1
=====Discovery Log Entry 0======
trtype: rdma
adrfam: ipv4
subtype: nvme subsystem
treq: not required
portid: 0
trsvcid: 4420
subnqn: nqn.2016-06.io.spdk:cnode1
traddr: 10.0.0.1
rdma_prtype: not specified
rdma_qptype: connected
rdma_cms: rdma-cm
rdma_pkey: 0x0000
無事検出出来たので、接続します。
root@initiator:~# nvme connect -t rdma -n "nqn.2016-06.io.spdk:cnode1" -a 10.0.0.1 -s 4420
認識しているか確認します。
root@initiator:~# nvme list
Node SN Model Namespace Usage Format FW Rev
--------------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- --------
/dev/nvme0n1 SPDK00000000000001 SPDK_Controller1 1 1.60 TB / 1.60 TB 512 B + 0 B 22.09
無事認識していることが確認できました。
fioを用いてベンチマーク
Initiator 側から NVMe に IO 負荷をかけてベンチマークを実施します。
ベンチマークには fio を用います。
root@initiator:~# apt install fio
シーケンシャルリードでベンチマークをまわしてみます。
root@initiator:~# fio --name=seqread --rw=read --filename=/dev/nvme0n1 --direct=1 --ioengine=libaio --bs=32m --numjobs=2 --size=10G --group_reporting
seqread: (g=0): rw=read, bs=(R) 32.0MiB-32.0MiB, (W) 32.0MiB-32.0MiB, (T) 32.0MiB-32.0MiB, ioengine=libaio, iodepth=1
...
fio-3.28
Starting 2 processes
Jobs: 2 (f=2): [R(2)][100.0%][r=3200MiB/s][r=100 IOPS][eta 00m:00s]
seqread: (groupid=0, jobs=2): err= 0: pid=3997: Sun Jun 26 07:40:59 2022
read: IOPS=100, BW=3222MiB/s (3378MB/s)(20.0GiB/6357msec)
slat (usec): min=7776, max=17896, avg=10373.59, stdev=1942.05
clat (usec): min=4888, max=11913, avg=9478.44, stdev=1901.61
lat (usec): min=17439, max=22960, avg=19853.26, stdev=319.36
clat percentiles (usec):
| 1.00th=[ 5342], 5.00th=[ 6456], 10.00th=[ 7635], 20.00th=[ 7898],
| 30.00th=[ 8029], 40.00th=[ 8160], 50.00th=[ 8586], 60.00th=[11076],
| 70.00th=[11338], 80.00th=[11469], 90.00th=[11600], 95.00th=[11731],
| 99.00th=[11863], 99.50th=[11863], 99.90th=[11863], 99.95th=[11863],
| 99.99th=[11863]
bw ( MiB/s): min= 3200, max= 3328, per=99.99%, avg=3221.33, stdev=24.36, samples=24
iops : min= 100, max= 104, avg=100.67, stdev= 0.76, samples=24
lat (msec) : 10=53.75%, 20=46.25%
cpu : usr=0.19%, sys=14.35%, ctx=10189, majf=0, minf=16408
IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
issued rwts: total=640,0,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=1
Run status group 0 (all jobs):
READ: bw=3222MiB/s (3378MB/s), 3222MiB/s-3222MiB/s (3378MB/s-3378MB/s), io=20.0GiB (21.5GB), run=6357-6357msec
Disk stats (read/write):
nvme0n1: ios=0/0, merge=0/0, ticks=0/0, in_queue=0, util=0.00%
3.3GB/s
となっており、これは使用したNVMeの公称値とほぼ同様です。
続いて、シーケンシャルライトを試します。
root@initiator:~# fio --name=seqwrite --rw=write --filename=/dev/nvme0n1 --direct=1 --ioengine=libaio --bs=32m --numjobs=2 --size=10G --group_reporting
seqwrite: (g=0): rw=write, bs=(R) 32.0MiB-32.0MiB, (W) 32.0MiB-32.0MiB, (T) 32.0MiB-32.0MiB, ioengine=libaio, iodepth=1
...
fio-3.28
Starting 2 processes
Jobs: 2 (f=2): [W(2)][100.0%][w=1345MiB/s][w=42 IOPS][eta 00m:00s]
seqwrite: (groupid=0, jobs=2): err= 0: pid=4369: Sun Jun 26 08:39:02 2022
write: IOPS=40, BW=1303MiB/s (1366MB/s)(20.0GiB/15720msec); 0 zone resets
slat (usec): min=10077, max=40126, avg=24915.36, stdev=5671.09
clat (usec): min=10707, max=43728, avg=24162.18, stdev=5742.35
lat (usec): min=30520, max=65408, avg=49078.53, stdev=3970.34
clat percentiles (usec):
| 1.00th=[12125], 5.00th=[13960], 10.00th=[16909], 20.00th=[19268],
| 30.00th=[20579], 40.00th=[22414], 50.00th=[23987], 60.00th=[26084],
| 70.00th=[27919], 80.00th=[29754], 90.00th=[31327], 95.00th=[32637],
| 99.00th=[35914], 99.50th=[37487], 99.90th=[43779], 99.95th=[43779],
| 99.99th=[43779]
bw ( MiB/s): min= 1152, max= 1408, per=99.99%, avg=1302.71, stdev=33.82, samples=62
iops : min= 36, max= 44, avg=40.71, stdev= 1.06, samples=62
lat (msec) : 20=26.56%, 50=73.44%
cpu : usr=3.30%, sys=3.68%, ctx=10582, majf=0, minf=19
IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
issued rwts: total=0,640,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=1
Run status group 0 (all jobs):
WRITE: bw=1303MiB/s (1366MB/s), 1303MiB/s-1303MiB/s (1366MB/s-1366MB/s), io=20.0GiB (21.5GB), run=15720-15720msec
Disk stats (read/write):
nvme0n1: ios=0/0, merge=0/0, ticks=0/0, in_queue=0, util=0.00%
こちらも 1.3GB/s
となっており公称値と同等です。
EthernetSwitchを挟む
NVMeをもう1台分設定したうえで、経路上に Ethernet Switch を挟んでみます。
使用する Switch は Azure/SONiC を搭載したホワイトボックススイッチです。
ブロックデバイスの設定と同様の手順をターゲット側に実施し、NVMeの設定を追加しておきます。
追加後の Initiator 側からの Discover 結果は次のようになります。
root@initiator:~# nvme discover -t rdma -a 10.0.0.1 -s 4420
Discovery Log Number of Records 2, Generation counter 2
=====Discovery Log Entry 0======
trtype: rdma
adrfam: ipv4
subtype: nvme subsystem
treq: not required
portid: 0
trsvcid: 4420
subnqn: nqn.2016-06.io.spdk:cnode1
traddr: 10.0.0.1
rdma_prtype: not specified
rdma_qptype: connected
rdma_cms: rdma-cm
rdma_pkey: 0x0000
=====Discovery Log Entry 1======
trtype: rdma
adrfam: ipv4
subtype: nvme subsystem
treq: not required
portid: 0
trsvcid: 4420
subnqn: nqn.2016-06.io.spdk:cnode2
traddr: 10.0.0.1
rdma_prtype: not specified
rdma_qptype: connected
rdma_cms: rdma-cm
rdma_pkey: 0x0000
2つの NVMe を認識していることがわかります。
本来であれば Switch 側に QoS 等の設定が必要なはずですが、今回使用した ConnectX-4 の Zero Touch RoCEv2 によって Switch 側の設定なしに機能しているようです。
複数のInitiatorからIO負荷をかける
複数の Intiator でそれぞれ1台ずつ NVMe を接続した状態で同時に IO 負荷をかけてみます。
結果は次のようになりました。
Initiator 1 : Run status group 0 (all jobs):
READ: bw=3221MiB/s (3377MB/s), 3221MiB/s-3221MiB/s (3377MB/s-3377MB/s), io=200GiB (215GB), run=63589-63589msec
Initiator 2 : Run status group 0 (all jobs):
READ: bw=3223MiB/s (3380MB/s), 3223MiB/s-3223MiB/s (3380MB/s-3380MB/s), io=200GiB (215GB), run=63538-63538msec
それぞれのディスクで 3.3GB/s
を確認できます。
この程度では特に問題なく動作するようです。
この時、 Switch 側のトラフィック量は次のようになっていました。
admin@sonic:~$ show interfaces counters
IFACE STATE RX_OK RX_BPS RX_UTIL RX_ERR RX_DRP RX_OVR TX_OK TX_BPS TX_UTIL TX_ERR TX_DRP TX_OVR
----------- ------- ---------- ------------ --------- -------- -------- -------- ---------- ------------ --------- -------- -------- --------
Ethernet0 U 51,631,461 6616.61 MB/s 52.93% 0 2 0 4,696,866 13.35 MB/s 0.11% 0 0 0
Ethernet4 D 0 0.00 B/s 0.00% 0 0 0 0 0.00 B/s 0.00% 0 0 0
Ethernet8 D 0 0.00 B/s 0.00% 0 0 0 0 0.00 B/s 0.00% 0 0 0
Ethernet12 D 0 0.00 B/s 0.00% 0 0 0 0 0.00 B/s 0.00% 0 0 0
Ethernet16 U 2,788,576 6529.03 KB/s 0.05% 0 0 0 31,156,784 3302.67 MB/s 26.42% 0 0 0
Ethernet20 D 0 0.00 B/s 0.00% 0 0 0 0 0.00 B/s 0.00% 0 0 0
Ethernet24 D 0 0.00 B/s 0.00% 0 0 0 0 0.00 B/s 0.00% 0 0 0
Ethernet28 D 0 0.00 B/s 0.00% 0 0 0 0 0.00 B/s 0.00% 0 0 0
Ethernet32 U 1,907,040 6825.01 KB/s 0.05% 0 0 0 20,460,390 3315.02 MB/s 26.52% 0 0 0
Ethernet36 D 0 0.00 B/s 0.00% 0 0 0 0 0.00 B/s 0.00% 0 0 0
Ethernet40 D 0 0.00 B/s 0.00% 0 0 0 0 0.00 B/s 0.00% 0 0 0
Ethernet44 D 0 0.00 B/s 0.00% 0 0 0 0 0.00 B/s 0.00% 0 0 0
Ethernet0
がターゲット側のインタフェースであり、 6.6GB/s
を観測しており、先ほどのベンチマーク結果と一致しています。
まとめ
今回は、SPDK を利用して NVMe over RDMA ターゲットを構築するとともに、Initiator から接続を行って検証を行うとともに、簡単なパフォーマンス測定を行いました。
検証に使用した環境では十分に性能が発揮できているようです。
後日、NVMe over TCP の検証も行う予定なので、その際にベンチマーク結果などを確認できればと思います。