Привет, Хабр! Меня зовут Сергей Баширов, я ведущий разработчик в Cloud.ru. Наша команда RnD занимается исследованиями и оптимизацией баз данных, файловых систем, объектных хранилищ, СХД и SDS в облачной инфраструктуре. И мы решили «импортозаместить в лучших традициях» pNFS. За дело взялись вместе с моим коллегой — Константином Евтушенко (@kevtushx). К сожалению менеджеров и на радость нам, годной инструкции по настройке в интернете не оказалось. Софт с открытым исходным кодом из коробки не завелся: пришлось погружаться в детали, отлаживать и чинить. В статье расскажу, как всё было, какие ошибки и выводы мы сделали, а также поделюсь подробной инструкцией по настройке стенда с блочным типом доступа к серверам данных. Если интересно — добро пожаловать под кат! За годы существования файловая система NFS обросла кучей стандартов и имеет множество реализаций. Как облачного провайдера, нас интересовал ее параллельный вариант и возможность применения на нашей инфраструктуре. Поэтому решили мы попробовать «импортозаместить» pNFS — свой SDS в компании имеется, почему бы и нет? Так возникла исследовательская задача: собрать из подручных средств стенд-прототип и по-быстрому измерить возможную производительность подобной системы. Отмечу, что на тот момент в ядре Linux были проблемы, из-за которых даже правильно настроенный стенд иногда мог просто не работать. Истории о поиске и устранении этих недоработок будет посвящена отдельная статья. А пока расскажу, с чего мы начинали. Cначала мы, конечно, изучили всю теорию и выяснили принципиально важные для нас моменты. Во-первых, в стандарте NFS 4.1 есть полезное нововведение — опциональный режим работы parallel NFS. Его главная идея заключается в разделении потоков данных и метаданных, что позволяет масштабировать серверы, на которых хранятся данные. Как это работает? Для некоторой части пространства имен файловой системы всегда есть ровно один активный сервер метаданных — MDS, а также множество DS — серверов данных. Клиенты файловой системы подключаются к MDS, проходят аутентификацию, монтируют корневой каталог, выполняют поиск, открывают файлы и т. д. В pNFS все эти процессы очень похожи на обычный NFS. Но для доступа к блокам данных клиент запрашивает у MDS схему размещения файла на DS-серверах, а затем выполняет чтение или запись, коммуницируя напрямую с DS-серверами. Благодаря этому и должно происходить горизонтальное масштабирование потока данных. Второй принципиально важный момент касался правил размещения файлов на DS. pNFS оперирует понятием layout — по сути это схема размещения блоков данных файла на одном или нескольких DS. Потенциально возможно множество различных типов layout, но для RFC-5661, RFC-7862, RFC-8154 и RFC-8435 работают следующие основные стандарты: NFSv4.1 Files — файловый доступ к данным, чередование данных между несколькими DS по протоколу NFS4.1; OSD2 Objects — хранение файлов на объектных хранилищах OSD; Block Volume — хранение данных на удаленных блочных устройствах, возможно объединение или чередование нескольких устройств; Block SCSI — хранение данных на iSCSI устройствах, возможно объединение или чередование нескольких устройств; Flexible Files — файловый доступ к данным на нескольких DS по протоколам NFSv3, NFSv4, NFSv4.1, NFSv4.2 (файл клиента представляется как один или несколько файлов на DS), поддерживается зеркалирование или чередование. В ходе подготовки мы изучили разные варианты уже готовых решений, некоторые из которых показались нам действительно интересными. Ниже сделали краткий обзор их возможностей и особенностей. Проприетарные решения: Clustered NFS (CNFS) — это вариант компании IBM, который они используют поверх своей кластерной файловой системы GPFS. В этом случае NFS-сервис фактически становится одним из способов доступа к GPFS и имеет с ней интеграцию. NetApp ONTAP — решение поддерживает pNFS и layout типа NFSv4.1 Files, т. е. файловый доступ к серверам данных. По отзывам коллег он показывает хорошую скорость. Однако бывали ситуации, когда один из клиентов при чтении получал устаревшие данные из кеша. В техническом отчете TR-4067 «NFS in NetApp ONTAP» за 2021 год пишут, что изредка можно наткнуться на баг. Например, получить испорченные данные. Рекомендуют обновляться до последних версий. pNFS в Dell EMC VNX — поддерживает только layout типа Block SCSI, т. е. блочный доступ к iSCSI-дискам. Для дисков подключенных через Fibre Channel используется мост iSCSI-to-FibreChannel. Решения с открытым исходным кодом: Первое open source решение, которое привлекло наше внимание — это user-space сервер NFS Ganesha. Он имеет реализацию pNFS для некоторых распределенных файловых систем, например, Ceph и Gluster. Из особенностей — поддерживаются файловые и объектные типы layout, однако документация проекта честно сообщает, что направление pNFS в комьюнити сейчас подзаброшено. В NFS-сервере ядра FreeBSD поддерживаются оба типа файловых layout — Flexible Files и NFSv4.1 Files. Настраивать и запускать его мы не пробовали, поскольку нас интересовал только layout типа Block Volume. Сервер NFS ядра Linux — решение, которое имеет реализацию layout следующих типов: Flexible Files, Block Volume и Block SCSI. При этом часть функционала реализована не полностью. Например, блочные типы layout могут экспортировать директории только для файловой системы XFS. Кроме того, не поддерживаются таблицы разделов на диске, логические разделы, а также объединение или чередование нескольких дисков. В реализации Flexible Files есть лишь минимальный функционал для разработки и тестирования кода клиента: один и тот же сервер выступает как в роли DS, так и в роли MDS. Таким образом, все проприетарные решения используют собственную реализацию сервера, но поддерживают работоспособность открытого клиента на Linux. А вот серверы с открытым исходным кодом уже требуют значительных доработок. У нас в Cloud.ru есть свой шустрый SDS, который умеет раздавать виртуальным машинам удаленные диски как устройства vhost-user-blk-pci. Так что после изучения теории по pNFS мы остановились на Block Volume Layout, а в качестве сервера для экспериментального стенда выбрали линуксовый nfsd. С точки зрения архитектуры эта комбинация выглядела привлекательно: в ней серверами данных (в терминологии pNFS) становились сервисы удаленных дисков SDS и оставалось только добавить в систему MDS. Что касается ограничений реализации сервера в Linux, то тут препятствий для нас не было. Отсутствие поддержки таблиц разделов компенсировалось гибкостью облака: вместо того чтобы делать несколько логических разделов на одном диске, можно просто создать несколько дисков. Их объединение также не имеет особого смысла, поскольку объем виртуального диска можно увеличивать, а чередование и отказоустойчивость в облаке уже есть. Как же работает блочный layout под капотом? Самое интересное здесь — это механизм доступа к данным файла. Вместо методов READ и WRITE, как в обычном NFS, клиент использует методы LAYOUTGET, LAYOUTCOMMIT и LAYOUTRETURN для работы с экстентами файла на MDS. А для доступа к самим данным пишет прямо на общее удаленное блочное устройство. На уровне протокола RPC между клиентами и сервером метаданных файлы являются списками экстентов. Каждый экстент может быть в одном из нескольких состояний, определяющих допустимые над ним операции: С помощью LAYOUTGET клиент получает список экстентов у MDS для открытого файла, а также право читать и писать в них. Обязательными параметрами в запросе идут смещение, желаемая длина и минимальная длина. Так же можно указать режим доступа для чтения и записи или только для чтения. В результате успешного выполнения запроса клиенту возвращается список экстентов файла. Первый экстент обязательно содержит запрошенное смещение, а все экстенты вместе покрывают непрерывный логический диапазон файла. Изменения в файле, которые вносит клиент, нужно подтвердить запросом LAYOUTCOMMIT. Для sparse-файлов можно добавлять новые экстенты с данными вместо дырок. А используя параметр запроса last write offset, клиент может сообщить MDS об увеличении размера файла. Вызовом LAYOUTRETURN клиент сообщает MDS о завершении работы с полученными экстентами для некоторой части или всего файла (либо всех открытых этим клиентом файлов). Получается, что клиент взаимодействует с уже существующим файлом по следующей схеме: Получает дескриптор файла вызовом LOOKUP на MDS. Открывает связанный с дескриптором файл вызовом OPEN на MDS. Получает схему размещения данных – LAYOUTGET на MDS. Читает и пишет данные прямо на DS (удаленный диск). Фиксирует изменения с помощью LAYOUTCOMMIT на MDS. Отпускает экстенты файла вызовом LAYOUTRETURN на MDS. Использует метод CLOSE на MDS, чтобы закрыть файл. Как и любое другое решение, Block Volume Layout имеет свои преимущества и недостатки. Плюсы: разделение потоков данных и метаданных позволяет получить масштабирование работы с файлами; тонкий программный стек без лишних абстракций между клиентами и удаленным блочным устройством. Минусы: баги в драйвере на клиенте могут приводить к повреждению чужих данных или даже всей файловой системы; пользователь с правами администратора на клиентской машине может получить данные со всего диска. Применительно к облачной инфраструктуре, перечисленные минусы не настолько фатальны. Отсутствие изоляции клиентов можно решить так: каждому пользователю сервиса создается свой pNFS на отдельном виртуальном диске и отдельной виртуальной машине для MDS. Дополнительную подстраховку можно организовать с помощью снапшотов общего диска и автоматических бэкапов. Для оценки производительности блочных устройств и файловых систем мы использовали FIO — достаточно гибкий инструмент в плане тонкой настройки большого количества параметров тестирования. Первоначально пытались завести pNFS версии 4.1. Для грубой прикидки решили оценить пропускную способность случайной записи большими блоками. Соответственно, команда запуска FIO была следующего вида: fio ... --rw=randwrite --ioengine=libaio --direct=1 --size=10G --blocksize=128K --iodepth=256 Результат оказался плачевным — меньше сотни мегабайт в секунду на all-flash хранилище. При этом после повторного запуска FIO с теми же параметрами производительность превзошла все наши ожидания и была такой, как хотелось. После эксперимента с запусками в одном и том же файле, а также в разных файлах, стало понятно — жутко тормозит создание новых файлов. Первичный анализ навел на мысль, что проблема может быть в sparse-представлении. Ведь под капотом pNFS у нас XFS, а значит новый файл — по умолчанию одна большая дырка. А в процессе измерения производительности происходит реальная аллокация экстентов перед каждой записью. Стало очевидно, что нужен костыль в виде опции fallocate для обхода. Поэтому запустили новый прогон с дополнительным флагом: fio ... --rw=randwrite --ioengine=libaio --direct=1 --size=10G --blocksize=128K --iodepth=256 --fallocate=posix Проверка подтвердила предположение и открыла еще один нюанс: сама пропускная способность была отличной, а вот предварительная аллокация файла с помощью fallocate работала крайне медленно. По ощущениям примерно столько же, сколько и прогон в новом файле без преаллокации. Запуск через strace показал, что для pNFS версии 4.1 нет реализации fallocate в ядре Linux: Решили, раз ядро не выделяет место на диске под файл, скорее всего, это делает glibc. Поиск по сорцам быстро дал результат: если системный вызов fallocate не поддерживается, то исполняется fallback-реализация в самой библиотеке C. Суть этой реализации в том, что для каждого 4 КБ блока в файле происходит запись одного нулевого байта. Таким образом круг замкнулся, и мы вернулись к проблеме медленной записи новых файлов. Деваться было некуда, пришлось расчехлять Wireshark и смотреть на RPC. Благо протокол NFS поддерживается там из коробки. Когда отфильтровали трафик по типу, в глаза бросилось подозрительно большое число запросов LAYOUTGET. Поэтому к аргументам FIO добавили еще одну опцию --debug=io, чтобы видеть оффсеты, по которым идут записи в файле. Стандарт NFS версии 4.1 на одном экране, трасса Wireshark на другом. Разбор RPC-запросов клиента показал, что LAYOUTGET вызывался для каждого адреса, по которому писал FIO. То есть наше предположение про sparse-файлы было верным. А это, по сути, убивало смысл параллельности в pNFS, ведь каждая операция происходила с участием MDS. Более того, количество RPC-запросов к MDS оказалось очень большим и сильно замедляло работу системы. При дальнейшем изучении стандарта мы выявили, что это проблема самого стандарта: он не учитывает особенности работы со sparse-файлами. Поэтому следующим шагом мы изучили стандарт NFS версии 4.2 на предмет изменений относительно версии 4.1. Буквально в самом начале документа нашли перечень, в который входила работа со sparse-файлами и резервирование места. Узнали, что появился новый RPC-вызов ALLOCATE, который умеет выделять место на диске под весь файл или его часть. Его-то и попробовали поискать в исходном коде сервера. К счастью, в nfsd уже реализована поддержка этого метода, оставалось использовать в настройках протокол NFS версии 4.2. Переконфигурировали сервер и сделали еще один прогон с Wireshark, чтобы увидеть разницу. Результат оказался отличный: количество RPC для работы с одним файлом маленькое, общий трафик на MDS меньше мегабита, загрузка процессора и памяти на MDS низкая. Кроме того, пропускная способность ввода-вывода в файл близка к возможностям удаленного блочного устройства. Так мы поняли, что для pNFS Block Volume Layout желательно использовать стандарт версии 4.2 и делать преаллокацию места на диске для эффективного ввода-вывода. Разобравшись с нюансами, мы решили сравнить производительность общего диска в нашем SDS без файловой системы и производительность размещенной на этом же диске файловой системы pNFS. Результаты представлены на графике: Стоит отметить, что в нашем распоряжении был небольшой рабочий стенд на базе процессоров Intel Xeon Gold. Запускали 16 виртуальных машин, которые имитировали клиентскую нагрузку, а в случае с pNFS еще одна виртуальная машина выполняла роль MDS. Ко всем был подключен один SDS-volume в режиме для чтения и записи. На первом прогоне каждый клиент работал в своей области диска размером 10 ГБ, напрямую с блочным устройством. Затем создавалась файловая система и каждый клиент работал уже в отдельном файле размером 10 ГБ. Перед тем как рассказать про процесс конфигурирования сервера и клиента, сделаю важную ремарку: механизмы обнаружения, подключения и работы с удаленными блочными устройствами выходят за рамки стандарта NFS. Соответственно, для работы pNFS клиента и сервера нужно, чтобы общий диск был предварительно подключен и доступен на всех хостах. При настройке мы использовали наш собственный SDS, но, конечно, можно использовать и другие решения. Общее блочное устройство для нескольких виртуальных машин можно создать с помощью открытого ПО. Главное соблюдать правило: в виртуальные машины диск передавать как устройство virtio-blk, а не virtio-scsi. Для QEMU в качестве backend-реализации можно использовать несжатый файл qcow2 (если все виртуальные машины на одном и том же физическом хосте), iSCSI-диск, Ceph RBD или аналогичные решения. Итак, приступим к созданию стенда из нескольких виртуальных машин. Будем считать, что на них установлена Ubuntu 22.04 — в примерах команды и настройки для этой ОС. Одна из машин должна выступать в качестве сервера MDS, а остальные — в качестве клиентов. Ко всем клиентам и MDS должен быть подключен один и тот же общий диск с возможностью чтения и записи, в примере это /dev/vda. В QEMU диск к виртуальной машине нужно подключать не как virtio-scsi, а как virtio-blk. В противном случае Linux будет пытаться использовать Block SCSI Layout вместо Block Volume Layout. Кроме того, клиенты должны иметь сетевое соединение с MDS. Мы использовали подсеть 192.168.1.0/24 и сервер с адресом 192.168.1.3. Перейдем непосредственно к настройке сервера. Cначала убедимся, что ядро Linux собрано с нужными опциями. В Ubuntu 22.04 они включены по умолчанию. Проверить это можно заглянув в конфиг ядра. Если в вашем случае опция CONFIG_NFSD_BLOCKLAYOUT отсутствует или выключена, то нужно включить ее, затем пересобрать ядро с модулями, установить его и перезагрузить MDS. Устанавливаем пакеты с утилитами, которые мы использовали для настройки: Как упоминали выше, существующая реализация pNFS Block Volume Layout в Linux умеет работать только с XFS и не поддерживает разметку на диске. Поэтому нужно создать файловую систему как показано ниже. Дополнительные опции XFS можно добавлять на свое усмотрение, для простоты используем значения по умолчанию в примере. В файле /etc/nfs.conf включаем версию NFSv4.2. При этом остальные версии лучше сразу отключить. Опцию rootdir здесь не используем, а если она указана, то убираем или комментируем. Ниже пример файла конфигурации, который в итоге получился на Ubuntu 22.04. Настраиваем экспорт папки для клиентов из подсети 192.168.1.0/24 в файле /etc/exports по аналогии с примером ниже. Перезапускаем NFS сервис и проверяем, что сработало без ошибок. Если всё окей, то переходим дальше. Теперь займемся настройкой клиента. Для работы pNFS клиента ядро должно быть собрано с опциями CONFIG_NFS_V4_2 и CONFIG_PNFS_BLOCK. В Ubuntu 22.04 они тоже включены по умолчанию. Проверить это можно так же, как это сделали мы в примере ниже. В противном случае нужно будет включить эти опции, пересобрать ядро с модулями, установить и перезапустить клиентскую машину. Устанавливаем необходимые пакеты для конфигурирования клиента как в примере ниже (однако серверный пакет для blkmapd тоже нужен). Теперь запускаем blkmapd сервис, который нужен для работы в режиме Block Volume Layout, и проверям его статус. Если возникнет ошибка, создайте конфигурационный файл по инструкции ниже. Теперь перезагружаем клиентскую машину и проверяем, что у сервиса хороший статус. Если всё окей, то переходим дальше. Монтировать саму файловую систему XFS, которая на общем диске, клиентам не нужно, поэтому остается только примонтировать NFS папку с MDS сервера 192.168.1.3 и проверить, что всё хорошо. Теперь переходим к проверке стенда. Надежный способ убедиться, что MDS корректно работает в блочном режиме pNFS — посмотреть на счетчики RPC-вызовов. Количество READ и WRITE не должно увеличиваться. Если другие режимы NFS не включали после перезагрузки сервера, то значения должны равняться нулю. При этом количество вызовов LAYOUTGET, LAYOUTRETURN и LAYOUTCOMMIT должно быть ненулевым и возрастать по мере работы с файлами. Аналогично тому, как делали это для сервера, стоит проверить счетчики RPC-вызовов. А еще можно сделать мониторинг сетевого трафика, например, утилитой bmon (или аналогичной). По сравнению с объемом ввода-вывода в файлы, для Block Volume Layout объем передаваемых данных между клиентом и MDS должен быть очень маленьким. Итак, мы изучили теорию, развернули стенд, попытались его настроить, столкнулись с рядом проблем и героически их побороли. В результате сделали важные выводы и делимся ими с вами: Архитектурно pNFS Block Volume Layout лучше всего сочетается с SDS. Роль серверов данных выполняют сами удаленные диски. Фактически добавляется только одна новая сущность — сервер MDS. Производительность файловой системы порадовала, она оказалась близка к производительности самого SDS. Масштабируемость архитектуры тоже оказалась хорошей — она пропорциональна возможностям SDS. Для MDS сервера необходимо самостоятельно докручивать HA. Например, можно использовать Pacemaker или другой подобный инструмент. Открытая реализация сервера с поддержкой блочного типа доступа к данным пока есть только в ядре Linux. Но и там заявлено, что в основном она для тестирования кода клиента. Так что используйте с осторожностью — есть большой шанс наткнуться на баги. Как я отметил в начале статьи, даже правильно настроенный стенд может не работать в некоторых сценариях тестирования. Мы сталкивались с тем, что иногда при корректных запросах на сервере возникала ошибка NFSERR_BAD_XDR. А в отдельных случаях сам клиент терял часть экстентов, когда пытался коммитить изменения в файле, или же указывал неверный last write offset из-за ошибки целочисленного переполнения. О том, как мы отлаживали ядро Linux, находили и чинили проблемы в коде, расскажем в нашей следующей статье про pNFS. Что еще есть у нас в блоге: Как освоить Tarantool за 3 месяца и выжить: учимся на ошибках DevOps-инженера On-premise vs облако: на что компаниям выгоднее тратить деньги Создание системы документирования, или как мы от «ворда» к docs as code за месяц переходили Инженер-программист, исследователь