Project

General

Profile

Come creare un ambiente "virtualizzato" con OpenVZ su Lenny

Il supporto per OpenVZ è presente in Debian a partire da Etch. Come per Vserver la tecnologia dei container usata da OpenVZ, pur non fornendo una macchina virtuale vera e propria, consente un uso estremamente efficiente delle risorse, permettendo creare una serie di sistemi indipendenti, chiamati virtual-environment (da qui in breve VE), che girano sotto lo stesso kernel, utilizzando lo stesso filesystem ed un unico stack di rete, che pur non essendo macchine virtuali in senso stretto, nella gestione ordinaria possono essere tranquillamente considerate come tali.

Ovviamente non trattandosi di una tecnologia di virtualizzazione, non consente di installare un virtual-server con un sistema operativo diverso, come Windows. Qualora si abbiano di queste esigenze si dovrà ricorrere a tecnologie alternative come Xen o KVM.

Installazione sistema ospite (HN)

Come accennato OpenVZ è supportato a partire da Debian Etch, una volta installato il sistema base (queste note fanno riferimento alla versione distribuita con Lenny), si dovrà installare il kernel modificato per l'uso dello stesso sulla macchina ospite (il cosiddetto Hardware Node, o HN) con il comando:

aptitude install linux-image-openvz-686

alternativamente, se la macchina è stata installata a 64 bit, si dovrà installare il pacchetto linux-image-openvz-amd64. L'installazione del kernel installa automaticamente anche i tool di base necessari alla gestione delle macchine contenute.

Su Debian tutti i file di OpenVZ e dei VE vengono mantenuti sotto /var/lib/vz/, ma per compatibilità con altre versioni è suggerito creare un link simbolico sotto la radice come:

ln -s /var/lib/vz /vz

Una volta installato il tutto occorrerà riavviare la macchina per utilizzare il nuovo server, eseguito il riavvio si potrà verificare il funzionamento del sistema verificando che in processo di controllo sia attivo:

# ps aux| grep vz
root      2488  0.0  0.0      0     0 ?        S    15:20   0:00 [vzmond]

e controllando lo stato con:

# vzlist -a
      VEID      NPROC STATUS  IP_ADDR         HOSTNAME                        

che sarà vuoto non essendo stato creato alcun VE.

Creazione di un Virtual Environment da un template

Il passo successivo è quello di installare un primo VE, per questo tornano estremamente utili i template, degli scheletri di sistema da cui si può creare in maniera rapida e semplice un nuovo VE; si tratta in sostanza di archivi (in formato .tar.gz) che contengono l'immagine del filesystem di un intero sistema. Questi si possono creare come vedremo più avanti, o scaricare già pronti da questo sito. Nel caso si usi un template giù pronto questo dovrà essere salvato nella directory /var/lib/vz/template/cache, ad esempio per una Debian si potrà fare:

cd /vz/template/cache
wget http://download.openvz.org/template/precreated/debian-5.0-x86.tar.gz

(nel caso si è scaricata è la versione per 686, si usi quella a 64 bit per macchine a 64 bit).

Dopo di che la creazione di un VE da un template può essere eseguita semplicemente con il comando vzctl create, questo in genere fa riferimento al file di configurazione generale di OpenVZ /etc/vz/vz.conf in cui viene definita la variabile (nel caso di Lenny è DEF_OSTEMPLATE="debian") che indica il template di default da usare se non se ne indica esplicitamente uno per nome. Si tenga presente però che questo fa riferimento ad un file con lo stesso nome sotto /vz/template/cache per cui se si è scaricato il file precedente occorrerà creare un link simbolico con:

ln -s debian-5.0-x86.tar.gz debian.tar.gz

A questo punto si potrà creare un nuovo VE indicando semplicemente un numero intero (quello che in gergo viene chiamato VEID) che serve ad identificare univocamente ciascuna macchina; nel nostro caso questo può essere creato semplicemente con il comando:

# vzctl create 101 
Creating VE private area (debian)
Performing postcreate actions
VE private area was created

che usa il VEID 101; una volta creato lo potrà far partire con il comando:

