NVME over RoCE. Примеряем на себя

56023ad5b3e58b3bc3d50a05ed3c2258.png

Так повелось, что по итогам своих опытов с различными технологиями подключения дискового пространства писал сюда небольшие заметки:

  • Настройка NVME over TCP — для тех, кому надо подключить больше 1 диска единственной конфигурации из всех примеров в Сети.

  • Попытка разогнать сеть для БД со 100 до 200Гб/c или «failure is always an option».

  • Настройка iScsi в L3-сети для эффективной утилизации возможностей канала и СХД.

Поскольку появился новый свежий опыт по настройке и тестированию подключения ещё одним способом, и все уже собрано для внутренней wiki-статьи, решил продолжить традицию.

Постановка задачи

Решили как-то раз создать небольшой тестовый стенд под базы данных. В нем было 4×3Тб NVME диска и 12×12Тб дисков с блинами, висящими на RAID контроллере.

При объединении магнитных дисков в RAID 10 места под базу не хватало. Но, по счастливой случайности, в той же стойке, что и сервер, был небольшой СХД, у которого было достаточно места и присутствовала возможность раздавать диски по NVME/ RoCE (RDMA over Converged Ethernet).

Возникла мысль:
А давайте соединим напрямую 4 порта из сервера с 4 портами на массиве и поиграемся с RoCE! Заодно посмотрим цифры, которые получатся на разных типах дисков. И сравним между собой.

Так было решено собрать тестовый стенд у которого требования к дисковому пространству: 140Тб. Для этого 70 дисков 2Тб будут поданы с СХД используя технологию NVME over RDMA

На сервере было установлен 4 сетевых карты по 2×25Гб порта в каждой. 4 порта изначально не были скомутированны.

На готовых был развернут FRR для доступа к СУБД, а вот оставшиеся 4 нескоммутированных попросили товарищей из дата-центра соединить напрямую с СХД, для NVME/RoCE результаты коммутации отобразить в NetBox.

Соединение напрямую для того, чтобы исключить влияние свитчей. У них не была настроена нужная для поддержки RoCE функциональность. Да и проще так эксперименты проводить.

Перед настройкой выделенной сети проверили работу NVME подсистемы «из коробки». С добавленными стандартными пакетами для NVME-cli и загруженными модулями NVMEt.

# lsmod | grep NVME
NVME_rdma              57344  0
rdma_cm               151552  1 NVME_rdma
ib_core               475136  6 rdma_cm,NVME_rdma,iw_cm,ib_uverbs,mlx5_ib,ib_cm
NVME                   57344  8
NVMEt_tcp              40960  0
NVMEt                 155648  1 NVMEt_tcp
NVME_fabrics           36864  1 NVME_rdma
NVME_core             180224  164 NVMEt,NVME,NVME_rdma,NVME_fabrics
t10_pi                 16384  3 NVMEt,sd_mod,NVME_core
# lsmod | grep mlx
mlx5_ib               450560  0
ib_uverbs             188416  1 mlx5_ib
ib_core               475136  6 rdma_cm,NVME_rdma,iw_cm,ib_uverbs,mlx5_ib,ib_cm
mlx5_core            1867776  1 mlx5_ib
mlxfw                  49152  1 mlx5_core
pci_hyperv_intf        16384  1 mlx5_core
tls                   126976  1 mlx5_core
psample                20480  1 mlx5_core

Сервер видел СХД в режиме discovery, но не мог подключиться по RoCE.

# NVME discover -t rdma -a 255.255.255.83 -s 4420
 
Discovery Log Number of Records 1, Generation counter 2
=====Discovery Log Entry 0======
trtype: rdma
adrfam: ipv4
subtype: NVME subsystem
treq: not specified
portid: 8
trsvcid: 4420
subnqn: QRG7T-8RHQY-B4CVX-R8MWM-W43BT
traddr: 255.255.255.83
rdma_prtype: roce-v2
rdma_qptype: connected
rdma_cms: rdma-cm
rdma_pkey: 0x0000

Подсистема была видна, но прицепиться к ней не получалось. Как говорилось выше сетевая инфраструктура к этому не была готова. После коммутации напрямую можно было попробовать правильно настроить выделенную сеть.

Настройка сети, зашедшая не в ту дверь

Почему-то с самого начала решили, что раз нужна специальная поддержка технологии в драйверах. По предложению команды СХД попробовали скачать специальный драйвер OFED. Если хотите скачать специальные Mellanox OFED драйвера с сайта nvidia, то они тут.
Mellanox OFED (MLNX_OFED) Software.

Но драйвер просто так не поставишь. Ядро не нравится и все такое. Предлагает все собрать в лучшем виде. Но для этого требует gcc, включая фортран,

Operating System

Required Packages Installation Command

RHEL/OL/Fedora

yum install perl pciutils python gcc-gfortran libxml2-python tcsh libnl.i686 libnl expat glib2 tcl libstdc++ bc tk gtk2 atk cairo numactl pkgconfig ethtool lsof

для компиляции под установленное ядро.

Драйвера и модули скомпилировались после доустановки требуемых пакетов. Попробуйте такой набор флагов для инсталлятора
./mlnxofedinstall --add-kernel-support --skip-repo

из такого каталога

# ls -l
total 356
-rwxr-xr-x 1 root root 3077 Dec 2 22:57 common_installers.pl
-rwxr-xr-x 1 root root 26026 Dec 2 22:57 common.pl
-rwxr-xr-x 1 root root 27150 Dec 2 22:57 create_mlnx_ofed_installers.pl
-rw-r--r-- 1 root root 6 Dec 2 22:57 distro
drwxr-xr-x 6 root root 153 Dec 2 22:58 docs
-rwxr-xr-x 1 root root 5508 Dec 2 22:57 is_kmp_compat.sh
-rw-r--r-- 1 root root 956 Dec 2 22:58 LICENSE
-rwxr-xr-x 1 root root 21541 Dec 2 22:57 mlnx_add_kernel_support.sh
-rwxr-xr-x 1 root root 228908 Dec 2 22:57 mlnxofedinstall
-rw-r--r-- 1 root root 1771 Dec 2 22:57 RPM-GPG-KEY-Mellanox
drwxr-xr-x 3 root root 8192 Dec 2 22:59 RPMS
drwxr-xr-x 2 root root 45 Dec 2 22:57 src
-rwxr-xr-x 1 root root 15883 Dec 2 22:57 uninstall.sh

По окончании сборки и установки будет пара советов. Например обновить загрузочные образы. Мы им последовали.

После обновления образов через dracut -f сервер успешно пережил ребут. Решили продолжить начатое. Для проверки сети попробовали повторить discover.

Но внезапно работавший до смены драйверов Discover перестал работать, ругаясь на псевдокаталог.

Failed to open /dev/NVME-fabrics: No such file or directory

Возможно, сначала надо настроить прямое подключение. Поэтому решили сначала настроить сеть.

Результаты настройки сети

На 4 физических устройствах выставлен MTU 5500, как на СХД. Далее, на них создано по VLAN интерфейсу в соответствии с коммутацией в
NetBox. СХД указало VLAN для конкретного порта и IP Подсети для каждого интерфейса /24

# ip a | grep -A2 ": ens"
4: ens2f0np0:  mtu 5500 qdisc mq state UP group default qlen 1000
link/ether d0:98 brd ff:ff:ff:ff:ff:ff
altname enp75s0f0np0
5: ens2f1np1:  mtu 5500 qdisc mq state UP group default qlen 1000
link/ether d0:99 brd ff:ff:ff:ff:ff:ff
altname enp75s0f1np1
6: ens4f0np0:  mtu 5500 qdisc mq state UP group default qlen 1000
link/ether d1:80 brd ff:ff:ff:ff:ff:ff
altname enp152s0f0np0
7: ens4f1np1:  mtu 5500 qdisc mq state UP group default qlen 1000
link/ether d1:81 brd ff:ff:ff:ff:ff:ff
altname enp152s0f1np1
--
14: ens2f0np0.200@ens2f0np0:  mtu 5500 qdisc noqueue state UP group default qlen 1000
link/ether d0:98 brd ff:ff:ff:ff:ff:ff
inet 192.168.200.20/24 brd 192.168.200.255 scope global noprefixroute ens2f0np0.200
--
15: ens2f1np1.200@ens2f1np1:  mtu 5500 qdisc noqueue state UP group default qlen 1000
link/ether d0:99 brd ff:ff:ff:ff:ff:ff
inet 192.168.202.21/24 brd 192.168.202.255 scope global noprefixroute ens2f1np1.200
--
16: ens4f0np0.201@ens4f0np0:  mtu 5500 qdisc noqueue state UP group default qlen 1000
link/ether d1:80 brd ff:ff:ff:ff:ff:ff
inet 192.168.201.22/24 brd 192.168.201.255 scope global noprefixroute ens4f0np0.201
--
17: ens4f1np1.201@ens4f1np1:  mtu 5500 qdisc noqueue state UP group default qlen 1000
link/ether d1:81 brd ff:ff:ff:ff:ff:ff
inet 192.168.203.23/24 brd 192.168.203.255 scope global noprefixroute ens4f1np1.201

Пинги проходили, арп таблица показывала верные IP

# arp -a | grep 168
? (192.168.202.11) at 4:e6 [ether] on ens2f1np1.200
? (192.168.201.12) at 9:d5 [ether] on ens4f0np0.201
? (192.168.200.10) at 4:e5 [ether] on ens2f0np0.200
? (192.168.203.13) at 9:d6 [ether] on ens4f1np1.201

Но вот discovery почему-то не работал.

Проверяли наличие модулей через

lsmod | grep mlx
lsmod | grep ib
lsmod | grep NVME 

Все на месте, Колесо пинали, но ничего не работает!

Шаг назад. Возвращаем ванильные драйвера

Поэтому спец драйвера откатили через uninstall.sh
Повторно выдали dracut -f и опять, после перезагрузки, все поднялось.
Но в такой конфигурации Discovery заработал!

Снова увидели имя неймспейса на СХД, в котором поданы диски и к которому можно подключиться.

На всякий случай отдали ID хоста на СХД, чтобы там его прописали ID ищется так:

# cat /etc/NVME/hostnqn
nqn.2014-08.org.NVMExpress:uuid:12345678-abcd-1234-abcd-abcd12345678
#

Проблема возникла с MTU. Оно не пережило перезагрузки и стало 1500. Это, наверное, потому, что на родительский интерфейс никакого IP не назначали.

Это решается переконфигурацией значения MTU таким способом:

ip a | grep ": ens" | tr "@:" " " | awk '{print $2}' | while read net; do ip link set $net mtu 5500; done

Phase 3. PROFIT!!!

Можно было продолжать начинать тесты.

Для начала делали тесты с единственным диском, для его подключения надо выполнить подобные команды:

NVME connect -t rdma -a 192.168.200.10 -n QRG7T-8RHQY-B4CVX-R8MWM-W43BT
NVME connect -t rdma -a 192.168.202.11 -n QRG7T-8RHQY-B4CVX-R8MWM-W43BT
NVME connect -t rdma -a 192.168.201.12 -n QRG7T-8RHQY-B4CVX-R8MWM-W43BT
NVME connect -t rdma -a 192.168.203.13 -n QRG7T-8RHQY-B4CVX-R8MWM-W43BT

Команд четыре, потому что у нас четыре провода скомутированно — каждый на свой порт.

Увидеть подключения можно так:

NVME list-subsys
NVME-subsys0 - NQN=nqn.2018-11.com.dapustor:NVME:nvm-subsystem-sn-1
\
+- NVME0 pcie 10000:01:00.0 live
NVME-subsys1 - NQN=nqn.2018-11.com.dapustor:NVME:nvm-subsystem-sn-2
\
+- NVME1 pcie 10000:02:00.0 live
NVME-subsys2 - NQN=nqn.2018-11.com.dapustor:NVME:nvm-subsystem-sn-3
\
+- NVME2 pcie 10000:03:00.0 live
NVME-subsys3 - NQN=nqn.2018-11.com.dapustor:NVME:nvm-subsystem-sn-4
\
+- NVME3 pcie 10000:04:00.0 live
NVME-subsys4 - NQN=QRG7T-8RHQY-B4CVX-R8MWM-W43BT
\
+- NVME4 rdma traddr=192.168.200.10 trsvcid=4420 live
+- NVME5 rdma traddr=192.168.203.13 trsvcid=4420 live
+- NVME6 rdma traddr=192.168.202.11 trsvcid=4420 live
+- NVME7 rdma traddr=192.168.201.12 trsvcid=4420 live

Первые 4 подсистемы, с 0 по 3, это локальные диски. Четвертая подсистема это rdma подключение с multipath по четырем путям. Чтобы утилизировать multipath надо прописать политику round-robin вместо дефолтовой numa таким образом:

echo round-robin > /sys/class/NVME-subsystem/NVME-subsys4/iopolicy

После чего результаты по чтению становятся такими…

Замеры скорости

Стенд создан на основе rpm/el8 совместимого дистрибутива с ядром 5.15. Более свежие ядра могут улуизменить результаты.

Случайное чтение 4k блока

# fio --ioengine=libaio --direct=1 --runtime=100s --time_based --name=testRoCe --filename /dev/NVME4n1 --bs=4k --iodepth=64 --size=10T --readwrite=randread
testRoCe: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=64
fio-3.19
Starting 1 process
Jobs: 1 (f=1): [r(1)][100.0%][r=1092MiB/s][r=280k IOPS][eta 00m:00s]
testRoCe: (groupid=0, jobs=1): err= 0: pid=34261: Wed Feb 12 15:41:17 2025
read: IOPS=263k, BW=1028MiB/s (1077MB/s)(100GiB/100001msec)
slat (nsec): min=1498, max=47876, avg=2842.19, stdev=1074.15
clat (usec): min=43, max=199500, avg=240.00, stdev=452.73
lat (usec): min=45, max=199503, avg=242.93, stdev=452.73
clat percentiles (usec):
| 1.00th=[ 122], 5.00th=[ 149], 10.00th=[ 169], 20.00th=[ 188],
| 30.00th=[ 200], 40.00th=[ 212], 50.00th=[ 227], 60.00th=[ 239],
| 70.00th=[ 251], 80.00th=[ 269], 90.00th=[ 293], 95.00th=[ 318],
| 99.00th=[ 379], 99.50th=[ 429], 99.90th=[ 2606], 99.95th=[ 5866],
| 99.99th=[20841]
bw ( MiB/s): min= 37, max= 1099, per=100.00%, avg=1028.48, stdev=194.54, samples=199
iops : min= 9570, max=281388, avg=263290.58, stdev=49801.55, samples=199
lat (usec) : 50=0.01%, 100=0.21%, 250=68.43%, 500=31.04%, 750=0.12%
lat (usec) : 1000=0.02%
lat (msec) : 2=0.05%, 4=0.07%, 10=0.03%, 20=0.03%, 50=0.01%
lat (msec) : 100=0.01%, 250=0.01%
cpu : usr=15.83%, sys=66.98%, ctx=110505, majf=0, minf=76
IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.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.1%, >=64=0.0%
issued rwts: total=26305570,0,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=64
 
Run status group 0 (all jobs):
READ: bw=1028MiB/s (1077MB/s), 1028MiB/s-1028MiB/s (1077MB/s-1077MB/s), io=100GiB (108GB), run=100001-100001msec
 
Disk stats (read/write):
NVME4n1: ios=0/0, merge=0/0, ticks=0/0, in_queue=0, util=0.00%

Последовательное чтение 1m блока

# fio --ioengine=libaio --direct=1 --runtime=100s --time_based --name=testRoCe --filename /dev/NVME4n1 --bs=1024k --iodepth=192 --size=10T --readwrite=read
testRoCe: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=192
fio-3.19
Starting 1 process
Jobs: 1 (f=1): [R(1)][100.0%][r=11.4GiB/s][r=11.7k IOPS][eta 00m:00s]
testRoCe: (groupid=0, jobs=1): err= 0: pid=34402: Wed Feb 12 15:43:43 2025
read: IOPS=11.7k, BW=11.4GiB/s (12.2GB/s)(1138GiB/100006msec)
slat (usec): min=20, max=1213, avg=85.07, stdev=170.91
clat (usec): min=4880, max=47071, avg=16381.94, stdev=649.94
lat (usec): min=5457, max=47386, avg=16467.10, stdev=659.94
clat percentiles (usec):
| 1.00th=[14877], 5.00th=[15401], 10.00th=[15533], 20.00th=[16188],
| 30.00th=[16188], 40.00th=[16188], 50.00th=[16319], 60.00th=[16319],
| 70.00th=[16319], 80.00th=[16909], 90.00th=[16909], 95.00th=[17695],
| 99.00th=[18482], 99.50th=[18482], 99.90th=[19792], 99.95th=[20055],
| 99.99th=[25560]
bw ( MiB/s): min=10879, max=11686, per=100.00%, avg=11669.20, stdev=56.61, samples=199
iops : min=10879, max=11686, avg=11669.20, stdev=56.62, samples=199
lat (msec) : 10=0.01%, 20=99.92%, 50=0.08%
cpu : usr=0.69%, sys=27.41%, ctx=129515, majf=0, minf=49163
IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.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.1%
issued rwts: total=1165727,0,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=192
 
Run status group 0 (all jobs):
READ: bw=11.4GiB/s (12.2GB/s), 11.4GiB/s-11.4GiB/s (12.2GB/s-12.2GB/s), io=1138GiB (1222GB), run=100006-100006msec
 
Disk stats (read/write):
NVME4n1: ios=0/0, merge=0/0, ticks=0/0, in_queue=0, util=0.00%

Для сравнения локальный NVME на случайном чтении 4k блока

# fio --ioengine=libaio --direct=1 --runtime=100s --time_based --name=testRoCe --filename /dev/NVME1n1 --bs=4k --iodepth=64 --size=10T --readwrite=randread
testRoCe: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=64
fio-3.19
Starting 1 process
Jobs: 1 (f=1): [r(1)][100.0%][r=1800MiB/s][r=461k IOPS][eta 00m:00s]
testRoCe: (groupid=0, jobs=1): err= 0: pid=34511: Wed Feb 12 15:45:51 2025
read: IOPS=418k, BW=1633MiB/s (1712MB/s)(159GiB/100001msec)
slat (nsec): min=1069, max=41278, avg=1643.67, stdev=549.70
clat (usec): min=6, max=351, avg=151.04, stdev=34.76
lat (usec): min=8, max=386, avg=152.77, stdev=35.12
clat percentiles (usec):
| 1.00th=[ 135], 5.00th=[ 135], 10.00th=[ 135], 20.00th=[ 137],
| 30.00th=[ 137], 40.00th=[ 137], 50.00th=[ 137], 60.00th=[ 139],
| 70.00th=[ 139], 80.00th=[ 141], 90.00th=[ 233], 95.00th=[ 237],
| 99.00th=[ 241], 99.50th=[ 241], 99.90th=[ 243], 99.95th=[ 245],
| 99.99th=[ 247]
bw ( MiB/s): min= 1023, max= 1817, per=100.00%, avg=1635.28, stdev=316.76, samples=199
iops : min=262137, max=465284, avg=418630.98, stdev=81089.70, samples=199
lat (usec) : 10=0.01%, 20=0.01%, 50=0.01%, 100=0.01%, 250=100.00%
lat (usec) : 500=0.01%
cpu : usr=25.55%, sys=67.93%, ctx=45, majf=0, minf=76
IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.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.1%, >=64=0.0%
issued rwts: total=41803341,0,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=64
 
Run status group 0 (all jobs):
READ: bw=1633MiB/s (1712MB/s), 1633MiB/s-1633MiB/s (1712MB/s-1712MB/s), io=159GiB (171GB), run=100001-100001msec
 
Disk stats (read/write):
NVME1n1: ios=41692898/0, merge=0/0, ticks=279606/0, in_queue=279606, util=99.96%

Локальный NVME, 1m блок, последовательное чтение.

# fio --ioengine=libaio --direct=1 --runtime=100s --time_based --name=testRoCe --filename /dev/NVME1n1 --bs=1024k --iodepth=192 --size=10T --readwrite=read
testRoCe: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=192
fio-3.19
Starting 1 process
Jobs: 1 (f=1): [R(1)][100.0%][r=6290MiB/s][r=6289 IOPS][eta 00m:00s]
testRoCe: (groupid=0, jobs=1): err= 0: pid=72615: Thu Feb 13 10:45:48 2025
  read: IOPS=6285, BW=6286MiB/s (6591MB/s)(614GiB/100021msec)
    slat (usec): min=21, max=894, avg=157.81, stdev=52.47
    clat (usec): min=5395, max=50417, avg=30379.53, stdev=1321.18
     lat (usec): min=5422, max=50597, avg=30537.44, stdev=1322.08
    clat percentiles (usec):
     |  1.00th=[29754],  5.00th=[30016], 10.00th=[30278], 20.00th=[30278],
     | 30.00th=[30278], 40.00th=[30278], 50.00th=[30278], 60.00th=[30278],
     | 70.00th=[30278], 80.00th=[30278], 90.00th=[30540], 95.00th=[30540],
     | 99.00th=[32900], 99.50th=[39060], 99.90th=[42206], 99.95th=[43779],
     | 99.99th=[44827]
   bw (  MiB/s): min= 6031, max= 6300, per=100.00%, avg=6294.40, stdev=18.76, samples=199
   iops        : min= 6031, max= 6300, avg=6294.40, stdev=18.81, samples=199
  lat (msec)   : 10=0.06%, 20=0.13%, 50=99.80%, 100=0.01%
  cpu          : usr=0.61%, sys=15.40%, ctx=548424, majf=0, minf=49163
  IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.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.1%
     issued rwts: total=628713,0,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=192

Run status group 0 (all jobs):
   READ: bw=6286MiB/s (6591MB/s), 6286MiB/s-6286MiB/s (6591MB/s-6591MB/s), io=614GiB (659GB), run=100021-100021msec

Disk stats (read/write):
  NVME1n1: ios=5017173/0, merge=0/0, ticks=101951869/0, in_queue=101951869, util=100.00%

Локальный RAID-10 на магнитных дисках при случайном чтении 4k блока

