Product SiteDocumentation Site

12.2. Virtualizzazione

La virtualizzazione è uno dei più grandi progressi dell'informatica negli ultimi anni. Il termine copre diverse astrazioni e tecniche per simulare macchine virtuali con grado variabile di indipendenza dall'effettivo hardware. Un server fisico può quindi ospitare diversi sistemi contemporaneamente in funzione e isolati fra loro. Le applicazioni sono molte e spesso derivano da questo isolamento; per esempio ambienti di prova con configurazioni variabili, oppure separazioni di servizi ospitati per ragioni di sicurezza su differenti macchine virtuali.
Ci sono numerose soluzioni di virtualizzazione, ciascuna coi suoi pro e contro. Questo libro si concentrerà su Xen, LXC e KVM, ma fra le altre implementazioni degne di nota vi sono le seguenti:

12.2.1. Xen

Xen è una situazione di «paravirtualizzazione». Introduce un sottile strato di astrazione, chiamato «ipervisore», fra l'hardware e i sistemi superiori; ciò agisce come un arbitro che controlla l'accesso all'hardware dalle macchine virtuali. Tuttavia, questo gestisce solo alcune delle istruzioni, mentre il resto è eseguito direttamente dall'hardware per conto dei sistemi. Il vantaggio principale è che non c'è degrado di prestazioni e i sistemi girano a una velocità prossima a quella nativa; il difetto è che i kernel dei sistemi operativi che si vogliono usare su un ipervisore Xen devono essere adattati per girare su Xen.
Un po' di terminologia. L'ipervisore è lo strato inferiore, che gira direttamente sull'hardware, addirittura sotto il kernel. Questo ipervisore può dividere il resto del software su più domini, che possono essere visti come altrettante macchine virtuali. Uno di questi domini (il primo che viene avviato) si chiama dom0 e ha un ruolo speciale, in quanto solo questo dominio può controllare l'ipervisore e l'esecuzione di altri domini. Questi altri domini si chiamano domU. In altre parole, e dal punto di vista dell'utente, il dom0 coincide con l'«host» di altri sistemi di virtualizzazione, mentre un domU può essere visto come «guest».
L'uso di Xen sotto Debian richiede tre componenti:
  • L'hypervisor stesso. A seconda dell'hardware disponibile, il pacchetto appropriato sarà xen-hypervisor-4.4-amd64, xen-hypervisor-4.4-armhf, o xen-hypervisor-4.4-arm64.
  • Un kernel che gira su tale hypervisor. Qualsiasi kernel più recente del 3.0 lo farà, inclusa la versione 3.16 presente in Jessie.
  • L'architettura i386 richiede inoltre una libreria standard con le patch appropriate che si appoggino a Xen; questa si trova nel pacchetto libc6-xen.
Per evitare il fastidio di scegliere a mano queste componenti, per comodità sono stati resi disponibili alcuni pacchetti (come xen-linux-system-amd64); essi scaricano una combinazione funzionante di adeguati pacchetti di hypervisor e kernel. L'hypervisor installa anche xen-utils-4.4, che contiene gli strumenti per controllare l'hypervisor dal dom0. Questo a sua volta installa la libreria standard appropriata. Durante l'installazione di tutto ciò, gli script di configurazione creano anche una nuova voce nel menu del bootloader Grub, in modo da poter avviare il kernel scelto in un dom0 Xen. Notare tuttavia che questa voce non è di solito impostata come la prima della lista, e quindi non sarà selezionata in modo predefinito. Se questo non è il comportamento desiderato, è possibile modificarlo con i seguenti comandi:
# mv /etc/grub.d/20_linux_xen /etc/grub.d/09_linux_xen
# update-grub
Una volta installati questi prerequisiti, il passo successivo è collaudare il comportamento del dom0 da solo; questo richiede un riavvio per entrare nell'ipervisore e nel kernel Xen. Il sistema dovrebbe avviarsi nel modo consueto, mostrando alcuni messaggi in più nella console durante i primi passi dell'inizializzazione.
Ora è il momento di installare veramente dei sistemi utili sui sistemi domU, usando gli strumenti di xen-tools. Questo pacchetto fornisce il comando xen-create-image, che automatizza gran parte del compito. L'unico parametro obbligatorio è --hostname, che dà un nome al domU; altre opzioni sono importanti, ma possono essere memorizzate nel file di configurazione /etc/xen-tools/xen-tools.conf e la loro mancanza dalla riga di comando non genera un errore. Perciò è importante controllare i contenuti di questo file prima di creare delle immagini oppure, in alternativa, usare parametri aggiuntivi nell'esecuzione di xen-create-image. I parametri importanti includono:
  • --memory, per specificare la quantità di RAM dedicata al sistema appena creato;
  • --size e --swap, per definire la dimensione dei «dischi virtuali» disponibili al domU;
  • --debootstrap, per poter installare il nuovo sistema con debootstrap; in quel caso, verrà usata spesso anche l'opzione --dist (con il nome di una distribuzione come jessie).
  • --dhcp dichiara che la configurazione di rete del domU deve essere ottenuta tramite DHCP mentre --ip permette di definire un indirizzo IP statico.
  • Da ultimo, bisogna scegliere un metodo di memorizzazione per le immagini da creare (quelle che saranno viste come dischi fissi dal domU). Il metodo più semplice, che corrisponde all'opzione --dir, è di creare un file sul dom0 per ogni dispositivo da rendere disponibile al domU. Per i sistemi che usano LVM, l'alternativa è usare l'opzione --lvm, seguita dal nome di un gruppo di volume; quindi xen-create-image creerà un nuovo volume logico dentro quel gruppo e questo volume logico sarà reso disponibile al domU come disco fisso.
Una volta effettuate queste scelte, si può creare l'immagine per il futuro domU Xen:
# xen-create-image --hostname testxen --dhcp --dir /srv/testxen --size=2G --dist=jessie --role=udev

[...]
General Information
--------------------
Hostname       :  testxen
Distribution   :  jessie
Mirror         :  http://ftp.debian.org/debian/
Partitions     :  swap            128Mb (swap)
                  /               2G    (ext3)
Image type     :  sparse
Memory size    :  128Mb
Kernel path    :  /boot/vmlinuz-3.16.0-4-amd64
Initrd path    :  /boot/initrd.img-3.16.0-4-amd64
[...]
Logfile produced at:
         /var/log/xen-tools/testxen.log

Installation Summary
---------------------
Hostname        :  testxen
Distribution    :  jessie
MAC Address     :  00:16:3E:8E:67:5C
IP-Address(es)  :  dynamic
RSA Fingerprint :  0a:6e:71:98:95:46:64:ec:80:37:63:18:73:04:dd:2b
Root Password   :  adaX2jyRHNuWm8BDJS7PcEJ
Adesso è stata creata una macchina virtuale, ma attualmente non è in esecuzione (e quindi occupa solo spazio sul disco fisso del dom0). Ovviamente si possono creare altre immagini, magari con parametri diversi.
Prima di accendere queste macchine virtuali, bisogna definirne le modalità di accesso. Ovviamente possono essere considerate come macchine isolate, a cui si accederà tramite la loro console di sistema, ma raramente vengono usate in questo modo. Nella maggior parte dei casi, un domU sarà considerato un server remoto e vi si accederà solo via rete. Tuttavia sarebbe molto scomodo aggiungere una scheda di rete per ogni domU; per questo Xen permette di creare interfacce virtuali, che ogni dominio può vedere e usare in modo standard. Notare che queste schede, seppur virtuali, saranno utili solo una volta connesse a una rete, anche solo virtuale. A questo scopo, Xen ha diversi modelli di rete:
  • Il modello più semplice è il modello bridge; tutte le schede di rete eth0 (sia nel dom0 che nei sistemi domU) si comportano come se fossero direttamente inserite in uno switch Ethernet.
  • C'è poi il modello routing, dove il dom0 si comporta come un router che sta fra i sistemi domU e la rete esterna (fisica).
  • Infine, nel modello NAT, il dom0 è di nuovo fra i sistemi domU e il resto della rete, ma i sistemi domU non sono direttamente accessibili dall'esterno e il traffico passa attraverso alcune traduzioni degli indirizzi di rete sul dom0.
Queste tre modalità di rete comprendono alcune interfacce dai nomi insoliti, come vif*, veth*, peth* e xenbr0. L'ipervisore Xen le dispone in qualunque configurazione sia stata definita, sotto il controllo degli strumenti nello spazio utente. Poiché le modalità NAT e routing si adattano solo a casi particolari, qui si descriverà solo il modello di bridge.
La configurazione standard dei pacchetti Xen non cambia la configurazione di rete di sistema. Tuttavia, il demone xend è configurato per integrare le interfacce di rete virtuali in qualunque bridge di rete preesistente (con precedenza a xenbr0 se esiste più di un bridge). Bisogna quindi impostare un bridge in /etc/network/interfaces (il che richiede l'installazione del pacchetto bridge-utils, che è il motivo per cui xen-utils-4.4 lo raccomanda) per sostituire la voce esistente relativa a eth0:
auto xenbr0
iface xenbr0 inet dhcp
    bridge_ports eth0
    bridge_maxwait 0
Dopo il riavvio per assicurarsi che il bridge sia creato automaticamente, si può ora avviare il domU con gli strumenti di controllo di Xen, in particolare il comando xl. Questo comando permette diverse manipolazioni sui domini, fra cui elencarli, avviarli e fermarli.
# xl list
Name                                        ID   Mem VCPUs      State   Time(s)
Domain-0                                     0   463     1     r-----      9.8
# xl create /etc/xen/testxen.cfg
Parsing config from /etc/xen/testxen.cfg
# xl list
Name                                        ID   Mem VCPUs      State   Time(s)
Domain-0                                     0   366     1     r-----     11.4
testxen                                      1   128     1     -b----      1.1
Notare che il domU testxen usa memoria fisica presa dalla RAM che altrimenti sarebbe disponibile per il dom0, non memoria simulata. Pertanto bisogna fare attenzione, quando si assembla un server che deve ospitare istanze di Xen, a fornire RAM fisica secondo le necessità.
Voilà! La macchina virtuale è partita. Vi si può accedere in uno dei due modi. Il modo consueto è di connettersi ad essa "in remoto" tramite la rete, come ci si connetterebbe a una macchina reale; questo di solito richiederà di impostare un server DHCP o qualche configurazione di DNS. L'altro modo, che potrebbe essere l'unico se la configurazione di rete era errata, è di usare la console hvc0, con il comando xl console:
# xl console testxen
[...]

Debian GNU/Linux 8 testxen hvc0

testxen login: 
A questo punto si può aprire una sessione, proprio come si farebbe davanti alla tastiera della macchina virtuale. Lo scollegamento da questa console si ottiene con la combinazione di tasti Control+].
Una volta che il domU è attivo, può essere usato come qualunque altro server (visto che dopo tutto è un sistema GNU/Linux). Tuttavia, il suo stato di macchina virtuale permette di sfruttare alcune funzionalità aggiuntive. Ad esempio, un domU può essere temporaneamente messo in pausa e poi fatto uscire dalla pausa con i comandi xl pause e xl unpause. Notare che, sebbene un domU in pausa non usi affatto il processore, la memoria ad esso allocata è ancora in uso. Può essere interessante considerare i comandi xl save e xl restore: salvare un domU libera le risorse precedentemente usate da questo domU, compresa la RAM. Al ripristino (o all'uscita dalla pausa, se è per quello) un domU non si accorge di alcunché al di là del passare del tempo. Se un domU era in esecuzione quando il dom0 viene spento, gli script nel pacchetto salvano automaticamente il domU e lo ripristinano all'avvio successivo. Questo ovviamente comporterà i consueti inconvenienti che si riscontrano quando si iberna un computer portatile, per esempio; in particolare, se il domU viene sospeso per troppo tempo, le connessioni di rete possono scadere. Notare inoltre che a tutt'oggi Xen è incompatibile con gran parte della gestione energetica ACPI, il che impedisce di sospendere il sistema host (dom0).
Si può arrestare o riavviare un domU da dentro il domU (con il comando shutdown) o dal dom0, con xm shutdown o xl reboot.

12.2.2. LXC

Anche se è usato per costruire "macchine virtuali", LXC non è, propriamente, un sistema di virtualizzazione, ma un sistema per isolare gruppi di processi l'uno dall'altro pur girando tutti sullo stesso host. Sfrutta alcune evoluzioni recenti nel kernel Linux, comunemente note come gruppi di controllo, con cui diversi insiemi di processi chiamati "gruppi" hanno visioni diverse di certi aspetti del sistema globale. Fra questi aspetti, i più importanti sono gli identificatori dei processi, la configurazione di rete e i punti di mount. Tale gruppo di processi isolati non avrà accesso agli altri processi nel sistema, ed i suoi accessi al file system possono essere ristretti a uno specifico sottoinsieme. Può anche avere la propria interfaccia di rete e la propria tabella di routing e può essere configurato per vedere solo un sottoinsieme dei dispositivi disponibili presenti sul sistema.
Queste funzionalità possono essere combinate per isolare un'intera famiglia di processi a partire dal processo init, e l'insieme che ne risulta è molto simile ad una macchina virtuale. Il nome ufficiale per una impostazione come questa è "contenitore" (da cui il nomignolo LXC: LinuX Containers), ma una differenza importante rispetto alle "vere" macchine virtuali come quelle fornite da Xen o KVM è che non c'è un secondo kernel; il contenitore usa lo stesso kernel del sistema host. Questo ha dei pro e dei contro: fra i vantaggi c'è la totale assenza di carico aggiuntivo e quindi costi prestazionali e il fatto che il kernel ha una visione globale di tutti i processi che girano sul sistema, quindi lo scheduling può essere più efficiente che nel caso in cui due kernel indipendenti dovessero ordinare diversi insiemi di task. Il principale svantaggio è l'impossibilità di far girare un diverso kernel in un contenitore (sia una diversa versione di Linux sia un sistema operativo del tutto diverso).
Poiché si parla di isolamento e non di virtualizzazione vera e propria, impostare i contenitori LXC è più complesso che far girare debian-installer su una macchina virtuale. Verranno descritti alcuni prerequisiti, e poi si passerà alla configurazione di rete; a questo punto si potrà effettivamente creare il sistema da far girare nel contenitore.

12.2.2.1. Passi preliminari

Il pacchetto lxc contiene gli strumenti necessari per far girare LXC e quindi deve essere installato.
LXC richiede anche il sistema di configurazione dei gruppi di controllo, che è un filesystem virtuale da montare su /sys/fs/cgroup. Dal momento che Debian 8 è passata a systemd, che si basa anche su gruppi di controllo, questo ora è fatto automaticamente al boot senza ulteriori configurazioni.

12.2.2.2. Configurazione di rete

Lo scopo dell'installazione di LXC è di impostare delle macchine virtuali; pur potendo ovviamente tenerle isolate dalla rete e comunicare con loro solo tramite il file system, la maggior parte dei casi d'uso richiede di dare almeno un minimo accesso di rete ai contenitori. Nel caso tipico, ciascun contenitore avrà un'interfaccia di rete virtuale, connessa con la rete reale tramite un bridge. Questa interfaccia virtuale può essere inserita direttamente sull'interfaccia fisica di rete dell'host (nel qual caso il contenitore è direttamente in rete) o su un'altra interfaccia virtuale definita sull'host (e l'host può allora filtrare o ridirigere il traffico). In entrambi i casi, sarà richiesto il pacchetto bridge-utils.
Il caso semplice richiede solo di modificare /etc/network/interfaces, spostare la configurazione dell'interfaccia fisica (per esempio eth0) su un'interfaccia bridge (di solito br0) e configurare il link fra essi. Per esempio, se il file di configurazione dell'interfaccia di rete contiene voci come le seguenti:
auto eth0
iface eth0 inet dhcp
Devono essere disabilitate e sostituite con le seguenti:
#auto eth0
#iface eth0 inet dhcp

auto br0
iface br0 inet dhcp
  bridge-ports eth0
L'effetto di questa configurazione sarà simile a ciò che si otterrebbe se i contenitori fossero macchine collegate alla stessa rete fisica dell'host. La configurazione «bridge» gestisce il transito dei frame Ethernet fra tutte le interfacce in bridge, il che include la eth0 fisica oltre alle interfacce definite per i contenitori.
Nei casi in cui questa configurazione non si può usare (per esempio se non si possono assegnare IP pubblici ai contenitori), un'interfaccia virtuale tap verrà creata e connessa al bridge. A quel punto la topologia di rete equivalente diventa quella di un host con una seconda scheda di rete inserita in uno switch separato, con i contenitori anch'essi inseriti in quello switch. L'host allora deve agire da gateway per i contenitori se questi devono comunicare con il mondo esterno.
Oltre a bridge-utils, questa configurazione «ricca» richiede il pacchetto vde2; il file /etc/network/interfaces allora diventa:
# Interface eth0 is unchanged
auto eth0
iface eth0 inet dhcp

# Virtual interface 
auto tap0
iface tap0 inet manual
  vde2-switch -t tap0

# Bridge for containers
auto br0
iface br0 inet static
  bridge-ports tap0
  address 10.0.0.1
  netmask 255.255.255.0
La rete allora può essere impostata staticamente nei contenitori o dinamicamente con un server DHCP che gira sull'host. Tale server DHCP dovrà essere configurato per rispondere alle richieste sull'interfaccia br0.

12.2.2.3. Impostazione del sistema

Ora si imposta il file system che il contenitore dovrà usare. Poiché questa "macchina virtuale" non girerà direttamente sull'hardware, servono alcuni accorgimenti rispetto a un filesystem standard, in particolare riguardo al kernel, i dispositivi e le console. Per fortuna, lxc include degli script che automatizzano gran parte di questa configurazione. Per esempio, i seguenti comandi (che richiedono i pacchetti debootstrap e rsync) installeranno un contenitore Debian:
root@mirwiz:~# lxc-create -n testlxc -t debian
debootstrap is /usr/sbin/debootstrap
Checking cache download in /var/cache/lxc/debian/rootfs-jessie-amd64 ... 
Downloading debian minimal ...
I: Retrieving Release 
I: Retrieving Release.gpg 
[...]
Download complete.
Copying rootfs to /var/lib/lxc/testlxc/rootfs...
[...]
Root password is 'sSiKhMzI', please change !
root@mirwiz:~# 
Notare che il file system è creato all'inizio in /var/cache/lxc e poi spostato nella sua directory di destinazione. Ciò permette di creare contenitori identici molto più rapidamente, visto che a questo punto basta copiarli.
Da notare che lo script di creazione dei modelli debian accetta l'opzione --arch per specificare l'architeturra del sistema da installare ed un'opzione --release se si vuole installare qualcos'altro rispetto all'attuale versione stabile di Debian. E' anche possibile impostare la variabile d'ambiente MIRROR per puntare ad un mirror locale di Debian.
Il filesystem appena creato contiene ora un sistema Debian minimale, e per impostazione predefinita il contenitore non ha alcuna interfaccia di rete (oltre il loopback uno). Poiché questo non è veramente voluto, è possibile modificare il file di configurazione del contenitore (/var/lib/lxc/testlxc/config) e aggiungere un paio di voci lxc.network.*:
lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = br0
lxc.network.hwaddr = 4a:49:43:49:79:20
Queste voci vogliono dire, rispettivamente, che verrà creata un'interfaccia virtuale nel contenitore; che verrà automaticamente attivata quando il suddetto contenitore verrà avviato; che verrà automaticamente connessa al bridge br0 sull'host; e che il suo indirizzo MAC sarà quello specificato. Se l'ultima voce fosse assente o disabilitata, verrà generato un indirizzo MAC casuale.
Un'altra voce di utile in quel file è l'impostazione del nome host:
lxc.utsname = testlxc

12.2.2.4. Avvio del contenitore

Ora che l'immagine della macchina virtuale è pronta, si avvia il contenitore:
root@mirwiz:~# lxc-start --daemon --name=testlxc
root@mirwiz:~# lxc-console -n testlxc
Debian GNU/Linux 8 testlxc tty1

testlxc login: root
Password: 
Linux testlxc 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt11-1 (2015-05-24) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
root@testlxc:~# ps auxwf
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.2  28164  4432 ?        Ss   17:33   0:00 /sbin/init
root        20  0.0  0.1  32960  3160 ?        Ss   17:33   0:00 /lib/systemd/systemd-journald
root        82  0.0  0.3  55164  5456 ?        Ss   17:34   0:00 /usr/sbin/sshd -D
root        87  0.0  0.1  12656  1924 tty2     Ss+  17:34   0:00 /sbin/agetty --noclear tty2 linux
root        88  0.0  0.1  12656  1764 tty3     Ss+  17:34   0:00 /sbin/agetty --noclear tty3 linux
root        89  0.0  0.1  12656  1908 tty4     Ss+  17:34   0:00 /sbin/agetty --noclear tty4 linux
root        90  0.0  0.1  63300  2944 tty1     Ss   17:34   0:00 /bin/login --     
root       117  0.0  0.2  21828  3668 tty1     S    17:35   0:00  \_ -bash
root       268  0.0  0.1  19088  2572 tty1     R+   17:39   0:00      \_ ps auxfw
root        91  0.0  0.1  14228  2356 console  Ss+  17:34   0:00 /sbin/agetty --noclear --keep-baud console 115200 38400 9600 vt102
root       197  0.0  0.4  25384  7640 ?        Ss   17:38   0:00 dhclient -v -pf /run/dhclient.eth0.pid -lf /var/lib/dhcp/dhclient.e
root       266  0.0  0.1  12656  1840 ?        Ss   17:39   0:00 /sbin/agetty --noclear tty5 linux
root       267  0.0  0.1  12656  1928 ?        Ss   17:39   0:00 /sbin/agetty --noclear tty6 linux
root@testlxc:~# 
Ora ci si trova nel contenitore; l'accesso ai processi è ristretto solo a quelli avviati dal contenitore stesso e l'accesso al filesystem è analogamente ristretto al sottoinsieme dedicato del filesystem completo (/var/lib/lxc/testlxc/rootfs). Si può uscire dalla console con Control+a q.
Da notare che abbiamo avviato il contenitore come processo in background, grazie all'opzione --daemon di lxc-start. Si può interrompere il contenitore con un comando del tipo lxc-stop --name=testlxc.
Il pacchetto lxc contiene uno script di inizializzazione che può avviare automaticamente uno o più contenitori quando si avvia l'host (si basa su lxc-autostart che avvia i contenitori che hanno l'opzione lxc.start.auto impostata a 1). Un controllo più dettagliato dell'ordine di avvio è possibile con lxc.start.order e lxc.group: per impostazione predefinita, lo script di inizializzazione avvia prima i contenitori che fanno parte del gruppo onboot e poi i contenitori che non fanno parte di alcun gruppo. In entrambi i casi, l'ordine all'interno di un gruppo è definito dall'opzione lxc.start.order.

