Pentru a pregăti configurația de laborator va trebui să descărcați pe mașina fizică (mjolnir
), în directorul saisp/
, arhiva laboratorului:
student@mjolnir:~/saisp$ wget --user=user-curs --ask-password http://repository.grid.pub.ro/cs/saisp/laboratoare/lab-08.zip student@mjolnir:~/saisp$ unzip lab-08.zip
În urma dezarhivării rezultă un fișier .iso
. Imaginea de bază base.qcow2
este deja în directorul saisp/
și va fi folosită pe parcursul laboratorului.
Puteți urma pașii de mai sus pentru a descărca infrastructura KVM pentru laborator pentru lucru acasă.
username:parola
):
root:student
root
).
Virtualizarea este folosită pe scară largă în centrele de date întrucât oferă flexibilitate foarte mare în managementul resurselor. Pentru a nu afecta perfomanța, producătorii de procesoare au introdus facilități de virtualizare pentru a permite sistemelor de operare să ruleze nemodificate. Entitatea software care folosește aceste facilități se numește hypervisor. KVM este un hypervisor ce oferă suport pentru virtualizarea nativă (implementează facilități de virtualizare). În continuare vom prezenta modul de lucru cu soluția implementată de KVM.
Trebuie să verificăm dacă hardware-ul are suport pentru virtualizare nativă (mai poartă denumirea și de extensie de virtualizare). Numele extensiilor de virtualizare diferă de la un producător la altul astfel:
Pentru a verifica prezența extensiilor de mai sus trebuie să ne uităm în /proc/cpuinfo
dacă câmpul Flags conține numele extensiei (vmx pentru Intel sau svm pentru AMD):
root@saisp:~$ cat /proc/cpuinfo |grep vmx flags : ... ds_cpl **vmx** smx...
Pentru a folosi KVM trebuie să instalăm pachetul qemu-kvm
, qemu fiind utilitarul din userspace ce porneste mașinile virtuale și transmite parametri doriți hypervisorului:
root@saisp:~# apt-get install qemu-kvm [...]
Înainte de a porni o mașina virtuală bazată pe KVM, trebuie să verificăm dacă modulul de kernel este încărcat:
root@saisp:~# lsmod|grep kvm kvm_intel 121968 0 kvm 287572 1 kvm_intel
Observați în acest caz folosirea unei arhitecturi Intel. Pentru fiecare arhitectură există un modul separat. Încărcarea modului de kernel atrage după sine crearea unui device /dev/kvm
prin care se controlează modulul de kernel folosind operații de tip ioctl
:
root@saisp:~# ls -l /dev/kvm crw-rw---T 1 root kvm 10, 232 Jan 5 21:34 /dev/kvm
Pentru a porni o mașină virtuală vom folosi comanda kvm
. Utilizatorul care execută comanda (dorește să pornească o mașină virtuală) trebuie să fie cel privilegiat (root
) sau să facă parte din grupul setat ca owner pe device-ul /dev/kvm
(în cazul de fată kvm
). În cadrul laboratorului vom lucra cu utilizatorul privilegiat, dacă nu se specifică altfel.
Vom crea o mașină virtuală având 256MB RAM (parametrul -m
), 2 procesoare (numărul este dat de parametrul -smp
) și va avea ca dispozitiv de stocare o imagine virtuală denumită base.qcow2
(mai multe detalii în exercițiul următor), specificată cu parametrul -hda
:
root@saisp:~# kvm -hda base.qcow2 -m 256 -smp 2
În acest moment se va deschide o fereastră în care se va afișa output-ul consolei mașinii virtuale (veți vedea cum bootează). Verificați că resursele mașinii virtuale coincid cu parametri trimiși comenzii kvm
inspectând sistemul de fișiere /proc
.
Deschidem o nouă consolă și vom afișa numărul de thread-uri kvm
prezente în sistem:
root@saisp:~# ps -eLf |grep kvm
Opriți rularea mașinii virtuale rulând Ctrl+c
în consola unde ați rulat comanda kvm
. Porniți-o din nou cu 4 procesoare și 512MB RAM.
Deschidem o nouă consolă și vom afișa numărul de thread-uri kvm
prezente în sistem:
root@saisp:~# ps -eLf |grep kvm
Observați că fiecare nou procesor adăugat în mașina virtuală crește numărul thread-urilor kvm
în sistemul gazdă (numărul de thread-uri este mai mare decât numărul de procesoare din cauza existenței unor thread-uri de management necesare procesului kvm
).
De cele mai multe ori nu dorim deschiderea unei console în sesiunea curentă ci dorim rularea mașinii în background, iar la nevoie să putem accesa consola acesteia. Acest lucru este posibil folosind parametrul -vnc
al comenzii kvm
care va porni un server de VNC prin care se va exporta consola mașinii virtuale:
root@saisp:~# kvm -hda base.qcow2 -m 512 -smp 4 -vnc :1
Observați în continuare că procesul kvm
nu a intrat în background. Pentru acest lucru trebuie să mai adăugăm parametroul -daemonize
:
root@saisp:~# kvm -hda base.qcow2 -m 512 -smp 4 -vnc :1 -daemonize
Parametrul -vnc :1
activează serverul VNC pe portul 1 al protocolului. Pentru a afla portul TCP pe care ascultă serverul de VNC prin care este exportată consola trebuie să adunați 5900 la numărul pe care l-ați pus parametrului -vnc
, în cazul nostru 5901
. Pentru a verifica acest lucru executați comanda netstat
:
root@saisp:~# netstat -ntpl
În acest moment mașina virtuală KVM rulează în background, singura modalitate de interacțiune cu aceasta fiind prin consola VNC. Ne vom conecta la portul 5901
folosind utilitarul vncviewer
(instalați-l dacă este cazul folosind apt-get install xtightvncviewer
):
root@saisp:~# vncviewer localhost:5901
Închideți mașina virtuală executând comanda poweroff
în consola acesteia. Porniți-o din nou având 256MB RAM și 2 procesoare, tot în background (-daemonize
). Găsiți o modalitate de a opri mașina virtuală executând o comanda pe mașina fizică (cea pe care lucrați voi).
La punctul anterior am pornit o mașină virtuală ce avea o imagine a disk-ului deja construită. Ați observat extensia specifică a imaginii .qcow2
(QEMU Copy-on-write). Acest tip de imagine virtuală ne permite să multiplicăm o mașină virtuală folosind o imagine de bază read-only. Pentru fiecare mașină virtuală pornită se va crea un fișier doar cu modificările aduse de aceasta imaginii de bază. Mai multe despre multiplicare vom vedea în următoarele exerciții
În continuare vom crea o imagine .qcow
pe care vom porni instalarea unui sistem de operare. Utilitarul cu ajutorul căruia se crează astfel de imagini este qemu-img
(dacă nu există, instalați-l folosind comanda apt-get install qemu-utils
):
root@saisp:~# qemu-img create -f qcow2 virtualdisk.qcow2 1G Formatting 'virtualdisk.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536
Se observă că utilitarul qemu-img
primește ca prim parametru o comandă (create
). Pentru comanda create
am specificat tipul dorit de imagine (qcow2
), numele disk-ului virtual și dimensiunea maximă a acestuia (1G
).
Dorim să instalăm un sistem de operare folosind un CD de instalare (fomat .iso
). Comanda kvm
ne permite să adăugăm un nou dispozitiv de tip cdrom în care să fie încărcată. Folosiți imaginea CD-ului Debian din arhiva descărcată (fișierul debian-7.3.0-amd64-netinst.iso
) și porniți o mașină virtuală cu disk-ul virtual creat anterior și un dispozitiv de tip cdrom:
root@saisp:~# kvm -hda virtualdisk.qcow2 -cdrom debian-7.3.0-amd64-netinst.iso -m 256 -smp 2
Mașina virtuala va porni de pe CD întrucât nu va găsi nici un bootloader pe disk-ul virtual. Porniți instalarea sistemului de operare folosind pașii impliciți. Din acest punct instalarea decurge ca în cazul unui sistem fizic obișnuit.
O dată începută instalarea, închideți mașina virtuală folosind una din metodele găsite/prezentate anterior și ștergeți imaginea nou creată.
O configurație uzuală a unei mașini virtuale este formată de obicei dintr-un disk virtual principal de dimensiuni reduse unde este instalat sistemul de operare și un disk virtual secundar unde se stochează datele efective.
Creați un nou disk virtual în format qcow2
de dimensiune 1G pe care să îl atașați unei mașini virtuale ce are ca disk principal base.qcow2
(unde se află sistemul de operare). Mașina virtuală va avea 256MB RAM și 2 procesoare. Hint: -hdb
.
Observați că dimensiunea fișierului qcow2
este foarte mică acesta extinzându-se pe măsură ce se vor scrie date:
root@saisp:~# ls -hl nume_fisier.qcow2
După ce ați pornit mașina virtuală, verificați existența disk-ului secundar adăugat folosind comanda ls /dev/sdb
. Creați 2 partiții de câte 500MB fiecare și formatațile folosind sistemul de fișiere ext4
. Montați partițiile noi create și scrieți câte un fișier de 100MB pe acestea.
Verificați dimensiunea imaginii create și observați că a crescut.
Închideți mașina virtuală și stergeți imaginea creată.
Folosind imaginea base.qcow2
dorim să pornim două mașini virtuale fără a crea două copii ale acesteia. Pentru acest lucru putem folosi conceptul copy-on-write specific acestui de tip de imagini (qcow2
).
Pentru a crea o nouă imagine ce reține doar diferențele față de imaginea de bază folosim comanda create
a utilitarului qemu-img
cu parametrul -b
care va specifica baza de la care va porni noua imagine:
root@saisp:~# qemu-img create -f qcow2 -b base.qcow2 sda-vm1.qcow2 Formatting 'sda-vm1.qcow2', fmt=qcow2 size=2147483648 backing_file='base.qcow2' encryption=off cluster_size=65536 root@saisp:~# ls -lh sda* -rw-r--r-- 1 root root 1.4G Jan 29 19:01 base.qcow2 -rw-r--r-- 1 root root 193K Jan 29 19:01 sda-vm1.qcow2
Afișați dimensiunea ambelor imagini.
Porniți o nouă mașină virtuală folosind imaginea sda-vm1.qcow2
.
Creați un fișier pe 100 MB în mașina virtuală. Afișați dimensiunea imaginilor (base.qcow2
și sda-vm1.qcow2
) și observați faptul că base.qcow2
a rămas constant , iar sda-vm1.qcow2
a crescut).
Închideți mașina virtuală și ștergeți imaginea creată (sda-vm1.qcow2
).
O altă comandă des folosită a utilitarului qemu-img
este convert
. Avem la dispozitie o imagine in format .qcow2
și dorim
obținerea unei imagini în formatul .vmdk
(format specific mașinilor virtuale VMware) sau .vdi
(format specific mașinilor virtuale VirtualBox), fără a fi nevoiți să reinstalăm sistemul de operare pe o nouă imagine:
root@saisp:~# qemu-img convert -O vdi base.qcow2 base.vdi
Parametrul -O
specifică formatul imaginii obținute (vdi
). Observați că nu a fost necesară specificarea formatului imaginii de intrare (-f
), qemu-img
fiind capabil să detecteze acest lucru.
Creați o nouă mașină virtuală folosind VirtualBox și configurați folosirea disk-ului obținut anterior.
Creați 2 imagini de disk pornind de la imaginea de baza base.qcow2
cu numele sda-vm1.qcow2
și sda-vm2.qcow2
.
Până în acest moment am creat mașini virtuale fără nici o legătură către Internet. Dorim să asigurăm accesul la Internet pentru acestea. Pentru acest lucru trebuie să adăugăm la crearea mașinii virtuale o interfață de rețea cu ajutorul parametrului '-net':
root@saisp:~# kvm -hda sda-vm1.qcow2 -m 256 -smp 2 -net nic,model=e1000,macaddr=00:11:22:33:44:55 -net tap,ifname=tap-vm1
tap
.
tap
.
Cu ajutorul primei opțiuni -net nic
specificăm proprietățile interfeței din mașina virtuală (în cazul de față se dorește emularea unei interfațe Intel e1000 cu adresa MAC 00:11:22:33:44:55
).
Toate opțiunile adăugate nu sunt obligatorii (dacă nu se specifică se folosesc valori implicite). A doua opțiune -net tap
specifică tipul interfeței virtuale din mașina fizică ce are corespondență 1 la 1 cu interfața din mașina virtuală (tot traficul trimis pe interfața eth0
de pe masina virtuala ajunge pe interfața tap-vm1
în mașina fizică). De asemenea toți parametri sunt opționali.
Porniți o a doua mașină virtuală folosind imaginea sda-vm2.qcow2
și adăugați-i o interfață de rețea cu adresa MAC AA:11:22:33:44:55
ce are corespondență în mașina fizică o interfață de tip tap
cu numele tap-vm2
.
root@saisp:~# kvm -hda sda-vm2.qcow2 -m 256 -smp 2 -net nic,model=e1000,macaddr=AA:11:22:33:44:55 -net tap,ifname=tap-vm2
Modificați hostname-urile mașinilor la VM1
, respectiv VM2
:
root@VM:~# hostname VM1 root@VM:~# su - root@VM1:~# root@VM:~# hostname VM2 root@VM:~# su - root@VM2:~#
Logați-vă din nou ca root
pentru a se modifica prompt-ul.
În acest moment am creat 2 legături virtuale între instanțele KVM și mașina fizică. În mod uzual se dorește ca mașinile virtuale și mașina fizică să aparțină aceleiași rețele. Pentru acest lucru avem nevoie de un switch virtual în care să conectăm legăturile create anterior (tap-vm1
și tap-vm2
) împreună cu mașina fizică. Switch-ul virtual poate fi emulat folosind conceptul de bridge din kernelul de Linux.
Vom crea bridge-ul/switch-ul virtual denumit brX
, unde X
va fi 0
(br urmat de un index):
root@saisp:~# brctl addbr br0 root@saisp:~# ip link set dev br0 up
tap-vm1
și tap-vm2
in bridge:
root@saisp:~# brctl addif br0 tap-vm1 root@saisp:~# brctl addif br0 tap-vm2
Configurați pe interfața br0
adresa IP 192.168.1.1/24
, iar pe mașinile virtuale adresele IP 192.168.1.2/24
, respectiv 192.168.1.3/24
. Verificați conectivitatea între cele 3 adrese IP folosind comanda ping
.
În acest moment avem conectivitate între toate cele host-uri (saisp
, VM1
, VM2
). Pentru a asigura conectivitatea la Internet trebuie să activăm rutarea și translatarea de adrese (NAT) pe mașina fizică:
root@saisp:~# echo 1 > /proc/sys/net/ipv4/ip_forward root@saisp:~# iptables -t nat -A POSTROUTING -o ethX -j MASQUERADE
unde ethX
este placa de retea de pe masina fizica.
Ne mai rămâne să configurăm ruta implicită și serverul de DNS pe mașinile virtuale. Conectați-vă folosind SSH la mașinile virtuale și adăugați ca rută implicită adresa 192.168.1.1
(mașina fizică) și ca server de DNS 8.8.8.8
. Verificați cu ajutorul comenzii ping
că site-ul www.google.ro
răspunde la cereri.
Scoatem interfețele tap-vm1
și tap-vm2
din bridge, după care îl ștergem pe acesta:
root@saisp:~# brctl delif br0 tap-vm1 root@saisp:~# brctl delif br0 tap-vm2 root@saisp:~# ip link set dev br0 down root@saisp:~# brctl delbr br0
Închideți mașinile virtuale și ștergeți imaginile create (sda-vm1.qcow2
și sda-vm2.qcow2
).
Un scenariu uzual este reprezentat de cazul în care pe mașinile virtuale se activează diverse servicii publice (exemplu: WEB). În acest caz nu este suficient să activăm translatarea de adrese pe mașina fizică, ci trebuie să conectăm mașina virtuală direct la rețeaua publică.
Porniți o nouă mașină virtuală KVM având o interfață de rețea cu adresa MAC 00:11:22:33:44:XX, unde XX reprezintă numărul vostru din catalogul de SAISP.
Pe mașina fizică, creați un nou bridge br1
în care adăugați interfața tap-vm
și interfața eth0
. Pe mașina virtuală executați comanda dhclient eth0
. Observați adresa IP obținută direct din rețeaua facultății.
Pentru a avea în continuare acces la Internet pe mașina fizică executați comenzile ip addr flush dev eth0
(șterge adresa IP de pe interfață) și dhclient br1
întrucât interfața principală este acum conectată în bridge la nivel 2 și nu mai poate oferi și servicii de nivel 3 (IP).
Intrebați pe unul din colegi adresa IP obținută pe mașina virtuală și conectați-vă la aceasta prin SSH. Observați că practic vă puteți conecta la orice mașină virtuală a oricărui coleg.
Eliminați din bridge interfațele tap-vm
și eth0
, ștergeți bridge-ul br1
, executați comanda dhclient eth0
și închideți mașina virtuală:
root@saisp:~# brctl delif br1 tap-vm root@saisp:~# brctl delif br1 eth0 root@saisp:~# ip link set dev br1 down root@saisp:~# brctl delbr br1 root@saisp:~# dhclient eth0
Pentru a ușura managementul mașinilor virtuale a fost dezvoltată o bibliotecă de interacțiune cu acestea ce poartă denumirea de libvirt
. Această bibliotecă oferă o interfață de programare comună pentru mai multe tehnologii (e.g.: KVM, LXC) fiind folosită de majoritatea tehnologiilor open-source de Cloud (e.g.: OpenStack, oVirt).
Pentru administratorii de rețea a fost dezvoltată o consolă numită virsh
ce folosește interfața oferită de libvirt
pentru interacțiunea cu mașinile virtuale.
Pentru a putea folosi facilitățile oferite de libvirt
trebuie să instalăm pachetele libvirt-bin
, virtinst
, virt-viewer
și virt-top
. De asemenea dacă utilizatorul e diferit de cel privilegiat trebuie adăugat în grupul libvirtd
(ca si la kvm
). În exemplul următor vom folosi utilizatorul privilegiat root
.
În primul pas trebuie să activăm serviciul de networking al libvirt
:
root@saisp:~# virsh -c qemu:///system net-start default
Pentru a crea o mașină virtuală folosim utilitarul virt-install
:
root@saisp:~# virt-install --connect qemu:///system --name VM1 --hvm --ram 256 --disk path=base.qcow2,format=qcow2 --network network=default --vnc --import
Semnificația parametrilor este după cum urmează:
* ''--connect qemu:///system'' - conectarea la sistemul local * ''--name VM1'' - numele mașinii virtuale * ''--hvm'' - să se folosească virtualizarea hardware (altfel se face emulare folosind QEMU, fără KVM) * ''--ram 256'' - cantitatea de memorie * ''--disk path=base.qcow2,format=qcow2'' - discul folosit și formatul acestuia * ''--network network=default'' - adăugare interfață de rețea cu proprietățile implicite * ''--vnc'' - să export consola VNC * ''--import'' - să folosească imaginea deja creată ''base.qcow2'', să NU creeze alta
În acest moment s-a creat un fișier de configurație XML al mașinii virtuale care poate fi vizualizat în /etc/libvirt/qemu/VM1.xml
.
Consola mașinii virtuale s-a deschis, programul virt-inst
rămânând să ruleze. Folositi CTRL+C
pentru a incheia instalarea masinii virtuale.
Pentru a controla mașina virtuală vom folosi consola virsh
. Ne conectăm la daemon-ul local cu ajutorul lui virsh
și vom afișa mașinile virtuale existente folosind comanda list
:
root@saisp:~# virsh --connect qemu:///system [...] virsh> list Id Name State ---------------------------------- 2 VM1 running
Observați starea mașinii virtuale și Id-ul acesteia (2
). Pe baza Id-ului veți realiza toate operațiile.
Dorim să aflăm portul VNC al mașinii virtuale cu ajutorul comenzii vncdisplay
urmat de Id-ul mașinii virtuale:
virsh # vncdisplay 2 :0
Deschideți o nouă consolă și conectați-vă la aceasta prin VNC folosind utilitarul vncviewer
:
root@saisp:~# vncviewer :0
Inchideți consola. ATENTIE: Mașina virtuala va continua să ruleze în background!
Opriți mașina virtuală cu ajutorul comenzii shutdown
urmată de Id-ul acesteia:
virsh # shutdown 2 Domain 2 is being shutdown virsh # list Id Name State ---------------------------------- 2 VM1 running virsh # list Id Name State ----------------------------------
Pentru a afișa toate mașinile virtuale indiferent de starea lor folosiți opțiunea –all
a comenzii list
:
virsh # list --all Id Name State ---------------------------------- - VM1 shut off
Vom porni mașina virtuală VM1 folosind comanda start
urmată de numele mașinii:
virsh # start VM1 Domain VM1 started virsh # list Id Name State ---------------------------------- 3 VM1 running
Observați că Id-urile se alocă la pornirea mașinilor virtuale.
Executați comanda destroy
pe Id-ul mașinii virtuale. Observați că mașina virtuală s-a închis, NU s-a șters.
virsh # destroy 3 Domain 3 destroyed virsh # list --all Id Name State ---------------------------------- - VM1 shut off
În echipe de câte doi, faceți schimb de adrese IP și asigurați accesul SSH fără parolă între calculatoarele voastre, folosind utilizatorul privilegiat root
.
Conectați-vă cu virsh
la calculatorului colegului și listați mașinile virtuale disponibile. La adresa de conectare folosiți parametrul:
qemu+ssh://$ADRESA_IP_COLEG/system
Porniți mașina creată de el și conectați-vă prin VNC la aceasta. Acesta este unul dintre cele mai utilizate cazuri în care aveți un server cu suport de virtualizare și doriți să rulați de la distanță mai multe mașini virtuale.
Ștergeți mașina virtuală definită folosind comanda undefine
urmată de numele mașinii virtuale:
virsh # undefine VM1 Domain VM1 has been undefined virsh # list Id Name State ---------------------------------- 6 VM1 running
Observați că mașina virtuală încă mai rulează. Ea va dispare complet după ce o închidem:
virsh # shutdown 6 Domain 6 is being shutdown virsh # list --all Id Name State ----------------------------------
Creați un nou grup în sistem kvm-users
și un utilizator ce aparține acestui grup. Configurați sistemul astfel încât utilizatorii din grupul kvm-users
să poate rula mașini virtuale KVM. (Hint: 01. [15p] Kernel-based Virtual Machine (KVM))
În echipe de câte doi, dorim să realizăm migrarea unei mașini virtuale (live migration) de pe o mașină fizică pe alta. Unul dintre voi va fi sursa, iar celălalt destinația.
Pe calculatorul sursă creați directorul /vm
și exportați-l prin NFS. Pe calculatorul destinație montați directorul exportat tot în /vm și asigurați-vă că îl puteți accesa.
Pe calculatorul sursă copiați imaginea base.qcow2
în directorul /vm
și porniți o mașină virtuală KVM în background (-daemonize
), având o consolă VNC. Realizați migrarea acesteia către destinație urmărind tutorialul de aici.