おうちで始めるNVMe over TCP入門

目次

概要

前回、NVMe over RDMA 環境を構築し、簡単な動作確認を行いました。
今回は、前回の概要でも触れたとおりNVMe over TCP を試してみます。

NVMe over TCP はその名の通り、NVMe プロトコルを TCP プロトコル上に流すことでリモートでの利用を可能にしたものです。
NVMe over RDMA (RoCEv2) と異なり、一般的な TCP プロトコルを使用しているため専用のスイッチなどを必要としないメリットがあります。
しかしながら、メモリ上のデータを直接扱う RDMA の恩恵を受けられないため、プロトコルスタックの処理によるCPU負荷の上昇やIOに対する遅延の増大、スループットの低下などのデメリットも存在します。

今回はそんな 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

準備

今回も、前回と同様に SPDK を用いて環境構築を行います。
SPDK をビルドし nvmf_tgt が実行可能な状態までは前回と同様の手順です。

ネットワーク設定

この段階でネットワーク設定を行っておきます。
手順については省略します。
なお、今回は次のような構成にしています。
なお、リンクスピードは100Gbps、MTUは9000です。

ターゲット側 10.0.0.1/24 ---------- 10.0.0.2/24 Initiator側

nvme-cliのインストール

ターゲット側とInitiator側の両方に nvme-cli をインストールしておきます。

# apt install nvme-cli

ターゲット側の設定

nvmf_tgt を起動した状態で設定を入れていきます。
前回と異なるのは、 nvmf_create_transportnvmf_subsystem_add_listener 設定において TCP を指定している部分です。

root@target:~/spdk# ./build/bin/nvmf_tgt
root@target:/usr/src/spdk# ./scripts/rpc.py nvmf_create_transport -t TCP -u 16384 -m 8 -c 8192
root@target:/usr/src/spdk# scripts/rpc.py bdev_nvme_attach_controller -b NVMe0 -a 0000:39:00.0 -t pcie
NVMe0n1
root@target:/usr/src/spdk# scripts/rpc.py bdev_nvme_get_controllers
[
  {
    "name": "NVMe0",
    "ctrlrs": [
      {
        "state": "enabled",
        "trid": {
          "trtype": "PCIe",
          "traddr": "0000:39:00.0"
        },
        "cntlid": 0,
        "host": {
          "nqn": "nqn.2014-08.org.nvmexpress:uuid:d4549310-b740-4edc-bed0-95dbcbec1178",
          "addr": "",
          "svcid": ""
        }
      }
    ]
  }
]
root@target:/usr/src/spdk# scripts/rpc.py nvmf_create_subsystem nqn.2016-06.io.spdk:cnode1 -a -s SPDK00000000000001 -d SPDK_Controller1
root@target:/usr/src/spdk# scripts/rpc.py nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 NVMe0n1
root@target:/usr/src/spdk# scripts/rpc.py nvmf_subsystem_add_listener nqn.2016-06.io.spdk:cnode1 -t tcp -a 10.0.0.1 -s 4420

なお、詳細な設定内容や複数ディスクへの対応方法などは前回の記事で解説しています。

Initiator側の設定

NVMe over TCP を利用するには nvme-tcp モジュールが必要となり、対応したカーネルに更新する必要があります。
今回は、 linux-modules-extra-5.15.0-1004-gke を利用します。

root@initiator:# apt install linux-modules-extra-5.15.0-1004-gke
root@initiator:# reboot
root@initiator:# mobprobe nvme_tcp

無事に nvme-tcp モジュールを有効化出来たら、nvme discover を実行して検出できるか確認します。

root@initiator:~# nvme discover -t tcp -a 10.0.0.1 -s 4420

Discovery Log Number of Records 1, Generation counter 1
=====Discovery Log Entry 0======
trtype:  tcp
adrfam:  ipv4
subtype: nvme subsystem
treq:    not required
portid:  0
trsvcid: 4420
subnqn:  nqn.2016-06.io.spdk:cnode1
traddr:  10.0.0.1
sectype: none

接続し、認識しているか確認します。

root@initiator:~# nvme connect -t tcp -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 で簡単にベンチマークしていきます。

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=3171MiB/s][r=99 IOPS][eta 00m:00s]
seqread: (groupid=0, jobs=2): err= 0: pid=2327: Fri Jul  1 13:32:00 2022
  read: IOPS=98, BW=3167MiB/s (3321MB/s)(20.0GiB/6466msec)
    slat (usec): min=6780, max=27344, avg=12248.84, stdev=2025.29
    clat (usec): min=5220, max=12290, avg=7910.04, stdev=1526.57
     lat (usec): min=15432, max=35796, avg=20160.18, stdev=1641.21
    clat percentiles (usec):
     |  1.00th=[ 5342],  5.00th=[ 5538], 10.00th=[ 5735], 20.00th=[ 6128],
     | 30.00th=[ 6718], 40.00th=[ 7504], 50.00th=[ 8291], 60.00th=[ 8717],
     | 70.00th=[ 8979], 80.00th=[ 9241], 90.00th=[ 9634], 95.00th=[10028],
     | 99.00th=[10552], 99.50th=[10814], 99.90th=[12256], 99.95th=[12256],
     | 99.99th=[12256]
   bw (  MiB/s): min= 3008, max= 3328, per=100.00%, avg=3173.33, stdev=42.02, samples=24
   iops        : min=   94, max=  104, avg=99.17, stdev= 1.31, samples=24
  lat (msec)   : 10=94.06%, 20=5.94%
  cpu          : usr=0.28%, sys=7.61%, ctx=11290, majf=0, minf=16410
  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=3167MiB/s (3321MB/s), 3167MiB/s-3167MiB/s (3321MB/s-3321MB/s), io=20.0GiB (21.5GB), run=6466-6466msec

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=1280MiB/s][w=40 IOPS][eta 00m:00s]
seqwrite: (groupid=0, jobs=2): err= 0: pid=2372: Fri Jul  1 13:32:35 2022
  write: IOPS=38, BW=1248MiB/s (1308MB/s)(20.0GiB/16414msec); 0 zone resets
    slat (usec): min=7971, max=51897, avg=27101.64, stdev=4912.11
    clat (usec): min=10756, max=46502, avg=24141.04, stdev=5077.53
     lat (usec): min=25093, max=79939, avg=51243.83, stdev=6243.81
    clat percentiles (usec):
     |  1.00th=[14484],  5.00th=[16909], 10.00th=[18482], 20.00th=[20055],
     | 30.00th=[21103], 40.00th=[22676], 50.00th=[23725], 60.00th=[24773],
     | 70.00th=[25822], 80.00th=[27395], 90.00th=[31065], 95.00th=[33817],
     | 99.00th=[39584], 99.50th=[40633], 99.90th=[46400], 99.95th=[46400],
     | 99.99th=[46400]
   bw (  MiB/s): min=  960, max= 1408, per=99.86%, avg=1246.00, stdev=60.59, samples=64
   iops        : min=   30, max=   44, avg=38.94, stdev= 1.89, samples=64
  lat (msec)   : 20=19.69%, 50=80.31%
  cpu          : usr=3.62%, sys=4.22%, ctx=11075, 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=1248MiB/s (1308MB/s), 1248MiB/s-1248MiB/s (1308MB/s-1308MB/s), io=20.0GiB (21.5GB), run=16414-16414msec

Disk stats (read/write):
  nvme0n1: ios=0/0, merge=0/0, ticks=0/0, in_queue=0, util=0.00%

こちらも 1.3GB/s となっており公称値と同等です。

まとめ

今回は、前回作成した環境と SPDK を利用して、NVMe over TCP 環境を構築し、簡単なパフォーマンス測定を行いました。
シーケンシャルリード/ライトにおいてはスループット面では特に問題はないようです。
次回、NVMe over RDMA と NVMe over TCP の詳細なパフォーマンス測定を行う予定です。

About

インフラエンジニア
インフラ系の技術を中心に紹介・解説しています。

お問い合わせはTwitter DMまで

Privacy Policy

About Me

Recommends

Archives