wiki/HowtoKVM.md
2016-12-26 22:32:43 +01:00

22 KiB

Howto KVM

Site officiel

KVM est une technologie de machines virtuelles (comme Xen ou VMware) intégrée au noyau Linux.

Installation

# apt install qemu-kvm bridge-utils qemu-utils libvirt-bin netcat-openbsd virtinst drbd-utils

On conseille l'utilisation du mode bridge pour le réseau. Voici un exemple de fichier /etc/network/interfaces :

auto lo br0
iface lo inet loopback
iface eth0 inet manual
iface br0 inet static
   address <address>
   netmask <netmask>
   gateway <gateway>
   bridge_ports eth0

Réseau bridgé avec openvswith

Notamment utile pour utiliser avec le RPN Online.

/etc/network/interfaces

# LAN bridge over RPN
# <https://documentation.online.net/fr/dedicated-server/tutorials/network/rpn-proxmox-openvswitch>
auto br1
iface br1 inet manual
    ovs_type OVSBridge
    post-up ovs-vsctl add-port br1 gre0 -- set interface gre0 type=gre options:remote_ip='10.XX.XX.XX'
    post-up ip link set ovs-system up
    post-up ip link set br1 up

Créer un fichier xml définissant le réseau.

<network>
  <name>br1</name>
  <forward mode='bridge'/>
  <bridge name='br1'/>
  <virtualport type='openvswitch'/>
</network>
# virsh net-define br1.xml
# virsh net-start br1
# virsh net-autostart br1

Installation d'une VM Debian (sans libvirt)

Création d'une image pour le système (exemple en QCOW2) :

# qemu-img create -f qcow2 debian1.qcow2 20G

Génération d'une adresse MAC aléatoire pour la machine avec le script suivant (issu de la documentation de KVM) :


# echo $(echo -n DE:AD:BE:EF ; for i in `seq 1 2` ; do echo -n `echo ":$RANDOM$RANDOM" | cut -n -c -3` ;done)

Démarrage de la machine virtuelle sur le fichier debian-504-amd64-netinst.iso :

# kvm -hda debian1.qcow2 -cdrom debian-amd64-netinst.iso -boot d -m 512 -net nic,macaddr=<mac_address> -net tap,script=/etc/qemu-ifup -vnc :1 -k fr

Ouverture d'un tunnel SSH vers le port VNC 5901 dans le cas où celui-ci n'est pas accessible directement :

$ ssh -L 5901:127.0.0.1:5901 <serveur>

Lancement du client VNC :

$ xvncviewer localhost:5901

Effectuer alors une installation Debian traditionnelle, et valider l'installation du boot loader Grub.

/!\ Faire l'installation d'un ordinateur utilisant la touche Fn, seule façon de saisir le caractère "point". Ensuite, mettre un mot de passe trivial (ex: toto) pour le mot de passe root, a changer dans la foulée après l'installation

À la fin de l'installation, lors du redémarrage, arrêter KVM (avec "CTRL+C" ou "kill ") puis on relance dans un screen avec une sortie en "ncurses" :

# /usr/bin/screen -S debian1 -d -m kvm -hda debian1.qcow2 -m 384 -net nic,macaddr=<mac_address> -net tap,script=/etc/qemu-ifup \
    -curses -k fr -monitor tcp:127.0.0.1:<port>,server,nowait

Gestion des images KVM

Info sur une image :

# qemu-img info debian1.qcow2 
image: debian1.qcow2
file format: qcow2
virtual size: 12G (12884901888 bytes)
disk size: 908M
cluster_size: 65536

Format RAW

L'avantage de ce format est sa simplicité ! C'est tout simplement une suite d'octets.. Cela permet de monter les partitions facilement (merci kpartx). Sous Linux, grâce au sparse file, c'est également un format à taille variable.

Création de l'image :

# qemu-img create -f raw test0.img 5G

Mountage de l'image (attention à ne jamais la monter en cours de fonctionnement) :

# modprobe dm-mod  # Si kpartx renvoi un /proc/misc: No entry for device-mapper found
# kpartx -v -a test0.img
loop1p1 : 0 9912042 /dev/loop1 63
loop1p2 : 0 562275 /dev/loop1 9912105
loop1p5 : 0 562212 loop1p1 63
# fdisk -l /dev/loop1
# mount /dev/mapper/loop1p1 /mnt/test0
# umount /mnt/test0
# kpart -d test0.img

Convertir ume image RAW en QCOW2 :

# qemu-img convert -f raw -O qcow2 test0.img test0.qcow2

Agrandir une image

  • Vérifier qu'aucun processus n'accède à l'image (la VM doit notamment être éteinte !)
# virsh list --all
 Id    Name                           State
----------------------------------------------------
 1     foo                           running
 2     bar                           running
 -     my_vm                         shut off

# ps aux |grep <nom_de_la_vm>
  • agrandir le fichier image avec la taille désirée :
# qemu-img resize host.img +50G
Image resized.

ou on peut utiliser dd, exemple pour une taille finale de 80G :

# dd if=/dev/zero of=host.img seek=80G count=0 bs=1
0+0 records in
0+0 records out
0 bytes (0 B) copied, 1.302e-05 s, 0.0 kB/s
  • monter l'image et vérifier qu'elle a la bonne taille :
# kpartx -v -a host.img
add map loop0p1 (254:4): 0 314572737 linear /dev/loop0 63
# fdisk -l /dev/loop0 

Disk /dev/loop0: 161.1 GB, 161061273600 bytes
[…]
  • supprimer puis recréer la partition avec la bonne taille à l'intérieur de l'image, après avoir sauvegarder la table des partitions :
# sfdisk -d /dev/loop0 >~/loop0.parts
# parted /dev/loop0
[…]

Voir http://trac.evolix.net/infogerance/wiki/HowtoParted

  • démonter et remonter l'image (un partprobe ne suffit visiblement pas pour détecter la nouvelle taille de la partition) :
# kpartx -d host.img 
loop deleted : /dev/loop0
# kpartx -v -a host.img 
add map loop0p1 (254:4): 0 314572737 linear /dev/loop0 63
  • lancer un fsck puis un resize2fs pour redimensionner le système de fichiers :
# e2fsck -f /dev/mapper/loop0p1
e2fsck 1.42.5 (29-Jul-2012)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/mapper/loop0p1: 46712/6111232 files (8.3% non-contiguous), 20257932/24414775 blocks
# resize2fs /dev/mapper/loop0p1
resize2fs 1.42.5 (29-Jul-2012)
Resizing the filesystem on /dev/mapper/loop0p1 to 39321592 (4k) blocks.
The filesystem on /dev/mapper/loop0p1 is now 39321592 blocks long.
  • vérifier que le système de fichiers voit la bonne taille en montant la partition :
# mount /dev/mapper/loop0p1 /mnt
# df -h /mnt
/dev/mapper/loop0p1                                     148G   76G   72G  52% /mnt
# umount /mnt
  • puis démonter l'image :
# kpartx -d host.img 
loop deleted : /dev/loop0

Format QCOW2

Ce format est spécifique à QEMU. C'est un format à taille variable (indépendamment du système de fichiers), et il dispose de fonctionnalités avancées permettant notamment de gérer des snapshots à chaud.

Création d'une image QCOW2 :

# qemu-img create -f qcow2 test0.qcow2 5G

Convertir une image RAW en QCOW2 :

# qemu-img convert -f qcow2 -O raw test0.qcow2 test0.img

On passe en mode "monitor" sur l'image QCOW2 :

$ telnet 127.0.0.1 <port monitor>
Trying 127.0.0.1…
Connected to 127.0.0.1.
Escape character is '^]'.
QEMU 0.9.1 monitor - type 'help' for more information
(qemu) savevm s0
savevm s0
(qemu) info snapshots
info snapshots
Snapshot devices: ide0-hd0
Snapshot list (from ide0-hd0):
ID        TAG                 VM SIZE                DATE       VM CLOCK
1         s0                      20M 2010-11-14 20:07:09   00:16:01.182
(qemu) 

Ce snapshot permet une restauration de l'état à chaud ou un redémarrage sur l'état courant.

Pour une restauration à chaud :

(qemu) loadvm s0

Ou alors un (re)démarrage sur cet état sauvegardé :

# /usr/bin/screen -S debian1 -d -m kvm -hda debian1.qcow2 -m 384 -net nic,macaddr=<mac_address> -net tap,script=/etc/qemu-ifup \
    -curses -monitor tcp:127.0.0.1:<port>,server,nowait -loadvm s0

En terme de sauvegarde, cela permet de réaliser des sauvegardes de l'état de la machine (et données) sans arrêt de la machine. Exemple :

#!/bin/sh
echo "savevm snap.current" | telnet 127.0.0.1 <port>
sync
cp debian1.qcow2 debian.current.qcow2

L'un des inconvénients est la difficulté à monter une image QCOW2. Dans les dernières versions, on peut le faire via NBD. Voir http://tjworld.net/wiki/Linux/MountQemuQcowImages

qemu-NBD

qemu-nbd permet de créer un point de montage NBD (Network Block Device) :

# modprobe nbd max_part=16;
# qemu-nbd -c /dev/nbd0 test0.qcow2;
# partprobe /dev/nbd0;

/dev/nbd0 est ensuite utilisable pour fdisk :

# fdisk /dev/nbd0
Command (m for help): p

Vous pouvez ensuite (un)mounter vos partitions situées dans votre image QCOW2. Note : si vous avez du LVM, vous devez activer les VG via vgscan && vgchange -ay

Cela peut ensuite être stoppé via :

# qemu-nbd -d /dev/nbd0

Agrandir une image

De façon similaire au format RAW, on peut agrandir une image QCOW2. Voir http://blog.majerti.fr/resize-qcow2.html

Mode MONITOR de QEMU/KVM

http://en.wikibooks.org/wiki/QEMU/Monitor

Extinction ACPI d'une VM

$ echo system_powerdown | nc localhost <port>
QEMU 0.12.5 monitor - type 'help' for more information
(qemu) system_powerdown
(qemu)
^C

pause/resume d'une VM

$ nc localhost <port>
QEMU 0.12.5 monitor - type 'help' for more information
(qemu) stop
stop
(qemu) cont
cont

Killer features de KVM

Snapshot temporaire

Un mode de démarrage intéressant est le mode "-snapshot" où rien n'est réellement écrit sur le disque :

# kvm -hda debian1.qcow2 -m 384 -net nic,macaddr=<mac_address> -net tap,script=/etc/qemu-ifup \
    -curses -monitor tcp:127.0.0.1:<port>,server,nowait

Au prochain redémarrage, le système revient à son état précédent. Si nécessaire on peut tout de même forcer l'écriture en passant en mode monitor :

(qemu) commit all

Note : à vérifier si les images RAW supportent le mode "-snapshot"

Images dérivées d'une image de base

http://wiki.qemu.org/Documentation/CreateSnapshot

Une option intéressante est de créer une image d'une installation de base et de créer des dérivées à partir de cette image. Non seulement permet de repartir d'une installation déjà faite, mais cela permet aussi une optimisation de la place (l'image dérivée est en Copy-on-Write de celle de base) voire même de la mémoire selon les rumeurs :-)

Création d'une image dérivée :

# qemu-img create -f qcow2 -b install-debian-base.qcow2base serveur01.qcow2snap
Formatting 'serveur01.qcow2snap', fmt=qcow2 size=12884901888 backing_file='install-debian-base.qcow2base' encryption=off cluster_size=0 
# qemu-img info serveur01.qcow2snap
image: serveur01.qcow2snap
file format: qcow2
virtual size: 12G (12884901888 bytes)
disk size: 140K
cluster_size: 65536
backing file: install-debian-base.qcow2base (actual path: install-debian-base..qcow2base)

Attention, ne jamais modifier une image de base si elle a des images dérivées sous peine de tout perdre !!

Libvirt

Installation

# aptitude install libvirt-bin

Pour un accès graphique distant, sur le poste client :

# aptitude install virt-manager netcat-openbsd

Ajouter une clé SSH permettant de se connecter en root au serveur KVM ou ajouter votre utilisateur dans le groupe libvirt (recommandé).

Démarrer virt-manager, et ajouter une connexion qemu+ssh vers le serveur.

Ou bien, installer virt-manager sur le serveur, et se connecter en SSH -X. Options recommandés :

ssh -X -C -c arcfour root@machine.kvm

Mode CLI : virsh

La commande virsh permet de faire de nombreuses manipulations en ligne de commande.

# virsh list
# virsh list --all
# virsh dominfo <vm-name>
# virsh autostart <vm-name>
# virsh autostart --disable <vm-name>
# virsh dumpxml <vm-name> > <vm-name>.xml
# virsh define <vm-name>.xml
# virsh undefine <vm-name>

Pour modifier les options avancées d'une machine créée avec virsh/virt-manager :

# virsh edit <vm-name>

*/!\ Attention, sans utiliser "virsh edit" les modifications sont systématiquement écrasées /!*

Installation d'une nouvelle VM

Nécessite le paquet virtinst.

Pour installer une nouvelle machine virtuelle à partir d'une image ISO :

virt-install \
--connect=qemu:///system \
--name=<hostname> \
--vcpus=2 \
--ram=1024 \
--disk path=/srv/<hostname>.raw,device=disk,format=raw
--network=bridge:lan1 \
--noautoconsole \
--vnc \
--vnclisten=127.0.0.1 \
--keymap=fr \
--cdrom=/path/to/image.iso

Si l'image disque est déjà existante, remplacer --cdrom=/path/to/image.iso par --import.

Pour éditer le fichier XML généré :

virsh edit <hostname>

Performances

Utiliser autant que possible les drivers virtio (disque et réseau) sur les invités le supportant.

Dans le cas d'un hyperviseur avec une carte RAID HW disposant d'un cache avec batterie, créer les machines avec l'option "cache=none" :

<driver name='qemu' type='raw' cache='none'/>

Et désactiver les barrières si Ext4 est utilisé.

Le scheduler deadline semble également donner les meilleures performances tant sur l'hôte que sur les invités.

On peut aussi présenter toutes les instructions du CPU hôte aux machines virtuelles :

  <cpu mode='host-model'>
    <model fallback='allow'/>
  </cpu>

Cloner une machine

Via clic-droit sur virt-manager ou en CLI :

# virt-clone --original mytemplate-domainame --name newmachine --file newmachine.img

Cela permet de dupliquer un domaine existante avec notamment changement de l'adresse MAC de la carte réseau.

Si besoin d'étendre l'image :

# dd oflag=append conv=notrunc if=/dev/zero of=./newmachine.img bs=1MB count=20480 #+20Go

En cas de fichier image de type « sparse file » que l'on veut conserver. Il faut d'abord le créer, l'étendre (si besoin), puis l'indiquer en chemin lors du clone.

# rsync -avS source.img destination.img
## Extend to 100G
# dd if=/dev/zero of=file.img bs=1 count=0 seek=100G
# virt-clone --original mytemplate-domainame --name newmachine --file destination.img --preserve-data

Une fois la machiné démarré, il faudra modifier son hostname, son adresse IP et ses clés SSH.

# echo example > /etc/hostname
# rm /etc/ssh/ssh_host_*
# dpkg-reconfigure openssh-server
# vim /etc/network/interfaces
## En Squeeze supprimer la règle pour eth0 et renommer eth1 en eth0
# vi /etc/udev/rules.d/70-persistent-net.rules

Migrer une machine

https://libvirt.org/migration.html

Note : Il faut s'assurer d'ouvrir les ports TCP 49152 à 49215 entre les machines car par défaut libvirtd utilisent ces ports pour faire des netcat des données !

Pour une migration à chaud, il faut avoir un storage commun pour les disques (SAN, réplication DRBD, etc.).

Pour envoyer une VM locale vers la machine foo :

# VIRSH_DEFAULT_CONNECT_URI='qemu:///system' virsh migrate --live --unsafe test qemu+ssh://foo/system

Pour rappatrier une VM depuis la machine foo :

# VIRSH_DEFAULT_CONNECT_URI='qemu+ssh://foo/system' virsh migrate --live --unsafe test qemu:///system

Note : on peut faire cela via virt-manager (attention, le mode --unsafe parfois pratique n'est pas supporté…)

Si l'on a plusieurs interfaces réseau sur l'hyperviseur (par exemple un réseau dédié entre les hyperviseurs), il faut l'indiquer à libvirtd sinon il tente de passer par l'interface principale :

# virsh migrate --live --unsafe test qemu+ssh://192.168.0.2/system tcp://192.168.0.2/
Migration: [100 %]

rem : sur des machines très proches matériellement, il est possible d'avoir un soucis du fait d'un system-uuid identique sur les deux hyperviseurs

error: internal error: Attempt to migrate guest to the same host 12341234-1234-1234-1234-1234123412341234

Il faut éditer /etc/libvirt/libvirtd.conf (cf # UUID of the host) et ajouter un autre uuid puis redémarrer le service libvirtd pour prise en compte

Désactiver l'interface réseau d'une VM à chaud

Pour ne pas avoir besoin de redémarrer une VM pour retirer une interface, on peut retirer son interface vnetX sur l'hyperviseur du bridge associé. Le nom de cette interface se trouve avec la commande "virsh dumpxml" :

# virsh dumpxml test
[…]
    <interface type='bridge'>
      <mac address='52:54:00:xx:xx:xx'/>
      <source bridge='br2'/>
      <target dev='vnet7'/>
      <model type='virtio'/>
      <alias name='net1'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
    </interface>

Il suffit ensuite de la retirer du bridge :

brctl delif br2 vnet7

Systemd

Libvirt fait appel à systemd (machinectl/systemd-run) pour lancer les processus des VM et les suivre. Pour avoir le status :

# machinectl                         
MACHINE                          CONTAINER    SERVICE      
qemu-mavm                          vm        libvirt-qemu 
                                                        
1 machines listed.                                      
# machinectl status qemu-mavm                                                                                   
qemu-mavm(db0b0ff5e71e4ed9813b226f6843729a)                                                                                        
           Since: Mon 2016-02-22 18:17:49 CET; 8 months 25 days ago                                                                    
          Leader: 33012 (qemu-system-x86)                                                                                              
         Service: libvirt-qemu; class vm                                                                                               
         Address: 192.0.2.1                                                                                                         
              OS: Debian GNU/Linux 8 (jessie)                                                                                          
            Unit: machine-qemu\x2dmavm.scope                                                                                       
                  ??33012 qemu-system-x86_64 -enable-kvm -name mavm -S -machine pc-i440fx-2.1,accel=kvm,usb=off -cpu SandyBridge…                               

En cas de plantage du processus qemu-system (OOMKill par exemple), il sera peut être nécessaire de faire un systemctl reset-failed avant de redémarrer la VM :

# systemctl reset-failed machine-qemu\\x2dmavm.scope

Munin

Il peut être intéressant de grapher quelques infos de KVM dans Munin. Pour cela on peut utiliser quelques plugins.

# mkdir -p /usr/local/share/munin/plugins/
# cd /usr/local/share/munin/plugins/
# wget <https://raw.githubusercontent.com/munin-monitoring/contrib/master/plugins/virtualization/kvm_cpu>
# wget <https://raw.githubusercontent.com/munin-monitoring/contrib/master/plugins/virtualization/kvm_io>
# wget <https://raw.githubusercontent.com/munin-monitoring/contrib/master/plugins/virtualization/kvm_mem>
# sed -i 's/pidof kvm/pidof qemu-system-x86_64/' kvm_*
# chmod -R 755 /usr/local/share/munin
# cd /etc/munin/plugins/
# ln -s /usr/local/share/munin/plugins/kvm_* .
# cat <<EOT >> /etc/munin/plugin-conf.d/munin-node

[kvm_io]
user root
EOT

FAQ

http://www.linux-kvm.org/page/FAQ

Je n'arrive pas à exécuter certaines commandes virsh

Solution : assurez-vous que vous avez bien positionnez la variable VIRSH_DEFAULT_CONNECT_URI !

Dans certains cas, elle se positionne par défaut à vbox:///system On peut évidemment s'assurer en la forçant :

VIRSH_DEFAULT_CONNECT_URI='qemu:///system' virsh list

En Debian 8, je ne trouve pas kvm-img

C'est désormais qemu-img inclu dans le paquet qemu-utils. a priori en Debian 6, c'était qemu-img (inclus par défaut) et en Debian 7 c'était kvm-img (inclus par défaut).

Soucis réseau avec machine clonée

Lorsqu'une machine est clonée avec virt-manager ou virsh, une nouvelle adresse MAC est générée (pour éviter les conflits). Cependant, comme il s'agit d'un clone, l'adresse MAC connue de Udev est toujours présente (dans /etc/udev/rules.d/z25_persistent-net.rules) et l'interface apparait donc comme eth1.

Deux solutions, utiliser eth1 au lieu de eth0, ou corriger /etc/udev/rules.d/z25_persistent-net.rules en mettant à jour l'adresse MAC de eth0 et en supprimant eth1.

Mode réseau NAT avec un laptop

Le mode NAT peut être intéressant si l'on ne peut pas avoir d'IP dans le réseau local. Une autre raison d'utiliser le NAT, est qu'une interface Wi-Fi n'est pas toujours utilisable dans un bridge :

# brctl addif br0 wlan0
can't add wlan0 to bridge br0: Operation not supported

On va donc prendre l'exemple où vous avez une interface Wi-Fi wlan0 :

# echo 1 > /proc/sys/net/ipv4/ip_forward
# iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE

Vous pouvez ainsi lancer votre KVM ainsi (on lance un CD-ROM d'OpenBSD dans notre cas) :

# kvm -hda routeur0.qcow2 -cdrom cd48.iso -boot d -m 384 -k fr -net nic,macaddr=52:54:00:de:ad:42,model=e1000 -net tap,vlan=0,ifname=tap0

Une fois démarré, attribuez l'IP 10.0.0.1/24 à l'interface tap0 sur votre portable. Dans vos machines virtuelles, prenez une adresse IP dans la plage 10.0.0.0/24 et indiquez 10.0.0.1 comme route par défaut.

Normalement, c'est tout !

Si vous lancez plusieurs machines virtuelles, vous penserez à modifier l'adresse MAC et à utiliser des tapN différents. Si vous voulez les faire communiquer entre elle, vous devrez simplement créer un bridge entre les interfaces tap :

# brctl addbr br0
# brctl addif br0 tap0
# brctl addif br0 tap1
# brctl addif br0 tap2
etc.

Erreur "KVM: disabled by BIOS"

Vous devez activer la virtualisation sur votre processeur (cela se fait par le BIOS).

Erreur "Unable to create cgroup for $VIRTIMAGE: No such file or directory" et problème avec machine-qemu\x2dfoo.scope

Si votre VM a crashée et n'est pas "redémarrable", notamment il reste des « traces » dans /run/systemd/system/machine-qemu\x2dfoo.scope vous pouvez faire un reset-failed :

# systemctl status machine-qemu\\x2dfoo.scope

# systemctl reset-failed machine-qemu\\x2dfoo.scope