# fio --ioengine=libaio --direct=1 --runtime=100s --time_based --name=testRoCe --filename /dev/sdb --bs=4k --iodepth=64 --size=10T --readwrite=randread
testRoCe: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=64
fio-3.19
Starting 1 process
Jobs: 1 (f=1): [r(1)][100.0%][r=9.99MiB/s][r=2556 IOPS][eta 00m:00s]
testRoCe: (groupid=0, jobs=1): err= 0: pid=34704: Wed Feb 12 15:55:42 2025
read: IOPS=2567, BW=10.0MiB/s (10.5MB/s)(1003MiB/100054msec)
slat (nsec): min=1767, max=47677, avg=2136.54, stdev=209.69
clat (usec): min=1106, max=387852, avg=24913.93, stdev=21125.61
lat (usec): min=1108, max=387854, avg=24916.15, stdev=21125.61
clat percentiles (msec):
| 1.00th=[ 4], 5.00th=[ 5], 10.00th=[ 7], 20.00th=[ 9],
| 30.00th=[ 12], 40.00th=[ 15], 50.00th=[ 18], 60.00th=[ 23],
| 70.00th=[ 29], 80.00th=[ 39], 90.00th=[ 55], 95.00th=[ 67],
| 99.00th=[ 95], 99.50th=[ 112], 99.90th=[ 161], 99.95th=[ 182],
| 99.99th=[ 239]
bw ( KiB/s): min= 9469, max=10848, per=100.00%, avg=10292.05, stdev=221.79, samples=199
iops : min= 2367, max= 2712, avg=2573.01, stdev=55.45, samples=199
lat (msec) : 2=0.09%, 4=2.51%, 10=22.23%, 20=30.00%, 50=32.94%
lat (msec) : 100=11.45%, 250=0.78%, 500=0.01%
cpu : usr=0.47%, sys=0.53%, ctx=252418, majf=0, minf=75
IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.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.1%, >=64=0.0%
issued rwts: total=256889,0,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=64
 
Run status group 0 (all jobs):
READ: bw=10.0MiB/s (10.5MB/s), 10.0MiB/s-10.0MiB/s (10.5MB/s-10.5MB/s), io=1003MiB (1052MB), run=100054-100054msec
 
Disk stats (read/write):
sdb: ios=256481/0, merge=0/0, ticks=6386655/0, in_queue=6386656, util=99.89%

Локальный RAID-10 на магнитных дисках при чтении последовательно 1m блоками

# fio --ioengine=libaio --direct=1 --runtime=100s --time_based --name=testRoCe --filename /dev/sdb --bs=1024k --iodepth=192 --size=10T --readwrite=read
testRoCe: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=192
fio-3.19
Starting 1 process
Jobs: 1 (f=1): [R(1)][100.0%][r=1532MiB/s][r=1532 IOPS][eta 00m:00s]
testRoCe: (groupid=0, jobs=1): err= 0: pid=72763: Thu Feb 13 10:49:02 2025
  read: IOPS=1526, BW=1527MiB/s (1601MB/s)(149GiB/100057msec)
    slat (usec): min=30, max=17583, avg=653.70, stdev=395.98
    clat (msec): min=56, max=2634, avg=125.00, stdev=185.45
     lat (msec): min=56, max=2634, avg=125.66, stdev=185.45
    clat percentiles (msec):
     |  1.00th=[   69],  5.00th=[   77], 10.00th=[   81], 20.00th=[   85],
     | 30.00th=[   90], 40.00th=[   94], 50.00th=[  100], 60.00th=[  105],
     | 70.00th=[  110], 80.00th=[  120], 90.00th=[  136], 95.00th=[  163],
     | 99.00th=[  885], 99.50th=[ 1754], 99.90th=[ 2567], 99.95th=[ 2601],
     | 99.99th=[ 2635]
   bw (  MiB/s): min= 1185, max= 1604, per=100.00%, avg=1528.30, stdev=48.53, samples=199
   iops        : min= 1185, max= 1604, avg=1527.58, stdev=48.31, samples=199
  lat (msec)   : 100=51.74%, 250=45.78%, 500=0.96%, 750=0.43%, 1000=0.22%
  lat (msec)   : 2000=0.47%, >=2000=0.41%
  cpu          : usr=0.14%, sys=3.96%, ctx=300216, majf=0, minf=49163
  IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.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.1%
     issued rwts: total=152778,0,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=192

Run status group 0 (all jobs):
   READ: bw=1527MiB/s (1601MB/s), 1527MiB/s-1527MiB/s (1601MB/s-1601MB/s), io=149GiB (160GB), run=100057-100057msec

Disk stats (read/write):
  sdb: ios=2442366/0, merge=0/0, ticks=25114665/0, in_queue=25114665, util=100.00%

Наводим порядок в работе с 70 дисками

При подключении 70 дисков они все в одной подсистеме, но номера неймспейсов
от 1 до 70

То есть диски называются так:
NVME4nXX. Где 4 — подсистема NVME, а XX — номер диска (неймспейс).

Через правила udev просто преобразовать ID дисков в wwid не получается, у них только несколько последних цифр совпадают.

Пример

Семидесятый диск на СХД это 12345678900987654321123450000046

Для создания правил ищем его атрибуты в udev

udevadm info -q all -a /dev/NVME4n70
 
 
looking at device '/devices/virtual/NVME-subsystem/NVME-subsys4/NVME4n70':
KERNEL=="NVME4n70"
SUBSYSTEM=="block"
DRIVER==""
ATTR{alignment_offset}=="0"
ATTR{capability}=="40"
ATTR{discard_alignment}=="0"
ATTR{diskseq}=="1557"
ATTR{events}==""
ATTR{events_async}==""
ATTR{events_poll_msecs}=="-1"
ATTR{ext_range}=="256"
ATTR{hidden}=="0"
ATTR{inflight}==" 0 0"
ATTR{nguid}=="xxx-2af100000046"
ATTR{nsid}=="70"
ATTR{range}=="0"
ATTR{removable}=="0"
ATTR{ro}=="0"
ATTR{size}=="4292870144"
ATTR{stat}==" 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"
ATTR{uuid}=="xxx-2af100000046"
ATTR{wwid}=="eui.xxx2af100000046"
 
looking at parent device '/devices/virtual/NVME-subsystem/NVME-subsys4':
KERNELS=="NVME-subsys4"
SUBSYSTEMS=="NVME-subsystem"
DRIVERS==""
ATTRS{firmware_rev}=="1000001"
ATTRS{iopolicy}=="round-robin"
ATTRS{model}=="san-model"
ATTRS{serial}=="2102352VVAFSMB000015"
ATTRS{subsysnqn}=="QRG7T-8RHQY-B4CVX-R8MWM-W43BT"
ATTRS{subsystype}=="nvm"

Пришлось запросить все wwid через листинг NVME4 устройств и сделать новые имена на основе окончания wwid

ls -1 /dev/NVME4n* | xargs -n1 udevadm info -q all -a | grep wwid | sort| tr "\"." " " | awk '{print $3}' | while read device; do echo -n $device " "; echo $device | cut -c30-35; done | awk '{print "ATTR{wwid}==\"eui."$1"\", SYMLINK+=\"gsmdisk/rdma_" $2"\", OWNER=\"vanga\", GROUP=\"dba\", MODE=\"0660\" "}'

Далее эти устройства были побиты на 100% партиции

ls -1 /dev/gsmdisk/rdma_0*| tr "/" " " | awk '{print $3}' | while read dsk; do parted -s /dev/gsmdisk/$dsk "mklabel gpt mkpart primary 0% 100% name 1 gsm_${dsk}"; done

И партиции были выведены в /dev/gsmdisk с нужными правами таким udev правилом.

ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*rdma*", SYMLINK+="gsmdisk/$env{ID_PART_ENTRY_NAME}", OWNER="vanga", GROUP="dba", MODE="0660"

Таким нехитрым образом добились того, что диски видны по уникальному номеру, неважно в каком порадке они будут подключаться. Нет привязки к нумерации дисков системой.

Для физических дисков есть правило, выводящее серийник в название раздела в точку, куда подают партиции для управления СУБД.

Подключение после ребута

После ребута у нас неправильно настроена сеть, MTU не тот, да и подключение к нужному неймспесу стоить выполнить.

В iscsi автоподключение можно настроить через

iscsiadm -m node -o update -n node.startup -v automatic

Тут же решили написать минимальный сервис для systemctl, в котором:

  1. Настраиваем MTU.

  2. Делаем подключение по всем четырем путям.

  3. Делаем листинг дисков через NVME list, чтобы все что нужно закэшировать закэшировалось.

Ограничения тестирования

Так как это быстрое ознакомление с технологие диструктивных тестов, таких как полное отключение СХД, отрубание сетевых карт на лету, отключение процессорных блоков не производилось. Если вы исследовали такое поведение, то интересно было бы узнать про полученый опыт.

Наблюдения:

sar не показывает утилизацию сетевых карт для roce

