понедельник, 2 августа 2010 г.

Memory management

В полете из солнечной Москвы в прохладный Баку
у меня появилось вдохновение для написания сего текста.
Лично мне он понравился.
Вот у нас есть сервер ESX(i), для простоты один. У его есть сколько-то оперативной памяти для виртуальных машин (“Доступная память”), и на нем работает сколько-то виртуальных машин. Этим виртуальным машинам выделено сколько-то памяти (“Показанная память” ), и они сколько-то этой памяти потребляют (“Потребляемая память” ). Что можно рассказать про это?


Несколько общих слов


Доступная память – это все гигабайты памяти сервера минус:
  1. Память, которую гипервизор тратит на себя. ESXi создает в памяти RAM диск для своей работы. ESX отрезает 300-800 мегабайт для Service Console. Виртуальным коммутаторам, iSCSI инициатору и прочим компонентам так же нужны ресурсы для своей работы.
  2. Память, которую гипервизор тратит для создания процесса виртуальной машины. Overhead, говоря в терминах счетчиков нагрузки. Когда мы создаем виртуалку и “показываем” ей гигабайт памяти, гипервизор ей выдает часть или 100% этого гигабайта. И даже в последнем случае еще 100-150 мегабайт тратит на оверхед.
  3. Еще HA может резервировать сколько-то памяти под свои нужды.
Показанная память – это тот объем памяти, который мы указываем в настройках ВМ, на закладке Hardware. Именно этот объем видит гостевая ОС. Это максимум, который гостевая ОС может использовать. Однако, гипервизор может выделить для ВМ из реальной оперативки и меньший объем, чем “показал” ей памяти. То, что гостю выделено лишь, например, 400 мегабайт из показанного гигабайта изнутри не заметить.  По каким причинам гипервизор будет так подло поступать поговорим чуть позже.
Потребляемая память – это сколько реальной оперативной памяти использует виртуальная машина. Или, правильнее сказать, какой объем памяти выделил ей гипервизор. В  терминах мониторинга это Consumed.


Memory Overcomitment


Все мы наслышаны про чудесный (без иронии) “Memory Overcomitment”. Что это? Это ситуация, когда “Показанная память” всех ВМ на сервере больше чем “Доступная память”. То есть мы “показали” нашим виртуальным машинам больше памяти, чем есть на сервере. Кстати говоря, и это важно, “Потребляемая память” в такой ситуации может быть как меньше (хороший случай), так и больше чем память “Доступная” (плохой случай).
Иллюстрация “хорошего” случая:
image
Memory Overcomitment достигается на ESX(i) за счет нескольких технологий. Перечислим их:
  • выделение по запросу;
  • transparent memory page sharing:
  • balloon driver или его еще можно обозвать vmmemctl;
  • memory compression (новинка 4.1);
  • vmkernel swap.
Что это? Каково место этих технологий? Насколько они офигенны? Насколько они бесполезны? Давайте поразмышляем.


Вводная к размышлениям


Мы будем рассуждать о потреблении памяти виртуальными машинами, а точнее – гостевыми ОС и приложениями.
Основная идея в чем – “показанная” серверу (тут неважно – физическому или виртуальному) оперативная память никогда не загружена на 100%. Почему? У вас загружена именно так? Значит вы хреново спланировали этот сервер, согласны?

Утверждение 1 – мы должны стремиться к тому. что виртуальные машины не должны все время потреблять 100% от “показанной” им памяти. Таким образом, прочие соображения я буду основывать на том, что у вас именно так. То, что некоторые задачи занимают чем-то всю свободную память – здесь не учитываем, так как разговор идет в общем.

Утверждение 2 – в разное время суток\дни недели наши приложения потребляют память по разному. Большинство задач хотят ресурсов в рабочие часы в будни, с пиками утром или в середине дня или <подставьте данные своей статистики>. Однако бывают приложения с нетипичными для нашей инфраструктуры профилем потребления памяти.

Утверждение 3 – в вашей инфраструктуре сделан запас по оперативной памяти серверов, то есть “доступной” памяти. Этот запас делается из следующих соображений:

  • архитектор боится промахнуться с необходимым объемом. промахнуться в смысле “продать меньше памяти чем потребуется”;
  • как запас на случай отказа сервера (или нескольких);
  • как запас на случай роста виртуальных машин или нагрузки на существующие виртуальные машины.

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

  • ВМ приложений – сколько памяти вы выделяете (“показываете” в моих определениях тут) своим серверам приложений? При опросах на курсах я слышу цифры 4-8, редко больше.  Вернее, для малого числа ВМ больше. Большинство таких приложений потребляет ресурсы в рабочие часы, однако бывают и исключения (например, сервера резервного копирования, работающие по ночам);
  • Инфраструктурные ВМ – всякие DNS, DC, и т.п. Обычно гигабайт или два. Потребляют ресурсов мало, пики если вообще есть – в рабочие часы;
  • Тестовые ВМ – думаю, гигабайт или два в среднем по больнице, и больше по требованию смотря что тестировать будем. Пики в рабочие часы, где-то бывает куча тестовых виртуалок, простаивающих подавляюще большую часть времени (как крайний случай – кто-то создал и забросил, а удалить виртуалку страшно – вдруг кому нужна).
Давайте теперь рассмотрим эти группы виртуальных машин в контексте механизмов работы с памятью.


Выделение по запросу


То, чего нет у других. Ну или я не знаю что есть. (помните, пост про vDiva – “Вы считаете себя нереально крутым спецом по виртуализации, хотя ни разу в жизни не видели Xen, Hyper-V или KVM”).

Как это работает
Виртуальной машине выделили (“показали”) два гигабайта. Виртуальную машину включили. Что происходит дальше?
Этап 1 – включение. Часто мне приходится слышать о том, что при старте гость забивает нулями всю память – уважаемые читатели, кто в теме просветите меня – так ли это на самом деле, мои изыскания привели к противоречивым выводам. Допустим это так – потому что это самый плохой случай – ведь гость на старте делает вид, что ему нужна вся “показанная” ему память. Ок, гипервизор ему всю выдает. Итак, на этапе 1 “потребляемая” память равна “показанной”, в самом плохом случае.
Этап 2 – гость стартовал все службы, службы начали работать, создавать нагрузку. Но не сто процентов памяти потребляется, см. утверждение 1. Например, 1200 мегабайт из выделенных 2000. То есть гость 800 мегабайт пометил у себя в таблице памяти как “свободная”. По хорошему, гипервизору надо бы ее забрать. Он и забирает, для этого используется механизм balloon(!). Т.е. одна из задач балон-драйвера (подробности о нем см. ниже) – это отбирать ранее выданною, но сейчас ненужную гостю память. Итак, балон раздулся, затем сразу сдулся. Что получилось – гостю 1200 мегабайт нужно, поэтому они балоном или не отнялись, или сразу вернулись обратно. Но больше памяти гостю, обычно, не нужно – и он больше не просит. А раз не просит, гипервизор ему больше и не дает.

Насколько часто это применяется к разным группам виртуальных машин
Работает этот механизм всегда, когда виртуальная машина потребляет не 100% “показанной” памяти.  То есть всегда, кроме первых минут после включения и редких пиков, этот механизм работает, и заметно экономит нам память.
Если виртуальная машина не потребляет всю память часто – механизм очень полезен. Для некоторых тестовых – 100% времени. Для производственных серверов – как минимум ночью. А если часть серверов нагружается в другое время суток чем оставшаяся часть – вообще шоколадно. Для инфраструктурных – иногда бОльшую часть времени.

Эффект на производительность
Если и есть негативный, то не должен быть большим.

transparent memory page sharing


Технология с самым сложно звучащим  названием.

Как это работает
Гипервизор считает контрольные суммы, хеши, страниц оперативной памяти. Находит одинаковые (для одной или нескольких виртуальных машин) страницы. И одинаковые страницы из разных “виртуальных таблиц памяти” адресует в одну единственную страницу в памяти реальной. Получается, на пустом месте гипервизор делает “потребляемую” память меньше чем она могла бы быть без данного механизма. Ну про “”на пустом месте” я конечно соврал  – гипервизор тратит ресурсы процессоров сервера на подсчет контрольных сумм. Но с учетом того, что, как правило, ресурсов процессора у нас дофига, этот обмен нам очень выгоден.
Иллюстрация:
image

