NVMe over TCP を試す
背景
NVMe は SCSI に変わるストレージ通信プロトコルで,SSD,フラッシュストレージの転送能力を最大限に活かすことができます.NVMe は PC やワークステーションの内蔵ディスクとしてよく用いられるようになってきましたが,NVMe-oF という,筐体間 NVMe を実現する規格が標準化され,対応製品が登場してきたことにより,大規模サーバー環境で用いられる SAN を NVMe で構築することができるようになってきました.
iSCSI の後継としての NVMe over TCP に注目
NVMe-oF で筐体間を接続する通信は,当初 Infiniband,Ethernet, FibreChannel が選べました(現在ストレージメーカーが出している製品はほとんどこれです)が,ここに TCP も選択肢に入ってきました.SCSI ベース SAN の時代を思い起こしてみると,大規模 SAN は FC で,中小規模 SAN は iSCSI でという棲み分けになっていたかと思いますが,数で言うと iSCSI 利用が圧倒的なんじゃないかと思います.ま,やはり FC (や FCoE)の知識,お作法というのはハードルが高いです.
今後 NVMe-oF でも同じようなことになると想像されますので,iSCSI の後継としての NVMe over TCP に着目している次第です.代表的な Linux ディストリビューションである RHEL でも,RHEL8.2 から NVMe over TCP が Technology Preview という状態ではありますが,利用可能です.
それを簡単に動かしてみようというのが今回のお題です.
試験部材を揃える
ダミーデバイスを用いて,プロトコル性能だけに着目して NVMe over TCP を動かしてみるということもできます.その場合なら,1台のホスト内に仮想マシンを2台立てて試験を行うということもできます.しかし,今回は実の NVMe を叩いて性能がどれだけ出るかを確かめたく,物理マシンを2台立てることにしました.
ターゲット機(ストレージサーバー)
本体: 富士通 TX1320 M3(E3-1220 v6,16GB RAM)
内蔵 NVMe SSD: Samsung 970 EVO Plus(PCIe 3.0 x4)
25GbE NIC: NVIDIA MCX4121A-ACAT(PCDe 3.0 x8)
SSD を実装した様子.TX1320 M3 には M.2 スロットがないので,PCIe スロットへの変換ライザー経由で取り付けます.
イニシエーター機(ストレージクライアント)
本体: 富士通 TX1320 M3(E3-1220 v6,16GB RAM)
25GbE NIC: NVIDIA MCX4121A-ACAT(PCDe 3.0 x8)
こちらは 25GbE NIC を取り付けたところ.
25GbE スイッチを持っていませんので,ターゲットとイニシエーターは,Back-to-back で DAC 接続としました.
TX1320 M3 は,富士通さんの1世代前の SFF 静音サーバーです.サーバー機のありがたいのは,BMC(iRMC)がついていて,ヘッドレスで大体の操作ができることです.ただし,仮想 KVM には iRMC のライセンス(高い)が必要ですので,仮想 KVM は使用せず,仮想シリアル接続で OS のインストールを行なっています.
MCX4121A-ACAT はコスパの良い 25GbE カードです(というか,現状,25GbE のコスパが優れているというべきか).ROCE 対応もしていますので,NVMe over TCP だけでなく,NVMe-oF の ROCE 接続もテストできます.
構成
まずは非常に単純な構成を試します.単純というのは,ターゲットとイニシエーターを 25GbE 1本で接続するということです.この構成だと2本使って Bonding するべきでは?と考えますが,通信相手が互いに一つしかない通信で Bonding をするのは???です.(古い考えかもしれませんが,1本の TCP フロー内のパケットを物理的に別経路を通した場合,追い越しの発生の可能性がありスループットが低下します.そこで,Bonding でロードバランスする時は,L2〜L4 の情報からハッシュを作って物理ポートを分散するというのが Best Practice です.しかし,1対1の通信で,しかもフローが1本の通信の場合はハッシュは1つになってしまいますので,結果としてロードバランスされません)
…とは言いつつ,後で2本目も利用すると思います(経路冗長のため).が,それは別記事で.
OS はちょうど先日 5/19 に RHEL8.4 がリリースされたのでそれを利用します.(RHEL8.4 でも,NVMe over TCP は相変わらず Technology Preview のままですが…)
インストール時に指定したパッケージは,kickstart から,
%packages
@^minimal-environment
kexec-tools
nfs-utils
nvme-cli
%end
です.Minimal を入れて,後の3つは追加,でももちろん OK.とりあえずその状態から設定をしていきます.あと,性能測定のため fio パッケージも追加しています.
ターゲットマシン内での NVMe 速度測定
ターゲットマシンでは,何もしなくても NVMe デバイスが認識されます.
[ 11.194790] nvme nvme0: pci function 0000:05:00.0
[ 11.201543] nvme nvme0: missing or invalid SUBNQN field.
[ 11.245501] nvme nvme0: Shutdown timeout set to 8 seconds
[ 11.295133] nvme nvme0: 4/0/0 default/read/poll queues
[root@nvmet ~]# ls -l /dev/nvme*
crw------- 1 root root 243, 0 5月 22 10:36 /dev/nvme0
brw-rw---- 1 root disk 259, 0 5月 22 10:59 /dev/nvme0n1
[root@nvmet ~]#
ここでターゲットマシンローカルでの速度を測定してみます.
hdparm で READ 速度測定
[root@nvmet ~]# hdparm -Tt --direct /dev/nvme0n1
/dev/nvme0n1:
Timing O_DIRECT cached reads: 4278 MB in 2.00 seconds = 2140.39 MB/sec
Timing O_DIRECT disk reads: 8026 MB in 3.00 seconds = 2674.93 MB/sec
[root@nvmet ~]#
dd で WRITE 速度測定
[root@nvmet ~]# dd if=/dev/zero of=/dev/nvme0n1 ibs=1M obs=1M count=1024
1024+0 レコード入力
1024+0 レコード出力
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 0.808906 s, 1.3 GB/s
[root@nvmet ~]#
ファイルシステムを作成して fio で測定
https://thr3a.hatenablog.com/entry/20180804/1533378243
↑参考にさせていただきました.
[root@nvmet ~]# parted /dev/nvme0n1 mklabel gpt
Warning: The existing disk label on /dev/nvme0n1 will be destroyed and all data
on this disk will be lost. Do you want to continue?
Yes/No? yes
Information: You may need to update /etc/fstab.
[root@nvmet ~]# parted /dev/nvme0n1 mkpart primary 0% 100%
Information: You may need to update /etc/fstab.
[root@nvmet ~]# parted /dev/nvme0n1 print
Model: NVMe Device (nvme)
Disk /dev/nvme0n1: 500GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:
Number Start End Size File system Name Flags
1 1049kB 500GB 500GB primary
[root@nvmet ~]# mkfs.xfs /dev/nvme0n1p1
meta-data=/dev/nvme0n1p1 isize=512 agcount=4, agsize=30524096 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1, sparse=1, rmapbt=0
= reflink=1
data = bsize=4096 blocks=122096384, imaxpct=25
= sunit=0 swidth=0 blks
naming =version 2 bsize=4096 ascii-ci=0, ftype=1
log =internal log bsize=4096 blocks=59617, version=2
= sectsz=512 sunit=0 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
Discarding blocks...Done.
[root@nvmet ~]# mount /dev/nvme0n1p1 /mnt
[root@nvmet ~]# TARGET=/mnt fio -f /tmp/fio.txt --output-format=terse | awk -F ';' '{print $3, ($7+$48) / 1000}'
Seq-Read 2255
Seq-Write 2064.12
Rand-Read-512K 1814.14
Rand-Write-512K 2072.28
Rand-Read-4K 82.325
Rand-Write-4K 193.714
Rand-Read-4K-QD32 1075.46
Rand-Write-4K-QD32 442.064
[root@nvmet ~]#
こんなものでしょうか.
NVMe over TCP の設定投入
IP アドレス設定
ターゲット
# nmcli con mod "System enp1s0f0" ipv4.address 192.168.0.0/31
# nmcli con mod "System enp1s0f0" ipv4.method manual
# nmcli con mod "System enp1s0f0" ipv6.method ignore
# nmcli conn mod "System enp1s0f0" connection.autoconnect yes
イニシエーター
# nmcli con mod "System enp1s0f0" ipv4.address 192.168.0.1/31
# nmcli con mod "System enp1s0f0" ipv4.method manual
# nmcli con mod "System enp1s0f0" ipv6.method ignore
# nmcli conn mod "System enp1s0f0" connection.autoconnect yes
ターゲットでの NVMe over TCP 設定
ターゲットマシンでのファイルシステムマウントは外しておきます.
[root@nvmet ~]# modprobe nvmet
[root@nvmet ~]# modprobe nvmet-tcp
[root@nvmet ~]# mkdir /sys/kernel/config/nvmet/subsystems/nvmet-test
[root@nvmet ~]# echo 1 > /sys/kernel/config/nvmet/subsystems/nvmet-test/attr_allow_any_host
[root@nvmet ~]# mkdir /sys/kernel/config/nvmet/subsystems/nvmet-test/namespaces/1
[root@nvmet ~]# echo -n /dev/nvme0n1 > /sys/kernel/config/nvmet/subsystems/nvmet-test/namespaces/1/device_path
[root@nvmet ~]# echo 1 > /sys/kernel/config/nvmet/subsystems/nvmet-test/namespaces/1/enable
[root@nvmet ~]# mkdir /sys/kernel/config/nvmet/ports/1
[root@nvmet ~]# echo 192.168.0.0 > /sys/kernel/config/nvmet/ports/1/addr_traddr
[root@nvmet ~]# echo tcp > /sys/kernel/config/nvmet/ports/1/addr_trtype
[root@nvmet ~]# echo 4420 > /sys/kernel/config/nvmet/ports/1/addr_trsvcid
[root@nvmet ~]# echo ipv4 > /sys/kernel/config/nvmet/ports/1/addr_adrfam
[root@nvmet ~]# ln -s /sys/kernel/config/nvmet/subsystems/nvmet-test/ /sys/kernel/config/nvmet/ports/1/subsystems/nvmet-t
ログ
[root@nvmet ~]# dmesg | tail
...
[ 9788.683284] TECH PREVIEW: NVMe/TCP Target may not be fully supported.
Please review provided documentation for limitations.
[ 9808.098889] nvmet: adding nsid 1 to subsystem nvmet-test
[ 9858.823349] nvmet_tcp: enabling port 1 (192.168.0.0:4420)
[root@nvmet ~]#
イニシエーター側の設定
[root@nvmei ~]# modprobe nvme-tcp
[root@nvmei ~]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 1.8T 0 disk
├─sda1 8:1 0 600M 0 part /boot/efi
├─sda2 8:2 0 1G 0 part /boot
└─sda3 8:3 0 77.8G 0 part
├─rhel_nvmei-root 253:0 0 70G 0 lvm /
└─rhel_nvmei-swap 253:1 0 7.8G 0 lvm [SWAP]
[root@nvmei ~]# nvme connect -n nvmet-test -t tcp -a 192.168.0.0 -s 4420
[root@nvmei ~]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 1.8T 0 disk
├─sda1 8:1 0 600M 0 part /boot/efi
├─sda2 8:2 0 1G 0 part /boot
└─sda3 8:3 0 77.8G 0 part
├─rhel_nvmei-root 253:0 0 70G 0 lvm /
└─rhel_nvmei-swap 253:1 0 7.8G 0 lvm [SWAP]
nvme0n1 259:0 0 465.8G 0 disk
└─nvme0n1p1 259:1 0 465.8G 0 part
[root@nvmei ~]#
ログ
[root@nvmei ~]# dmesg | tail
...
[ 9991.759136] TECH PREVIEW: NVMe/TCP may not be fully supported.
Please review provided documentation for limitations.
[10013.576673] nvme nvme0: creating 4 I/O queues.
[10013.593230] nvme nvme0: mapped 4/0/0 default/read/poll queues.
[10013.613556] nvme nvme0: new ctrl: NQN "nvmet-test", addr 192.168.0.0:4420
[10013.648547] nvme0n1: p1
[root@nvmei ~]#
無事,イニシエーター側でブロックデバイス nvme0n1 が認識されました.(さっき,ターゲットローカルでパーティションを切ってしまったので,パーティションも見えてます)
NVMe over TCP の性能測定
hdparm で READ 速度測定
[root@nvmei ~]# hdparm -Tt --direct /dev/nvme0n1
/dev/nvme0n1:
Timing O_DIRECT cached reads: 2702 MB in 2.00 seconds = 1351.03 MB/sec
Timing O_DIRECT disk reads: 4660 MB in 3.00 seconds = 1552.73 MB/sec
[root@nvmei ~]#
fio でファイルシステム速度測定
[root@nvmei ~]# mount /dev/nvme0n1p1 /mnt
[root@nvmei ~]# TARGET=/mnt fio -f /tmp/fio.txt --output-format=terse | awk -F ';' '{print $3, ($7+$48) / 1000}'
Seq-Read 1125.08
Seq-Write 1175.53
Rand-Read-512K 804.739
Rand-Write-512K 1074.36
Rand-Read-4K 39.528
Rand-Write-4K 110.469
Rand-Read-4K-QD32 375.699
Rand-Write-4K-QD32 474.683
[root@nvmei ~]#
dd で WRITE 速度測定
[root@nvmei ~]# dd if=/dev/zero of=/dev/nvme0n1 ibs=1M obs=1M count=1024
1024+0 レコード入力
1024+0 レコード出力
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 0.881616 s, 1.2 GB/s
[root@nvmei ~]#
評価
ローカルの時に比べると,Queue Depth が少なく BS の大きい Read/Write は半分ぐらいになってしまいますが,それでも 1GByte/sec 出ていて,SATA 速度よりもだいぶ速い結果になっています.ですので,現在一般的なサーバーの内蔵ストレージや,〜8Gbps FC 接続ストレージ,〜10G iSCSI 接続ストレージで動いているアプリケーションなら,だいたい収容できそうだという感覚です.
また,たまたま見つけたので比較ですが,ニフティクラウドさんのサーバーのディスク I/O 性能(2016年8月15日)が出ています↓
https://cloudserv.jp/niftycloud/disktype/
2016年のクラウドサーバーと比較するのもどうかと思いますが,
READ
ニフクラ/フラッシュドライブ: 340MB/s
NVMe over TCP(今回試験): 1550MB/s
WRITE
ニフクラ/フラッシュドライブ: 561MB/s
NVMe over TCP(今回試験): 1200MB/s
ということで,まずまずかと思います.
今後やること
いろいろやりたいことはありますが,
運用上のこと
* 2ポート目を接続して冗長にする(記事化予定)
* 起動時にファイルシステムとしてマウントする(記事化予定)
性能上のこと
* fio を使い慣れていないので,パラメーターをいろいろ変更して数字を出してみたい
* iSCSI との性能比較
* NVMe-oF (ROCE) との性能比較