Живая миграция виртуалок: что происходит с памятью в процессе
Когда виртуалку переносят с одного физического узла на другой без единой секунды простоя, в голове сразу возникает вопрос: а как же память? Ведь в ней прямо сейчас крутится состояние приложения — открытые соединения, кэши, данные, которые ничем, кроме RAM, не зафиксированы. Наивная интуиция подсказывает, что для переноса такого объёма данных VM обязана хотя бы на мгновение остановиться. На практике всё устроено хитрее: гипервизор копирует память работающей машины итеративно, догоняя её же собственные изменения, и останавливает VM лишь на финальную долю секунды. Разберём этот механизм по шагам — вместе с тем, где он ломается на практике.
Содержание
Зачем вообще нужна живая миграция
Живая миграция (live migration) — это перенос работающей виртуальной машины с одного физического сервера на другой без остановки гостевой ОС и без разрыва сетевых соединений клиента (не считая той самой финальной паузы в доли секунды). Задача возникает регулярно:
- плановое обслуживание узла — обновление прошивки, замена диска, перезагрузка хост-сервера после патча ядра;
- балансировка нагрузки между узлами кластера — если один физический сервер перегружен, а соседний простаивает;
- вывод оборудования из эксплуатации — переезд VM на новое железо перед списанием старого;
- реакция на предупреждение о деградации железа (SMART-ошибки диска, ECC-коррекции памяти) — снять VM с проблемного узла заранее, не дожидаясь аварии.
Без живой миграции любая из этих задач означала бы выключение VM, перенос образа диска, включение на новом узле — простой от нескольких минут до часов в зависимости от объёма диска. С живой миграцией простой сокращается до времени финальной паузы — обычно от долей секунды до нескольких секунд.
Важно сразу разделить два процесса, которые часто путают:
- Перенос диска — если диск VM лежит на локальном хранилище исходного узла (а не на общем NAS/SAN, видимом обоим узлам), его тоже нужно скопировать. Это отдельная, куда более медленная и объёмная задача, которую обычно решают через блочную репликацию (drive mirroring) параллельно с миграцией памяти, либо просто требуют общее хранилище для живой миграции.
- Перенос памяти — то, о чём эта статья. Именно память определяет, сколько времени займёт миграция и почему процесс не может быть мгновенным.
Дальше говорим только о памяти, считая, что диск VM уже виден обоим узлам (типичный сценарий для кластеров на shared storage).
Стандартный подход: pre-copy migration
Стандартный механизм живой миграции в KVM/QEMU (и концептуально похожий в других гипервизорах — VMware vMotion, Xen, Hyper-V) называется pre-copy migration — копирование памяти происходит до переключения VM на новый узел, а не после. Логика в четыре шага:
Шаг 1. VM продолжает работать на исходном узле. Гостевая ОС и все процессы внутри неё не останавливаются и не замечают, что где-то в фоне что-то происходит. QEMU на исходном узле начинает читать всю оперативную память VM постранично (обычно страницы по 4 КБ) и передавать её по сети на целевой узел, где QEMU в это же время уже запущен в специальном режиме приёма миграции.
Шаг 2. Память "грязнится" во время копирования. Здесь и кроется весь фокус. Пока идёт передача, скажем, 16 ГБ памяти по сети — а на это уходит время, зависящее от скорости канала между узлами — VM не стоит на месте: она продолжает писать в свою память. Страница, которая уже была скопирована минуту назад, может быть перезаписана прямо сейчас. Такие страницы называют dirty pages ("грязными") — их скопированная копия на целевом узле уже устарела.
Чтобы отслеживать это, гипервизор использует механизм отслеживания записи в память — на уровне таблиц страниц процессора помечает страницы как "только для чтения" и ловит page fault при попытке записи (или использует более новые аппаратные механизмы вроде dirty page logging), формируя постоянно обновляемый список "какие страницы изменились с момента последнего снимка".
Шаг 3. Итеративное копирование дельты. После того как первый полный проход по всей памяти завершён, QEMU не останавливает VM — вместо этого он смотрит на список "грязных" страниц, накопившийся за время первого прохода, и копирует только их. Поскольку это уже не весь объём памяти, а лишь та часть, что реально менялась, этот второй проход обычно занимает заметно меньше времени, чем первый.
Но пока идёт и этот второй проход, VM опять продолжает работать и опять что-то меняет в памяти — включая, возможно, часть тех страниц, которые только что были повторно скопированы. Поэтому процесс повторяется: третья итерация копирует дельту, накопившуюся за время второй, четвёртая — дельту за время третьей, и так далее.
С каждой итерацией — при нормальном, не экстремальном профиле нагрузки — объём "грязных" страниц обычно сокращается, потому что каждая итерация занимает всё меньше времени, а значит VM успевает "нагрязнить" всё меньше новых данных. Это классическая сходящаяся геометрическая прогрессия: первая итерация — все 16 ГБ, вторая — условно 1,5 ГБ, третья — 300 МБ, четвёртая — 80 МБ и так далее, пока дельта не станет достаточно маленькой.
Шаг 4. Финальная пауза и переключение. Когда объём оставшихся "грязных" страниц падает ниже порога (гипервизор сравнивает время, нужное на копирование текущей дельты, с настраиваемым допустимым временем простоя — обычно порядка десятков-сотен миллисекунд, точное значение настраивается), происходит решающий момент:
- VM на исходном узле приостанавливается — процессор гостевой ОС буквально замирает;
- копируется последняя, уже совсем небольшая дельта "грязных" страниц, а также состояние CPU (регистры, флаги) и состояние устройств (сетевые карты, диски, таймеры);
- VM на целевом узле возобновляет выполнение с той же самой точки, где остановилась исходная;
- исходная копия VM на старом узле уничтожается.
Именно этот момент — единственная реальная пауза во всём процессе. Она может длиться от долей секунды до нескольких секунд — зависит от скорости сети и от того, насколько интенсивно менялась память VM непосредственно перед переключением.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНаглядно: как выглядит сходимость по итерациям
Условный пример для VM с 16 ГБ RAM и сетью 10 Гбит/с между узлами (числа ориентировочные, у вас будет иначе — реальная скорость зависит от профиля нагрузки конкретного приложения):
| Итерация | Объём для копирования | Примерное время |
|---|---|---|
| 1 (полная) | 16 ГБ | ~15-20 сек |
| 2 | ~1,2 ГБ (дельта за время итерации 1) | ~1-1.5 сек |
| 3 | ~150 МБ | ~150 мс |
| 4 | ~30 МБ | ~30 мс |
| 5 (финал) | ~8 МБ | пауза VM, копирование, переключение |
Обратите внимание: после итерации 1 время резко падает, потому что дельта — это уже не весь объём памяти, а лишь изменения за относительно короткий промежуток. Дальше прогрессия обычно продолжает сходиться, пока не станет настолько маленькой, что гипервизор решает: дешевле поставить VM на паузу и добить остаток, чем продолжать бесконечно гоняться за новыми "грязными" страницами.
Когда процесс не сходится: риск для высоконагруженных VM
Вся эта красивая сходящаяся прогрессия работает, только если скорость "загрязнения" памяти со временем отстаёт от скорости копирования. Если VM пишет в память настолько интенсивно и постоянно, что за время каждой итерации успевает "нагрязнить" почти столько же данных, сколько было скопировано — прогрессия перестаёт сходиться или сходится крайне медленно.
Типичный кандидат на такую проблему — база данных под высокой нагрузкой (особенно с большим буферным кэшем и активной записью — PostgreSQL/MySQL с высоким TPS, Redis с интенсивной записью, любая in-memory аналитика). Если приложение непрерывно меняет большую часть своего рабочего набора памяти, а не узкую "горячую" область, dirty rate может держаться стабильно высоким итерация за итерацией.
Последствия для такого сценария — два возможных исхода, оба неприятные:
- Миграция сильно затягивается. Гипервизор продолжает гонять итерации, пытаясь дождаться, пока дельта опустится ниже порога, но она не опускается или опускается очень медленно. Миграция, которая должна была занять минуту, может растянуться на много минут, потребляя сетевой канал всё это время.
- Финальная пауза оказывается заметно длиннее ожидаемой. Некоторые гипервизоры по истечении заданного числа итераций или времени принудительно переходят к финальной остановке независимо от того, сошлась ли дельта до маленького размера — тогда сама пауза (шаг 4) может занять не привычные доли секунды, а секунды или даже больше, потому что копируется не маленький "хвост", а всё ещё довольно большой остаток "грязных" страниц.
Это не теоретический риск, а обычная практика: если вы планируете живую миграцию VM с базой данных под серьёзной нагрузкой записи, стоит либо запланировать миграцию на период минимальной нагрузки, либо заранее смириться с тем, что пауза будет длиннее типичной, либо (для действительно критичных случаев) держать такую VM вообще без живой миграции — на выделенном узле, который не планируется трогать.
Есть и настраиваемый предохранитель — многие гипервизоры позволяют задать жёсткий лимит на допустимое время финальной паузы (max downtime) и/или лимит на пропускную способность, отводимую под миграцию. Но это компромисс: чем строже лимит на паузу, тем выше риск, что миграция вообще не завершится при высоком dirty rate, и наоборот.
Требование к сети между узлами
Отдельный фактор, никак не связанный с паттерном нагрузки VM, — банальная пропускная способность канала между исходным и целевым узлом. Даже для VM с абсолютно спокойной, почти не меняющейся памятью (простаивающий веб-сервер, statless-приложение) первая, полная итерация копирования должна передать весь объём RAM VM целиком. Для VM с 32 ГБ памяти по каналу 1 Гбит/с это уже несколько минут одной только первой итерации, а для канала 10 Гбит/с — секунды.
Если сеть между узлами медленная или перегружена посторонним трафиком, страдают оба параметра сразу: и время первой полной итерации (просто дольше передавать те же гигабайты), и способность догнать dirty rate на последующих итерациях (даже умеренно нагруженная VM может "перегрязнять" память быстрее, чем медленный канал успевает её вычитывать). Поэтому в продуманных кластерах живую миграцию часто выносят на отдельный, выделенный сетевой интерфейс, не разделяемый с обычным трафиком VM — это не всегда возможно на всех платформах, но там, где возможно, ощутимо снижает и время миграции, и риск затянутой финальной паузы.
Практическое следствие для тех, кто арендует инфраструктуру, а не строит её с нуля: если провайдер предлагает живую миграцию как часть обслуживания (например, при плановых работах на узле), стоит поинтересоваться, на каком канале она выполняется и есть ли изоляция от трафика клиентов — это прямо влияет на то, насколько заметной будет пауза именно для вашей нагрузки.
Как это выглядит на практике в KVM/QEMU
Для тех, кто управляет своей инфраструктурой на KVM через libvirt, команда живой миграции выглядит примерно так:
virsh migrate --live --persistent \
--undefinesource \
vm-name \
qemu+ssh://target-host/system
Ключевые флаги:
--live— собственно живая миграция (без него будет offline-перенос с остановкой VM);--persistent— сохранить определение VM на целевом узле после миграции;--undefinesource— удалить определение VM с исходного узла после успешного переноса.
Настроить лимит на допустимое время финальной паузы и пропускную способность миграции можно так:
# Максимальное время простоя на финальном шаге — 300 мс
virsh migrate-setmaxdowntime vm-name 300
# Ограничить миграцию 500 Мбит/с, чтобы не забивать канал
virsh migrate-setspeed vm-name 500
Следить за прогрессом и понимать, сходится ли dirty rate, можно через:
virsh domjobinfo vm-name
В выводе смотрите на поля Data remaining (сколько ещё осталось передать в текущей итерации) и Memory iteration — если Data remaining от итерации к итерации не падает или падает незначительно, это ранний признак того, что миграция не сходится, и стоит либо снизить нагрузку на VM, либо прервать миграцию и перепланировать её.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Приложение внутри VM вообще замечает живую миграцию?
В подавляющем большинстве случаев нет — сетевые соединения не рвутся (MAC/IP-адрес сохраняется, ARP-объявление на новом узле актуализирует таблицы коммутаторов), процессы продолжают работать с той же памятью. Единственное, что теоретически можно заметить изнутри VM — небольшой скачок задержки в момент финальной паузы, обычно незаметный для большинства приложений, но потенциально ощутимый для задач с жёсткими требованиями к таймингам (аудио/видео реального времени, некоторые высокочастотные торговые системы).
Что будет, если во время миграции пропадёт сеть между узлами?
Миграция прерывается с ошибкой, VM остаётся работать на исходном узле как ни в чём не бывало — pre-copy миграция спроектирована так, что до самого последнего шага исходная копия остаётся полностью рабочей и актуальной. Риск потери VM в процессе минимален именно благодаря этому: неудачная попытка почти всегда откатывается на исходный узел, а не оставляет VM в подвешенном состоянии.
Живая миграция переносит и содержимое диска тоже?
Нет, если диск на общем хранилище (NAS/SAN), видимом обоим узлам — тогда переносится только память и состояние CPU/устройств, а диск физически никуда не копируется, просто меняется узел, который к нему обращается. Если диск локальный, нужен отдельный механизм блочной репликации, который на порядок медленнее и объёмнее переноса памяти.
Можно ли ускорить сходимость итераций принудительно?
Один из способов — временно снизить интенсивность записи в память VM (например, приостановить фоновые batch-задачи в БД на время миграции) или использовать функцию auto-converge, которую поддерживают некоторые гипервизоры — она сама притормаживает выполнение гостевой VM на время миграции, искусственно снижая dirty rate, чтобы дать копированию шанс догнать. Это компромисс: VM работает чуть медленнее во время миграции, зато сама миграция гарантированно завершается.
Сколько времени в среднем занимает финальная пауза?
Зависит от объёма оставшейся "грязной" дельты и скорости сети — обычно это доли секунды при спокойной нагрузке и хорошем канале, но точная цифра у каждого сильно своя и её стоит не ожидать по чужим цифрам, а измерять на своей нагрузке через virsh domjobinfo.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →