В процессе тюннинга производительности VDI инфраструктуры для одного из заказчиков, задался вопросом выравнивания машин по NUMA нодам для более быстрого доступа к памяти и как следствие — увеличение общей производительности системы.
В качестве платформы используется Citrix XenServer, который по моему скромному мнению является немного отсталым, но все же приходится выжимать из него максимум. К сожалению, эта статья скорее не руководство к действию а констатация печальной действительности.
Не буду долго распинаться о том, что такое NUMA, информации полно в самых разных источниках и если вы не знаете что это — то скорее всего оно вам и не надо. В контексте виртуализации NUMA позволяет размещать vCPU и RAM выделенные виртуальной машине в рамках одной NUMA ноды для более быстрого доступа к памяти, что снижает overhead и позволяет повысить производительность самой ВМ.
На двухсокетных машинах чаще всего 2 NUMA ноды, в каждой из которых 1 сокет и половина от общего количества RAM (если память установлена правильно). Однако, по умолчанию XenServer не пытается разместить машины в пределах ноды и раскидывает vCPU динамично между всеми физическими ядрами сервера. Данный подход более универсальный, т.к. позволяет более равномерно распределить нагрузку, однако бывают случаи, когда на сервере работает множество одинаковых машин, при этом относительно равномерно нагруженных. Один из таких случаев — VDI, а как мы знаем, XenServer как раз чаще всего и позиционируется как гипервизор для VDI решений…
Распределение vCPU работающих на гипервизоре машин между физическими ядрами можно посмотреть с помощью команды:
# xl vcpu-list
Что бы заставить XenServer различать NUMA ноды, можно использовать утилиту cpupool. Для начала посмотрим существующие пулы:
# xl cpupool-list -c Name CPU list Pool-0 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39
Как видим, по умолчанию все физические ядра в одном пуле. Их можно разделить вручную, как душе пожелается, или автоматически разбить на несколько пулов:
# xl cpupool-numa-split
После чего видим, как встроенный интеллект гипервизора разделил общее количество ядер на 2 пула:
# xl cpupool-list -c Name CPU list Pool-0 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 Pool-1 20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39
«Круто!! Теперь виртуальная машина, работающая на хосте будет работать в пределах одной NUMA ноды и не будет далеко бегать за паматью!!» — скажете вы, и это правда, НО есть несколько нюансов, которые все портят.
Во-первых, конфигурация пулов не сохраняется после перезагрузки, но это не прям аж так страшно, теоретически, можно автоматизировать их создание скриптами при загрузке.
Во-вторых, при запуске новых машин на хосте, по умолчанию они будут запускаться только в первом пуле (Pool0), что без ручного вмешательства приведет к тому, что 2-й пул будет простаивать. Решения здесь вроде как два, но по факту оно одно:
- можно перемещать руками запущенные машины между пулами, однако, насколько я понял, память за ними не переедет и это может только ухудшить производительность ВМ
- остается только вариант — руками прописывать в настройке машины в каком пуле она должна запускаться тем самым нарываясь на проблемы с грамотным распределением нагрузки
Ни один ни другой вариант не является приемлемым при большом количестве машин, что сводит всю затею на нет. Однако, если у вас 2-3 хоста и десяток виртуальных машин — можно попробовать что-то из этого выжать.
Я же в итоге от этого отказался, т.к. ~800 XenApp виртуальных машин мониторить руками будет сложновато :)
Я зарекся ждать какого-то существенного прогресса от Citrix в плане XenServer-а, поэтому не думаю что в ближайшем будущем что-то изменится, но поживем — увидим.
Буду рад, если кто-то поделится в комментариях опытом применения подобного тюнинга на практике, желательно в цифрах и статистике, очень интересно насколько это выгодно.