Смысл в том, что все виртуальные блочные устройства ZFS на одной физической машине объединяются в общий пул потоков ZVOL. И если у вас 2 пула - один на быстрых NVME, а второй - на медленных HDD, запросы к ним будут сливаться в общий поток. При нагрузке на пул с HDD и выстраивании очереди запросов к нему, в эту же очередь встают и запросы на пулы с остальными дисками. В итоге, сильно нагрузив ZVOL на одном медленном пуле, вы получаете задержки с ZVOL на других, более быстрых пулах.
На практике это выглядит так. Ночью вы запускаете бэкап большой ВМ, виртуальный диск которой находится в ZVOL на медленном HDD-пуле. При интенсивном чтении этого диска рабочие потоки ZVOL могут оказаться заняты, из-за чего увеличиваются задержки запросов к виртуальным дискам на NVMe, хотя сами NVMe практически не загружены.
Я лично похожую схему использования дисков применяю повсеместно. Если сервера общего назначения, то так и делаю - быстрые диски под нагрузки, а большие медленные - под локальные бэкапы, чтобы можно было оперативно восстановиться в случае чего, не вытягивая тяжёлые бэкапы по сети, либо под локальное хранение статики.
Другое дело, что я в таких схемах не использую ZFS. Под рабочей нагрузкой мне не нужны возможности ZFS, как и для локальных бэкапов. Поэтому лично я с такой проблемой не сталкивался. ZFS обычно ставлю на бэкап серверы с кучей дисков. Там я вижу использование этой ФС оправданной и полезной. В остальных случаях, если не нужны снэпшоты именно в реализации ZFS, не вижу смысла её использовать.
Автор решил проблему в лоб самым простым и очевидным способом - вынес NVME диски на LVM-thin, отделив их от общего пула потоков ZFS. Это сразу решило проблему узкого горлышка.
Я немного поспрашивал ИИ на эту тему. Он подтвердил, что это архитектурная особенность ZFS, которую он увидел в исходниках. Там же есть и комментарии разработчиков на этот счёт:
We can have up to zvol_threads concurrent i/o's being processed for all zvols on the system. This is typically a vast improvement over the zvol_request_sync=1 behavior of one i/o at a time per zvol. However, an even better design would be for zvol_request() to initiate the zio directly, and then be notified by the zio_done callback, which would call END_IO().
Каким-то образом обыграть эту ситуацию можно, меняя параметры:
zvol_request_sync = 0 или 1◽️ 0 - асинхронная обработка через общий пул потоков ZVOL.
◽️ 1 - обработка непосредственно в контексте вызывающего потока, без передачи в общий пул ZVOL.
Казалось бы, второй вариант решает проблему конкуренции между пулами. Но разработчики предупреждают, что такая схема может существенно ухудшить параллелизм и производительность i/o. Поэтому использовать её без тестирования не стоит.
Можно увеличить кол-во потоков
zvol_threads, но всё равно нет гарантии, что все эти потоки не будут заняты медленным пулом. Ещё вариант - изменить механизм очереди -
zvol_use_blk_mq = 1. Но тут надо тестировать на практике, потому что потоки будут по-прежнему в одном общем пуле, изменится распределение. Не факт, что при нагрузке на медленный пул, запросы к быстрому не будут вставать в очередь.Если от ZFS отказываться не хочется, а проблема настройками не решается, то я бы замедлил скорость снятия бэкапов программно, чтобы уменьшить очередь к медленным дискам. Теоретически, это в той или иной мере должно решить проблему.
В общем, как я всегда говорил и говорю, ZFS - нишевая файловая система под конкретные задачи и потребности. У неё много возможностей, нюансов и настроек. Ставить её по умолчанию, как ФС общего назначения, не стоит. Преимущества вам могут оказаться не нужны, а все особенности и недостатки вы по умолчанию потянете за собой.
❗️Если заметка вам полезна, не забудьте 👍 и забрать в закладки.
———
ServerAdmin: 📱 Telegram | 🌐 Сайт | 📲 MAX 😩
#pve #zfs
