2021年12月7日火曜日

Oracle Linux 8 で NVMe over TCP ターゲットを構築してみる。

JPOUG Advent Calendar 2021 の 7日目の投稿です。
6日目は charade_oo4o さんの Oracle on Hyper-V 2021 でした。

今日は、Oracle Linux 8 で NVMe over TCP(NVMe-TCP)ターゲットを構築してみます。

手順については、下記を参考にしています。

Oracle Linux Blog
NVMe over TCP
https://blogs.oracle.com/linux/post/nvme-over-tcp


(仮想)マシンの環境

今回は、下記のような環境を用意しました。

NVMe-TCP ターゲットを構築する VM(lab-nvmet-01)と、
クライアント用の VM(lab-client-01)は、どちらも ESXi 7.0 の上で稼働しています。


どちらも、「ネットワーク アダプタ 2」を
NVMe over TCP むけネットワークに接続してあります。

そして lab-nvmet-01 には、仮想 NVMe デバイスとなる
「ハード ディスク 2」を追加してあります。


「ハード ディスク 2」は、ESXi による仮想 NVMe コントローラで接続してあります。


Oracle Linux ゲスト OS の様子

今回は、Oracle Linux 8.5 を利用します。
カーネルは、UEK R6 U3 です。(5.4.17-2136)

[root@lab-nvmet-01 ~]# cat /etc/oracle-release
Oracle Linux Server release 8.5
[root@lab-nvmet-01 ~]# uname -r
5.4.17-2136.301.1.3.el8uek.x86_64


NVMe コントローラが認識されています。
ただし、これは ESXi による仮想 NVMe コントローラです。

[root@lab-stg-01 ~]# lspci | grep mem
13:00.0 Non-Volatile memory controller: VMware Device 07f0


VMware Virtual NVMe Disk__1 が、
/dev/nvme0n1 というデバイスとして認識されています。

[root@lab-stg-01 ~]# lsscsi
[0:0:0:0]    disk    VMware   Virtual disk     2.0   /dev/sda
[3:0:0:0]    cd/dvd  NECVMWar VMware SATA CD00 1.00  /dev/sr0
[N:0:0:1]    disk    VMware Virtual NVMe Disk__1                /dev/nvme0n1


UEK R6 では、デフォルトで NVMe-TCP が有効にされています。

[root@lab-nvmet-01 ~]# ls /boot/config-$(uname -r)
/boot/config-5.4.17-2136.301.1.3.el8uek.x86_64
[root@lab-nvmet-01 ~]# cat /boot/config-$(uname -r) | grep "NVME.*TCP"
CONFIG_NVME_TCP=m
CONFIG_NVME_TARGET_TCP=m


NVMe のデバイスは、nvme list でも確認できます。

[root@lab-nvmet-01 ~]# nvme list Node                  SN                   Model                                    Namespace Usage                      Format           FW Rev --------------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- -------- /dev/nvme0n1          VMWare NVME_0000     VMware Virtual NVMe Disk                 1         214.75  GB / 214.75  GB    512   B +  0 B   1.0


見栄えの都合で、JSON 形式で表示しておきます。

[root@lab-nvmet-01 ~]# nvme list -o json
{
  "Devices" : [
    {
      "NameSpace" : 1,
      "DevicePath" : "/dev/nvme0n1",
      "Firmware" : "1.0",
      "Index" : 0,
      "ModelNumber" : "VMware Virtual NVMe Disk",
      "ProductName" : "Non-Volatile memory controller: VMware Device 0x07f0",
      "SerialNumber" : "VMWare NVME_0000",
      "UsedBytes" : 214748364800,
      "MaximumLBA" : 419430400,
      "PhysicalSize" : 214748364800,
      "SectorSize" : 512
    }
  ]
}


NVMe-TCP ターゲットの構築

firewalld は、停止しておきます。

[root@lab-nvmet-01 ~]# systemctl stop firewalld
[root@lab-nvmet-01 ~]# systemctl disable firewalld


NVMe-TCP に必要なモジュールを読み込んでおきます。

[root@lab-nvmet-01 ~]# modprobe nvme_tcp
[root@lab-nvmet-01 ~]# modprobe nvmet
[root@lab-nvmet-01 ~]# lsmod | grep nvme
nvmet                  94208  0
nvme_tcp               36864  0
nvme_fabrics           24576  1 nvme_tcp
nvme                   45056  0
nvme_core              98304  4 nvmet,nvme_tcp,nvme,nvme_fabrics


このモジュールは、OS 起動時に読み込まれるようにしておきます。

[root@lab-nvmet-01 ~]# echo nvme_tcp >> /etc/modules-load.d/nvme.conf
[root@lab-nvmet-01 ~]# echo nvmet >> /etc/modules-load.d/nvme.conf
[root@lab-nvmet-01 ~]# cat /etc/modules-load.d/nvme.conf
nvme_tcp
nvmet


今回利用する nvme コマンドと nvmetcli コマンドは、
デフォルトでインストールされていました。

[root@lab-nvmet-01 ~]# which nvme nvmetcli
/usr/sbin/nvme
/usr/sbin/nvmetcli
[root@lab-nvmet-01 ~]# rpm -qf /usr/sbin/nvme
nvme-cli-1.14-3.el8.x86_64
[root@lab-nvmet-01 ~]# rpm -qf /usr/sbin/nvmetcli
nvmetcli-0.7-3.0.1.el8.noarch


ひたすら NVMe-TCP ターゲットの設定をします。
今回は参考にしたブログにあわせて、nvmetcli ではなく sysfs で設定します。

  • NVMe サブシステムの名前は nvmet-test
  • NVMe デバイスは /dev/nvme0n1
  • ターゲットの IP アドレスは、10.12.7.11
  • ポートは TCP 4420

[root@lab-nvmet-01 ~]# mkdir /sys/kernel/config/nvmet/subsystems/nvmet-test
[root@lab-nvmet-01 ~]# echo 1 > /sys/kernel/config/nvmet/subsystems/nvmet-test/attr_allow_any_host
[root@lab-nvmet-01 ~]# mkdir /sys/kernel/config/nvmet/subsystems/nvmet-test/namespaces/1
[root@lab-nvmet-01 ~]# echo -n /dev/nvme0n1 > /sys/kernel/config/nvmet/subsystems/nvmet-test/namespaces/1/device_path
[root@lab-nvmet-01 ~]# echo 1 > /sys/kernel/config/nvmet/subsystems/nvmet-test/namespaces/1/enable
[root@lab-nvmet-01 ~]# mkdir /sys/kernel/config/nvmet/ports/1
[root@lab-nvmet-01 ~]# echo 10.12.7.11 > /sys/kernel/config/nvmet/ports/1/addr_traddr
[root@lab-nvmet-01 ~]# echo tcp > /sys/kernel/config/nvmet/ports/1/addr_trtype
[root@lab-nvmet-01 ~]# echo 4420 > /sys/kernel/config/nvmet/ports/1/addr_trsvcid
[root@lab-nvmet-01 ~]# echo ipv4 > /sys/kernel/config/nvmet/ports/1/addr_adrfam
[root@lab-nvmet-01 ~]# ln -s /sys/kernel/config/nvmet/subsystems/nvmet-test/ /sys/kernel/config/nvmet/ports/1/subsystems/nvmet-test


設定を保存します。設定の永続化には、nvmetcli を利用します。

[root@lab-nvmet-01 ~]# ls /etc/nvmet/
[root@lab-nvmet-01 ~]# nvmetcli save
[root@lab-nvmet-01 ~]# ls /etc/nvmet/
config.json


これで、ターゲットが設定できました。

[root@lab-nvmet-01 ~]# nvmetcli ls



OS 再起動後も、ターゲットの設定が読み込まれるようにしておきます。

[root@lab-nvmet-01 ~]# systemctl enable nvmet
[root@lab-nvmet-01 ~]# systemctl start nvmet


クライアントからの接続

クライアントでも、Oracle Linux 8.5 を利用します。