Насколько часто это применяется к разным группам виртуальных машин
Сложный вопрос. Сложность во все более широко используемом механизме Large Pages. Если у вас Windows 7/2008 + Nehalem, и используются страницы памяти по 2 МБ, теория гласит что эффект от page sharing будет маленьким. Хотя в реальности там довольно сложный алгоритм:
ESX(i) разбивает 2 МБ страницу на 4 КБ, считает их хеши, но не использует механизм page sharing пока памяти хватает. А вот если перестало хватать – перед тем как включать какой-то из механизмов подкачки начинает делать sharing этих маленьких 4 КБ кусков.
Что говорит практика по эффективности и безболезненности такого подхода– у меня пока мнения не сложилось.
А если у вас железо или софт постарее, или эти большие страницы принудительно отключены – эффект обычно офигенный.

Эффект на производительность
Именно производительность негативного эффекта не испытывает. Тем более что ESX(i) знает, что такое архитектура NUMA, и если сервер у нас этой архитектуры, то дедупликация страниц памяти идет внутри каждого одного  NUMA узла независимо, чтобы виртуалке не приходилось за отдельными, дедуплицированными страницами лазать в память другого процессора.
Однако в теории накладные расходы имеют место быть – если какая-то ВМ хочет изменить разделяемую с другими страницу памяти, с помощью технологии Copy-on-write делается копия страницы, которая и отдается приватно данной ВМ. Это медленнее, чем просто дать записать в неразделяемую страницу. Насколько заметен эффект в реальной жизни – сказать очень сложно.
Официально данные вот какие:
image
За единицу выбраны данные тестов с отключенным page sharing. Как видно, средний столбец – со включенным page shring и параметрами по умолчанию, не хуже, а иногда лучше(!) по производительности.Улучшение связывают с тем, что memory footprint у виртуалки становится меньше, и лучше помещается в кэш (видимо, процессорный?).
Крайние тесты – это число транзакций в секунду, компилирование ядра - время.


UPD. от июня 2011 - отключение Large Pages позволяет сэкономить треть ОЗУ, есть такое мнение - TPS vs. Large Pages in real life.

balloon driver / vmmemctl


Самая радостная для русского уха технология.

Как это работает
Один из компонентов VMware tools – это драйвер устройства vmmemctl. По команде ESX(i) он начинает запрашивать у гостевой ОС память, так же как это делают приложения гостевой ОС. Т.е. этот драйвер раздувает тот самый “баллон” внутри, отнимая память у гостя.
Зачем это надо? Для решения двух задач.
Первая уже описана выше – если гостю были выделены страницы памяти, которые он уже перестал использовать, то гипервизор не может такие свободные страницы отличить и забрать. А раздувшемуся внутри “баллону” гость сам их отдаст в первую очередь, и затем не попросит у гипервизора их обратно. Страницы памяти, занятые “баллоном”, гипервизор отличит, и перестанет их адресовать для виртуальной машины, которой они были адресованы до сего момента. Посмотрите на иллюстрацию ниже – страницы памяти, гостем для себя помеченные как “свободные”, помечены звездочками слева. А справа, в следующем состоянии описываемого механизма, они уже не занимают железную память сервера.
Иллюстрация:
 
image
Вторая задача – когда гостю выделено, например, два гигабайта, а один гигабайт из них надо отнять. Характерный пример – когда памяти сервера перестало хватать резко увеличившемуся числу виртуальных машин. А число их резко увеличилось из-за сбоя одного из серверов, и рестарта его ВМ на другом.
Так вот, у ВМ надо отнять память. Гипервизор раздувает баллон, гость начинает свопировать (в свой собственный файл\раздел подкачки. То есть balloon – это способ гипервизору задействовать механизм подкачки гостя). Реальную память, теперь занятую баллоном(с точки зрения гостя), гипервизор отдает другой ВМ. Кстати, в моем примере гипервизор отдаст команду отнять гигабайт. А весь ли гигабайт баллон отнимет? Может быть, и не весь – гость вполне может отказать ему в памяти, если сочтет что другим компонентам память нужнее. Если баллон не справился, то гипервизор доотнимет оставшееся с помощью memory compression и vmkernel swap.

