Quelques travaux avec QEMU/KVM et virsh¶
Résumé
Administrations de machines virtuelles.
Installer une machine virtuelle Linux¶
Virt-install¶
Installez une machine virtuelle debian12.5 netinstall dans la machine virtuelle Ubuntu
virt-install \
--name debian12.5 \
--ram 1024 \
--disk size=10 \
--vcpus 1 \
--network bridge=virbr0 \
--location debian-12.5.0-amd64-netinst.iso \
--graphics none \
--console pty,target_type=serial \
--extra-args='console=ttyS0' \
--initrd-inject preseed.cfg \
--os-variant debian12
Utiliser PXE pour installer Linux dans QEMU/KVM¶
Modifier la définition du réseau par défaut
<network>
<name>default</name>
<uuid>5c804474-b1ce-4e0f-8b72-b2036b6c735b</uuid>
<forward mode='nat'/>
<bridge name='virbr0' stp='on' delay='0'/>
<mac address='52:54:00:f8:84:59'/>
<ip address='192.168.124.1' netmask='255.255.255.0'>
<tftp root='/tftpboot'/>
<dhcp>
<range start='192.168.124.2' end='192.168.124.254'/>
<bootp file='pxelinux.0'/>
</dhcp>
</ip>
</network>
Puis rechargez le réseau
# virsh net-destroy default Network default destroyed # virsh net-edit default # virsh net-start default Network default started
Installer les fichiers PXE sur le host¶
On peut trouver les fichiers d’installation pour PXE dans le répertoire partagé du paquet syslinux.
Attention, le répertoire des fichiers tftp peut être /var/lib/tftp ou /tftpboot. Y copier le fichier de boot et le loader ainsi que le noyau et l’initramfs du CDROM d’installation de la cible.
Pour le pxe, il continent un initramfs avec les modules virtio.
# yum install syslinux
# cp /usr/share/syslinux/pxelinux.0 /tftpboot
# cp /usr/share/syslinux/ldlinux.c32 /tftpboot/
# cp /cdrom/images/pxeboot/vmlinuz /tftpboot
# cp /cdrom/images/pxeboot/initrd.img /tftpboot/
Vérifiez que le répertoire tftpboot est accessible en lecture pour tous.
Configurer PXE
Créer le répertoire /tftpboot/pxelinux.cfg et le fichier de configuration.
# mkdir /tftpboot/pxelinux.cfg
# cat < EOF > /tftpboot/pxelinux.cfg/default
PROMPT 1
TIMEOUT 50
DEFAULT Linux
LABEL Linux
KERNEL vmlinuz
INITRD initrd.img
APPEND inst.ks=http://xxx.xxx.xxx.xxx/ks.cfg ip=dhcp net.ifnames=0
EOF
Autoriser http à passer le firewall
# firewall-cmd --add-service=http --zone=libvirt
# firewall-cmd --add-service=http --zone=libvirt --permanent
Lancer virt-install
# virt-install --pxe --network network=default --os-variant=almalinux9
créer une VM busybox avec virsh define/start¶
Utiliser la définition de busybox.xml que vous pouvez trouver dans les sources de rfs: rfs , utilisez le noyau de votre host puis fabriquez rfs00.gz en utilisant le script build.me
rfs # cp /boot/vmlinuz .
rfs # ./build.me
Ou bien, récupérez vmlinuz et rfs00.gz du formateur
$ virsh define busybox.xml
$ virsh start Busybox
Ensuite vous pouvez utiliser virsh pour travailler sur la nouvelle vm
lister toutes les VM
$ virsh list --all
sauvegarder puis restaurer une VM
$ virsh save Busybox BB.state
$ virsh restore BB.state
Modifier le XML d’une VM pour autoriser le hotplug¶
<acpi/>
<vcpu placement='static' cpuset="1-4,^3,6" current="1">2</vcpu>
<vcpus>
<vcpu id='0' enabled='yes' hotpluggable='no' order='1'/>
<vcpu id='1' enabled='no' hotpluggable='yes'/>
</vcpus>
utiliser virsh setcpus et virsh setcpu¶
Lister les vCPUs disponibles
$ virsh vcpucount Busybox
maximum config 2
maximum live 2
current config 1
current live 1
Ajouter un vCPU
$ virsh setvcpus Busybox 2
vérifier sur le guest
# cat /proc/cpuinfo
mettre un CPU offline dans le guest
# echo 0 > /sys/devices/system/cpu/cpu1/online
Retirer un CPU du guest
$ virsh setvcpus Busybox 1
Changer la taille de la mémoire dans le XML
<maxMemory slots='16' unit='KiB'>1524288</maxMemory>
<memory unit='KiB'>524288</memory>
<currentMemory unit='KiB'>524288</currentMemory>
Modifier pour :
<maxMemory slots='16' unit='KiB'>1524288</maxMemory>
<memory unit='KiB'>524288</memory>
<currentMemory unit='KiB'>524288</currentMemory>
Modifier la taille de mémoire utilisée¶
Effectuer des statistiques toutes les 5s et arrêter la VM
# virsh dommemstat --domain almalinux9-2 --period 5
# virsh shutdown BB2
# virsh destroy BB2
# virsh setmaxmem BB2 2G
$ virsh dominfo BB2
Id: -
Name: BB
UUID: 1e071db9-b12f-40bb-930a-e16ab9d979f5
OS Type: hvm
State: shut off
CPU(s): 1
Max memory: 1048576 KiB
Used memory: 1048576 KiB
Persistent: yes
Autostart: disable
Managed save: no
Security model: none
Security DOI: 0
Redémarrez la machine virtuelle. Après redémarrage :
$ virsh dominfo BB2
Used memory:
2096128 KiB
Réduire la taille de la mémoire utilisée par la VM
$ virsh setmem BB2 1G
$ virsh dominfo BB2
1048576 KiB
UItiliser le Stockage¶
Créer un disque QCOW2 et assignez le à une VM existante
Créer Le fichier qcow2
# qemu-img create -f qcow2 disk_01.qcow2 5G
Monter le disque virtuel et le formater, écrire dedans
# modprobe nbd
# qemu-nbd --connect=/dev/nbd0 -f qcow2 disk_01.qcow2
# fdisk /dev/nbd0 -l
# mkfs.ext4 /dev/nbd0
# mount /dev/nbd0 /mnt/
# date > /mnt/date.txt
# umount /dev/nbd0
# qemu-nbd --disconnect /dev/nbd0
Créer le fichier XML de définition du disque, disk.xml
<disk type="file" device="disk">
<driver name="qemu" type="qcow2" discard="unmap"/>
<source file="/home/user/OKVM/disk_01.qcow2" index="3"/>
<target dev="vdb" bus="virtio"/>
<alias name="virtio-disk1"/>
<address type="pci" domain="0x0000" bus="0x08" slot="0x00" function="0x0"/>
</disk>
Attacher le disque au domaine
$ virsh attach-device debian12.5 disk.xml
Créer un pool à partir d’un volume groupe LVM et l’assigner à une VM existante
Sur le host, assigner une partition comme physical volume puis créer le Volume Group
# pvcreate /dev/vdb1
# vgcreate vg0 /dev/vdb1
Définir le pool et le démarrer
# virsh pool-define-as deb-lvm logical --source-name vg0 --target /dev/vg0
# virsh pool-start deb-lvm
# virsh pool-autostart deb-lvm
Créer un logical volume dans le pool
$ virsh vol-create-as deb-lvm raw_disk.img 5G --format raw
Vérifier et attacher le disque
$ virsh vol-list --pool deb-lvm --details
$ virsh attach-disk debian12.5 /dev/vg0/raw_disk.img vdb
Créer un snapshot pour une VM existante et vérifier le nom des périphériques block associés à la VM
# virsh domblklist debian12.5
Target Source
------------------
vda /home/user/OKVM/sn1.qcow2
sda /home/user/OKVM/debian-12.5.0-amd64-netinst.iso
Créez un snapshot et vérifier
# virsh snapshot-create-as debian12.5 snap_01 --disk-only --atomic \
--diskspec vda,file=/var/lib/libvirt/images/snap_01.qcow2
Domain snapshot snap_01 created
# virsh snapshot-list debian12.5
Name Creation Time State
------------------------------------------------
snap_01 2024-04-12 16:35:10 +0200 running
Vérifiez que vous pouvez revenir en arrière en créant un fichier sur le guest
# date > /Estampille.txt
Arrêtez la VM
# virsh shutdown debian12.5
Supprimez le snapshot
# rm /var/lib/libvirt/images/snap_01.qcow2
# virsh snapshot-delete debian12.5 --metadata snap_01
Editez le XML et définissez l’ancien disque
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2' discard='unmap'/>
<source file='/var/lib/libvirt/images/debian12.5.qcow2'/>
<backingStore/>
<target dev='vda' bus='virtio'/>
<address type='pci' domain='0x0000' bus='0x04' slot='0x00'
function='0x0'/>
</disk>
Utiliser soit 9P soit VIRTIO-FS pour échanger des fichiers entre host et guest¶
Ajouter le XML au domaine
<filesystem type='mount' accessmode='passthrough'>
<source dir='/home/user/9p_share'/>
<target dir='mount_1'/>
</filesystem>
Dans le guest charger le module puis monter le fichiers
# modprobe 9p
[ 7.102545] 9pnet: Installing 9P2000 support
[ 7.113678] FS-Cache: Loaded
[ 7.120316] 9p: Installing v9fs 9p2000 file system support
[ 7.122468] FS-Cache: Netfs '9p' registered for caching
/ # modprobe 9pnet-virtio
/ # lsmod
Module Size Used by Not tainted
9pnet_virtio 20480 0
9p 61440 0
fscache 380928 1 9p
9pnet 86016 2 9pnet_virtio,9p
/ #
# mount -t 9p mount_1 /mnt/
NOTE en cas de problème vérifiez que le fichier virtio-9p fait bien partie des modules de votre guest.
VFIO: associer une clef USB à un guest¶
Trouver la clef USB avec lsusb
# lsusb
Créer le fichier usb_dev.xml
<devices>
<hostdev mode='subsystem' type='usb' managed='yes'>
<source>
<vendor id='0x093a'/>
<product id='0x2510'/>
</source>
</hostdev>
</devices>
Attacher le périphérique au guest
# virsh attach-device deb12 --file usb_dev.xml --config
Utiliser un disque encrypté par Luks¶
Soyez sur de démarrer le daemon virtsecretd
$ sudo systemctl enable virtsecretd
$ sudo systemctl start virtsecretd
Créer le disque avec qemu-img
$ sudo qemu-img create --object secret,id=sec0,data="super secret" \
-f qcow2 \
-o encrypt.format=luks,encrypt.key-secret=sec0 \
/var/lib/libvirt/images/crypted_volume.img 1G
Formatting 'base.qcow2', fmt=qcow2 encrypt.format=luks encrypt.key-secret=sec0
cluster_size=65536 extended_l2=off compression_type=zlib size=1073741824
lazy_refcounts=off refcount_bits=16
Générer le UUID
$ uuidgen
65e8ecb8-6141-48b6-bae4-aa879c3e2245
Définir le secret pour le volume encrypté pour Libvirt
$ cat volume-secret.xml
<secret ephemeral='no' private='yes'>
<description>Encryption :Luks</description>
<uuid>65e8ecb8-6141-48b6-bae4-aa879c3e2245</uuid>
<usage type='volume'>
<volume>/var/lib/libvirt/images/crypted_volume.img</volume>
</usage>
</secret>
Déclarer le secret dans Libvirt
$ virsh secret-define volume-secret.xml
Secret 65e8ecb8-6141-48b6-bae4-aa879c3e2245 created
Définir le secret dans Libvirt « super secret » comme lors de la fabrication de l’image disque
$ virsh secret-set-value --interactive 65e8ecb8-6141-48b6-bae4-aa879c3e2245
Enter new value for secret:
Secret value set
...
Définir le disque avec l’encryption
<disk type='file'>
<driver name='qemu' type='raw' cache='none'/>
<source file='/var/lib/libvirt/images/crypted_volume.img'/>
<target dev='vdb' bus='virtio'/>
<encryption format='luks'>
<secret type='passphrase' uuid='65e8ecb8-6141-48b6-bae4-aa879c3e2245'/>
</encryption>
<address type='pci' domain='0x0000' bus='0x08' slot='0x00' function='0x0'/>
</disk>
Attacher le disque à la VM
Attention, si vous avez une erreur disant que le hotplug PCI n’est pas possible, vérifiez que vous avez bien défini l’ACPI et l’APIC dans le XML du domaine.
$ sudo virsh -c qemu:///system attach-device almalinux9 disk-luks.xml
</os>
<features>
<acpi/>
<apic/>
</features>
TP Monitoring¶
Utiliser QMP pour vérifier si KVM est activé
$ virsh qemu-monitor-command debian12.5 --pretty '{"execute":"query-kvm"}'
{
"return": {
"enabled": true,
"present": true
},
"id": "libvirt-12770"
}
Utiliser HMP pour afficher les informations sur l’arbre des périphériques (info qtree) et les mémoires (info mtree)
$ virsh qemu-monitor-command debian12.5 --hmp "info qtree"
et
$ virsh qemu-monitor-command debian12.5 --hmp "info mtree"
Retrouver pour le guest le nombre d’injections d’interruption et de sortie d’interruption
# cat /sys/kernel/debug/kvm/2360172-16/{irq_injections,irq_exits}
162 187270
Utiliser QMP pour gérer le balooning¶
Ecoutez sur la socket QMP
# virsh -c qemu:///system qemu-monitor-event debian12.5 --pretty --loop
Vous pouvez recevoir ce type d’événement :
<- { "event": "BALLOON_CHANGE",
"data": { "actual": 944766976 },
"timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
actual représente la taille de la mémoire utilisable par le guest logical_vm_size = vm_ram_size - balloon_size
Définir la taille de la mémoire utilisable par le guest avec QMP
<- {"execute": "balloon", "arguments": {"value": 2147483648}}
-> {"return": {}}
Demander la taille du ballon
<- {"execute": "query-balloon"}
-> {"return":{"actual":2147483648}}
TP Migration¶
Suivre avec QMP
$ virsh qemu-monitor-event debian12.5 --pretty --loop
event VSERPORT_CHANGE at 1712907760.795105 for domain 'debian12.5': {
"open": false,
"id": "channel0"
}
Effectuer une migration live
$ virsh migrate --live debian12.5 almaLinux
Effectuer une migration de Busybox avec QEMU
Dans QEMU monitor effectuer les commandes suivantes (définir la vitesse est optionnel):
(qemu) stop
(qemu) migrate_set_speed 4095m
(qemu) migrate "exec:gzip -c > STATEFILE.gz"
du coté de la cible il sera nécessaire de copier tous les supports de stockage et de rétablir le réseau de l’hôte correctement avant d’ajouter à la commande QEMU:
-incoming "exec: gzip -c -d STATEFILE.gz"
Note
Attention à la compatibilité entre les machines distantes et les CPU disponibles sinon la migration sera impossible.
TP outils¶
Guestfs¶
Créer un programme utilisant guestfs
Libguesrfs propose un nouveau disque comme /dev/sda. Le programme suivant crée un fichier hello à la racine du disque.
#include <guestfs.h>
int main()
{
guestfs_h \*g = guestfs_create ();
guestfs_add_drive (g, "guest.img");
guestfs_launch (g);
guestfs_mount (g, "/dev/sda", "/");
guestfs_touch (g, "/hello");
guestfs_umount (g, "/");
guestfs_shutdown (g);
guestfs_close (g);
}
Le compiler
$ cc do_guest_img.c -o do_guest_img -lguestfs
Fabriquer un disque format raw
$ dd if=/dev/zero of=guest.img bs=1M count=4
$ mkfs.ext2 guest.img
Créer le fichier hello sur le disque
$ /do_guest_img
Analyser le disque avec virt-rescue
Virt-rescue lance un mini guest et propose le disque jouté comme /dev/sda
$ virt-rescue -a guest.img
…
><rescue> mount /dev/sda /sysroot/
><rescue> ls /sysroot/
hello lost+found
><rescue> exit
polkit¶
Lister les actions disponibles
$ pkaction
org.cockpit-project.cockpit.root-bridge
org.fedoraproject.FirewallD1.all
…
org.libvirt.api.storage-pool.delete
org.libvirt.api.storage-pool.format
org.libvirt.api.storage-pool.getattr
org.libvirt.api.storage-pool.read
org.libvirt.api.storage-pool.refresh
org.libvirt.api.storage-pool.save
org.libvirt.api.storage-pool.search-storage-vols
org.libvirt.api.storage-pool.start
org.libvirt.api.storage-pool.stop
org.libvirt.api.storage-pool.write
org.libvirt.api.storage-vol.create
org.libvirt.api.storage-vol.data-read
org.libvirt.api.storage-vol.data-write
org.libvirt.api.storage-vol.delete
org.libvirt.api.storage-vol.format
org.libvirt.api.storage-vol.getattr
org.libvirt.api.storage-vol.read
org.libvirt.api.storage-vol.resize
org.libvirt.unix.manage
org.libvirt.unix.monitor
Autoriser tout le monde à manager libvirtd
Editer /usr/share/polkit-1/rules.d/01-mylibvirt.rules
polkit.addRule(function(action, subject) {
if (action.id == "org.libvirt.unix.manage") {
polkit.log("Hello my libvirt rule");
return polkit.Result.YES;
}
});