12.2.3. Virtualizzazione con KVM

KVM, che sta per Kernel-based Virtual Machine (Macchina Virtuale basata su Kernel), è prima di tutto un modulo del kernel che fornisce la maggior parte dell'infrastruttura che può essere usata da un virtualizzatore, ma di per sé non è un virtualizzatore. Il controllo effettivo della virtualizzazione è gestito da un'applicazione basata su QEMU. Non c'è da preoccuparsi se questa sezione menziona comandi qemu-*: si parla comunque di KVM.
Contrariamente ad altri sistemi di virtualizzazione, KVM è stato incluso nel kernel Linux fin dall'inizio.I suoi sviluppatori hanno scelto di sfruttare le istruzioni dei processori dedicate alla virtualizzazione (Intel-VT e AMD-V), cosa che mantiene KVM leggero, elegante e parco di risorse. Il rovescio della medaglia, ovviamente, è che KVM non funziona su tutti i computer ma solo su quelli con procesori adatti. Per i computer x86-based, è possibile verificare di avere un tale processore cercando vmx” o “svm” tra i flag della CPU elencati in /proc/cpuinfo.
Con il supporto attivo al suo sviluppo da parte di Red Hat, KVM sembra destinato a diventare il punto di riferimento per la virtualizzazione in Linux.

12.2.3.1. Passi preliminari

Contrariamente a strumenti come VirtualBox, KVM di per sé non include un'interfaccia utente per creare e gestire macchine virtuali. Il pacchetto qemu-kvm fornisce solo un eseguibile in grado di avviare una macchina virtuale, oltre a uno script di inizializzazione che carica i moduli appropriati del kernel.
Per fortuna, Red Hat fornisce anche un altro insieme di strumenti per affrontare questo problema, sviluppando la libreria libvirt e gli strumenti virtual machine manager associati. libvirt permette di gestire macchine virtuali in modo uniforme, indipendentemente dal sistema di virtualizzazione dietro le quinte (attualmente supporta QEMU, KVM, Xen, LXC, OpenVZ, VirtualBox, VMWare e UML). virtual-manager è un'interfaccia grafica che usa libvirt per creare e gestire macchine virtuali.
Prima di tutto si installano i pacchetti richiesti, con apt-get install qemu-kvm libvirt-bin virtinst virt-manager virt-viewer. libvirt-bin fornisce il demone libvirtd, che permette di gestire (potenzialmente da remoto) le macchine virtuali che girano sull'host e fa partire le VM richieste all'avvio dell'host. Inoltre, questo pacchetto fornisce lo strumento a riga di comando virsh, che permette di controllare le macchine gestite da libvirtd.
Il pacchetto virtinst fornisce virt-install, che permette di creare macchine virtuali da riga di comando. Infine, virt-viewer permette di accedere alla console grafica di una VM.

12.2.3.2. Configurazione di rete

Proprio come in Xen e LXC, la configurazione di rete più frequente richiede un bridge che raggruppa le interfacce di rete delle macchine virtuali (vedere Sezione 12.2.2.2, «Configurazione di rete»).
In alternativa e nella configurazione predefinita fornita da KVM, alla macchina virtuale è assegnato un indirizzo privato (nell'intervallo 192.168.122.0/24) e viene impostato il NAT cosicché la VM possa accedere alla rete esterna.
Il resto di questa sezione assume che l'host abbia un'interfaccia fisica eth0 e un bridge br0 e che la prima sia connessa al secondo.

12.2.3.3. Installazione con virt-install

Creare una macchina virtuale è molto simile a installare un sistema normale, tranne che le caratteristiche della macchina virtuale sono descritte da una riga di comando che sembra infinita.
In pratica, questo vuol dire che si userà l'installer Debian, avviando la macchina virtuale su un lettore DVD-ROM virtuale che viene mappato su un'immagine DVD di Debian memorizzata sul sistema host. La VM esporterà la sua console grafica sul protocollo VNC (vedere Sezione 9.2.2, «Utilizzo di desktop remoti grafici» per i dettagli), il che consentirà di controllare il processo di installazione.
Prima bisogna dire a libvirtd dove memorizzare le immagini su disco, se non va bene la posizione predefinita (/var/lib/libvirt/images/).
root@mirwiz:~# mkdir /srv/kvm
root@mirwiz:~# virsh pool-create-as srv-kvm dir --target /srv/kvm
Pool srv-kvm created

root@mirwiz:~# 
Si avvia il processo di installazione per la macchina virtuale e si guardano più da vicino le opzioni più importanti di virt-install. Questo comando registra la macchina virtuale e i suoi parametri in libvirtd, quindi la avvia cosicché la sua installazione può procedere.
# virt-install --connect qemu:///system  1
               --virt-type kvm           2
               --name testkvm            3
               --ram 1024                4
               --disk /srv/kvm/testkvm.qcow,format=qcow2,size=10 5
               --cdrom /srv/isos/debian-8.1.0-amd64-netinst.iso  6
               --network bridge=br0      7
               --vnc                     8
               --os-type linux           9
               --os-variant debianwheezy

Starting install...
Allocating 'testkvm.qcow'             |  10 GB     00:00
Creating domain...                    |    0 B     00:00
Guest installation complete... restarting guest.

1

L'opzione --connect specifica l'«ipervisore» da usare. La sua forma è quella di un URL contenente un sistema di virtualizzazione (xen://, qemu://, lxc://, openvz://, vbox:// e così via) e la macchina che deve ospitare la VM (questo può essere lasciato vuoto nel caso dell'host locale). Inoltre e nel caso di QEMU/KVM ciascun utente può gestire macchine virtuali che funzionano con permessi ristretti e il percorso nell'URL permette di differenziare le macchine «di sistema» (/system) dalle altre (/session).

2

Poiché KVM è gestito allo stesso modo di QEMU, --virt-type kvm permette di specificare l'uso di KVM anche se l'URL sembra quello di QEMU.

3

L'opzione --name definisce un nome (unico) per la macchina virtuale.

4

L'opzione --ram permette di specificare la quantità di RAM (in MB) da allocare per la macchina virtuale.

5

--disk specifica la posizione del file immagine che deve rappresentare il disco fisso della macchina virtuale; quel file è creato, se non presente, con una dimensione (in GB) specificata dal parametro size. Il parametro format permette di scegliere fra vari modi di memorizzare il file immagine. Il formato predefinito (raw) è un singolo file che combacia esattamente con la dimensione e i contenuti del disco. Qui la scelta è di prendere un formato più avanzato, specifico di QEMU e che permette di iniziare con un file piccolo che cresce solo quando la macchina virtuale comincia effettivamente ad usare spazio.

6

L'opzione --cdrom è usata per indicare dove trovare il disco ottico da usare per l'installazione. Il percorso può essere un percorso locale di un file ISO, un URL dove reperire il file o il device di un lettore CD-ROM fisico (es. /dev/cdrom).

7

--network specifica come la scheda di rete virtuale si integra nella configurazione di rete dell'host. Il comportamento predefinito (che in questo esempio è esplicitamente forzato) è di integrarla in un qualunque bridge di rete preesistente. Se non esiste un tale bridge, la macchina virtuale raggiungerà la rete fisica solo tramite NAT, quindi riceve un indirizzo in un intervallo di una sottorete privata (192.168.122.0/24).

8

--vnc indica che la console grafica deve essere resa disponibile tramite VNC. Il comportamento predefinito per il server VNC associato è di ascoltare solo sull'interfaccia locale; se il client VNC deve girare su un host diverso, si dovrà impostare un tunnel SSH per stabilire la connessione (vedere Sezione 9.2.1.3, «Creazione di tunnel cifrati con il port forwarding»). In alternativa, si può usare --vnclisten=0.0.0.0 in modo che il server VNC sia accessibile da tutte le interfacce; notare che in questo caso, sarebbe veramente necessario configurare un firewall di conseguenza.

9

Le opzioni --os-type e --os-variant permettono di ottimizzare alcuni parametri della macchina virtuale, basandosi su alcune delle funzionalità note del sistema operativo lì menzionato.
A questo punto la macchina virtuale è in esecuzione e bisogna connettersi alla console grafica per procedere con il processo di installazione. Se la precedente operazione è stata lanciata da un ambiente desktop grafico, questa connessione dovrebbe essere avviata automaticamente. In caso contrario, o in caso si operi da remoto, si può eseguire virt-viewer da qualunque ambiente grafico per aprire la console grafica (notare che la password di root dell'host remoto viene chiesta due volte perché l'operazione richiede 2 connessioni SSH):
$ virt-viewer --connect qemu+ssh://root@server/system testkvm
root@server's password: 
root@server's password: 
Al termine del processo di installazione, la macchina virtuale viene riavviata ed è ora pronta all'uso.

12.2.3.4. Gestire macchine con virsh

Ora che l'installazione è terminata, si passa a come gestire le macchine virtuali disponibili. La prima cosa da provare è chiedere a libvirtd la lista delle macchine virtuali che gestisce:
# virsh -c qemu:///system list --all
 Id Name                 State
----------------------------------
  - testkvm              shut off
Si avvia la macchina virtuale di prova:
# virsh -c qemu:///system start testkvm
Domain testkvm started
Si possono ora ottenere le istruzioni per connettersi alla console grafica (il display VNC restituito può essere passato come parametro a vncviewer):
# virsh -c qemu:///system vncdisplay testkvm
:0
Altri sottocomandi disponibili di virsh includono:
  • reboot per riavviare una macchina virtuale;
  • shutdown per provocare uno spegnimento pulito;
  • destroy per fermarla brutalmente;
  • suspend per metterla in pausa;
  • resume per farla uscire dalla pausa;
  • autostart per abilitare (o disabilitare, con l'opzione --disable) l'avvio automatico della macchina virtuale all'avvio dell'host;
  • undefine per rimuovere ogni traccia della macchina virtuale da libvirtd.
Tutti questi sottocomandi accettano un identificatore di macchina virtuale come parametro.

12.2.3.5. Installazione di un sistema basato su RPM in Debian con yum

Se la macchina virtuale è destinata a far girare una Debian (o una delle sue derivate), il sistema può essere inizializzato con debootstrap, come descritto sopra. Ma se la macchina virtuale deve essere installato con un sistema basato su RPM (come Fedora, CentOS o Scientific Linux), l'installazione dovrà essere effettuata utilizzando l'utility yum (disponibile nel pacchetto dello stesso nome).
La procedura richiede l'uso di rpm per estrarre un set iniziale di file, tra cui in particolare il file di configurazione di yum, e quindi chiamando yum per estrarre il rimanente gruppo di pacchetti. Ma dal momento che noi chiamiamo yum da fuori dalla chroot, abbiamo bisogno di fare alcune modifiche temporanee. Nell'esempio sotto, l'obiettivo di chroot è /srv/centos.
# rootdir="/srv/centos"
# mkdir -p "$rootdir" /etc/rpm
# echo "%_dbpath /var/lib/rpm" > /etc/rpm/macros.dbpath
# wget http://mirror.centos.org/centos/7/os/x86_64/Packages/centos-release-7-1.1503.el7.centos.2.8.x86_64.rpm
# rpm --nodeps --root "$rootdir" -i centos-release-7-1.1503.el7.centos.2.8.x86_64.rpm
rpm: RPM should not be used directly install RPM packages, use Alien instead!
rpm: However assuming you know what you are doing...
warning: centos-release-7-1.1503.el7.centos.2.8.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID f4a80eb5: NOKEY
# sed -i -e "s,gpgkey=file:///etc/,gpgkey=file://${rootdir}/etc/,g" $rootdir/etc/yum.repos.d/*.repo
# yum --assumeyes --installroot $rootdir groupinstall core
[...]
# sed -i -e "s,gpgkey=file://${rootdir}/etc/,gpgkey=file:///etc/,g" $rootdir/etc/yum.repos.d/*.repo