Предоставление доступа к блочным устройствам посредством сети - технология не новая, наиболее простым и недорогим решением для этих целей был протокол iSCSI, который широко применяется и на сегодняшний день. Но технологии не стоят на месте, новый протокол NVMe вывел работу с накопителями на новые уровни производительности, которым стало тесно в рамках стандартных технологий. Сохранить высокие показатели при работе с новыми накопителями по сети нам поможет протокол NVMe-over-TCP, с которым мы сегодня познакомился не только в теории, но и на практике.
Онлайн-курс по устройству компьютерных сетей
На углубленном курсе "Архитектура современных компьютерных сетей" вы с нуля научитесь работать с Wireshark и «под микроскопом» изучите работу сетевых протоколов. На протяжении курса надо будет выполнить более пятидесяти лабораторных работ в Wireshark.
Начнем с небольшого исторического экскурса, долгое время накопители строились на базе технологии магнитной записи и ввиду физических особенностей технологии слабо позволяли использовать параллелизм. Поэтому случайные операции всегда были слабым местом магнитных дисков, сильно уступая линейным режимам доступа. Это можно увидеть невооруженным глазом увидеть в любом тесте, где скорости линейного и случайного доступа различаются даже не в разы, а на порядки.
Это было связано с тем, что при случайном доступе диску могла поступить команда считать сектор А, а следующей командой система могла попросить считать сектор Б, который находится раньше и для доступа к нему диску придется ждать целый оборот. Для борьбы с этим негативным явлением была придумана очередь команд, которая позволяла диску проанализировать запросы и оптимизировать свою работу, например, объединив несколько случайных запросов соседних областей в один последовательный, когда диск мог считать все необходимые данные за один оборот просто изменив порядок выполнения команд.
Глубина очереди обычных SATA дисков составляла 32 команды, для SCSI - 256. Для существующих магнитных накопителей этого было достаточно, но все изменилось с появлением твердотельном памяти. Такие диски могут читать и писать из разных областей одновременно и показывать гораздо более высокий параллелизм. И очень скоро стало ясно, что разработанные для жестких дисков интерфейсы и протоколы твердотельным накопителям не подходят. Поэтому был разработан полностью новый протокол, рассчитанный именно на работу с твердотельной памятью - NVMe. В нем изначально заложено 65536 очередей с глубиной 65536 команд каждая.
Сейчас совершенно очевидно, что NVMe вывел производительность накопителей на совершенно новый, ранее недосягаемый уровень и основным ограничением для него является скорость транспорта, для физических дисков это скорость шины PCIe. Это хорошо заметно ростом линейных скоростей дисков при переходе с одного поколения PCIe на другой.
Что касается доступа к блочным устройствам по сети, то в бюджетном сегменте безальтернативно применялся протокол iSCSI, обеспечивавший передачу команд SCSI поверх транспортного протокола TCP, для более производительных сред могли использоваться сети хранения на основе Fibre Channel или InfiniBand, но все это было значительно дороже и требовало вложений в соответствующее оборудование. При этом внутри продолжал использоваться все тот же протокол SCSI.
С приходом NVMe стало понятно, что SCSI не позволяет в полной мере реализовать возможности новых накопителей и была инициирована разработка протокола NVMe over Fabric (NVMe-oF), основной его целью было предоставить возможность передачи команд NVMe по сети с низкой дополнительной задержкой, не более 10 мкс. В качестве транспортных протоколов были выбраны Fibre Channel, InfiniBand и Ethernet, также был разработан специальный протокол RDMA (Remote Direct Memory Access) - для прямого доступа к памяти на канальном уровне, устранив возможные задержки в сетевом стеке.
Но все это было сложно и/или дорого, так как требовало изменение сетей хранения и приобретения специального оборудования. Ни одна из указанных технологий не позволяла использовать ее на текущих сетях хранения, поэтому очень скоро в семейство NVMe-oF был добавлен протокол NVMe-over-TCP, который является компромиссным вариантом. NVMe-over-TCP не способен обеспечить низкие дополнительные задержки, зато его можно использовать в текущих сетях Ethernet без всяких дополнительных затрат.
Сегодня мы предлагаем практически познакомиться с этой технологией, чтобы понять ее особенности, преимущества и недостатки. Практического сценария здесь не будет. Но благодаря нашему материалу вы сможете быстро настроить NVMe-over-TCP и оценить на практике нужна ли вам она или нет.
Чтобы понимать о чем пойдет речь в дальнейшем, рассмотрим некоторые ключевые особенности протокола NVMe, причем это касается не только NVMe-over-TCP, но и NVMe в целом. Давайте посмотрим на типичное наименование NVMe диска:
Оно состоит из нескольких частей, каждая из которых указывает на отдельную сущность:
- Подсистема - набор программных и аппаратных средств предоставляющих нам интерфейс для работы с NVMe устройствами, это может быть совокупность контроллера, драйвера, порта (для NVMe-over-TCP) и т.д.
- Пространство имен - собственно блочное устройство, для физических NVMe дисков пространство имен соответствует отдельному диску.
- Раздел - дисковый раздел, логическая сущность которая возникает при разбивке диска.
Работая с NVMe-over-TCP мы будем иметь дело только с подсистемами и пространствами имен, так как NVMe-over-TCP таргет предоставляет инициаторам именно блочные устройства, а как они их будут размечать на другой стороне его не касается. Одна подсистема может содержать несколько пространств имен, но все они будут ограничены общей производительностью подсистемы.
Настройка NVMe-over-TCP таргета
Таргет (цель) предоставляет инициаторам дисковые устройства и является полным аналогом iSCSI-таргета, хотя в сети могут использоваться названия "сервер" или "донор", но правильно называть данную роль именно таргетом.
Поддержка NVMe-over-TCP присутствует в ядрах начиная с 5.0 и для работы нам достаточно выполнить загрузку двух модулей:
modprobe nvmet
modprobe nvmet-tcp
Теперь мы можем управлять подсистемой путем редактирования файлов в /sys/kernel/config, это не самый оптимальный вариант, но именно он поможет понять как это все устроено и почему работает так или иначе. Обратите внимание, что все внесенные изменения пропадут при перезагрузке узла.
Создание подсистемы
Для создания подсистемы перейдем в каталог /sys/kernel/config/nvmet/subsystems:
cd /sys/kernel/config/nvmet/subsystems
И создадим каталог с именем подсистемы, подсистемы в NVMe-over-TCP называются по именам, в нашем случае это будет test, после чего перейдем в него:
mkdir test
cd test
В каталоге автоматически будут созданы нужные файлы с настройками, нам останется только записать в них нужные значения. В нашем случае достаточно одной настройки, разрешим подключаться к нашей подсистеме с любого хоста:
echo -n 1 > attr_allow_any_host
Ключ -n в команде echo предотвращает перенос строк, что важно при редактировании файлов подсистемы.
Создание пространства имен
Находясь в каталоге подсистемы перейдем в каталог пространств имен:
cd namespaces
И создадим новое пространство имен, пространства имен задаются по номерам, начиная с единицы. Создадим каталог с номером пространства и перейдем в него:
mkdir 1
cd 1
После чего сопоставим пространству имен дисковое устройство хоста. Это может быть как физический диск, так и раздел или виртуальный диск.
echo -n /dev/nvme0n1 > device_path
Одно пространство имен может содержать одно дисковое устройство.
Включим его:
echo -n 1 > enabled
Все изменения происходя налету, сразу после добавления в файл нужного значения.
Теперь у нас есть подсистема test, которое содержит пространство имен с номером 1 с которым сопоставлен физический диск /dev/nvme0n1.
Создание порта
Для доступа по сети мы должны создать и настроить порт службы, для этого перейдем в /sys/kernel/config/nvmet/ports
cd /sys/kernel/config/nvmet/ports
Точно также же создадим директорию с номером порта и перейдем в нее, порты также как и подсистемы нумеруются начиная с единицы:
mkdir 1
cd 1
Перейдем к настройкам. Прежде всего укажем адрес на котором мы будем принимать подключения:
echo -n 192.168.3.112 > addr_traddr
Транспортный протокол:
echo -n tcp > addr_trtype
Номер порта, стандартно используется 4420:
echo -n 4420 > addr_trsvcid
И тип сетевого протокола, в нашем случае IPv4:
echo -n ipv4 > addr_adrfam
Теперь подключим нашу подсистему к порту создав символическую ссылку на директорию подсистемы:
ln -s /sys/kernel/config/nvmet/subsystems/test /sys/kernel/config/nvmet/ports/1/subsystems/test
На этом настройка таргета закончена.
Настройка NVMe-over-TCP инициатора
Для работы с дисками NVMe установим дополнительный пакет:
apt install nvme-cli
Подгрузим модули ядра:
modprobe nvmet
modprobe nvmet-tcp
Настраивать ничего не надо, просто подключим подсистему таргета командой:
nvme connect -t tcp -n test -a 192.168.3.112 -s 4420
Параметры команды понятны и комментариях не нуждаются, проверить подключенные диски мы можем командой:
nvme list
Также можем убедиться в том, что диск появился в доступных блочных устройствах командой:
lsblk
Как видим у нас появился новый диск с именем nvme0n1, моделью Linux и некоторым серийным номером. Эти параметры можно переопределить, но только на уровне всей подсистемы.
Посмотрим, что у нас с сетевыми подключениями, выполним команду:
ss | grep 4420
Сразу увидим пять доступных очередей, количество очередей создается по количеству ядер процессора инициатора плюс еще одна. Так как ядер у нас четыре, то мы получили пять очередей, что позволяет эффективно распараллеливать запросы.
Мы можем добавить в подсистему неограниченное количество пространств имен (дисков), но количество очередей на инициаторе останется прежним, что может в некоторых сценариях стать узким местом. Поэтому попробуем другой вариант, создадим на таргете еще одну подсистему и подключим на этот же инициатор. Количество очередей сразу выросло до десяти.
Но здесь есть обратная сторона медали, как можем видеть для каждой очереди на инициаторе создается порт из динамического диапазона 49152 - 65535, таких портов нам доступно 16384, можно подумать что это много. Но на самом деле не очень. Если взять какой-нибудь AMD Epyc 7713 с его 64 ядра/128 потоков, то каждая подсистема будет создавать 129 соединений, а следовательно количества портов нам хватит всего на 127 подсистем и то, если мы исключим другую сетевую активность.
Также излишне большое количество очередей может упереться в производительность сетевого оборудования, поэтому в этом вопросе нужно соблюдать разумный компромисс и тестировать различные варианты распределения дисков по подсистемам.
Что касается пропускной способности сети, то помним:
- 1 Гбит/с = 125 МБ/с
- 2,5 Гбит/с = 312,5 МБ/с
- 10 Гбит/с = 1250 МБ/с
Как видим, если брать широкодоступные сетевые адаптеры, то скорости там далеки от NVMe и ближе к дешевым SATA SSD. Получается в таком случае в NVMe-over-TCP нет смысла? Отнюдь, основное достоинство данного протокола - это не высокие линейные скорости, а большая параллельность запросов, что важно при множественном случайном доступе от различных процессов. И если выбор будет между iSCSI и NVMe-over-TCP в обычных сетях, то преимущество будет у последнего.
И что еще важно - в качестве презентуемого через пространство имен блочного устройства вы можете использовать не только NVMe, а абсолютно любой накопитель, даже флешку. Так что NVMe-over-TCP вполне можно использовать и с обычными жесткими дисками, но здесь также следует проявлять разумную осторожность, чтобы не перегрузить диск множественными параллельными запросами.
Как видим, ничего сложного в NVMe-over-TCP нет, но так как технология новая, то перед принятием решения о целесообразности ее применения рекомендуется выполнить всестороннее тестирование.
Онлайн-курс по устройству компьютерных сетей
На углубленном курсе "Архитектура современных компьютерных сетей" вы с нуля научитесь работать с Wireshark и «под микроскопом» изучите работу сетевых протоколов. На протяжении курса надо будет выполнить более пятидесяти лабораторных работ в Wireshark.
Последние комментарии