Table of Contents

Virtualizzazione con Linux

Sistemi di virtualizzazione provati:

Linux kernel KVM

Una soluzione più semplice (perché integrata nel kernel) e più performante (perché realizzata in hardware) è costituita dal Kernel Based Virtual Machine. Per attivarla bisogna avere un kernel >= 2.6.20 e un processore con le estensioni Intel VT oppure AMD-V.

Per vedere se il processore ha tali estensioni controllare che in /proc/cpuinfo sia presente il flag vmx (per i processori Intel) svm (per i processori AMD).

Per utilizzare KVM in Debaian Lenny si installa il pacchetto kvm, i moduli kernel necessarie vanno compilati manualmente grazie ai pacchetti kvm-source e module-assistant:

module-assistant auto-install --force kvm

Se si sta usando un kernel compilato in casa alla Debian way, il programma make-kpkg provvede a creare insieme al pacchetto del kernel anche il pacchetto con i moduli kvm-modules.

Vedere le istruzioni /usr/share/doc/kvm/README.Debian.

Dopo aver installato i moduli si caricano (esempio per processore AMD):

modprobe kvm
modprobe kvm_amd

Configurazione host virtuale KVM

Installando la libreria libvirt si controllano in modo uniforme e trasparente macchine virtuali di tipo diverso: Qemu, KVM, Xen, ecc. La libreria fornisce tool grafici o testuali

Un host virtuale (detto anche dominio nella terminologia KVM) si configura in libvirt tramite un file .xml, la cui sintassi è documentata su libvirt.org.

Nella pagina Switching to libvirt managed QEMU instances sono documentati alcune funzioni di qemu/kvm e i corrispondenti comandi della virsh e le opzioni di configurazione per il file .xml.

In questa pagina trovate alcuni esempi di configurazione.

Configurazione network

Esistono due modalità per il networking delle macchine guest (macchine virtuali). La modalità bridge è più potente e flessibile, ma richiede una configurazione speciale del networking sulla macchina host (macchina ospitante). La modalità network è invece più semplice da attivare, offre alle macchine guest un ambiente DHCP ed un NAT virtualizzato dalla macchina ospitante.

Modalità bridge

L'host (macchina ospitante) deve modificare la configurazione della propria scheda Ethernet (eth1 nel nostro esempio) in un bridge, mentre l'interfaccia eth1 rimane non configurata. Il tutto si ottiene con una entry di questo tipo in /etc/network/interfaces:

auto br0
iface br0 inet static
        address 192.168.3.1
        netmask 255.255.255.0
        network 192.168.3.0
        broadcast 192.168.3.255
        bridge_ports eth1
        bridge_stp off
        bridge_maxwait 10

Bisogna aver installato il pacchetto bridge-utils e si può consultare la man page di bridge-utils-interfaces(5) per i vari parametri.

Un guest virtuale dovrà avere nella propria configurazione, nella sezione <devices>, una sezione del tipo:

    <interface type='bridge'>
      <model type='virtio'/>
      <mac address='52:54:00:12:34:65'/>
      <source bridge='br0'/>
      <target dev='tap65'/>
      <script path='no'/>
    </interface>

Quando la macchina virtuale verrà avviata, avrà a disposizione una scheda Ethernet virtuale con il MAC address specificato, tramite un'interfaccia tapX (univoca per ogni guest) sarà connessa al bridge virtuale br0 (condiviso da tutti i guest e dalla macchina host).

In questo modo le macchine virtuali si troveranno sulla stessa LAN della macchina ospitante e potranno usufruire dei servizi esistenti, tipo DHCP, default gateway, ecc. Per vedere quali interfacce sono state collegate al bridge virtuale br0 si esegue il comando:

# brctl show 
bridge name     bridge id               STP enabled     interfaces
br0             8000.002586e5fb2e       no              eth1
                                                        tap65

La scheda di rete emulata ha bisogno di opportuni driver nella macchina guest. Linux offre nativamente il supporto per tale scheda di rete (virtio-net), mentre con sistemi MS-Windows bisogna procurarsi i driver kvm-guest-drivers-windows-2.zip dal repository di Linux KVM.

Modalità network

Nella modalità network al guest viene offerto un servizio DHCP ed accesso alla rete dietro NAT offerto dall'host.

Bisogna attivare la configurazione di rete default definita in /etc/libvirt/qemu/networks/default.xml:

virsh # net-start default

Oppure rendere tale configurazione attiva per default (questa azione crea un link simbolico in /etc/libvirt/qemu/networks/autostart/):

virsh # net-autostart default

Con queste premesse un guest può avere la seguente sezione nel file .xml:

    <interface type='network'>
      <source network='default'/>
      <mac address='52:54:00:00:03:41'/>
    </interface>

Anche questa configurazione comunque crea un'interfaccia virtuale vnet0 e la aggiunge ad un bridge virbr0. L'interfaccia bridge avrà indirizzo 192.168.122.1 e verrà usata per tutti gli host virtuali che invece apparterranno alla network 192.168.122.[2-254].

La configurazione dell'interfaccia virbr0 della macchina ospitante e le impostazioni del DHCP virtuale sono nel file /etc/libvirt/qemu/networks/default.xml:

<network>
  <name>default</name>                    
  <bridge name="virbr0" />
  <forward/>                    
  <ip address="192.168.122.1" netmask="255.255.255.0">
    <dhcp>                  
      <range start="192.168.122.100" end="192.168.122.254" />
      <host mac="52:54:00:00:03:11" name="omega.rigacci.org" ip="192.168.122.11" />
    </dhcp>                  
  </ip>                    
</network>

Dopo aver cambiato il file di configurazione non basta riavviare il servizio libvirt-bin, perché l'istanza di dnsmasq che gestisce il DHCP non viene riavviata. La procedura corretta dovrebbe essere quella di riavviare la rete virtuale default dalla virsh, con i comandi net-destroy default e net-start default, ma si deve fare attenzione alle macchine virtuali in esecuzione (perdono l'accesso alla rete?). È possibile killare il processo dnsmasq e riavviarlo con i parametri nuovi impostandoli manualmente.

Starting a KVM virtual machine on an headless host

:!: NOTE: This is not the preferred way to handle a virtual machine. See the following paragraph about libvirt toolkit instaed. Anyway this method - which uses screen(1) - requires very few resources on the host and on the guest machine; no VNC or X11 forwarding is required. To control the virtual host you have only a serial console, so you can disable consoles on tty[1-6] changing /etc/inittab and using the console kernel boot parameter. :!:

We have a guest host without monitor (headless). After installing KVM on it, we prepared a Qemu disk image and installed it with a basic Debian system using debootstrap.

The first run of the virtual machine will use the kernel and initrd files of the guest host. The virtual machine is operated via the console on the (virtual) serial line, attacched to the controlling terminal via the -nographic option:

# Create a Qemu disk image with minimal Debian system:
qemu-make-debian-root 3096 lenny http://ftp.us.debian.org/debian debian_lenny.img
 
# Start KVM Qemu using the guest kernel/initrd. Console on the virtual serial line:
kvm -nographic \
    -kernel /vmlinuz \
    -initrd /initrd.img \
    -append "console=ttyS0,115200n8 root=/dev/hda1 ro" \
    debian_lenny.img

The -nographic option causes the program to run without Simple Direct Layer graphics and without the VNC server.

Default password for deboostrap Debian base system is root / root.

After this first install we logged into the new host and we installed the linux-image, grub and some other packages so it becomes a full featured installation. We prefer to have the virtual system to appear on the local network, so we used the bridge setup. Into /etc/network/interfaces we have disabled eth0 and added br0:

# Bridge
auto br0
iface br0 inet static
        address 192.168.2.7
        netmask 255.255.255.0
        network 192.168.2.0
        broadcast 192.168.2.255
        gateway 192.168.2.2
        bridge_ports eth0
        bridge_stp off
        bridge_maxwait 5
        up /usr/sbin/ethtool -s eth0 wol g || true

Because we want to start the virtual machine in a non-interactive, headless mode, this is the script we need:

#!/bin/sh
screen -d -m -S "virtual_lenny" \
    kvm -m 384 -nographic \
        -net nic,vlan=0,model=rtl8139 -net tap,vlan=0 \
        debian_lenny.img

After the virtual host is running, use screen -r to attach the console on the virtual serial line, or (better) do a remote login via ssh.

Libvirt to manage KVM hosts: install, start, stop

The libvirt toolkit allow to manage some virtualization systems (namely xen, qemu and kvm) in a consistent and uniform way. It provides an infrastructure to manage virtual machines: create, start, destroy, shutdown, …

Install the following Debian Lenny packages:

The virt-install can be used to create a new virtual host, here it is an example:

virt-install \
    --name virtual_lenny \
    --virt-type kvm \
    --memory 1024 \
    --disk /home/kvm/virtual_lenny.img,size=3 \
    --cdrom /var/lib/libvirt/images/debian-10.0.0-amd64-netinst.iso \
    --os-variant auto \
    --network bridge=br0 \
    --vnc

The disk size is specified in Gibabytes, the network uses the bridge mode via the br0 interface. If you don't specify the --network option, it will use the network mode named default. You can also use the option --network none.

See the man page for using a CD-ROM boot image, etc. Now list the existing virtual machines and connect to the VNC console with the viewer (it requires X forward):

virsh list
virt-viewer virtual_lenny

The virt-install creates the xml configuration file into /etc/libvirt/qemu/virtual_lenny.xml and creates the virtual disk file into /home/virt/virtual_lenny.img.

In the following example the orginal file created by virt-install was partially modified by hand. See the syntax documentation.

<domain type='kvm'>
  <name>virtual_lenny</name>
  <uuid>e445f6a2-d358-f118-a4c3-7498702fb39f</uuid>
  <memory>393216</memory>
  <currentMemory>393216</currentMemory>
  <vcpu>1</vcpu>
  <os>
    <type arch='x86_64' machine='pc'>hvm</type>
    <boot dev='hd'/>
  </os>
  <features>
    <acpi/>
  </features>
  <clock offset='utc'/>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>destroy</on_crash>
  <devices>
    <emulator>/usr/bin/kvm</emulator>
    <disk type='file' device='disk'>
      <source file='/home/kvm/virtual_lenny.img'/>
      <target dev='hda'/>
    </disk>
    <disk type='file' device='disk'>
      <source file='/home/kvm/home_partition.img'/>
      <target dev='hdc'/>
    </disk>
    <disk type='file' device='disk'>
      <source file='/home/kvm/data_partition.img'/>
      <target dev='hdd'/>
    </disk>
    <interface type='bridge'>
      <mac address='52:54:00:12:34:56'/>
      <source bridge='br0'/>
      <target dev='tap0'/>
      <script path='no'/>
    </interface>
    <graphics type='vnc' port='autoport' listen='127.0.0.1'/>
    <serial type="null">
    </serial>
  </devices>
</domain>

NOTICE: The attributes arch and machine of tag <os><type> are madatory with Libvirt 3.0.0 (provided by Debian 9 Stretch), they were instead not required in previous versions. To view the the list of supported machines, execute qemu-system-x86_64 -machine help.

The VNC port can be assigned automatically (seting it to port='autoport'), or assigned statically, e.g. port='5901'.

The <os><type> and <cpu> tags of the XML configuration file, determines what CPU is offered to the guest machine. Here it is the default choosed by virt-install on a Xeon host (Debian 10 Buster with libvirt 5.0):

  <os>
    <type arch='x86_64' machine='pc'>hvm</type>
  </os>
  <cpu mode='host-model' check='partial'>
    <model fallback='allow'/>
  </cpu>

See the documentation about Operating system booting and CPU model and topology for more info about <os><type> and <cpu> tags.

You can use a specific option if you have an Intel CPU:

  <cpu mode='custom' match='exact'>
    <model fallback='allow'>Skylake-Client</model>
  </cpu>

or this one if you have an AMD CPU:

  <cpu mode='custom' match='exact'>
    <model fallback='allow'>Opteron_G3</model>
  </cpu>

Libvirt and ACPI

The nice thing is that you can control automatic start and stop of the virtual machines. Install the acpid package on the virtual host and load the button kernel module. Then you can start a shutdown sequence on the virtual host issuing the following command on the hosting machine:

virsh shutdown virtual_lenny

The default shutdown of the host machine, will perform a clean shutdown of the guest machines.

Connecting to the virtual host

Using graphical display

The virt-viewer utility can display the graphical console of a virtual machine, it needs that the kvm emulation is started with the -vnc option and it needs a graphical display.

If you are connecting from a remote host you can export the DISPLAY (or do X11 forward via ssh), but performances will be poor. In this case it is preferable to use a vnc client, like krdc, and connect to the vnc port of the hosting machine. Check that the vnc server is listening on the public IP address, not just onto 127.0.0.1 loopback.

Using text only

:-( NOTE: The following method (serial console on pty) unfortunately does not work. Bug #494831 blocks Qemu/Kvm startup, untill a client opens the pty. Another option shall be to connect the emulatad serial line to a telnet server, but as far I know there is no way to request this configuration via libvirt xml configuration file. The Qemu/Kvm relevant option is:

-serial telnet:0.0.0.0:4444,server,nowait,nodelay

If you don't want to access the graphical display (because you are on a slow connection), you can always setup an emulated serial console to be used via virsh. Change the serial section in the xml file, setting <serial type="pty">, start the virsh and issue the console command:

virsh # console virtual_lenny

To disconnect from the console, hit Ctrl-].

Obviously you need that the virtual host is using the serial line for the console, this means starting the kernel with something like this (/boot/grub/menu.lst):

kernel  /vmlinuz console=tty0 console=ttyS0,38400n8 root=/dev/hda1 ro

and put a getty program on the serial line /etc/inittab:

T0:2345:respawn:/sbin/getty 38400 ttyS0

Autostart

See the virsh(1) man page for details. Example: let the lenny domain (virtual host) to autostart on boot:

# virsh autostart lenny
Domain lenny marked as autostarted

This simply add a symbolic link to the domain xml file into the /etc/libvirt/qemu/autostart/ directory.

Poweroff

Attenzione, il comando /etc/init.d/libvirt-bin stop termina il demone libvirtd e tutte le macchine virtuali attivi senza inviare il segnale di poweroff. Come si fa a fare uno shutdown regolare?

Da riga di comando è semplice (installare la gestione dell'ACPI sulla macchina virtuale):

# virsh shutdown lenny
Domain lenny is being shutdown

Per automatizzare lo shutdown di tutti i domini attivi al momento dello shutdown della macchina ospite si può aggiungere uno script /etc/init.d/libvirt-shutdown, attivandolo in questo modo:

update-rc.d libvirt-shutdown stop 15 0 1 6 .

il numero di ordine 15 fa in modo che lo script venga eseguito prima di /etc/init.d/libvirt-bin, i runlevel interessati sono 0, 1 e 6, cioè halt, single-user e reboot. Lo script è il seguente:

#!/bin/sh
#
# Send an ACPI poweroff event to each running virtual hosts.
#
# Add this script at runlevel 0, 1 and 6 with the command:
# update-rc.d libvirt-shutdown stop 15 0 1 6 .
#
# Do NOT "set -e"
 
NAME=$(basename $0)
SHUTDOWN_TIMEOUT=30         # Max time for virtual domains shutdown.
SHUTDOWN_EXTRA_WAIT=15      # Extra wait, to be safe.
 
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
 
case "$1" in
 
    stop)
        virsh list | grep '\brunning$' | awk '{print $2}' \
            | xargs -r -n1 virsh shutdown
 
        I=0
        echo -n "Wait for virtual domains to shutdown."
        while (virsh list | egrep -q '\b((running)|(shutdown))$'); do
            sleep 1
            echo -n '.'
            I=$[ $I + 1 ]
            test $I -gt $SHUTDOWN_TIMEOUT && break
        done
        sleep $SHUTDOWN_EXTRA_WAIT
 
        if [ $I -gt $SHUTDOWN_TIMEOUT ]; then
            echo " FAILED"
        else
            echo " DONE"
        fi
        ;;
 
    *)
        echo "Usage: $NAME stop"
        ;;
 
esac

Windows XP as a guest on libvirt KVM host

I had some problem installing Windows XP Professional as a guest system under KVM handled via Libvirt.

It turned out that kvm requires two extra parameters to complete the Windows installation: -win2k-hack and -std-vga, otherwise the process will hang indefinitely during peripheral probing.

This is the /etc/libvirt/qemu/winxp.xml configuration file I used:

<domain type='kvm'>
  <name>winxp</name>
  <uuid>bb8db601-8184-f438-bb25-a2d6c97c9ec3</uuid>
  <memory>524288</memory>
  <currentMemory>393216</currentMemory>
  <vcpu>1</vcpu>
  <os>
    <type>hvm</type>
    <boot dev='cdrom'/>
    <boot dev='hd'/>
  </os>
  <features>
    <acpi/>
  </features>
  <clock offset='utc'/>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>destroy</on_crash>
  <devices>
    <emulator>/usr/bin/kvm</emulator>
    <disk type='file' device='disk'>
      <source file='/home/kvm/winxp/rootfs.img'/>
      <target dev='hda'/>
    </disk>
    <disk type='file' device='cdrom'>
      <source file='/home/kvm/common/winxp.iso'/>
      <target dev='hdb'/>
    </disk>
    <interface type='bridge'>
      <mac address='52:54:00:12:34:70'/>
      <source bridge='br0'/>
      <target dev='tap6'/>
      <script path='no'/>
    </interface>
    <graphics type='vnc' port='autoport' listen='192.168.2.7'/>
    <serial type="null">
    </serial>
  </devices>
</domain>

To solve the installation problem I stopped the libvirt instance of winxp and started the kvm process manually adding the extra parameters:

#!/bin/sh
/usr/bin/kvm -M pc -m 512 -smp 1 -name winxp -boot dc \
    -drive file=/home/kvm/winxp/rootfs.img,if=ide,index=0,boot=on \
    -drive file=/home/kvm/common/winxp.iso,if=ide,media=cdrom,index=1 \
    -serial null -parallel none -usb -win2k-hack -std-vga

The reboot performed during the install process did not work, I had to restart manually the virtual machine a second time.

Once the installation was completed, I restarted the Windows XP guest using libvirt and adjusted the screen settings as needed. The emulated VGA card is a VESA 2.0 VBE when option -std-vga is in use, a Cirrus Logic GD5446 otherwise.

I also got a mouse synchronization problem using the VNC console: the local pointer moves more than the emulated one, causing severe problem pointing objects at the edges of the emulated screen. I solved disabling Enhance pointer precision in Control Panel, mouse settings.

Changing CD-ROM image in a running KVM Qemu

With a modern and working libvirt it is possibile to eject or load a new image into a virtual CD-ROM drive with virsh:

virsh attach-disk --type cdrom --mode readonly guestname "" hdb
virsh attach-disk --type cdrom --mode readonly guestname "/home/kvm/common/disc2.iso" hdb

The following recipe uses the monitor interface of the qemu/kvm emulator instead. To swap CD-ROM image in a virtual host we need to access the host kvm/qemu monitor. Libvirt starts the KVM hypervisor with the -monitor pty option, so we need to know which pty terminal was allocated for the monitor. Read the libvirt log:

cat /var/log/libvirt/qemu/domain.log
/usr/bin/kvm -S -M pc -m 384 -smp 1 -name domain -monitor pty -boot c -drive file=...
char device redirected to /dev/pts/1
info cpus

so the pseudo terminal is /dev/pts/1 in this case, we can use minicom to attach to the monitor:

minicom -op /dev/pts/1

Now we can check which block devices are in use, eject a CD-ROM image and insert a new one:

(qemu) info block
ide0-hd0: type=hd removable=0 file=/home/kvm/domain/rootfs.img ro=0 drv=raw
ide0-cd1: type=cdrom removable=1 locked=0 file=/home/kvm/common/CentOS-4.5-i386-bin1of4.iso ro=0 drv=raw
(qemu) eject ide0-cd1
(qemu) change ide0-cd1 /home/kvm/common/CentOS-4.5-i386-bin2of4.iso

Problema con KVM e disco NFS

Pare che ci sia un problema con KVM e i filesystem montati via NFS: in caso di intensa attività di rete si arriva ad un forte rallentamento fino allo stallo completo. Sulla macchina virtuale si possono notare messaggi di errore del tipo:

nfs: server 192.168.2.7 not responding, still trying

Il problema sembra non presentarsi se si utilizza come scheda di rete virtuale il model=virtio (il modello predefinito dovrebbe essere rtl8139). Bisogna ovviamente che il sistema operativo ospite lo supporti, cosa che il kernel Linux fa, almeno dalla versione 2.6.26 di Debian Lenny.

Questo un estratto dal file di configurazione libvirt:

    <interface type='bridge'>
      <model type='virtio'/>
      <mac address='52:54:00:12:34:70'/>
      <source bridge='br0'/>
      <target dev='tap70'/>
      <script path='no'/>
    </interface>

Riferimenti:

Problema di timeout su disco

Se la piattaforma ospitante è troppo carica (o forse se vengono effettuate operazioni di migrazione della macchina virtuale?), può accadere che il disco virtuale non risponda per diversi secondi. Ecco il messaggio registrato da dmesg:

[100449.816153] ata1: lost interrupt (Status 0x50)
[100467.710383] end_request: I/O error, dev sda, sector 43343336
[100467.710922] Aborting journal on device sda3-8.
[100467.712097] EXT4-fs error (device sda3): ext4_journal_start_sb: Detected aborted journal
[100467.712594] EXT4-fs (sda3): Remounting filesystem read-only
[100469.754031] EXT4-fs error (device sda3) in ext4_da_writepages: IO failure
[100469.754676] EXT4-fs (sda3): ext4_da_writepages: jbd2_start: 840 pages, ino 138323; err -30

In tal caso è possibile incrementare il timeout predefinito di 30 secondi:

echo 180 > /sys/block/sda/device/timeout

Per rendere permanente questa modifica in Debian si può installare il pacchetto sysfsutils e aggiungere questa riga al file /etc/sysfs.conf:

block/sda/device/timeout = 180

C'è chi suggerisce addirittura timeout di 900 secondi.

Virtuozzo

Il software di virtualizzazione Virtuozzo viene utilizzato da alcuni provider di VPS. Si tratta di software non libero.

Limite regole iptables

La piattaforma di virtualizzazione pone dei limiti sul numero di regole netfilter che si possono attivare. Per vedere il limite attuale:

cat /proc/user_beancounters

Vedere il valore barrier e limit di numiptent. Solo l'amministratore Virtuozzo può modificare tale limite.

L'errore che si può verificare - ad esempio avviando il firewall Shorewall - è:

iptables-restore: line 129 failed

Virtualbox

Prove fatte su Ubuntu 11.10. Si installa il pacchetto linux-headers-* relativo alla versione kernel in uso (uname -a) e il pacchetto con i sorgenti del modulo kernel virtualbox-dkms. Al termine dell'installazione dei sorgenti il modulo kernel dovrebbe risultare compilato e installato:

find /lib/modules/ | grep vboxdrv
/lib/modules/3.0.0-30-generic-pae/updates/dkms/vboxdrv.ko
/lib/modules/3.0.0-32-generic-pae/updates/dkms/vboxdrv.ko

In caso di aggiornamento del kernel bisogna ricordarsi di installare anche il pacchetto linux-headers-* relativo, e tutto dovrebbe funzionare automaticamente.