Оверкоммит CPU и памяти: где предел безопасности
Если вы держите свой гипервизор — Proxmox, голый KVM, ESXi — рано или поздно встаёт вопрос: можно ли раздать виртуалкам суммарно больше vCPU и памяти, чем физически есть на хосте. Формально ответ «да, так делают все», но у CPU и у RAM это работает принципиально по-разному, и путаница между ними — частая причина, почему один и тот же термин «оверкоммит» у одного администратора означает безобидную экономию, а у другого — ночной звонок из-за упавшего хоста целиком. Разберём оба случая по отдельности, с механикой отказа и конкретными границами.
Содержание
Что такое оверкоммит и почему это не жадность, а расчёт
Оверкоммит ресурсов виртуализации — это выделение виртуальным машинам в сумме больше vCPU или RAM, чем физически доступно на хосте, в расчёте на то, что не все VM используют выделенное на 100% одновременно. Это тот же принцип, что лежит в основе оверселлинга на стороне хостинг-провайдера — оверселлинг в цифрах подробно разбирает, как это выглядит со стороны арендатора VPS, когда провайдер продаёт больше ресурсов, чем есть физически. Разница здесь в том, что вы сами себе хостинг-провайдер: это ваш собственный хост виртуализации, вы сами решаете степень оверкоммита и сами расхлёбываете последствия, если просчитались.
Экономика та же: если резервировать ресурсы 1:1 под пиковую нагрузку каждой VM, большая часть железа будет простаивать бо́льшую часть времени — сайт с редкими визитами, тестовый контур, бот, просыпающийся раз в минуту, почти никогда не используют выделенное им на полную. Оверкоммит размазывает эту простаивающую мощность между всеми VM на хосте и позволяет разместить больше машин на том же железе. Проблема не в самом факте оверкоммита, а в том, что CPU и память ведут себя при превышении лимита совершенно по-разному — и если применять к памяти ту же логику, что и к процессору, легко получить каскадный отказ вместо плавной деградации.
Оверкоммит CPU: почему деградация обычно плавная
vCPU — это не физическое ядро, закреплённое за машиной, а квота процессорного времени, которую гипервизор выдаёт через планировщик (в KVM это обычно cgroups: cpu.cfs_quota_us и cpu.shares на каждую VM). Когда вы выделяете, скажем, 4 vCPU каждой из десяти VM на хосте с 16 физическими ядрами, вы формально раздали 40 vCPU на 16 ядер — оверкоммит на бумаге налицо. Но пока не все десять VM одновременно и полностью грузят свои 4 ядра, планировщик хоста просто перераспределяет свободное процессорное время между теми, кому оно сейчас нужно.
Когда наступает момент, что все VM разом упираются в CPU, механизм деградации предсказуем: планировщик хоста начинает делить физическое время каждого ядра между несколькими vCPU, которые его хотят получить, — каждая VM получает пропорционально меньше времени, чем ей выделено номинально. Это не отказ, а линейное замедление: если на ядро претендуют вдвое больше vCPU, чем оно может обслужить одновременно, каждая VM в среднем получает примерно вдвое меньше времени в моменты пиковой конкуренции. Изнутри гостевой ОС это видно напрямую через рост показателя steal time (%st в top или vmstat) — метрику, которая существует именно для этого случая. Подробный разбор того, как её читать и отличать от обычной загрузки, — в статье steal time: как понять, что сосед ест ваш процессор; там же механика планировщика описана со стороны гостя, здесь стоит зафиксировать главное для владельца хоста: рост steal time на нескольких VM одновременно в часы пиковой нагрузки — это прямой сигнал, что степень оверкоммита CPU уже задевает продакшен, а не запас на будущее.
Проверить конкуренцию за CPU можно и снаружи, со стороны хоста:
# суммарная загрузка физических ядер хоста
mpstat -P ALL 1
# по каждой VM в libvirt/KVM
virsh dommemstat <vm-name>
virsh cpu-stats <vm-name>
# в Proxmox — вкладка VM -> Summary, график CPU usage хоста в целом
Если суммарная загрузка физических ядер хоста стабильно упирается в потолок в рабочие часы, а не только в редкие пики, — оверкоммит по CPU уже выбран слишком агрессивно для этого набора нагрузок, независимо от того, что написано в тарифах VM.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверПрактический предел для CPU: не число, а сигнал
Универсального коэффициента «сколько vCPU на одно физическое ядро безопасно» не существует — он полностью зависит от профиля нагрузки. Компилятор, который грузит ядро на 100% на протяжении часа, и веб-сервер, который просыпается на десятки миллисекунд на запрос и потом простаивает, требуют разного подхода даже при одинаковом номинальном числе vCPU. Смешивать на одном хосте вычислительно тяжёлые VM (рендеринг, ML-инференс, СУБД под нагрузкой) с оверкоммитом почти так же рискованно, как оверселлить их у стороннего провайдера — для таких VM разумно держать коэффициент, близкий к 1:1 по CPU, либо использовать CPU pinning (закрепление конкретных физических ядер за конкретной VM), которое вообще выводит их из общего пула планировщика.
Для смешанных нагрузок с редкими одновременными пиками (десятки лёгких VM: сайты с невысокой посещаемостью, тестовые контуры, боты, внутренние утилиты) более высокий оверкоммит по CPU — обычная практика, и типичное консервативное правило здесь заметно мягче, чем для вычислительных нагрузок. Но конкретное соотношение — вопрос эмпирики на вашем наборе VM, а не таблицы из статьи: начинайте с умеренного оверкоммита, снимайте steal time под реальной пиковой нагрузкой (не в статике, а в часы максимума), и увеличивайте плотность только пока метрика остаётся в пределах, приемлемых для ваших SLA. Если steal time начал расти месяц к месяцу при том же числе VM — это не повод для паники, а сигнал остановить добавление новых машин на этот хост и планировать следующий.
Оверкоммит памяти: другая природа риска
Здесь принципиальное отличие от CPU: механизм отказа не плавный, а пороговый. Пока сумма реально используемой (не просто выделенной) памяти всех VM на хосте меньше физической RAM хоста, всё работает штатно — часть выделенной, но не используемой памяти просто не занята физически. Проблема начинается в момент, когда сумма реально используемой памяти превышает физическую RAM: тогда хосту физически некуда девать данные гостевых VM, и он вынужден задействовать своп на диск — то есть выгружать страницы памяти гостевых машин на диск хоста и подгружать обратно по мере обращения.
Своп на уровне хоста для гостевой памяти — это на порядки более медленная операция, чем обращение к RAM: там, где обращение к памяти занимает наносекунды, обращение к диску под свопом — миллисекунды, разница в тысячи раз. И критично: как только хост начинает активно свопить, страдает не только та VM, что превысила свой лимит, — деградация I/O и планировщика затрагивает ВСЕ VM на этом хосте одновременно, потому что своп конкурирует за тот же дисковый контроллер и ту же шину памяти, которыми пользуются все машины сразу. Это принципиально другая картина отказа по сравнению с CPU: там нехватка ресурса делится честно и пропорционально, здесь превышение порога одной или несколькими VM может обрушить производительность всего хоста разом.
Похожий по духу, но частный случай этой проблемы внутри одной VM (когда своп подменяет собой нехватку памяти вместо апгрейда) разобран в статье антипаттерн: swap вместо нормальной памяти — там про гостевую машину, а не хост, но механика деградации от свопа (thrashing) та же самая, только на уровне хоста последствия шире. Если своп хоста разрастётся настолько, что начнёт давить и на память самого гипервизора, ядро хоста может дойти и до OOM killer — какой процесс он убьёт первым и как это предсказать, разобрано в статье как ядро выбирает жертву OOM killer; для хоста виртуализации жертвой в худшем случае может стать процесс самого гипервизора или демон управления (libvirtd, qemu-процесс конкретной VM), а не безобидный тестовый скрипт.
Отдельно стоит сказать про механизмы, которые смягчают, но не отменяют риск оверкоммита памяти:
- Memory ballooning (virtio-balloon) — гипервизор просит гостевую ОС «вернуть» часть памяти, которую она сама считает неиспользуемой, и передаёт её другой VM. Работает только если гость кооперативен и действительно не использует эту память в моменте — не спасает, если все VM разом реально нуждаются в своей памяти.
- KSM (Kernel Samepage Merging) — дедупликация одинаковых страниц памяти между VM на хосте: если у десятка машин один и тот же образ ОС в памяти, ядро хоста может держать одну физическую копию вместо десяти. Эффективность сильно зависит от того, насколько похожи гостевые системы, и заранее посчитать выигрыш от KSM почти невозможно — это бонус, а не гарантия, и полагаться на него как на основу для расчёта оверкоммита не стоит.
Проверить состояние KSM на хосте:
cat /sys/kernel/mm/ksm/pages_shared
cat /sys/kernel/mm/ksm/pages_sharing
cat /sys/kernel/mm/ksm/pages_volatile
Рост pages_sharing показывает реальную экономию памяти от дедупликации — но это диагностика постфактум, а не число, которое можно заложить в план ёмкости заранее.
Безопасные границы для памяти и обязательный мониторинг
Для критичных продакшн-нагрузок разумный подход к памяти — держать оверкоммит близким к отсутствию вовсе, то есть выделять VM памяти примерно столько же, сколько физически заложено на хост, с небольшим запасом на служебные процессы самого гипервизора (обычно достаточно нескольких гигабайт под сам хост, точная цифра зависит от числа VM и используемых механизмов вроде KSM). Это осторожнее, чем для CPU, ровно потому, что цена ошибки другая: неправильно посчитанный оверкоммит CPU даёт заметные, но локальные тормоза, а неправильно посчитанный оверкоммит памяти может положить производительность всех VM на хосте разом, причём резко и без плавного предупреждения.
Если оверкоммит памяти всё же нужен — например, на хосте с однотипными VM из одного шаблона, где KSM даёт предсказуемую экономию, или на тестовом/staging-контуре, где VM заведомо не выйдут на пик одновременно, — критично мониторить не сумму выделенного (это статичная цифра из конфигов, которая ничего не говорит о реальности), а фактическое использование памяти хостом в целом:
# общая картина по хосту: занято, доступно, своп
free -h
# si/so — активность свопа хоста в реальном времени (в идеале — стабильный ноль)
vmstat 1
# сколько памяти реально доступно без свопинга прямо сейчас
grep MemAvailable /proc/meminfo
# память по каждой VM с точки зрения хоста (реальное потребление процесса qemu)
ps aux --sort=-%mem | grep qemu
Показатели si/so в vmstat, стабильно отличные от нуля, — это уже не предупреждение, а свершившийся факт: хост свопит гостевую память прямо сейчас. К этому моменту деградация уже заметна пользователям всех VM на хосте, поэтому порог для алертов стоит ставить заметно раньше — по MemAvailable и по общему проценту занятой RAM хоста, а не дожидаться реальной активности si/so. Если вы отключаете balloon-драйвер ради предсказуемости (в Proxmox это флаг balloon: 0 в конфиге VM), закладывайте память ещё консервативнее — без ballooning гипервизор не может подвинуть память между VM «на лету», и жёстко выделенная сумма должна укладываться в физику хоста без запаса на динамическое перераспределение.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Можно ли вообще не оверкоммитить память, а оверкоммитить только CPU?
Да, и для многих хостов с продакшн-нагрузкой это разумный компромисс: CPU оверкоммитить умеренно и с мониторингом steal time, а память резервировать почти впритык к физической, без расчёта на то, что VM «не используют всё выделенное».
Своп хоста — это то же самое, что своп внутри гостевой VM?
Нет, это разные уровни. Своп внутри гостя решает нехватку памяти конкретно в этой VM и касается только её. Своп хоста возникает, когда гипервизору не хватает физической RAM под все гостевые VM разом, и задевает все VM на хосте одновременно — именно поэтому он опаснее.
KSM гарантированно спасает от нехватки памяти при оверкоммите?
Нет, это дедупликация одинаковых страниц, а не резервный пул памяти. Она снижает фактическое потребление, если гостевые системы похожи, но не даёт формальной гарантии и не должна быть единственным основанием для расчёта степени оверкоммита.
Если steal time невысокий, значит оверкоммит CPU настроен правильно?
Это хороший знак на текущий момент, но не гарантия на будущее — снимайте метрику именно в часы пиковой нагрузки, а не в среднем по суткам, и повторяйте проверку по мере добавления новых VM на тот же хост.
Что произойдёт раньше — рост steal time или проблемы с памятью, если наращивать плотность VM бездумно?
Обычно раньше проявляется CPU: рост steal time заметен постепенно и даёт время среагировать. Память может выглядеть нормально до самого порога, а затем деградация наступает резко — поэтому мониторинг именно памяти должен быть настроен консервативнее и с более ранними алертами.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →