Источник изображения: NVIDIA
Крупнейшие облачные провайдеры подключают виртуальные накопители к выделенным физическим серверам. Но если заглянуть в ОС сервера, то там будет физический диск с именем провайдера в поле «производитель». Сегодня мы разберем, как это возможно.
Что такое Smart NIC?
В основе этой «магии» лежат «умные» сетевые карты (Smart NIC). Такие сетевые карты имеют собственный процессор, оперативную память и накопитель. По факту это мини-сервер, выполненный в виде PCIe-карты. Основная задача «умных» карт — разгрузка CPU от операций ввода-вывода.
В данной статье мы поговорим о конкретном устройстве — NVIDIA BlueField 2. NVIDIA называет такие устройства DPU (Data Processing Unit), целью которых производитель видит «освобождение» центрального процессора от множества инфраструктурных задач — СХД, сети, информационной безопасности и даже управления хостом.
В нашем распоряжении устройство, внешне похожее на обычную 25GE сетевую карту, но в ней установлен восьмиядерный ARM-процессор Cortex-A72 c 16 ГБ оперативной памяти и 64 ГБ постоянной eMMC-памяти.
Дополнительно видны разъемы Mini-USB, NC-SI и RJ-45. Первый разъем предназначен исключительно для отладки и в продуктовых решениях не используется. NC-SI и RJ-45 позволяют подключаться к BMC-модулю сервера через порты карты.
Хватит теории, время запускать.
Первый запуск
После установки сетевой карты первый запуск сервера будет непривычно долгим. Все дело в том, что UEFI-прошивка сервера опрашивает подключенные PCIe-устройства, а «умная» сетевая карта блокирует этот процесс, пока не загрузится сама. В нашем случае процесс загрузки сервера длился около двух минут.
После загрузки ОС сервера можно увидеть два порта умной сетевой карты.
root@host:~# lspci
98:00.0 Ethernet controller: Mellanox Technologies MT42822 BlueField-2 integrated ConnectX-6 Dx network controller (rev 01)
98:00.1 Ethernet controller: Mellanox Technologies MT42822 BlueField-2 integrated ConnectX-6 Dx network controller (rev 01)
98:00.2 DMA controller: Mellanox Technologies MT42822 BlueField-2 SoC Management Interface (rev 01)
На этот момент умная сетевая карта ведет себя как обычная. Для взаимодействия с картой нужно скачать и установить BlueField-драйверы со страницы NVIDIA DOCA SDK. По окончании процесса установщик предложит перезапустить сервис openibd, чтобы загрузились установленные драйверы. Перезагружаем:
/etc/init.d/openibd restart
Если все было выполнено правильно, то в ОС появится новый сетевой интерфейс tmfifo_net0.
root@host:~# ifconfig tmfifo_net0
tmfifo_net0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::21a:caff:feff:ff02 prefixlen 64 scopeid 0x20<link>
ether 00:1a:ca:ff:ff:02 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 13 bytes 1006 (1.0 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
По умолчанию BlueField имеет адрес 192.168.100.2/24, поэтому назначаем интерфейсу tmfifo_net0 адрес 192.168.100.1/24. Затем запускаем сервис rshim и ставим его в автозагрузку.
systemctl enable rshim
systemctl start rshim
После этого доступ к ОС карты с сервера возможен двумя способами:
- через SSH и интерфейс tmfifo_net0;
- через консоль — символьное устройство /dev/rshim0/console.
Второй способ работает вне зависимости от состояния ОС карты.
Также возможны и другие способы удаленного подключения к ОС карты, которые не требуют доступа к серверу, в который установлена карта:
- по SSH через 1GbE out-of-band mgmt порт или через uplink интерфейсы (в том числе с поддержкой PXE boot);
- консольный доступ через выделенный RS232 порт;
- RSHIM интерфейс через выделенный USB порт.
Взгляд изнутри
Теперь, когда мы получили доступ в ОС карты, есть возможность удивиться тому факту, что внутри нашего сервера стоит сервер поменьше. Предустановленный на карте образ ОС содержит все необходимое ПО для управления картой и, кажется, даже больше. На сетевой карте стоит полноценный дистрибутив Linux, в нашем случае — Ubuntu 20.04.
При необходимости на сетевую карту возможно установить любой дистрибутив Linux, поддерживающий архитектуру aarch64 (ARMv8) и UEFI. Если подключиться через консоль, то нажатием ESC при загрузке карты можно попасть в ее собственный UEFI Setup Utility. Здесь непривычно мало настроек, по сравнению с серверным аналогом.
ОС на BlueField 2 можно загрузить при помощи протокола PXE, а UEFI Setup Utility позволяет настраивать порядок загрузки (Boot Order). Вы только представьте: сетевая карта загружает по PXE сначала себя, а потом сервер!
UEFI Setup Utility в BlueField 2
Так было обнаружено, что по умолчанию в ОС доступен docker, хотя кажется, что для сетевой карты это избыточно. Хотя если уж речь зашла про избыточность, то мы поставили JVM из пакетного менеджера и запустили на сетевой карте сервер Minecraft. Хотя никаких серьезных тестов не проводилось, на сервере вполне комфортно можно играть маленькой компанией.
Для отображения информации о сервере пришлось поставить пару плагинов.
В ОС сетевой карты отображается множество сетевых интерфейсов:
ubuntu@bluefield:~$ ifconfig | grep -E '^[^ ]'
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
oob_net0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
p0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
p1: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
p0m0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
p1m0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
pf0hpf: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
pf0sf0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
pf1hpf: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
pf1sf0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
tmfifo_net0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
Интерфейсы tmfifo_net0, lo и docker0 нам уже известны. Интерфейс oob_net0 соответствует out-of-band mgmt порту RJ-45. Остальные интерфейсы связаны с оптическими портами:
- pX — представление (representor device) физического порта карты;
- pfXhpf — представление физической функции хоста (Host Physical Function), интерфейса, который доступен хосту;
- pfXvfY — представление виртуальной функции хоста (Host Virtual Function), виртуальных интерфейсов, используемых для виртуализации SR-IOV на хосте;
- pXm0 — это специальный PCIe Sub-Function интерфейс, который используется для взаимодействия карты с портом. Данный интерфейс может использоваться для доступа карты в сеть;
- pfXsf0 — представление PCIe Sub-Function интерфейса pXm0.
Проще всего понять назначение интерфейсов по диаграмме:
Соответствие интерфейсов BlueField 2 и хоста (источник NVIDIA)
Интерфейсы напрямую связаны с виртуальным коммутатором (Virtual Switch), который реализован в BlueField 2. Но об этом мы поговорим в другой статье. В этой же статье мы хотим рассмотреть эмуляцию NVMe, которая позволяет подключать программно-определяемые хранилища (Software Defined Storage, SDS) к выделенным серверам как физические диски. Это позволит использовать все плюсы SDS на «голом железе».
Настройка эмуляции NVMe
По умолчанию режим эмуляции NVMe отключен. Включаем его командой mlxconfig.
mst start
# Общие настройки
mlxconfig -d 03:00.0 s INTERNAL_CPU_MODEL=1 PF_BAR2_ENABLE=0 PER_PF_NUM_SF=1
mlxconfig -d 03:00.0 s PF_SF_BAR_SIZE=8 PF_TOTAL_SF=2
mlxconfig -d 03:00.1 s PF_SF_BAR_SIZE=8 PF_TOTAL_SF=2
# Включаем эмуляцию NVMe
mlxconfig -d 03:00.0 s NVME_EMULATION_ENABLE=1 NVME_EMULATION_NUM_PF=1
После включения режима NVMe Emulation нужно перезагрузить карту. Настройка эмуляции NVMe сводится к двум этапам:
- Настройка SPDK.
- Настройка snap_mlnx.
На данный момент официально заявлены только два способа доступа к удаленному хранилищу: NVMe-oF и iSCSI. Причем только NVMe-oF имеет аппаратное ускорение. Тем не менее, возможно использовать и другие протоколы. Наш интерес — подключить ceph-хранилище.
К сожалению, поддержки rbd «из коробки» нет. Поэтому для подключения к ceph-хранилищу необходимо использовать модуль ядра rbd, который создаст блочное устройство /dev/rbdX. А стек для эмуляции NVMe, в свою очередь, будет работать с блочным устройством.
В первую очередь необходимо указать, где находится хранилище, которое мы будем представлять как NVMe. Делается это через аргументы скрипта spdk_rpc.py. Для персистентности при перезагрузках карты команды записываются в /etc/mlnx_snap/spdk_rpc_init.conf.
Для подключения блочного устройства используется команда bdev_aio_create <путь до блочного устройства> <имя> <размер блока в байтах>. Параметр <имя> используется далее в настройках, и он не обязательно должен совпадать с именем блочного устройства. Например:
bdev_aio_create /dev/rbd0 rbd0 4096
Для прямого подключения rbd устройства необходимо перекомпилировать SPDK и mlnx_snap с поддержкой rbd. Мы получили исполняемые файлы, скомпилированные с поддержкой rbd от технической поддержки. Для подключения мы использовали команду bdev_rbd_create <имя пула> <имя rbd> <размер блока в байтах>. Эта команда не позволяет задавать имя устройства, а придумывает его сама и выводит по завершении. В нашем случае — Ceph0. Имя устройства нужно запомнить, оно потребуется нам в дальнейшей настройке.
Хотя решение подключить rbd через модуль ядра и использовать в качестве накопителя блочное устройство вместо прямого использования rbd кажется несколько некорректным решением, оказалось, что это тот самый случай, когда «костыли» работают лучше, чем решение «по уму». В тестах производительности получилось, что «правильное» решение работает медленнее.
Далее необходимо настроить представление, которое будет видеть хост. Настройка производится через snap_rpc.py, а команды сохраняются в /etc/mlnx_snap/snap_rpc_init.conf. Сперва создаем накопитель следующей командой.
subsystem_nvme_create <NVMe Qualified Name (NQN)> <Серийный номер> <модель>
Далее создаем контроллер.
controller_nvme_create <NQN> <менеджер эмуляции> -c <конфигурация контроллера> --pf_id 0
Менеджер эмуляции чаще всего называется mlx5_x. Если вы не используете аппаратное ускорение, то можно использовать первый попавшийся, то есть mlx5_0. После выполнения этой команды будет выведено имя контроллера. В нашем случае — NvmeEmu0pf0.
В заключение добавляем пространство имен (NVMe Namespace) к созданному контроллеру.
controller_nvme_namespace_attach <тип устройства> <идентификатор устройства> -c <имя контроллера>
Тип устройства у нас всегда spdk, идентификатор устройства мы получили на этапе настройки spdk, а имя контроллера — в предыдущем шаге. В результате файл /etc/mlnx_snap/snap_rpc_init.conf у нас выглядит так:
subsystem_nvme_create nqn.2020-12.mlnx.snap SSD123456789 "Selectel ceph storage"
controller_nvme_create nqn.2020-12.mlnx.snap mlx5_0 --pf_id 0 -c /etc/mlnx_snap/mlnx_snap.json
controller_nvme_namespace_attach -c NvmeEmu0pf0 spdk Nvme0n10 1
Перезапускаем сервис mlnx_snap:
sudo service mlnx_snap restart
Если все настроено правильно, то сервис запустится. На хосте NVMe диск сам не появится. Нужно перезагрузить модуль ядра nvme.
sudo rmmod nvme
sudo modprobe nvme
И вот, на хосте появился наш виртуальный физический диск.
root@host:~# nvme list
Node SN Model Namespace Usage Format FW Rev
---------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- --------
/dev/nvme0n1 SSD123456789 Selectel ceph storage 1 515.40 GB / 515.40 GB 4 KiB + 0 B 1.0
Теперь у нас есть виртуальный диск, представленный системе как физический. Проверим его на обычных для физических дисков задачах.
Тестирование
В первую очередь мы решили установить ОС на виртуальный диск. В ход пошел установщик CentOS 8. Он увидел диск без особых проблем.
Установка CentOS 8
Установка прошла в штатном режиме. Проверяем в UEFI Setup Utility загрузчик CentOS.
UEFI Setup Utility видит виртуальный диск
Мы загрузились в установленную CentOS и убедились, что корень ФС находится на NVMe-диске.
[root@localhost ~]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 447.1G 0 disk
|-sda1 8:1 0 1M 0 part
|-sda2 8:2 0 244M 0 part
|-sda3 8:3 0 977M 0 part
`-sda4 8:4 0 446G 0 part
`-vg0-root 253:3 0 446G 0 lvm
sdb 8:16 0 447.1G 0 disk
sr0 11:0 1 597M 0 rom
nvme0n1 259:0 0 480G 0 disk
|-nvme0n1p1 259:1 0 600M 0 part /boot/efi
|-nvme0n1p2 259:2 0 1G 0 part /boot
`-nvme0n1p3 259:3 0 478.4G 0 part
|-cl-root 253:0 0 50G 0 lvm /
|-cl-swap 253:1 0 4G 0 lvm [SWAP]
`-cl-home 253:2 0 424.4G 0 lvm /home
[root@localhost ~]# uname -a
Linux localhost.localdomain 4.18.0-305.3.1.el8.x86_64 #1 SMP Tue Jun 1 16:14:33 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
Мы также провели тестирование эмулируемого диска утилитой fio. Вот что получилось.
Тест | AIO bdev, IOPS | Ceph RBD bdev, IOPS |
---|---|---|
randread, 4k, 1 | 1 329 | 849 |
randwrite, 4k, 1 | 349 | 326 |
randread, 4k, 32 | 15 100 | 15 000 |
randwrite, 4k, 32 | 9 445 | 9 712 |
Получается, что пропускная способность удаленного накопителя, подключенного к тестовому и не самому быстрому кластеру ceph, примерно равна пропускной способности быстрых дисков в нашей Облачной Платформе. Конечно, полученный результат не сравнится с доступом к локальным дискам, но и у локальных дисков есть свои ограничения.
Заключение
«Умные» сетевые карты — это настоящая техническая магия, которая позволяет не только разгружать центральный процессор от операций ввода-вывода, но и «обманывать» его, представляя удаленный накопитель локальным.
Такой подход позволяет использовать преимущества программно-определяемых хранилищ на выделенных серверах: по «щелчку пальцев» переносить диски между серверами, менять размер, делать снапшоты и разворачивать готовые образы операционных систем за секунды.
Автор: Владимир