[root@lab-client-01 ~]# cat /etc/oracle-release
Oracle Linux Server release 8.5
[root@lab-client-01 ~]# uname -r
5.4.17-2136.301.1.3.el8uek.x86_64


クライアントのマシンには、まだ NVMe のデバイスは存在しません。

[root@lab-client-01 ~]# nvme list -o json
{
  "Devices" : [

  ]
}


そのため、まだ nvme 関連モジュールも読み込まれていません。

[root@lab-client-01 ~]# lsmod | grep nvme
[root@lab-client-01 ~]#


NVMe-TCP で必要なモジュールを読み込みます。

[root@lab-client-01 ~]# modprobe nvme
[root@lab-client-01 ~]# modprobe nvme_tcp
[root@lab-client-01 ~]# lsmod | grep nvme
nvme_tcp               36864  0
nvme_fabrics           24576  1 nvme_tcp
nvme                   45056  0
nvme_core              98304  3 nvme_tcp,nvme,nvme_fabrics


NVMe-TCP ターゲットのデバイスを discover で検出します。

[root@lab-client-01 ~]# nvme discover -t tcp -a 10.12.7.11 -s 4420

Discovery Log Number of Records 1, Generation counter 5
=====Discovery Log Entry 0======
trtype:  tcp
adrfam:  ipv4
subtype: nvme subsystem
treq:    not specified, sq flow control disable supported
portid:  1
trsvcid: 4420
subnqn:  nvmet-test
traddr:  10.12.7.11
sectype: none


接続します。NVMe サブシステムの名前は、nvmet-test です。

[root@lab-client-01 ~]# nvme connect -t tcp -n nvmet-test -a 10.12.7.11 -s 4420


NVMe-TCP ターゲットのデバイスが接続され、/dev/nvme0n1 という名前で認識されました。

[root@lab-client-01 ~]# nvme list -o json
{
  "Devices" : [
    {
      "NameSpace" : 1,
      "DevicePath" : "/dev/nvme0n1",
      "Firmware" : "5.4.17-2",
      "Index" : 0,
      "ModelNumber" : "Linux",
      "SerialNumber" : "6adec1eff8a56335",
      "UsedBytes" : 214748364800,
      "MaximumLBA" : 419430400,
      "PhysicalSize" : 214748364800,
      "SectorSize" : 512
    }
  ]
}


lsblk でも確認してみます。nvme0n1 が認識されています。

[root@lab-client-01 ~]# lsblk -i
NAME        MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda           8:0    0   16G  0 disk
|-sda1        8:1    0  600M  0 part /boot/efi
|-sda2        8:2    0    1G  0 part /boot
`-sda3        8:3    0 14.4G  0 part
  |-ol-root 252:0    0 12.8G  0 lvm  /
  `-ol-swap 252:1    0  1.6G  0 lvm  [SWAP]
sr0          11:0    1 1024M  0 rom
nvme0n1     259:1    0  200G  0 disk


/dev/nvme0n1 にパーティションを作成します。

[root@lab-client-01 ~]# echo "2048,," | sfdisk -uS /dev/nvme0n1
このディスクを使用しているユーザがいないかどうかを調べています ... OK

ディスク /dev/nvme0n1: 200 GiB, 214748364800 バイト, 419430400 セクタ
単位: セクタ (1 * 512 = 512 バイト)
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O サイズ (最小 / 推奨): 512 バイト / 512 バイト

>>> 新しい DOS ディスクラベルを作成しました。識別子は 0x5b17cb18 です。
/dev/nvme0n1p1: 新しいパーティション 1 をタイプ Linux、サイズ 200 GiB で作成しました。
/dev/nvme0n1p2: 終了。

新しい状態:
ディスクラベルのタイプ: dos
ディスク識別子: 0x5b17cb18

デバイス       起動 開始位置  終了位置    セクタ サイズ Id タイプ
/dev/nvme0n1p1          2048 419430399 419428352   200G 83 Linux

パーティション情報が変更されました。
ioctl() を呼び出してパーティション情報を再読み込みします。
ディスクを同期しています。