# vzctl start 101 
Starting VE ...
VE is mounted
Setting CPU units: 1000
Configure meminfo: 65536
VE start in progress...

verificarne l'attività con:

# vzlist -a
      VEID      NPROC STATUS  IP_ADDR         HOSTNAME                        
       101          8 running -               -                               

entrarci con:

# vzctl enter 101
entered into VE 101
vzserver:/#

fermarlo con:

# vzctl stop 101
Stopping VE ...
VE was stopped
VE is unmounted

Come si può notare dalla lista precedente in questo modo però il nostro VE non è dotato né di un indirizzo IP né di un nome; una serie di proprietà delle singole macchine possono infatti essere specificate sia in sede di creazione con vzctl create, che successivamente con il comando vzctl set, passando a questi comandi delle opportune opzioni. Ad esempio per impostare un hostname si può creare un VE direttamente con il comando:

vzctl create 101  --hostname testlenny

oppure una volta creato senza nome lo si può impostare con:

vzctl set 101 --hostname testlenny --save

dove senza il --save il comando effettua una assegnazione temporanea (effettuabile solo a VE attivo), mentre con --save la configurazione viene registrata in maniera permanente e può essere fatta in qualunque momento. Effettuata questa configurazione la si potrà controllare con:

# vzlist -a
      VEID      NPROC STATUS  IP_ADDR         HOSTNAME                        
       101          5 running -               testlenny

Se infine si vuole che il nuovo VE sia lanciato automaticamente all'avvio della macchina, si dovrà attivarne la partenza in maniera esplicita con:

vzctl set 101 --onboot yes --save

Configurazione della rete

Una delle caratteristiche più interessanti di OpenVZ (in particolare rispetto a Vserver) è quella di consentire una separazione netta della rete fra i vari VE. Il default è quello che fa ricorso ad una speciale interfaccia virtuale, venet che opera a livello di routing IP diretto. E' possibile anche usare una interfaccia virtuale ethernet, veth, che realizza una comunicazione diretta fra interfacce ethernet, su cui torneremo più avanti.

Configurazione di rete con venet

La configurazione più comune è quella realizzata attraverso l'uso di una interfaccia virtuale venet, in questo caso la gestione degli indirizzi assegnati ai singoli VE è possibile solo dalla macchina ospite. Questo può comportare alcuni problemi nella fornitura di alcuni servizi (in particolare in questo l'interfaccia non è in grado di supportare pienamente IPv6) ma ha il grosso vantaggio di non consentire alle macchine virtuali di modificare i propri indirizzi IP o eseguire operazioni di basso livello sull'interfaccia, che in caso di cessione a terzi dell'amministrazione delle macchine virtuali sono un bonus di sicurezza.

La configurazione della rete di un VE in questo caso viene effettuata attraverso il comando vzctl set che di default usa l'interfaccia venet; per assegnare un IP ad un VE è sufficiente eseguire il comando:

vzctl set 101 --ipadd 192.168.0.100  --save

da ripetere quante volte si vuole per assegnare ulteriori indirizzi che saranno aggiunti alla lista, questi potranno essere rimossi completamente con il comando:

vzctl set 101 --ipdel all --save

o singolarmente, specificando l'indirizzo IP al posto della parola chiave all.

L'utilizzo di questa impostazione ha due effetti, nel sistema ospite verrà creata, all'avvio di ciascun VE, una rotta statica per ciascuno degli IP ad esso assegnati, i quali a loro volta, all'interno del VE, verranno impostati sulle interfacce virtuali venet0:0, venet0:1, ecc. configurate in modalità punto-punto con se stesse.

I meccanismo fa sì che pacchetti inviati dalla macchina ospite verso l'interfaccia venet vengano automaticamente instradati al VE che ha quell'indirizzo, mentre viceversa i pacchetti inviati dal VE verso quell'interfaccia emergono da questa sulla macchina ospite. L'interfaccia venet fa sostanza da router ma il meccanismo a prima vista risulta abbastanza strano in quanto nel passaggio da macchina ospite a VE non c'è nessun hop; inoltre se si controlla la tabella di instradamento dentro un container si troverà qualcosa del tipo:

# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.0.2.1       0.0.0.0         255.255.255.255 UH    0      0        0 venet0
0.0.0.0         192.0.2.1       0.0.0.0         UG    0      0        0 venet0

che fa riferimento ad un indirizzo che non è assegnato a nessuna interfaccia, si tratta infatti dell'impostazione di un default gateway fittizio (uno qualunque facente riferimento all'interfaccia venet0 funzionerebbe lo stesso), che serve solo a dirigere i pacchetti in uscita verso gli altri indirizzi all'interfaccia venet0, da cui emergeranno nella macchina ospite.

Questo meccanismo piuttosto complesso ha però il risultato di rendere ogni VE del tutto indipendente dagli altri, e consente di impostare delle regole di firewall all'interno dei singoli VE senza dover essere costretti, come avveniva con VServer, a gestire le regole esclusivamente a livello di sistema ospite.

Una volta assegnato l'IP al nostro container il resto della configurazione deve essere effettuato sulla macchine ospite, che deve occuparsi di instradarlo correttamente verso internet, per questo una delle configurazioni essenziali è l'abilitazione del reinoltro dei pacchetti, da farsi manualmente con il comando:

echo 1 > /proc/sys/net/ipv4/ip_forward

e da impostare permanentemente inserendo in /etc/sysctl.conf la riga:

net.ipv4.ip_forward=1

Viene inoltre consigliato, quando si effettua la configurazione di VE con IP pubblico proprio, di abilitare il proxy arp per l'interfaccia della macchina ospite dietro la quale operano, questo si fa manualmente con il comando:

echo 1 > /proc/sys/net/ipv4/conf/eth0/proxy_arp

e si imposta in modo permanentemente inserendo in /etc/sysctl.conf la riga:

net.ipv4.conf.eth0.proxy_arp=1

Il primo caso è quello in cui per i VE si utilizzano degli IP privati, in questo caso perché internet sia raggiungibile occorrerà effettuare un opportuno NAT degli stessi, per cui si dovrà impostare anche:

iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

A parte la configurazione degli indirizzi e dell'instradamento, un'altra configurazione necessaria a consentire l'uso della rete ai singoli VE è quella del DNS, che può essere effettuata con il comando:

vzctl set 101 --nameserver IP.PUB.DEL.DNS --save

e quella del dominio locale in cui eseguire le ricerche, effettuabile con:

vzctl set 101 --searchdomain dominio.local --save

che sono i comandi che consentono di creare le opportune direttive in /etc/sysctl.conf.

Configurazione di rete con veth

Come accennato questa modalità di gestione della rete è leggermente meno efficiente e più complessa rispetto alla precedente, ma consente di delegare al VE la gestione di una vera e propria interfaccia ethernet virtuale, sulla quale compiere tutte le operazioni volute. Inoltre in caso di macchine dotate di più interfacce di rete consente anche di associare un VE ad una interfaccia o all'altra, ottenendo una separazione delle rispettive reti.

Questa modalità è alternativa alla precedente, e non si deve pertanto assegnare un IP dall'HN, ma farlo direttamente all'interno del VE, come una macchina reale; per questo, qualora esso fosse presente, occorre rimuoverlo preventivamente con il comando:

vzctl set 101 --ipdel all --save

inoltre occorre anche eliminare il template del file /etc/network/interfaces usato dai singoli VE, che si ritrova a partire dai file relativi a ciascuno di essi posti in /vz/private/VEID/. Una volta fatto questo si potrà passare alla creazione di una interfaccia ethernet virtuale da assegnare al container VEID con il comando:

vzctl set VEID --netif_add ethN --save

Una volta eseguito il comando sull'hardware node sarà disponibile una nuova interfaccia virtuale vethVEID.N corrispondente alla interfaccia ethN in modo che ogni pacchetto inviato sulla prima uscirà sulla seconda e viceversa. Si possono poi anche specificare ulteriori dettagli, la sintassi completa del comando precedente infatti è:

vzctl set VEID --netif_add <ifname>[,<mac>,<host_ifname>,<host_mac>,<bridge>] --save

che consente di impostare sia MAC address che nome sia per l'interfaccia nel VE che nell'HN (cosa che in genere non si fa mai, utilizzando i default del comando) ed infine un eventuale bridge (che invece è la configurazione consigliata) attraverso il quale usare l'interfaccia.

Benché sia possibile usare direttamente le varie interfacce virtuali vethVEID.N assegnandovi gli indirizzi usati all'interno del VE corrispondente ed instradandovi i pacchetti, in realtà la modalità più comoda e più pulita per utilizzare questa modalità di gestione della rete è quella di usare un bridge sull'HN per associare ad una interfaccia ethernet reale le varie interfacce virtuali, senza doversi preoccupare di effettuare instradamenti; con questa modalità il quinto parametro del comando precedente consente di indicare agli script di inizializzazione della rete a quale bridge associare l'interfaccia ethernet virtuale del proprio VE.

Per questa modalità occorre però configurare il proprio hardware node per usare le interfacce in bridging, per questo occorre anzitutto installare il pacchetto bridge-utils, dopo di che si dovrà effettuare impostare l'HN per usare il bridge invece dell'interfaccia fisica, nel caso di Debian questo potrà essere fatto usando un /etc/network/interfaces come il seguente:

auto br0
iface br0 inet static
      address 192.168.1.1
      netmask 255.255.255.0
      bridge_ports eth0
      bridge_maxwait 5

Una volta configurata la rete dell'HN per usare il bridge br0 al posto dell'interfaccia fisica eth0, si potranno creare le interfacce virtuali dei VE configurando l'associazione al bridge utilizzando un comando come il seguente:

vzctl set 101 --netif_add eth0,,,,br0 --save

Perché l'associazione sia automatica però questo non è sufficiente, deve anche essere indicato agli script di configurazione della rete delle macchine virtuali di utilizzare questa configurazione; allo scopo è sufficiente creare il file /etc/vz/vznet.conf con il seguente contenuto:

#!/bin/bash
EXTERNAL_SCRIPT="/usr/sbin/vznetaddbr" 

che utilizza lo script citato per effettuare automaticamente l'associazione della interfaccia ethernet virtuale veth101.0 creata dal comando precedente al bridge br0 quando il VE viene avviato, e per disassociarla quando questo viene fermato. Lo script si incarica anche di eseguire le opportune chiamate a sysctl per abilitare i necessari parametri associati all'interfaccia.

Con questa configurazione si potrà poi passare a configurare la rete all'interno del VE come se l'interfaccia eth0 in esso presente fosse una normale interfaccia ethernet; avendo associato la corrispondente veth101.0 al bridge i pacchetti verranno automaticamente fatti uscire sulla scheda di rete fisica eth0 dell'hardware node come se provenissero da un ramo di rete ethernet associato a questa appunto tramite il bridge.

Configurazione delle proprietà di un VE

Una delle caratteristiche più interessanti di OpenVZ è quella di un controllo estremamente dettagliato sulle risorse che si possono assegnare ai vari VE. In particolare tutta una serie di risorse sono associate a dei bean counters che ne tracciano l'utilizzo, sui quali poi si possono imporre soft ed hard limit. Ciascuna di queste risorse è identificata da un nome, e se ne può anche controllare l'utilizzo, le impostazioni e l'eventuale superamento dei limiti nel file /proc/user_beancounters.

Ad esempio se la RAM disponibile in un VE è insufficiente, e se ne vuole aumentare la disponibilità con un limite fra 512M e 1G si potrà utilizzare il comando:

vzctl set 101 --privvmpages 512M:1G --save

se non si vogliono imporre limiti si potrà usare il valore speciale unlimited al posto del valore numerico.

Oltre alle risorse associate ai bean counters si possono impostare limiti anche su altre risorse generiche come lo spazio disco operando allo stesso modo con ulteriori parametri; se ad esempio si vuole impostare un limite sullo spazio disco totale si potrà usare il comando:

vzctl set 101 --save --diskspace 10G

e dopo averlo utilizzato il VE vedrà il valore impostato come totale, anche in seguito all'esecuzione del comando df.

Creazione manuale di un template

Nel caso si voglia creare un proprio template si potrà, nel caso di Debian, ricorrere a deboostrap, in questo caso il primo passo è installarlo nel sistema ospite con:

aptitude install deboostrap

poi si potrà creare un ambiente iniziale con:

debootstrap --arch amd64 lenny /var/lib/vz/private/101 http://ftp.it.debian.org/debian

(si usi il valore di architettura adeguato alla propria macchina). Questo preparerà i file per un VE con id 101; una volta completato il comando occorrerà generare una opportuna configurazione per lo stesso con:

vzctl set 101 --applyconfig vps.basic --save

inoltre occorrerà aggiungere alla configurazione del VE la variabile OSTEMPLATE con:

echo 'OSTEMPLATE="debian"' >> /etc/vz/conf/101.conf

infine si potrà configurare la rete come visto in precedenza per poter operare all'interno dello stesso.

Prima di avviare il nuovo VE occorrerà però eseguire alcune correzioni per adattare quanto installato da deboostrap ad un ambiente che non corrisponde ad una vera macchina, in particolare si dovranno rimuovere le console virtuali da /etc/inittab, bloccare la scrittura sincrona sul syslog ed usare /proc/mounts al posto di /etc/mtab, e cancellare tutti gli pacchetti non necessari (come quanto relativo ad udev e a al kernel), questo si può ottenere con i comandi:

sed -i -e '/getty/d' /var/lib/vz/private/101/etc/inittab
sed -i -e 's@\([[:space:]]\)\(/var/log/\)@\1-\2@' /var/lib/vz/private/101/etc/*syslog.conf
rm -f /var/lib/vz/private/101/etc/mtab
ln -s /proc/mounts /var/lib/vz/private/101/etc/mtab

A questo punto si potrà far partire il nostro VE e completare le operazioni di aggiornamento e pulizia dal suo interno, per questo si eseguano i comandi:

apt-get update
apt-get upgrade
apt-get clean
dpkg --purge module-init-tools
apt-get remove --purge XXX (lista di altra roba da completare)
update-rc.d -f klogd remove

e si rimuova tutto quello ritenuto non necessario.

Una volta completata la pulizia all'interno si potrà uscire e passare alla creazione del template, per questo però occorre prima rimuovere i dati di indirizzo e DNS inseriti per consentire al nostro VE di accedere ad internet, per questo si faccia:

vzctl stop 101
vzctl set 101 --ipdel all --save
echo > /vz/private/101/etc/resolv.conf
rm /vz/private/101/etc/hostname

e poi si potrò creare l'immagine del template con:

cd /vz/private/101/
tar --numeric-owner -zcf /vz/template/cache/debian-5.0-x86_64-minimal.tar.gz .

ed a questo punto una volta creato il link simbolico:

ln -sf debian-5.0-x86_64-minimal.tar.gz debian.tar.gz

si potrà creare un nuovo VE con:

vzctl create 102

e verificare che funziona con:

vzctl start 102 
vzctl enter 102

Dump e backup

Un'altro programma molto utile per la gestione dei VE è vzdump, il pacchetto Debian è disponibile solo da Squeeze, ma può essere facilmente installato (almeno fino alla versione 1.2.4-2) anche su Lenny. Il comando consente di creare un dump di un VE, anche attivo, che può essere poi facilmente ripristinato con il comando vzrestore.

Esistono tre modalità diverse di eseguire il comando, ma quelle realmente significative sono due. La migliore è quella attivata con l'opzione --snapshot ma richiede che i file siano stati installati su un volume logico e che sia disponibile LVM ed in tal caso il dump sarà fatto senza downtime. L'altra alternativa è usare --suspend, in tal caso verranno fatti due cicli di rsync sospendendo la macchina solo per il secondo ciclo, in modo da minimizzare il downtime. Questo secondo metodo ha il vantaggio che si può usare sempre, ma porta ad un limitato tempo di fermo per la macchina.

Per eseguire un dump basta indicare il VEID del VE che si vuole salvare, non indicando nulla saranno salvati tutti quelli presenti. Ad esempio si potrà salvare la precedente macchina virtuale con:

vzdump --snapshot 101

e verranno creati un .tar ed un .log in /var/lib/vz/dump/, con nomi nella forma:

vzdump-openvz-VEID-YYYY_MM_DD-HH_MM_SS