La virtualisation facile, pour de vrai !

GNU/Linux Magazine n° 140 | juillet 2011 | Philippe Harrand
Creative Commons
  • Actuellement 0 sur 5 étoiles
0
Merci d'avoir participé !
Vous avez déjà noté cette page, vous ne pouvez la noter qu'une fois !
Votre note a été changée, merci de votre participation !
Afin d'augmenter le nombre de machines mises à disposition des étudiants lors de travaux pratiques, j'avais besoin de gérer plusieurs machines virtuelles identiques, de les re-créer rapidement et je voulais permettre à d'autres personnes moins attirées par la sobre beauté d'une commande de 3 lignes d'en faire autant.En installant CentOS sur les nouvelles machines qui devaient héberger tout ça, j'ai vu le nom de virt-manager...

1. Introduction

La ligne de commande est le moyen le plus simple et efficace pour travailler avec notre OS préféré et la gestion des machines virtuelles ne déroge pas à ce postulat. Cependant certains outils graphiques comme virtualBox séduisent quelques-un d'entre vous (ne niez pas, j'en ai vu). Mais quand vous succombez aux sirènes du clickodrome difficile de s'en échapper sans perdre son travail. Il existe pourtant une bibliothèque qui réunit les avantages des deux sans aucun de leurs inconvénients ! Il s'agit de libvirt avec virt-manager.

Nous ne ferons que survoler quelques-unes des innombrables possibilités offertes par libvirt, les lecteurs anglophones pourront se référer à la documentation et au wiki sur le site officiel [libvirt]. Pour les non-anglophones il va falloir attendre un peu...

virt-manager [virt-manager] permet de gérer des machines virtualisées par Xen, QEMU, VMWare, ... et KVM. Dans cet article nous utiliserons KVM que vous avez déjà pu découvrir dans un excellent magazine.

2. Installation

Nous pouvons faire confiance aux gestionnaires de paquets évolués et installer le tout très simplement.

debian :

aptitude install virt-manager kvm

fedora :

yum install virt-manager kvm libvirt

archlinux :

pacman -S libvirt urlgrabber qemu-kvm dnsmasq bridge-utils

pacman -S virtviewer virtinst virt-manager

Selon les dépendances à installer, vous allez peut-être prendre un petit café ?

3. La bibliothèque libvirt

La bibliothèque libvirt fournit un démon libvirtd et quelques outils. Libvirt supporte plusieurs méthodes de (para)virtualisation dont Xen, QEMU, KVM, VirtualBox, Vmware et de confinement dont OpenVZ, LXC... Le choix est donc plus étendu que pour virt-manager.

Un peu de vocabulaire (traduit de la documentation de libvirt) :

- nœud (node) : machine physique

- hyperviseur (hypervisor) : couche logicielle permettant de (para)virtualiser ou confiner des machines

- domaine (domain) : instance d'un OS (ou conteneur) lancé sur une machine virtuelle fournie par un hyperviseur.

Le but de libvirt est donc de fournir un moyen stable de gérer des domaines hébergés par un nœud, que celui-ci soit local ou distant.

Lors de l'installation de la bibliothèque, quelques outils sont installés, voila ceux que j'ai utilisés :

virt-clonepermet de cloner un domaine. Cette commande crée une nouvelle machine virtuelle avec un nouvel UUID, de nouvelles adresses MAC pour les interfaces ethernet et bien sûr le nouveau nom que vous lui avez donné. L'image du système est copiée.

virt-install permet de créer un domaine à partir d'une image iso d'installation ou par le réseau.

virsh fournit un shell et un ensemble de commandes permettant de gérer les domaines et les réseaux virtuels.

virt-convert qui convertit une machine virtuelle d'un format (vmx, virt-image et ovf

pour l'instant) vers un autre (virt-image et vmx pour l'instant).

virt-viewer qui ouvre une console vnc vers un domaine (local ou distant).

4. L'interface graphique virt-manager

L'interface graphique nous permet de créer, modifier, supprimer et superviser les domaines et les réseaux virtuels. Pour ma part, je l'utilise pour créer le premier domaine d'une série et pour la supervision.

virt-manager s'exécute avec les droits de root.

À la première utilisation, vous devez créer une connexion avec un hyperviseur (au sens libvirt, voir plus haut) par Fichier > Ajouter une connexion... Cette connexion peut être locale ou distante, ce qui explique que la liste des choix qui vous est proposée peut comporter des hyperviseurs qui ne sont pas installés sur votre système mais peut-être sur un hôte distant. Si vous cochez la case « autoconnect », virt-manager tentera de se connecter au démarrage. Il vous faudra connaître le mot de passe de root de l'hôte auquel vous voulez accéder. Il est nécessaire que le démon libvirtd soit en fonction sur l'hôte distant, virt-manager se chargera de vous le rappeler le cas échéant. N'hésitez pas à lire les détails du message car c'est là que vous trouverez la cause réelle de l'échec.

Fig.1 : Fenêtre principale de virt-manager

Cette fenêtre vous montre l'état des domaines ainsi que leur activité et vous permet de les démarrer, arrêter, regarder.

Fig 2 : Détails d'un domaine

Fig 3 : Nouveau domaine

Vous pouvez installer l'OS de votre choix (attention cependant, il paraît que certains OS ne soient pas libres et qu'il vous faille obtenir une licence), indiquer le type et la version du système permet à libvirt d'optimiser l'image.

Par défaut les images des disques sont dans /var/lib/libvirt/images.

Comme toujours lorsqu'on utilise une interface graphique, certaines opérations sont compliquées, voire impossibles. Ainsi la modification des réseaux virtuels est impossible (en tous cas je n'ai pas réussi) et la modification d'un certain nombre d'éléments nécessite de les supprimer/recréer.

5. La description des objets

Les différents objets (les domaines et les réseaux mais aussi le stockage et tout ce que libvirt manipule) sont représentés par des fichiers XML compréhensibles par un humain. Ces fichiers ne doivent pas être édités directement mais en utilisant virsh (voir un peu plus loin) sous peine de devenir fou à brève échéance. Les modifications que vous leur faites subir n'ont absolument aucun effet même si vous arrêtez et redémarrez l'objet en question.

5.1 Les domaines

Les fichiers xml qui décrivent les domaines sont stockés dans /etc/libvirt/<hyperviseur>/ :

Commençons par un exemple :

# cat /etc/libvirt/qemu/Fedora14.xml

<domain type='kvm'>

  <name>Fedora14</name>

  <uuid>42f1adb0-9ac8-9321-bdba-d5c2413b5be3</uuid>

  <memory>524288</memory>

  <currentMemory>524288</currentMemory>

  <vcpu>1</vcpu>

  <os>

    <type arch='x86_64' machine='fedora-14'>hvm</type>

    <boot dev='hd'/>

  </os>

  <features>

    <acpi/>

    <apic/>

    <pae/>

  </features>

  <clock offset='utc'/>

  <on_poweroff>destroy</on_poweroff>

  <on_reboot>restart</on_reboot>

  <on_crash>restart</on_crash>

  <devices>

    ...

  </devices>

</domain>

La racine de notre fichier xml est domain son attribut type définit l'hyperviseur.

Attention destroy ne signifie pas que le domaine sera détruit mais sauvagement arrêté.

Intéressons-nous aux périphériques. Dans les exemples suivants, le nœud XML address est optionnel et il doit certainement servir à quelque chose.

Les disques :

<disk type='file' device='disk'>

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

      <source file='/var/lib/libvirt/images/fedora14.img'/>

      <target dev='vda' bus='virtio'/>

      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>

</disk>

Nous avons ici affaire à une image de disque qui aurait pu être créée par qemu-img au format brut. D'autres formats sont disponibles, notamment qcow2. L'emplacement du fichier est ensuite indiqué puis le nom du volume et le type de contrôleur pour notre hôte. Vous pouvez utiliser ide ou scsi et combiner les différents contrôleurs comme vous le feriez dans une machine réelle (bien que je n'aie pas trouvé de contrôleurs virtio chez les marchands de matériel). Voici la définition d'un lecteur de cdrom s'appuyant sur un vrai lecteur :

<disk type='block' device='cdrom'>

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

      <target dev='hdc' bus='ide'/>

      <readonly/>

      <address type='drive' controller='0' bus='1' unit='0'/>

</disk>

On aurait tout aussi bien pu écrire :

<disk type='file' device='cdrom'>

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

      <source file='/var/lib/libvirt/images/fedora14.iso'/>

      <target dev='hdc' bus='ide'/>

      <readonly/>

      <shareable/>

      <address type='drive' controller='0' bus='1' unit='0'/>

</disk>

Pour que plusieurs domaines se partagent la même image de cdrom.

Si vous choisissez ide comme interface, ne dépassez pas 4 disques. Certaines versions anciennes de libvirt ne supportent pas scsi ni virtio, notamment celle fournie avec CentOS-5.4.

Les interfaces réseaux :

<interface type='network'>

      <mac address='00:16:32:00:00:01'/>

      <source network='default'/>

      <model type='virtio'/>

      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>

</interface>

L'adresse MAC est générée à la création mais vous pouvez choisir ce que vous voulez à condition de veiller à conserver l'unicité d'icelle au sein d'un même réseau (virtuel ou pas). Pour ma part, je la modifie afin d'obtenir des adresses simples à retenir et à leur affecter des adresses fixes par DHCP. Nous verrons une application de ceci un peu plus loin.

Le modèle doit être choisi dans l'intersection de la liste de ceux supportés par l'hyperviseur.

$ qemu-kvm -net nic,model=? /dev/null

qemu: Supported NIC models: ne2k_pci,i82551,i82557b,i82559er,rtl8139,e1000,pcnet,virtio

et de la liste de ceux supportés par la distribution que vous allez installer (virtio me donne satisfaction)...

Et voila la fin :

<serial type='pty'>

      <target port='0'/>

    </serial>

    <console type='pty'>

      <target type='serial' port='0'/>

    </console>

    <input type='tablet' bus='usb'/>

    <input type='mouse' bus='ps2'/>

Ce nœud nous permet d'obtenir une console sur la machine, graphique ou texte.

    <graphics type='vnc' port='-1' autoport='yes'/>

J'avoue ne pas me préoccuper du son ni du modèle de carte graphique, peut-être ai-je tort.

    <sound model='ac97'>

      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>

    </sound>

    <video>

      <model type='cirrus' vram='9216' heads='1'/>

      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>

    </video>

Et pour finir le ballon de mémoire qui permet de prendre de la RAM à un domaine pour le donner à un autre en cas de besoin pressant.

    <memballoon model='virtio'>

      <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>

    </memballoon>

L'idée est que l'hyperviseur stresse l'OS invité en gonflant le ballon (il lui fait croire qu'il lui prend de la mémoire), l'invité swappe éventuellement et la mémoire récupérée peut être allouée à un autre domaine. Un OS invité swappe plus efficacement que l'hôte qui ne sais pas à quoi est utilisée la mémoire de ses invités. Cette technique de partage de la mémoire physique entre les domaines n'est pas spécifique à libvirt.

5.2 Les réseaux

En langage libvirt, on dit réseaux, au pluriel ! Vous pouvez en effet définir un ou plusieurs réseaux pour y connecter vos machines (pardon, vos domaines). Les réseaux sont définis dans des fichiers xml.

Voici le réseau par défaut :

<network>

  <name>default</name>

  <uuid>845ea2e9-050a-709e-4734-5b54f2ee75cf</uuid>

  <forward mode='nat'/>

Les deux principaux modes de transfert entre le réseau virtuel et le reste du monde sont 'nat' ou 'route'. Dans le premier vos domaines sont invisibles de l'extérieur mais vous pouvez accéder au monde au travers d'une « box » sans avoir à y toucher. Comme on ne crée pas des machines virtuelles uniquement pour jouer à la maison, le deuxième expose vos serveurs au monde (n'oubliez pas de configurer les routes de l'hôte et de choisir des adresses éventuellement publiques).

  <bridge name='virbr0' stp='on' forwardDelay='0' />

  <ip address='192.168.122.1' netmask='255.255.255.0'>

Adresse du pont qui est aussi un serveur dhcp si besoin est. Cette adresse est ici en IPV4, nous verrons plus tard pour la version 6.

    <dhcp>

      <range start='192.168.122.2' end='192.168.122.254' />

    </dhcp>

Paramétrage basique du serveur dhcp :

  </ip>

</network>

Et voilà.

Mais si vous choisissez d'exposer vos domaines au monde, il vaudrait mieux qu'ils aient toujours la même adresse. Cela peut se faire en configurant l'OS invité mais si vous avez noté les adresses MAC des domaines invités (et que vous en avez choisi des simples), vous pouvez configurer le serveur dhcp comme ceci :

<network>

  <name>default</name>

  <uuid>feec4a89-af2f-423b-a34d-85b9f425e902</uuid>

  <forward dev='eth0' mode='route'/>

  <bridge name='virbr0' stp='on' forwardDelay='0' />

  <ip address='192.168.201.254' netmask='255.255.255.0'>

    <dhcp>

      <range start='192.168.201.128' end='192.168.201.253' />

      <host mac='00:16:32:01:02:01' name='vtp1' ip='192.168.201.1' />

      <host mac='00:16:32:01:02:02' name='vtp2' ip='192.168.201.2' />

[...]

      <host mac='00:16:32:01:12:09' name='vtp_fc12_9' ip='192.168.201.129' />

      <host mac='00:16:32:01:12:10' name='vtp_fc12_10' ip='192.168.201.130' />

    </dhcp>

  </ip>

</network>

Vos domaines obtiennent donc un nom et une adresse personnelle sans configuration de l'OS ce qui permet de répliquer les domaines facilement comme nous le verrons plus loin.

5.3 Autres objets

D'autres objets sont modélisés comme des « pools » de stockage ou des règles de filtrage du trafic réseau.

6. La coquille dédiée virsh

Le premier (voire le principal) intérêt de libvirt, nous avons à notre disposition un shell dédié à la gestion de nos jolies petites machines.

Deux façons de l'utiliser :

en mode commande

[root@philippe ~]# virsh <commande> [paramètres]

en mode interactif

[root@philippe ~]# virsh

Bienvenue dans virsh, le terminal de virtualisation interactif.

Taper : « help » pour l'aide ou « help » avec la commande

         « quit » pour quitter

virsh #

La documentation mentionne 145 commandes (21/04/2011) que je ne vais pas énumérer, vous les trouverez ici [Commandes virsh].

Quelques commandes utiles :

  • gestion des domaines
    • define, create → création d'un domaine (la seconde le démarre)
    • start → démarrage d'un domaine
    • destroy → arrêt du domaine
    • edit → modification du domaine, ne surtout pas éditer directement les fichiers
  • gestion du réseau
    • net-define, net-create => création d'un réseau (la seconde le démarre automatiquement)
    • net-start → démarre le réseau
    • net-destroy → arrêt du réseau
    • net-undefine → supprime le réseau
    • net-edit → modification du réseau, ne surtout pas éditer directement les fichiers (je l'ai déjà dit ?)

Un détail qui ne vous aura pas échappé si vous avez lu le début de l'article, « destroy » ne se traduit pas par « détruire » mais par « arrêt » !

7. Automatisons un peu

J'utilise des machines virtuelles pour des séances de travaux pratiques. Il me faut 10 machines identiques que je dois régénérer à la fin de chaque séance. J'ai donc installé classiquement un domaine de référence en utilisant virt-manager.

Je vais ensuite cloner cette machine pour créer automatiquement les fichiers XML qui vont bien. Les nouvelles machines sont dotées de 4 disques (dont 3 servent à jouer avec le RAID logiciel) :

for i in {1..10}

do

#Au cas ou les domaines existeraient on les arrête

virsh destroy TP$i

# on les supprime

virsh undefine TP$i

# et on efface les fichers

rm -f /var/lib/libvirt/images/TP$i

rm -f /var/lib/libvirt/images/hd?TP$i

qemu-img create /var/lib/libvirt/images/hdaTP$i 1G

qemu-img create /var/lib/libvirt/images/hdbTP$i 1G

qemu-img create /var/lib/libvirt/images/hdcTP$i 1G

virt-clone -o /var/lib/libvirt/images/Fedora14 --name TP$i -f /var/lib/libvirt/images/TP$i -f /var/lib/libvirt/images/hdaTP$i -f /var/lib/libvirt/images/hdbTP$i -f /var/lib/libvirt/images/hdcTP$i

done

C'est le moment d'aller boire un petit café... (Ce script est un peu bourrin, je sais)

La partie un peu fastidieuse consiste à éditer chaque domaine pour modifier l'adresse MAC (voir la configuration dhcp ci dessus) et modifier le driver du disque système. Nous allons en effet utiliser pour le disque système une image de type « copy on write ».

La ligne :

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

est remplacée par

<driver name='qemu' type='qcow2'/>

et

<mac address='54:52:00:XX:YY:ZZ'/>

par :

<mac address='00:16:32:00:00:01'/>

Une adresse différente et facilement mémorisable pour chaque domaine !

Puis remplacer les copies en dur des images système par des « copy on write » en utilisant le script de régénération :

for i in {1..10}

do

etat=`virsh domstate TP$i`

if [[ $etat != "fermé" ]]

then virsh destroy TP$i

fi

rm -f /var/lib/libvirt/images/TP$i

rm -f /var/lib/libvirt/images/TP_hd ?_$i

qemu-img create -fqcow2 -b /var/lib/libvirt/images/ Fedora14 /var/lib/libvirt/images/TP$i

qemu-img create /var/lib/libvirt/images/TP_hda_$i 1G

qemu-img create /var/lib/libvirt/images/TP_hdb_$i 1G

qemu-img create /var/lib/libvirt/images/TP_hdc_$i 1G

wait

virsh start TP$i

done

Si le domaine n'est pas « fermé » (à modifier si votre locale n'est pas fr_FR) on le ferme sauvagement. De toute façon on va tout écraser.

On efface l'image système puis on (re)crée une image « copy on write ». L'avantage d'utiliser une telle image , outre l'encombrement disque nettement plus faible, est le temps de copie très réduit (puisqu' on ne copie rien à la création). Je déconseille de modifier l'image de base pendant que les copies sont en service car on ne sait pas ce qui a été modifié sur celles-ci. Enfin on démarre le domaine régénéré.

Un petit script pour arrêter les machines avant d'éteindre l'hôte  :

for i in {1..10}

do

virsh shutdown TP$i

done

Je vous laisse le soin d'en écrire pour démarrer vos domaines (virsh start ?) ou que sais-je encore.

Conclusion

Voilà donc une introduction à cette merveilleuse bibliothèque qu'est libvirt, solution solide complétée d'une suite d'outils en ligne et même graphiques. De plus la possibilité de scripter la gestion des domaines ouvre des perspectives que je n'avais pas osé rêver...

La documentation officielle n'est pas terminée mais est déjà très importante et je vous recommande de la consulter régulièrement.

J'ai à peine évoqué la connexion à distance (utilisable avec virt-manager) qui décuple les possibilité de cet outil. Elle utilise soit un tunnel SSH, soit SSL/TLS (avec certificats), soit une connexion TCP avec SASL ou kerberos. Mais ceci est une autre histoire...

Webographie

[libvirt] libvirt.org

[virt-manager] virt-manager.et.redhat.com

[Commandes virsh] libvirt.org/sources/virshcmdref/html/