Насколько часто это применяется к разным группам виртуальных машин
Первая задача – отнимание незанятой памяти, стабильно актуальна для виртуальных машин любой группы.
Вторая задача – мне видится мало актуальной. Давайте разберем ее слегка подробнее.
Итак, из чего может взяться ситуация, когда у одной ВМ память надо отнять, чтобы отдать другой? Я вижу несколько вариантов:

  1. когда у нас memory overcommitment. и сразу у многих ВМ наступили пики нагрузки, что привело к загрузке сервера на 100%. Это, кстати говоря, причина пользоваться MO аккуратно. Впрочем, совсем отказываться от MO, как призывает нас делать Майкрософт, по моему мнению, тоже не стоит.
  2. когда у нас ломается один из серверов кластера HA. Например, у нас 10 серверов, все загружены по памяти(днем, в будни) процентов на 70. Вы знаете, что HA все упавшие ВМ перезапустит на одном из оставшихся серверов? Т.е. виртуалкам потребуется 140% его памяти.

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

    Далее ситуация разветвляется. Если у вас есть DRS, то он быстренько разнесет виртуалки по разным серверам, и баллону работать не придется.
    А вот если DRS нет, или он не в автоматическом режиме – вот тут мы приходим к нехватке на всех физической памяти. И гипервизор отнимет память у части машин с помощью баллона.
    У многих из вас нет DRS?
    У тех, у кого таки нет DRS – часто у вас ломаются серверы?
    Наконец, даже наличие баллона что означает – что запустятся все виртуалки, но всем будет хреново от нехватки памяти (правда, какие-то виртуальные машины могут “остаться на коне”, за счет опускания других ВМ в еще большее свопирование – см. такие настройки как reservation и shares).
Отсюда вывод – наличие баллона это несомненный плюс для решения задачи один, но не панацея для решения задачи два.

Эффект на производительность
Для задачи один незаметный – отнимается только ненужная память.
Для задачи два разный – см. далее.

memory compression


Новинка, этот механизм появился только в 4.1.

Как это работает
Гипервизор использует часть памяти под создание эдакого кэша. Теперь, когда встает задача два из описания баллона – впихнуть ВМ в меньший чем им хочется объем памяти, часть их памяти будет сжиматься и помещаться в ранее зарезервированную область.
Идея в том, что сжать и записать данные в память , или прочитать и разжать – быстрее чем без сжатия читать или писать с диска, из файла подкачки.
Иллюстрация:
image
Состояние “а” – надо выгрузить две страницы памяти из реальной оперативки.
Состояние “b” – решение вопроса “по старинке”, свопированием.
Состояние “с” – модерновое решение вопроса. Страницы сжаты и помещены в ранее зарезервированную область памяти сервера.
Обратите внимание, что память под этот кэш не резервируется на сервере заранее – это часть памяти самой ВМ. Т.е. когда для ВМ перестает хватать памяти, гипервизор отнимает у нее еще немного, и в этом немного размещает сжатые страницы ее памяти.

Насколько часто это применяется к разным группам виртуальных машин
Редко – как я выше писал, при адекватном планировании и наличии DRS почти никогда.

Эффект на производительность
См. далее.

vmkernel swap


Последняя надежда.

Как это работает
При включении виртуальной машины гипервизор создает файл .vswp – файл подкачки vmkernel для этой ВМ.
Теперь, когда гипервизору надо забрать часть памяти у виртуальной машины, он может просто из части страниц скопировать содержимое в данный файл, и перенаправлять обращения ВМ к памяти в файл. Гость ничего про это знать не будет.
Будет ли гипервизор пользовать vmkernel swap, а не balloon или memory compression? См. ниже.

Насколько часто это применяется к разным группам виртуальных машин
См. описание второй задачи для баллона – отнять и поделить. Я думаю, что такая задача встает редко.
Плюс vmkernel swap в том, что он работает всегда. Гипервизору нет дела ни до чего – ни до наличия vmware tools и vmmemctl в госте, ни до чего другого – задействовать vmkernel swap можно всегда.
Минус – тормоза будут значительными.

Эффект на производительность
См. далее.

Нехватка памяти на всех – какой механизм будет использован?


У ESX(i) есть счетчик – процент свободной памяти.
Его пороговые значения это 6% (high, типа все ок), 4% (soft, типа маловато осталось), 2% (hard, пипец как мало осталось, атас!), и 1%(low, все, приехали).
  • Free RAM>=6%. Пока свободной памяти остается не меньше 6 процентов (состояние high) задействуется только page sharing.
  • Free RAM<6%. Свободной памяти меньше чем хотелось бы. Начинаем использовать balloon.
  • Free RAM<4%. Свободной памяти мало. Используем еще и compression и vmk swap.
  • Free RAM<1%. Полный пипец. В доке вот что написано: “..в дополнение к balloon, swap и compression, гипервизор делает еще and additionally blocks the execution of all virtual machines that consume more memory than their target memory allocations”. Я пока не понял что это значит.
Balloon и vmkernel swap и memory compression делают одну и ту же работу. Но ballon делает ее более оптимально – позволяет гостю самому выбрать что класть в своп. Данные тестов:
image
Крайняя правая позиция – когда из 512 мегабайт памяти у гостя оставалось только 128. Красная линия – скорость компиляции когда до 384 мегабайта памяти отнимается только balloon, зеленая – только vmkernel swap.
Специфика компиляции в том, что самому процессу память особо не нужна – основной объем занят под кэш данных. Так вот, в случае balloon гость понимал, что в своп лучше положить сначала данные, чем ядро ОС. А в случае vmkernel swap такой выбор сделать нельзя, и в своп идет “что-то”.
А вот данные при похожих условиях для базы данных Oracle, нагруженной соответствующей утилитой:
image
Обратите внимание – пока balloon отнимал меньше чем 1792 мегабайта из 3840, производительность практически не падала. Это опять же специфика приложения, но приложения характерного.
А вот для каких-то приложений разницы не будет:
image
И в начальном диапазоне vmkernel swap даже меньше негатива оказывает на производительность ВМ. Впрочем, процент приложений вот так использующих память весьма мал. Здесь использовалась бенчмарка SPECjbb.
А вот для Exchange разница кардинальна:
image
Если отнять 9 из 12 гигабайт памяти баллоном, то это даст удвоение latency, в то время как отнятие двух гигабайт из двенадцати при помощи vmkernel swap – утридцетитворение.

Таким образом, если свопирование неизбежно, balloon это меньшее зло.
Однако, еще разок замечу – по моему мнению, вам редко придется оказаться в ситуации, когда balloon будет работать из за нехватки памяти. И даже если такая ситуация случится, balloon даст снижение производительности, просто почти всегда меньшее снижение, чем использование vmkernel swap.
А теперь сравним это еще и с compression:
image
image

Вкратце – compression не панацея, но если уж памяти жестко не хватает, то с compression лучше чем без него (только с vmkernel swap, если быть точным). Нагрузка на процессоры увеличивается на 1-2%, что неощутимо.
Все.

P.S.

За кадром остались limit, reservation и shares. Первым пользоваться для памяти не надо, вторым – лишь для самых требовательных к памяти ВМ, третьим – в двух словах не скажешь.
Первоисточник картинок и данных тестирования - Understanding Memory Resource Management in VMware ESX 4.1.
Я тут сделал несколько утверждений – буду рад комментариям.

