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. Проверяй автозапуск сразу на всех трёх уровнях: пул, сеть, машина.

Что дальше

Виртуализация тоже покрыта в серии — вот с чем можно свериться дальше: