KVM + libvirt: управление виртуальными машинами из командной строки
KVM — это не отдельный продукт, а модуль ядра Linux, превращающий обычный сервер в полноценный гипервизор первого типа. Сам по себе KVM — только движок виртуализации; удобным инструментом администрирования его делает libvirt — демон и набор утилит, которые управляют виртуальными машинами, сетями и хранилищами через единый API. Именно libvirt использует под капотом Proxmox, а virsh — его штатный консольный клиент — даёт полный контроль без графической оболочки вообще.
Разворачиваем стенд на srv-kvm01, IP 192.168.10.50, Ubuntu Server 22.04 в сети 192.168.10.0/24, домен itpages.lab.local. Всё управление — только из терминала, что удобно для серверов без монитора и для автоматизации через скрипты.
Проверка поддержки виртуализации и установка пакетов
Прежде чем ставить пакеты, убедись, что процессор и BIOS сервера поддерживают аппаратную виртуализацию и она включена:
egrep -c '(vmx|svm)' /proc/cpuinfo
Если команда вернула 0 — виртуализация выключена в BIOS или процессор её не поддерживает вообще, дальше двигаться бессмысленно, пока не включишь VT-x/AMD-V. Если число больше нуля, ставим необходимый набор пакетов:
apt update
apt install -y qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virtinst virt-manager
virt-manager в списке не обязателен для чисто консольной работы, но пригодится, если когда-нибудь понадобится графический доступ через X11-forwarding. Добавь своего пользователя в группу libvirt, чтобы не работать под root постоянно:
usermod -aG libvirt $(whoami)
systemctl enable --now libvirtd
После добавления в группу перелогинься или выполни newgrp libvirt, иначе изменения не применятся в текущей сессии.
Создание пула хранения
Пул хранения (storage pool) — это место, где libvirt хранит диски виртуальных машин. По умолчанию используется /var/lib/libvirt/images, но для порядка лучше завести отдельный именованный пул:
virsh pool-define-as vmpool dir --target /var/lib/libvirt/images/vmpool
virsh pool-build vmpool
virsh pool-start vmpool
virsh pool-autostart vmpool
Флаг pool-autostart критичен — без него после перезагрузки хоста пул останется в состоянии inactive, и виртуальные машины, зависящие от него, не смогут запуститься автоматически. Проверить состояние пулов:
virsh pool-list --all
Создание виртуальной сети
Для сервера, который должен быть виден в лабораторной сети 192.168.10.0/24 наравне с остальными хостами, лучше всего подходит bridge к физическому интерфейсу — тогда виртуальные машины получают адреса из той же подсети, что и сам хост. Но для примера разберём и вариант с изолированной NAT-сетью libvirt, которая часто нужна для тестовых стендов без прямого доступа снаружи. Опиши сеть в XML-файле:
<network>
<name>lab-net</name>
<forward mode="nat"/>
<bridge name="virbr1" stp="on" delay="0"/>
<ip address="192.168.20.1" netmask="255.255.255.0">
<dhcp>
<range start="192.168.20.100" end="192.168.20.200"/>
</dhcp>
</ip>
</network>
Регистрируем и запускаем сеть:
virsh net-define lab-net.xml
virsh net-start lab-net
virsh net-autostart lab-net
Для полноценного bridge к физической сети создаётся сетевой мост средствами netplan или systemd-networkd на уровне ОС, а затем libvirt-сеть типа bridge просто ссылается на уже существующий интерфейс — это отдельная настройка на уровне сетевого стека Linux, а не только libvirt.
Создание виртуальной машины через virt-install
virt-install — это инструмент, который одной командой описывает и запускает создание виртуальной машины, включая диск, память, процессор и сеть:
virt-install --name srv-web01 --memory 4096 --vcpus 2 --disk pool=vmpool,size=40,format=qcow2 --os-variant ubuntu22.04 --network network=lab-net --graphics none --console pty,target_type=serial --location /var/lib/libvirt/boot/ubuntu-22.04-server.iso
Параметр —graphics none вместе с —console pty удобен именно для безголового сервера — установка идёт через последовательную консоль прямо в терминале SSH-сессии, без необходимости в VNC. Список поддерживаемых значений —os-variant можно посмотреть командой osinfo-query os.
Управление виртуальными машинами через virsh
Дальнейшая работа с машинами — это, по сути, набор команд virsh, которые быстро входят в привычку:
virsh list --all
virsh start srv-web01
virsh shutdown srv-web01
virsh destroy srv-web01
virsh console srv-web01
virsh dominfo srv-web01
virsh domifaddr srv-web01
virsh shutdown посылает гостевой ОС корректный сигнал завершения работы через ACPI (требует установленных гостевых утилит вроде acpid внутри ВМ), а virsh destroy — это принудительное «выключение из розетки», используй его только когда штатное выключение не сработало. Снапшоты — ещё один полезный инструмент перед рискованными изменениями внутри ВМ:
virsh snapshot-create-as srv-web01 clean-install "Чистая установка перед обновлением"
virsh snapshot-list srv-web01
virsh snapshot-revert srv-web01 clean-install
Автозапуск и обслуживание
Чтобы виртуальная машина поднималась вместе с хостом после перезагрузки сервера, включи автозапуск отдельно для каждой ВМ:
virsh autostart srv-web01
Изменить параметры уже созданной машины (например, добавить память) можно через редактирование XML-описания напрямую или точечными командами:
virsh setmaxmem srv-web01 8G --config
virsh setmem srv-web01 8G --config
Клонирование виртуальных машин
Разворачивать однотипные серверы удобнее не через повторный virt-install, а клонированием уже настроенной машины. Сначала выключи исходную ВМ, затем:
virt-clone --original srv-web01 --name srv-web02 --file /var/lib/libvirt/images/vmpool/srv-web02.qcow2
virt-clone автоматически генерирует новый UUID и MAC-адрес, но имя хоста, ключи SSH и machine-id внутри гостевой ОС остаются от оригинала — их нужно сбросить вручную (или через cloud-init, если образ его поддерживает) прежде, чем вводить клон в сеть 192.168.10.0/24 рядом с исходной машиной.
Живая миграция между хостами KVM
Если в лаборатории несколько хостов KVM с доступом к общему хранилищу (NFS или Ceph) и одинаковой сетевой конфигурацией, virsh поддерживает живую миграцию без остановки ВМ:
virsh migrate --live --persistent srv-web01 qemu+ssh://srv-kvm02.itpages.lab.local/system
Без общего хранилища миграция тоже возможна с флагом —copy-storage-all, но тогда по сети передаётся ещё и содержимое диска целиком, что заметно дольше и сильнее нагружает канал между хостами.
Резервное копирование через внешние снапшоты
У libvirt нет встроенного аналога vzdump, но для консистентного бэкапа без остановки ВМ используется механизм внешних снапшотов: создаётся снапшот только метаданных диска, оригинальный файл копируется как есть, затем изменения сливаются обратно:
virsh snapshot-create-as srv-web01 backup-snap --disk-only --atomic
cp /var/lib/libvirt/images/vmpool/srv-web01.qcow2 /mnt/backup/srv-web01-$(date +%F).qcow2
virsh blockcommit srv-web01 vda --active --pivot
Ограничение ресурсов через cgroups
Помимо параметров памяти и vCPU, заданных при создании ВМ, libvirt позволяет точечно ограничивать потребление CPU через группы cgroups — полезно, если несколько тестовых машин соседствуют на одном хосте с продуктивной нагрузкой:
virsh schedinfo srv-web01 --set cpu_shares=512
virsh blkiotune srv-web01 --weight 500
Автоматизация через cloud-init
Для быстрого развёртывания однотипных Linux-серверов удобнее не устанавливать систему с ISO вручную, а использовать готовый cloud-образ дистрибутива вместе с cloud-init — это тот же подход, что применяют публичные облака. Готовим конфигурацию первого запуска и диск с ней:
cloud-localds seed.iso user-data.yaml meta-data.yaml
virt-install --name srv-db01 --memory 4096 --vcpus 2 --disk pool=vmpool,size=40,backing_store=/var/lib/libvirt/images/ubuntu-22.04-cloudimg.qcow2 --disk seed.iso,device=cdrom --os-variant ubuntu22.04 --network network=lab-net --import --graphics none
Машина стартует уже с заданным именем хоста, пользователем и SSH-ключом — вручную вводить учётные данные через консоль не требуется, что особенно ценно при разворачивании десятка одинаковых серверов подряд.
Экспорт и редактирование конфигурации через XML
Всё описание виртуальной машины в libvirt — обычный XML, и иногда проще отредактировать его напрямую, чем искать нужный флаг у virsh:
virsh dumpxml srv-web01 > srv-web01.xml
virsh edit srv-web01
Команда virsh edit открывает XML в редакторе (по умолчанию $EDITOR) и на выходе сама валидирует схему перед применением — если допустить синтаксическую ошибку, libvirt откажется сохранять изменения и укажет строку с проблемой, что спасает от случайной поломки конфигурации работающей машины.
Мониторинг через virt-top и системные логи
Для оперативного контроля нагрузки на все запущенные ВМ хоста удобен virt-top — по сути тот же top, но по виртуальным машинам вместо процессов:
apt install -y virt-top
virt-top
Логи самого libvirtd и отдельных ВМ (QEMU-логи) лежат в предсказуемых местах и первыми смотрятся при диагностике проблем с загрузкой или сетью:
journalctl -u libvirtd -f
tail -f /var/log/libvirt/qemu/srv-web01.log
Проброс USB и PCI-устройств
Иногда виртуальной машине нужен прямой доступ к физическому устройству хоста — USB-токену, GPU для расчётов или отдельной сетевой карте. Для USB-устройств достаточно узнать идентификатор и подключить его «на лету», без остановки ВМ:
lsusb
virsh attach-device srv-web01 usb-device.xml --live --config
Для проброса PCI-устройств (например, отдельного сетевого адаптера под выделенный VLAN) требуется поддержка IOMMU на уровне процессора и включённая в BIOS виртуализация ввода-вывода (VT-d/AMD-Vi), плюс отвязка устройства от драйвера хоста через vfio-pci перед стартом ВМ — это более тяжёлый сценарий, который стоит отдельного внимания при реальной необходимости, а не берётся «про запас». Список устройств, готовых к пробросу после настройки IOMMU, удобно смотреть командой virsh nodedev-list —tree — она показывает всю топологию PCI-шины хоста с привязкой к текущим драйверам. Для повседневных лабораторных задач проброс устройств скорее исключение, чем правило, — большинству серверных сервисов достаточно виртуального диска, сети и стандартного набора vCPU, а прямой доступ к железу стоит держать в уме как опцию на случай специфичной нагрузки вроде транскодирования видео или расчётов на GPU. Начинай с виртуальных устройств по умолчанию и переходи к пробросу только тогда, когда конкретная задача действительно этого требует. Такой постепенный подход экономит время на диагностике — большинство проблем с загрузкой и производительностью на голом libvirt связаны именно с излишне сложной конфигурацией устройств, а не с самим гипервизором.
Частые ошибки и как их избежать
- Пользователь не добавлен в группу libvirt. Команды virsh либо требуют sudo на каждый вызов, либо падают с ошибкой доступа к сокету /var/run/libvirt/libvirt-sock. Один раз добавь пользователя в группу и перелогинься — это снимает проблему полностью.
- NAT-сеть вместо bridge там, где нужен прямой доступ из локальной сети. Виртуальная машина поднимается и работает, но недоступна с других хостов сети 192.168.10.0/24 — трафик остаётся внутри NAT-транслируемого сегмента 192.168.20.0/24. Для серверных ролей используй bridge к физическому интерфейсу, NAT оставляй для изолированных тестов.
- Забытый net-autostart или pool-autostart. После перезагрузки хоста сеть или хранилище остаются неактивными, а зависящие от них виртуальные машины не запускаются даже с включённым virsh autostart. Проверяй автозапуск сразу на всех трёх уровнях: пул, сеть, машина.
Что дальше
Виртуализация тоже покрыта в серии — вот с чем можно свериться дальше: