Настройка дисковой подсистемы Qemu KVM для увеличения быстродействия в Ubuntu по сравнению с Hyper-V


Иногда я берусь за различные задачи по настройке серверов. Некоторое время назад ко мне обратился владелец небольшой хостинговой компании, с интересной проблемой. Он хотел бы на своих серверах, где уже стоял Ubuntu 18.04, запускать виртуальные машины с Windows под KVM.

Однако проведённое им тестирование показало, что дисковая система KVM прилично отставала от показателей, которые у него были под Hyper-V. Он хотел раскочегарить qemu на своих Ubuntu серверах, чтобы избежать закупок дорогих серверных лицензий Windows.

Также мы решили что нам нужно избежать привязывания железа к виртуальной машине, так как это осложняет переносимость виртуальных машин, поэтому варианты с прокидыванием SSD в виртуалки были признанны нежелательными.

Для тестирования использовался SSD Samsung 970 Pro 1TB. Заказчик проверял результаты работы в CrystalDiskMark, поэтому далее в статье все графики из него.

Windows 10 LTSC Hyper-V
2 CPU
KVM
2 CPU

Начнём с того, что в Ubuntu (16.04 LTS и 18.04) до сих пор используется qemu версии 2.11. Поэтому некоторые плюшки самых последних qemu в этой статье не рассмотрены.

1. Используем LVM-тома, а не файлы для хранения дисков виртуальных машин.


Логика такая. Файл с виртуальным диском хранится в файловой системе Linux, внутри самого файла находится NTFS. Каждая файловая система потребляет ресурсы при дисковых операциях. Поэтому чем меньше глубина матрёшки, тем быстрее ввод/вывод.

Слой LVM потребляет значительно меньше процессорных ресурсов, чем файловая система. Одна из причин этого то, что блоки в нём значительно больше, чем типичный блок файловой системы (4КБ). Чем больше блок (extent) на физическом устройстве LVM, тем более быстро происходит IO и тем меньше фрагментация.

А ведь даже для SSD случайный ввод/вывод значительно более медленнее, чем последовательный.

LVM довольно удобно использовать для хостинга виртуальных машин. Тем более, что virt-manager (libvirt) умеет из коробки создавать логические тома под диски виртуальных машин.

Поэтому при создании Volume Group мы укажем очень большую величину extent: 256MB.

Привлекательной выглядит также возможность создать thin volumes («тонкие» тома), но если учесть, что тонкий том — это дополнительный слой абстракции, то, очевидно, что он будет ухудшать производительность IO. К тому же в libvirt нет элегантного пути автоматического создания дисков для виртуальных машин в «тонком» пуле.

Read ahead на логическом томе стоит отключить, так как он расходует IO без выигрыша, так как теперь никто не занимается дефрагментацией дисков в Windows на SSD.

# Инициализируем физический раздел SSD для группы томов (volume group)
pvcreate /dev/nvme1n1p1

# Создаёт группу том win с размером блока (extent) 256МБ.
vgcreate -s 256M win_pool /dev/nvme1n1p1

# Создаём логический том vm1. Подойдёт для диска C
lvcreate -n vm1 -L 100G -c 4m

# Отключаем упреждающее кеширование при чтении (read ahead)
lvchange -r none /dev/win_pool/vm1


1.1. Thin volume как диск и/или настройки логических томов для снимков


Если вы хотите использовать thin pool, в котором будете создавать тонкие тома, то имеет смысл установить размер чанка у пула в 4МБ, что значительно больше размера по-умолчанию в 64КБ.
Что повлечёт более быструю работу этого слоя абстракции.

Механизм снимков в LVM работает на тех же самых механизмах, что и тонкие тома, поэтому для увеличения скорости снимков (snapshot) настройки будет теми же самыми.

lvcreate -c 4m ....

Или в lvm.conf или подобном файле (например lvmlocal.conf):

thin_pool_chunk_size = 4096

Вы можете сами определить оптимальный размер чанка выполнив тестирование следующей командой, подобрав величину --blocksize:

fio --name=randwrite --filename=/dev/nvme0n1p9 --size=10G --ioengine=libaio --iodepth=1 --buffered=0 --direct=1 --rw=randwrite --blocksize=4m

2. Увеличение количества логических процессоров, выделяемых на каждую виртуальную машину KVM, улучшает дисковую производительность.


10 CPU 8 CPU 4 CPU

Понятно, что вряд ли кто будет выделять на виртуалку 10 процессоров, но было интересно посмотреть на крайний случай.

Тут уже зависит от числа свободных процессоров. На мой взгляд больше 4-х выделять нецелесообразно. При числе потоков равному 8 мы получили максимальную производительность случайного чтения и записи. Это специфика CrystalDiskMark 6.0.2, в котором второй тест проводит 8-ю потоками.

Из чего можно сделать вывод, что хорошо иметь по одному логическому процессору на каждую задачу, активно использующую IO.

3. Используем огромные страницы оперативной памяти, чтобы избежать снижения производительности из-за фрагментации RAM.


Это пакет может пригодиться, когда нам понадобиться различная информацию о hugepages в процессе эксплуатации.

apt install hugepages

Правим /etc/default/grub:

GRUB_CMDLINE_LINUX="default_hugepagesz=1GB hugepagesz=1G hugepages=64"


