ZFS и KVM. @home

в 16:49, , рубрики: iscsi, Steam, Ubuntu, zfs, системное администрирование

ZFS и KVM. @home - 1

Символика эта и так на слуху, потому нет смысла подробно объяснять, что это за зверь.

Здесь не будет сравнения с LVM, ибо сравнивать muscle-car с jet-truck'ом хоть и можно, но бессмысленно.
Графиков и комиксов так же не завезли.
Это, скажем так, незавершенная история успеха, потому что апгрейд, которым она была вызвана, можно лишь прекратить, но не завершить.

Предпосылки

Практически в каждой айтишной семье присутствует несколько ПК и/или ноутбуков, наверняка имеется перероутер-HTPC, а то и вовсе какой-нибудь NAS. Вот и у нас было два ПК, потом добавился большой настенный монитор, а при таком раскладе рождение HTPC — вопрос времени. Когда он появился на свет, тут же был наречен гордым именем — Сервер. Позже выяснилось, что чаcть любимых игр умеет в Linux — и Сервер тут же обзавелся аккаунтом Steam.

Так прошло несколько лет, в течение которых Сервер получил 16ГБ памяти, на нем появились виртуалки с gitlab и проектами...

А потом пришло понимание, что назревает апгрейд, и апгрейдить сразу 3 ПК слишком накладно; в то же время, уже был успешный опыт с PCIE-passthrough и игровой виртуалкой.

Подходящим решением стала поэтапная миграция игровых машин в виртуалки. Пока смигрирована только одна, в дальнейшем под гостем я буду подразумевать именно её.
Из трех процессоров (i3-3220, i5-3470, i5- 3470K) VT-d поддерживал только второй, он и ушел в Сервер вместо i3. Была куплена низкопрофильная 1050Ti на замену 7970, а старое железо продано по частям.

Сервер получил последнюю UEFI-прошивку, Ubuntu 16.04 и самый большой из имевшихся SSD — Crucial M4, на 256 ГБ, под корневой пул. Установка проводилась по этому манулу, из которого был накопипащен в live-системе установочный скрипт. Потом, уже после успешной загрузки, были доустановлены virt-manager, libvirt-bin, ovmf и xubuntu-desktop. Разумеется, были включены VT-x и VT-d.

Кстати, отделение бинарной части системы от логов, кешей и прочих хомяков не раз помогало в процессе вивисекции системы экспериментов, позволяя вернуть рабочее состояние простым zfs rollback, доступным даже из initrd (но есть особенность: если вы используете отдельный датасет для /root — используйте параметр ядра init=/bin/sh для аварийной загрузки; sh, в отличие от bash, не будет создавать мусор в точке монтирования /root).
Чтобы в случае проблем с загрузкой не гадать, что же случилось, в /etc/default/grub закомментируйте все, что касается HIDDEN_TIMEOUT, установите явно таймаут отличный от 0 (у меня 10), раскомментируйте/добавьте GRUB_TERMINAL=console и обязательно уберите quiet splash из GRUB_CMDLINE.

Error-43 и все-все-все

  • первоначальная конфигурация
    UEFI на хосте и госте оказались залогом безболезненного проброса и работы видеокарты, но для этого видеокарта должна иметь полностью UEFI-совместимую прошивку. Мне повезло с этим, и старая референсная 7970 и новая 1050Ti были совместимы. Для создания UEFI-гостя потребуется пакет ovmf, также (если будете создавать гостя с помощью virt-manager) необходимо поставить галочку на последней странице мастера выбрать и выбрать UEFI в качестве микрокода и Q35 в качестве чипсета. На моей системе без выполнения этих действий гостевая система имела странные проблемы с замираниями и заиканием звука.
  • проброс устройств
    Для проброса устройств необходимо указать ядру использовать специальный драйвер — vfio-pci или pci-stub (уже устарел) для определенных id vfio-pci.ids=10de:1c82,10de:0fb9,8086:1e31 (у меня проброшены сама видеокарта, ее звук и четырехпортовый USB3-контроллер) и разрешить использование iommu intel_iommu=on (на AMD прописываются другие параметры, но за неимением врать не буду). Также для проброса видеокарты понадобится явно запретить ее использование видеодрайвером, так как пробрасывалась nvidia и проприетарный драйвер установлен не был, то у меня это выглядит так: modprobe.blacklist=nouveau nouveau.modeset=0. Все эти параметры прописываются в файле /etc/default/grub, в строке GRUB_CMDLINE. Не забудьте выполнить update-grub и перезапуститься перед добавлением подготовленных к пробросу устройств в гостя.
  • nVidia, или "ничего личного, это просто бизнес".
    К сожалению, в этой жизни ничего не получается просто и сразу, вот и меня ждала печально известная Error 43. Трюк с cpu=host хоть и полезен, но не помог. К счастью, оказалось достаточно воспользоваться virsh edit <guestname>, и в секции <features>/<hyperv> добавить строчку <vendor_id state='on' value='ASUS'/>. Но в свою очередь, libvirt из репозитория (включая backports) слишком протух и не понимал этого параметра. После нескольких неудачных попыток собрать и установить более свежую версию, я обратился к ppa, и у Jacob Zimmermann нашелся подходящий пакет. Рекомендую, найдя стабильную версию, не обновлять драйвера на видеркарту в госте, ввиду постоянной мутации бизнес-бага с пробросом.

Использование ZFS

  • LXC
    Тут все очень просто — создаем датасет (например, так: zfs create rpool/lxc), запускаем lxc init, выбираем тип хранилища ZFS, отказываемся от создания пула, соглашаемся на использование датасета, вводим имя созданого чуть ранее датасета. Сейчас там живут gitlab, samba-dc (с ключем --usentvfs, на самбе старше 4.3 не заведется), пара мелких проектов.
  • Дедупликация, сжатие и блоки.
    Для предоставления гостям блочных устройств я использую zvol — его сложно случайно удалить, просто создавать индивидуальные снимки. Но у zvol есть одна хитрая особенность: если он не разреженый (ключ -s при создании), то снимок будет занимать столько же места, сколько и оригинал.

    При использовании ZFS есть два способа сэкономить место на диске — это сжатие и дедупликация. И если со сжатием все понятно, то дедупликацию стоит разобрать поподробнее.

    Накладные расходы на дедупликацию составляют от ~200 до ~700 байт в памяти на реальных системах (подробнее можно посмотреть в выводе zdb -D <poolname>), а DDT (таблица дедупликации), размер которой зависит напрямую от количества уникальных блоков, должна храниться в памяти целиком; и это значит, что использовать дедупликацию с маленьким размером блока попросту невыгодно (кроме, разве что, фермы виртуалок с dedup_ratio намного большим единицы и титаническим количеством оперативной памяти). Кроме того, с маленьким размером блока (это касается в первую очередь zvol) невозможно использовать сжатие, отличное от zle.

    Стоит ли говорить, что размер блока zvol должен совпадать с размером кластера расположенной на нем ФС, а сама она должна быть выровнена по размеру блока (современные утилиты давно выранивают по границе 1МБ, чего более чем достаточно)?

    Получается, использование больших блоков благоприятно сказывается на сжатии, дедупликации и накладных расходах по памяти, при этом сжатие еще и позволяет минимизировать оверхэд от больших блоков в ФС гостя.

    Так что, все zvol я создаю командой zfs create -V <size> -s -b 64K <zfs path>, добавляя -o dedup=on и -o compression=lz4 по необходимости.

  • 64КБ — наш выбор.
    Осталось научить гостя загружаться и работать с ФС с таким размером кластера. В случае с Linux применение ext4, например, с таким размером кластера невозможно — ее просто не удастся смонтировать. Однако, манипулируя параметрами монтирования stride и stripe-width можно добиться желаемого поведения.

    В случае с NTFS все хитрее и проще одновременно: Windows создает служебные разделы, содержимое которых меняется крайне редко, поэтому на размер их кластера можно не обращать внимания (а у загрузочного раздела еще и не удастся поменять — ни bootmgr ни ntldr не понимают размер кластера больше 4КБ). Если после выбора нераспределенного места нажать не кнопку "Установить", а "Создать", то установщик создаст и отформатирует все нужные разделы, а у вас появится номер диска и номер раздела, в который будет установлена гостевая система.

    Далее понадобится немного черной магии:

    • чертим пентаграмму, не забывая на каждое перекрестье линий установить по одной черной однажды уже горевшей свече
    • вызываем дремора рангом не ниже кинвал cmd (нажимаем Shift-F10)
    • запускаем diskpart, и далее в нем
    • select disk <номер диска>
    • select partition <номер раздела>
    • format FS=NTFS UNIT=64K QUICK
    • assign LETTER=S
    • покидаем diskpart
    • в cmd выполняем mkdir s:windows.

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