04:31:17 PM IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
04:31:22 PM lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
04:31:22 PM eno1np0 3.20 13.80 0.28 3.46 0.00 0.00 0.00 0.00
04:31:22 PM eno2np1 4.20 3.20 0.33 0.21 0.00 0.00 0.00 0.00
04:31:22 PM ens2f0np0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
04:31:22 PM ens2f1np1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
04:31:22 PM ens4f0np0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
04:31:22 PM ens4f1np1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
04:31:22 PM eno3np0 8.40 3.40 0.65 0.22 0.00 0.00 0.00 0.00
04:31:22 PM eno4np1 3.40 3.40 0.28 0.22 0.00 0.00 0.00 0.00
04:31:22 PM eno1np0.101 3.20 4.60 0.23 2.23 0.00 0.00 0.00 0.00
04:31:22 PM eno2np1.101 4.20 3.20 0.27 0.21 0.00 0.00 0.00 0.00
04:31:22 PM eno3np0.102 8.40 3.40 0.53 0.22 0.00 0.00 0.00 0.00
04:31:22 PM eno4np1.102 3.40 3.40 0.23 0.22 0.00 0.00 0.00 0.00
04:31:22 PM ens2f0np0.200 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
04:31:22 PM ens2f1np1.200 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
04:31:22 PM ens4f0np0.201 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
04:31:22 PM ens4f1np1.201 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00

Это ожидаемо, но может вызвать сложности с нахождением сбойного пути.

Cо стороны СХД получили такой отзыв

Ну вот 10 гигибайт — это потолок для наших 25 Гб х 4 интерфейса.
Ну и RDMA сразу виден, при таком огромном блоке (1 мбайт) и
утилизации каналов в 100% — респонз 0,6 мс.*

Отличия от NVME Over tcp.

В RDMA вариснте нет, как в NVME over tcp, соединений по числу процессоров на подсистему. Все поданыне диски имеют один серийник, поэтому найти конкретный диск будет непросто

TCP вариант значительно менее требователен к сетевой инфраструктуре и физическому доступу к карте. Пэтому его прокидывали на виртуалки через очень разные по качеству сетевые соединения.

Выводы

Хорошие сапоги, надо брать!

Лучшее — враг хорошего.

Если до этого наработали опыт работы с NVME over Fabrics, то разобраться с новым транспортом не сложно. Подсистема ведет себя понятно.

Скорость последовательного чтения у нас вышла выше, чем на локальном NVME диске. Локальный диск выигрывает по случайном чтению.

Если инфраструктура позволяет использвать такой вариант, настроить его достаточно просто и быстро на стандартных драйверах mlx5.

And one more thing…

Возвращаяясь к вопросам из Попытка разогнать сеть для БД со 100 до 200Гб/c или «failure is always an option».

Раз так сложилось, что сеть приложения отделена от сети данных, то решили попробовать запустить fio и iperf3 в параллель. Получилось 10,7ГБ на чтение.

# fio --ioengine=libaio --direct=1 --runtime=1000s --time_based --name=testRoCe --filename /dev/nvme4n1 --bs=1024k --iodepth=192 --size=10T --readwrite=read
testRoCe: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=192
fio-3.19
Starting 1 process
Jobs: 1 (f=1): [R(1)][44.7%][r=10.7GiB/s][r=10.0k IOPS][eta 09m:13s]

При параллельной утилизации примерно на 75% 100Гб сети.

10:51:04 AM eno1np0.101      2.67  83389.33      0.14 2062421.90      0.00      0.00      0.00     67.58
10:51:04 AM eno2np1.101     18.33 102769.33      1.07 2546602.74      0.00      0.00      0.00     83.45
10:51:04 AM eno3np0.102  34579.67  93598.00   1756.00 2331386.93      0.00      0.00      0.00     76.39
10:51:04 AM eno4np1.102  35358.67  80535.67   1795.56 2034233.98      0.00      0.00      0.00     66.66

10:51:07 AM eno1np0.101      2.67  83484.33      0.14 2064896.44      0.00      0.00      0.00     67.66
10:51:07 AM eno2np1.101     17.33 102815.67      1.02 2548177.28      0.00      0.00      0.00     83.50
10:51:07 AM eno3np0.102  34608.00  93598.00   1757.44 2333621.43      0.00      0.00      0.00     76.47
10:51:07 AM eno4np1.102  35385.33  80581.00   1796.91 2035388.15      0.00      0.00      0.00     66.70

Суммарно при NUMA=off c 50% утилизации сетевой инфраструктуры на 8 портовом сервере смогли скакнуть до примерно 80% эффективности.

Но, как известно, серебрянной пули не существует. И оптимизировать работу СУБД, чтобы достигнуть результатов из синтетики, ещё то приключение.

P.S.: RickRoll тут, выше именно Daft Punk.

© Habrahabr.ru