ファイルシステムを作成します。

[root@lab-client-01 ~]# mkfs -t xfs /dev/nvme0n1p1
meta-data=/dev/nvme0n1p1         isize=512    agcount=4, agsize=13107136 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=1
data     =                       bsize=4096   blocks=52428544, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=4096   blocks=25599, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
Discarding blocks...Done.


そしてマウントします。

[root@lab-client-01 ~]# mount /dev/nvme0n1p1 /mnt
[root@lab-client-01 ~]# df -h /mnt
ファイルシス   サイズ  使用  残り 使用% マウント位置
/dev/nvme0n1p1   200G  1.5G  199G    1% /mnt


NVMe-TCP による領域に、書き込み & 読み取りができました。

[root@lab-client-01 ~]# echo "JPOUG Advent Calendar is 10th anniversary" > /mnt/motd.txt
[root@lab-client-01 ~]# cat /mnt/motd.txt
JPOUG Advent Calendar is 10th anniversary


/dev/nvme0n1p1 をアンマウントして、切断しておきます。

[root@lab-client-01 ~]# umount /mnt
[root@lab-client-01 ~]# nvme disconnect -n nvmet-test
NQN:nvmet-test disconnected 1 controller(s)


ついでに NVMe-TCP ターゲット自身でも接続

NVMe-TCP ターゲット自身でも、nvmet-test サブシステムに接続してみます。


それでは、nvme connect で接続します。

[root@lab-nvmet-01 ~]# nvme discover -t tcp -a 10.12.7.11 -s 4420
[root@lab-nvmet-01 ~]# nvme connect -t tcp -n nvmet-test -a 10.12.7.11 -s 4420


ローカル デバイス(nvme0n1)と、
NVMe-TCP 経由のデバイス(nvme1n1)として2重に認識されています。


lsblk の様子です。さきほど lab-client-01 で作成したパーティションは、
ターゲット側のローカル デバイス である nvme0n1 では、まだ認識されていません。

[root@lab-nvmet-01 ~]# lsblk -i /dev/nvme0n1 /dev/nvme1n1
NAME        MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
nvme0n1     259:0    0  200G  0 disk
nvme1n1     259:2    0  200G  0 disk
`-nvme1n1p1 259:3    0  200G  0 part


nvme0n1 と nvme1n1 は、実際は同一デバイスなので、
partprobe で nvme0n1 のパーティション テーブルを読み直すと、
nvme0n1p1 が認識されます。

[root@lab-nvmet-01 ~]# partprobe /dev/nvme0n1
[root@lab-nvmet-01 ~]# lsblk -i /dev/nvme0n1 /dev/nvme1n1
NAME        MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
nvme0n1     259:0    0  200G  0 disk
`-nvme0n1p1 259:4    0  200G  0 part
nvme1n1     259:2    0  200G  0 disk
`-nvme1n1p1 259:3    0  200G  0 part


さきほど lab-client-01 で書き込んだテストファイルが読み取れます。

[root@lab-nvmet-01 ~]# mount /dev/nvme0n1p1 /mnt
[root@lab-nvmet-01 ~]# cat /mnt/motd.txt
JPOUG Advent Calendar is 10th anniversary
[root@lab-nvmet-01 ~]# umount /mnt
[root@lab-nvmet-01 ~]# mount /dev/nvme1n1p1 /mnt
[root@lab-nvmet-01 ~]# cat /mnt/motd.txt
JPOUG Advent Calendar is 10th anniversary
[root@lab-nvmet-01 ~]# umount /mnt


ターゲット自身が NVMe-TCP でデバイスに接続したままだと、
OS 再起動などがうまくいかなかったりするので、切断しておきます。

[root@lab-nvmet-01 ~]# nvme disconnect -n nvmet-test
NQN:nvmet-test disconnected 1 controller(s)


以上、Oracle Linux で NVMe-TCP ターゲットを構築してみる話でした。

明日の JPOUG Advent Calendar 2021 は HiroyukiNakaie さんです。
よろしくお願いします。