28 комментариев:

  1. ммм память :) Какой отличный пост!

    ОтветитьУдалить
  2. Как работает резервирование памяти тут - http://blog.vadmin.ru/2010/03/cpu.html

    ОтветитьУдалить
  3. Монументальненько!
    Скопировал в свою локальную KB для перечитывания и осмысления. Респект!

    С уважением,
    Umlyaut.

    P.S. Михаил, насчёт эпиграфа... если мне не изменяет мой склероз родом ещё из советских времён, то Баку всегда употреблялся в мужском роде. :)

    ОтветитьУдалить
  4. Кстати, здесь один из моментов, которым пользуется MS для раздувания FUD'а.

    В терминах VMware: Overcommitment = Memory Granted > Physical Memory. Здесь мы имеем довольно широкую полосу, когда даже ballon = 0. За счет выделения памяти только по запросу и TPS.

    В терминах MS: Overcommitment = Swap > 0, а все, что до - это Oversubscribe.

    ОтветитьУдалить
  5. да, что такое МО имеет смысл проговаривать подробно, есть такое дело.

    ОтветитьУдалить
  6. Отличная статья, Спасибо!

    ОтветитьУдалить
  7. А почему не надо пользовать Limit?

    ОтветитьУдалить
  8. Респект Михаилу за доступное изложение! а то на буржуйском не всегда всё понятно. Побольше бы подобных статей))
    Skyrod7

    ОтветитьУдалить
  9. 2Анонимный - потому что для объяснения в двух словах это так.
    в реальности есть всего полтора случая, когда limit по памяти имеют смысл.
    Самый адекватный случай - когда для пула с тестовыми машинками limit ставим, чтобы они бесконтрольно не плодились, вернее чтобы при бесконтрольном размножении всю память не отъедали.

    2Skyrod7 спасибо

    ОтветитьУдалить
  10. 2michigun
    Ну я обычно так и делаю...а то уже испугался, что не то что-то делаю :)
    Статья очень зачётная! Респект!

    ОтветитьУдалить
  11. Да, Михаил забыл упомянуть, что по умолчанию baloon не может взять у геста больше 65% от памяти.
    Skyrod7

    ОтветитьУдалить
  12. да я специально не упомянул - ну и что из за этого?

    ОтветитьУдалить
  13. Для полноты картины)
    Skyrod7

    ОтветитьУдалить
  14. Очень,очень правильная статья !
    Как говорится - респект и уважуха !:)

    По поводу Limit - жуткая вешь: зарезает память машины на уровне Hypervisora и заставляет свопиться независимо от того,сколько ресурсов есть и проскакивая все предыдущие способы ограничения. В итоге - SWAP и тормоз

    ОтветитьУдалить
  15. Забыл добавить - сама машина вообше ничерта не знает о том,что её пишут на диск и ничего не может с этим поделать.

    ОтветитьУдалить
  16. "при старте гость забивает нулями всю память" - с чего бы гостю это делать? Зануляет память BIOS, когда тестирует память при включении. Виртуальный BIOS в гостевой системе естественно этого не будет делать, сама же гостевая ОС инициализирует при старте только нужные ей участки памяти.

    ОтветитьУдалить
  17. Отличная статья, спасибо!

    Практический вопрос к всем: что по вашему мнению лучше - уместить все виртуалки в память или выделить им больше памяти (с оверкомитментом)?
    Предполагаем, что машины одни и те же и без оверкомитмента они могут работать. Так вот - будет ли смысл увеличить для всех на 20-25% объем сконфигурированной памяти?

    Николай.

    ОтветитьУдалить
  18. Всегда есть возможность увеличения количества ВМ, так что OM рулит
    Skyrod7

    ОтветитьУдалить
  19. 2Николай
    лучше чтобы всем ВМ было выделено сколько надо (не больше не меньше)памяти, и памяти сервера для всех ВМ хватало.

    :)

    ОтветитьУдалить
  20. Аркадий, это не так. При выставлении Limit надувается баллон ровно на разницу Configured и Limit, и заставляет свопиться машину.

    Своп ESX в данном случае не используется.

    ОтветитьУдалить
  21. Не совсем так, наверное - если consumes меньше чем limit - вряд-ли даже баллон будет раздуваться.

    ОтветитьУдалить
  22. Есть такой момент что например windows 7 и к примеру freebsd используют механизм контроля памяти они забирают всю свободную память и контролируют ее сами. То есть если не установленны vmware tools то механизм не будет работать и гостевая система захватит всю память. Ну да ладно вроде все понятно статья крутая. Механиз для к примеру vmware view очень нужный я считаю да и вообще для VDI в целом.

    ОтветитьУдалить