В данном случае было выделено 64ГБ памяти для всех виртуальных машин в виде hugepages. В вашем случае может быть меньше/больше.

Применяем эти настройки для GRUB, чтобы при следующей загрузке системы они стали активны:

grub-mkconfig -o /boot/grub/grub.cfg


Редактируем конфиг виртуальной машины:

virsh edit vm_name

Добавляем:

<memoryBacking>
  <hugepages/>
</memoryBacking>


4. Добавляем в каждую виртуальную машину выделенный поток для обслуживания IO.



Нужно добавить то, что выделено жирным. Используем virsh, как и в предыдущем пункте.

<iothreads>1</iothreads>

<disk type='block' device='disk'>
<driver name='qemu' type='raw' cache='none' io='threads' iothread='1'/>
<source dev='/dev/win/terminal'/>
<target dev='vda' bus='virtio'/>
<boot order='2'/>
<address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
</disk>


4.1. Для ускорения случайной записи используем кеширование writeback.



Для ускорения случайной записи на диск, но с увеличением риска потери данных, можно использовать cache=writeback в предыдущем пункте. Можно использовать только если есть большая уверенность в качестве и резервировании питания и при наличии бакапов.

5. Настройки дисковой подсистемы в В Virt Manager.


Disk bus: VirtIO
Storage format: raw
Cache mode: writeback
IO mode: threads

5.1. Настройка дисковой подсистемы через конфигурационный файл.


Qemu 2.11 (который сейчас используется в Ubuntu) поддерживает для типа дисковых виртуальных устройств: virtio-blk и virtio-scsi. Когда в Virt Manager указывается Disk bus: VirtIO — это означает использование устройства virtio-blk.

Во всех случаях по скорости лучше virtio-blk. Использовать virtio-scsi имеет смысл в экзотических случаях, например, когда нужно подключать сотни дисков к виртуальной машине.

6. В процессе инсталляции Windows устанавливаем драйвер VirtIO.


Иначе диск не будет доступен для ОС. Для этого используем образ с драйверами, который предварительно подключаем к виртуальной машине.

7. Результаты после применения всех твиков.


На самом деле твик 4.1 не был применён, так как я не был уверен в надёжности питания у клиента.
Hyper-V
2 CPU
KVM
2 CPU
KVM
4 CPU

Нужно понимать, что эти результаты грешат некой условностью, так как при каждом запуске CrystalDiskMark значения немного отличаются.

KVM из коробки
2 CPU
KVM после твиков
2 CPU

Мы видим, что удалось существенно ускорить работу дисковой подсистемы в qemu (kvm) при одном и том же числе ядер. Запись была ускорена в среднем на 58%, а чтение на 25%.

Основные элементы успеха: использование LVM-томов вместо файлов qcow2, отдельный поток ввода/вывода и hugepages.

Замеченные ошибки направляйте в личку. Повышаю за это карму.

P.S. vhost-user-blk и vhost-user-nvme


В ходе экспериментов были также и скомпилированы Qemu 2.12 и 3-й версии. Тестировалась опция vhost-user-blk для диска.

В итоге она работала хуже, чем virtio-blk.

vhost-user-blk
4 CPU
virtio-blk
4 CPU

Для использования vhost-user-nvme нужно было патчить qemu, этот вариант усложнял автоматическое обновление серверов в продакшене, поэтому не был протестирован.

P.P.S. SPDK


Intel разработала этот фреймворк для достижения выдающих показателей производительности у дисковых систем в виртуальных машинах, которые должны работать на процессорах Intel.

Чтобы заставить spdk хорошо работать идут на массу ухищрений — выделяют ему отдельные ядра, размещают ядра spdk и виртуальной машины в одном сокете. Загружают виртуальную машину в непрерывный кусок памяти. Если такие меры применять к обычному virtio-blk, то он тоже будет быстрее работать.

SPDK способен работать в 3-х режимах: vhost-user-scsi, vhost-user-blk и vhost-user-nvme. Второй режим доступен только в qemu от 2.12, который ещё не доступен в ubuntu. Режим vhost-user-nvme вообще мегаэкспериментальный — для него нужно патчить qemu. На текущий момент работает только эмуляция scsi и она медленная.

Есть ещё одно серьёзное ограничение для режима vhost-user-scsi — диск spdk не может быть загрузочным.
Make sure bootindex=2 Qemu option is given to vhost-user-scsi-pci device.

Рекорды ставятся, когда они с помощью своего драйвера разделяют SSD на несколько устройств и прокидывают их как vhost-user-nvme. Подход с прокидыванием железа нам не подходил.

Сложилось впечатление, что нормально использовать SPDK можно только с их реализацией логических дисков (которая совершенно отличается от стандартных lvm). Это такой самопальный велосипед со своими снимками и клонированием. Команды все отличаются от LVM.

Сложность в настройке SPDK, поддержке и переносимости виртуальных машин, а также привязанность к процессорам Intel отвернула от его использования.

Благодарности


За изображение спасибо TripletConcept. Его лучше смотреть в полном размере в отдельном окне.
За разрешение поделиться рабочими материалами — st_neon



Вы можете заказать виртуальную машину с SSD у RUVDS по купону ниже.



Замеченные ошибки направляйте в личку. Повышаю за это карму.