去る10月にUbuntu DiscourseにてNVMe/
このPoCについては、実際に試せるスクリプト群がGitHubのnvme-tcp-pocレポジトリ
でも、それでは
今回はこのPoCを参考に、NVMe/
NVMe over TCP(NVMe/TCP)についての簡単な整理
実際の作業に移る前にNVMe over TCP
NVMe自体はご存じの方も多いかと思いますが、SSD用に開発されたストレージ接続の通信規格です。NVMe登場以前はハードディスク用に作られたSATAやSASを使ってSSDも通信していました。しかし、これらの規格では高性能するSSDの性能を活かしきれなくなり、NVMeという規格が現れました。今どきのほとんどのPCはNVMe SSDが接続されているのではないかと思います。
通常、NVMeデバイスはPCI Expressバスを通じて直接マシンに接続されますが、NVM Express over Fabrics
冒頭で
NVMe/
なお、NVMe/
ホストマシンの設定
PoCにならい、本稿でもターゲットおよびイニシエーターは仮想マシンとして用意します。AMD64
まずは、必要なパッケージをインストールします。端末を開き、次のコマンドを実行します。
host$ sudo apt install libvirt-daemon libvirt-clients virtinst virt-viewer virt-manager qemu-system-x86 libcap2-bin wget python3-jinja2 python3-yaml fusefat fdisk dosfstools git
インストール中に管理者ユーザーがlibvirtdグループに追加されますが、ログイン済みのセッションのままでは有効にならず、KVMを使用できません。一度ログアウトしてログインし直します。
また、仮想マシンへのOSのインストールに使用するISOイメージを任意の場所にダウンロードしておきます。本稿ではホームディレクトリ直下にisoディレクトリを用意して配置しているものとします。
- ターゲット用:Ubuntu Server 24.
04 LTS - イニシエーター用:Ubuntu Server 24.
10[3]
ターゲット側の仮想マシンの作成
ISOイメージがダウンロードできたら
ウィザードのステップ1ではOSのインストール方法に
ステップ2で
ステップ3では仮想マシンに割り当てるメモリとCPU数を選びます。特に要件はありませんが、メモリは前述の通り2GiB=2048MiB分割り当てておきます。
ステップ4についてはデフォルトのまま
ステップ5で
ステップ5で
NVMe/TCPターゲットの設定
仮想マシンの作成とOSのインストールが完了したら、NVMe/
ストレージのサイズは任意ですが、このストレージにイニシエーターとなる仮想マシン側のOSをインストールすることは忘れないでください。今回はデフォルト値である20GiBのままで進めるため、そのまま
すると、追加したストレージがターゲット上でも次のように認識されるはずです。
target$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sr0 11:0 1 1024M 0 rom vda 253:0 0 25G 0 disk ├─vda1 253:1 0 1M 0 part ├─vda2 253:2 0 2G 0 part /boot └─vda3 253:3 0 23G 0 part └─ubuntu--vg-ubuntu--lv 252:0 0 11.5G 0 lvm / vdb 253:16 0 20G 0 disk
追加されたブロックデバイスである/dev/
をNVMe/
NVMe/nvmet-tcp
カーネルモジュールnvmet
は"NVMe Target"という意味かと思います。
target$ sudo modprobe nvmet-tcp
カーネルモジュールが読み込まれると、/sys/
という設定用のツリーが作成され、このツリーの中で操作することでNVMe/
それでは早速、この中に移動します。
target$ cd /sys/kernel/config/nvmet/subsystems
ちなみにsubsystems
とあるのは、NVM Subsystemのことです。1つのNVM Subsystemにはこのあとの設定の中でも出てくるポートや名前空間を複数設定できます[5]。柔軟性があるとも複雑ともいえますが、今回はすべて1つずつのシンプルな構成します。
早速、サブシステムを作成します。といっても、subsystems
フォルダ直下にディレクトリを作成するだけです。NVMe-oFではNVMe Qualified Name
target$ pwd /sys/kernel/config/nvmet/subsystems target$ sudo mkdir nqn.2024-12.ubuntu-nvme-tcp-target
ディレクトリを作成したら、その中に移動し、すべてのホストからの接続を受け入れるように設定します[7]。
target$ cd nqn.2024-12.ubuntu-nvme-tcp-target/ target$ pwd /sys/kernel/config/nvmet/subsystems/nqn.2024-12.ubuntu-nvme-tcp-target target$ echo 1 | sudo tee attr_allow_any_host
続いて、サブシステムに名前空間[8]を作成します。
target$ pwd /sys/kernel/config/nvmet/subsystems/nqn.2024-12.ubuntu-nvme-tcp-target target$ sudo mkdir namespaces/1
名前空間に実体となるデバイス/dev/
)
target$ cd namespaces/1/ target$ pwd /sys/kernel/config/nvmet/subsystems/nqn.2024-12.ubuntu-nvme-tcp-target/namespaces/1 target$ cat device_path (null) target$ echo /dev/vdb | sudo tee device_path /dev/vdb target$ cat device_path /dev/vdb target$ echo 1 | sudo tee enable
続いて、イニシエーターとの接続に使用されるポートを作成します。
target$ sudo mkdir /sys/kernel/config/nvmet/ports/1 target$ cd /sys/kernel/config/nvmet/ports/1 target$ echo 0.0.0.0 | sudo tee addr_traddr # ローカルマシンのすべてのアドレスでリスンするように設定 target$ echo tcp | sudo tee addr_trtype # トランスポートタイプをTCPに設定 target$ echo 4420 | sudo tee addr_trsvcid # ポート番号を4420に設定 target$ echo ipv4 | sudo tee addr_adrfam # アドレスファミリーをIPv4に設定
最後に先ほど作成した名前空間とポートを対応させます。
target$ sudo ln -s /sys/kernel/config/nvmet/subsystems/nqn.2024-12.ubuntu-nvme-tcp-target/ /sys/kernel/config/nvmet/ports/1/subsystems/
ここまで設定するとdmesgに次のような出力があるはずです。
target$ sudo dmesg |grep nvmet [ 3923.477064] nvmet: adding nsid 1 to subsystem nqn.2024-12.ubuntu-nvme-tcp-target [ 4527.316441] nvmet_tcp: enabling port 1 (0.0.0.0:4420)
nvmeコマンドでもサブシステムが見えるか確認しておきます。
$ sudo apt install nvme-cli $ sudo nvme discover -t tcp -a 127.0.0.1 -s 4420 ... =====Discovery Log Entry 1====== trtype: tcp adrfam: ipv4 subtype: nvme subsystem treq: not specified, sq flow control disable supported portid: 1 trsvcid: 4420 subnqn: nqn.2024-12.ubuntu-nvme-tcp-target traddr: 127.0.0.1 eflags: none sectype: none
これでターゲット側の設定が完了しました。イニシエーターからの接続の際に使用するため、ターゲット側のマシンのIPアドレスを確認・
なお、/sys/
以下は揮発性で、OSを再起動すると設定が消えます。今回は順を追って流れを説明するために1つずつコマンドを実行しましたが、永続化させたいのであれば、PoCレポジトリにあるように一連の流れをシェルスクリプトにして、システム起動時にsystemdのサービスユニットとして実行するといった対応が必要です。
イニシエーター側の仮想マシンの作成
次はイニシエーター側のマシンの準備です。NVMe/
host$ git clone https://github.com/canonical/nvme-tcp-poc.git
また、仮想マシンで使用するファームウェアを指定したいのですが、仮想マシンマネージャーだと複雑なXMLを手で編集する羽目になります。よって、ここはコマンドで対応することにします。
host$ virt-install \ --autoconsole graphical \ --noreboot \ --name initiator \ --disk none \ --memory 2048 \ --virt-type kvm \ --location /$HOME/iso/ubuntu-24.10-live-server-amd64.iso \ --network network=default \ --boot loader=$HOME/nvme-tcp-poc/resources/OVMF_CODE.fd,loader.readonly=yes,loader.type=pflash,loader.secure=false,nvram.template=$HOME/nvme-tcp-poc/resources/OVMF_VARS.fd,menu=on
先ほど仮想マシンマネージャーで仮想マシンを作成したところなので、このコマンド
--disk none
として、ストレージレスにします。--location
オプションで事前にダウンロードしたUbuntu Server 24.10のISOイメージを指定しています。 --boot
オプションでNVMe-oF(NVMe/ TCP) 対応のファームウェアを利用するように指定しています。
コマンドを実行するとvirt-viewer
が開き、先ほどターゲット側のマシンをインストールするときに見たような画面が表示されます。そのままvirt-viewer
を使ってインストール作業を続けても構いませんし、仮想マシンマネージャーからinitiator
マシンを開いて切り替えても構いません。使いやすい方で操作を進めてください。
インストーラーの画面を次々進めていると、そのうち次のようにストレージが見つからないというエラーに遭遇するはずです。ストレージをアタッチしていないのでエラーが出ること自体は当たり前です。
インストール先となるストレージが必要です。NVMe/
といってもインストーラー画面からはこれ以上どうしようもないので、右上の"Help"を開いて、"Enter shell"からLiveセッションにログインします[9]。
Liveセッションが開いたら、次のコマンドを実行し、先ほど用意したNVMe/
live-session# nvme connect-all -t tcp -a <ターゲットのIPアドレス> -s 4420
すると、次のようにNVMe/
live-session# nvme list Node Generic SN Model Namespace Usage Format FW Rev --------------------- --------------------- -------------------- ---------------------------------------- ---------- -------------------------- ---------------- -------- /dev/nvme1n1 /dev/ng1n1 deef6bef77cfc6f1e98c Linux 0x1 21.47 GB / 21.47 GB 512 B + 0 B 6.8.0-50 live-session# lsblk |grep nvme nvme1n1 259:1 0 20G 0 disk
NVMe/exit
コマンドでLiveセッションを抜けます。そして"Refresh"を押してストレージ一覧を読み込み直します。すると、今度はインストーラー側でも次のようにNVMe/
残りのインストール作業はそのまま進めます。
インストールが完了したら、あとは再起動するだけ……というわけにはいかず、Ubuntu DiscourseのPoCに関する投稿にもある通り、既知の問題への対処がまずは必要です[10]。調整せずに作業を続けるとブート中にNVMe/
ということで、再びLiveセッションに入り、/target/
を次のような内容に変更して保存します。
network: ethernets: nbft0: dhcp4: true critical: true version: 2
変更が済んだら、先程と同様にLiveセッションを抜けて、今度こそインストールを完了します
NVMe/TCPでシステムをブートするようUEFIを構成・操作する
NVMe/
仮想マシンマネージャーから仮想マシンを起動したら、次のような表示が出ているうちにEscを連打して、UEFIの設定画面に入ります[12]。
すると次のような設定画面に遷移します。
矢印キーとEnterキーを使い"Device Manager" - "NVMe-oF Configuration" - "Attempt 1"と遷移してください。
するとNVMe-oFデバイスの設定画面に移りますので、上から順に次のように設定
- NVM Subsystem: Enabled
- Network Device List: 使用するNICのMACアドレスを選択する
(今回は1つしかありませんので、Enterを押していれば設定できます) - Enable DHCP: X
(チェックを入れる) - NVM Subsystem NQN: nqn.
2024-12. ubuntu-nvme-tcp-target - NVM Subsystem Address: <ターゲット側のマシンのIPアドレス>
- NVM Subsystem Port: 4420
そして、同じ画面の下の方にある"Save Changes"を押して、設定を保存します。
設定を保存したら、Escで図4の画面まで戻り、"Continue"を押します。すると、青背景のボックスに"Configuration changed. Reset to apply it Now. Press Enter to reset"というメッセージが表示されます。
言われたとおりにEnterを押して、再びEsc連打に備えます。Esc連打チャレンジに成功するとまた図4の画面が表示されますので、今度は"Boot Manager"に遷移します。
設定がうまくできていると、次のように"UEFI NVMeOF Device - Linux"から始まるエントリが見えているはずです。これがNVMe/
このエントリにフォーカスしてEnterを押すと、Ubuntu Server 24.
ストレージレスの仮想マシンですが、起動後に確認するとNVMe
initiator$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sr0 11:0 1 1024M 0 rom nvme0n1 259:1 0 20G 0 disk ├─nvme0n1p1 259:2 0 953M 0 part /boot/efi └─nvme0n1p2 259:3 0 19.1G 0 part /
dmesgの中身をnvme
でgrepしてみてもよいでしょう。次のような出力が見えて、NVMe/
$ sudo dmesg |grep nvme [ 3.152001] nvme nvme0: creating 2 I/O queues. [ 3.154376] nvme nvme0: mapped 2/0/0 default/read/poll queues. [ 3.159688] nvme nvme0: new ctrl: NQN "nqn.2024-12.ubuntu-nvme-tcp-target", addr 192.168.122.155:4420, hostnqn: nqn.2014-08.org.nvmexpress:uuid:34859525-128E-45A7-AAA4-3AEE826B44F2 [ 3.168029] nvme0n1: p1 p2 [ 4.076132] EXT4-fs (nvme0n1p2): orphan cleanup on readonly fs [ 4.076635] EXT4-fs (nvme0n1p2): mounted filesystem aecf3fdc-09fe-42c0-aec1-d39c78a39d58 ro with ordered data mode. Quota mode: none. [ 4.989983] systemd[1]: Starting modprobe@nvme_fabrics.service - Load Kernel Module nvme_fabrics... [ 5.027524] EXT4-fs (nvme0n1p2): re-mounted aecf3fdc-09fe-42c0-aec1-d39c78a39d58 r/w. Quota mode: none. [ 7.545300] nvme nvme1: new ctrl: NQN "nqn.2014-08.org.nvmexpress.discovery", addr 192.168.122.155:4420, hostnqn: nqn.2014-08.org.nvmexpress:uuid:34859525-128E-45A7-AAA4-3AEE826B44F2 [ 7.554014] nvme nvme1: Removing ctrl: NQN "nqn.2014-08.org.nvmexpress.discovery"
ちなみに、筆者は試しにServer版でインストールしたあとに、ubuntu-desktop
パッケージを入れてDesktop化しようとしました。しかし、前述の既知の問題と同じような状態となってNVMe/ubuntu-desktop
パッケージと依存関係を持つネットワーク関連のパッケージがインストールされる際に、ネットワークの再設定が走って発生する事象ではないかと考えています。
その他にも
いかにも
ちなみに、勘のよい方はお気づきかと思いますが、実はイニシエーターからネットワーク接続できるところに存在すれば、ターゲット側は仮想マシンである必要はありません