Что это было?

 Ну, системный диск отформатирован с размером кластера 64КБ, и Windows внушено, что на диске уже есть ее инсталляция, поэтому форматировать его с настройками по умолчанию - не нужно. Грязный хак? Ну да, так в этом же суть построения Windows-систем: заставить ее делать то, что нужно, а не то, что ей хочется.

Дедуплицировать Steam, или с чего все и начиналось

Самый простой способ был завести smb или nfs шары и создать на них библиотеки Steam. Обычные файлы легко и естественно дедуплицируются, включая как экспортированные библиотеки, так и библиотеку Linux-клиента, без лишних телодвижений. Как и всегда, самое очевидное решение оказалось в принципе рабочим, но неправильным. SMB в таком режиме шевелится со скоростью ноутбучного жесткого диска, несмотря на то, что файлы лежат на SSD. NFS (а Windows 8.1 поддерживает NFS v3, не 4) шевелился чуть шустрее, по ощущениям как какой-нибудь "зеленый" десктопный диск, но этого все равно было слишком мало для комфортной игры. К тому же, все ОС от M$ обожают терять сетевые диски, автоматически подключаемые при логоне, и каждый раз указывать Steam на местоположение библиотеки быстро надоест.

Подходящим решением стало iSCSI. На Сервер был установлен targetcli, настроен по этому манулу с поправкой на использование blockio и путем вида /dev/zvols/<zfs path>. Для правильной работы unmap нужно использовать Windows 8+ (начиная с 8 реализована поддержка unmap для iSCSI-инициатора), а для каждого блочного устройства прописать set attributes emulate_3pc=1,emulate_tpu=1,emulate_caw=1,emulate_tpws=1,emulate_tas=1. Если все сделано правильно, то и удаление файлов и форматирование должно освобождать место и сокращать размер zvol.

Теперь одинаковые файлы в экспортируемых под библиотеки томах занимают место лишь однажды, однако библиотека Steam для Linux все равно занимает отдельное место. Создадим отдельный датасет под эту библиотеку с опциями -o dedup=on -o recordsize=64K. Если разделы в zvol выровнены по границе 1МБ, и размер кластера установлен в 64КБ, то логично, что делить файлы на хосте надо с той же гранулярностью, чтобы дедупликация могла найти одинаковые блоки.

Последствия

  • 16ГБ памяти, имеющихся у меня дома, таки маловато для гостя с 8ГБ, firefox на хосте, контейнеров и прочего — время от времени наступают странные подтормаживания, сопровождающиеся интенсивной дисковой активностью.
  • дедуплицированное хранилище лучше сделать отдельным пулом.
  • установка zfs set logbias=throughput <pool> значительно влияет на производительность, особенно при использовании дедупликации. Мало того, что при этом исключается дополнительный цикл запись-в-лог/стирание-лога, так еще и перестает вымываться кеш записи накопителя.
  • на гигабитной сети zvol на SSD ощущается намного быстрее жесткого диска, хотя заметно медленнее локального SSD
  • я задумался о 960EVO на 1ТБ

Вместо заключения

Что ж, мой маленький эксперимент завершился несомненным успехом, хотя на пути возникли трудности. Далее хотелось бы апгрейд на Zen2, 32-64ГБ памяти, NVME SSD, перенос второго ПК на Сервер… но это уже будет совсем иное колдунство.

Автор: khajiit

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js