Introducción a Linux/Virtualización
Introducción
[editar]Con el término virtualización nos referiremos a las distintas técnicas para simular por software una máquina física (p.ej, un servidor con su CPU, memoria, bus,...).
Debido al constante aumento de la velocidad de los procesadores a mediados de los años 90 se hizo evidente que en gran cantidad de ocasiones, la potencia del hardware estaba siendo infrautilizada por el software. Así, no es anormal comprobar como el tiempo de utilización de CPU en un servidor dedicado a correo o servicios web tan apenas supera de media el 1% de uso. Incluso en aplicaciones más pesadas como bases de datos la utilización media puede no suponer más de un 20%. Con contadas excepciones (programas 3D CAD/CAM o juegos) la mayor parte de las aplicaciones no utilizan más que un pequeño tanto por cien de la potencia disponible en la CPU. Con la llegada de las arquitecturas multi-core (varios procesadores integrados en un único chip) el problema aumenta ya que muchas aplicaciones no están diseñadas para ejecutarse simultáneamente de forma paralela en varios procesadores. Es decir, que aun cuando el software esté consumiendo el 100% de tiempo de un procesador, los demás procesadores estarán parados a la espera de alguna tarea pendiente.
La virtualización permite reutilizar (parte de) la potencia disponible en las modernas CPUs permitiendo que aplicaciones que se ejecutaban de forma independiente en distintas máquinas físicas continuen ejecutándose de forma independiente en distintas máquinas virtuales dentro de una misma máquina física sin que disminuya de forma apreciable el rendimiento y ahorrando gran cantidad de costes de mantenimiento.
De forma muy resumida e imprecisa pero pedagógica la virtualización funciona del siguiente modo. Ejecutamos un programa 'virtualizador' dentro de la máquina física. Este 'virtualizador' simula una segunda máquina dentro de nuestro sistema. Desde el punto de vista de nuestro sistema operativo este virtualizador no es mas que una aplicación más. Por otro lado si ahora dentro del virtualizador arrancamos un programa, p.ej, un CD de instalación, dicho programa creerá que se está ejecutando dentro del entorno virtual creado y no tendrá constancia alguna del hardware real sobre el que corre. Así, al acceder al disco duro no verá el disco duro real, sino un disco duro virtual creado por el virtualizador y que probablemente corresponda a un fichero dentro del sistema de ficheros de nuestra máquina física. Lo mismo ocurrirá con la memoria RAM o la conexión de red.
Existen gran cantidad de aplicaciones capaces de crear un virtualizador como VM-Ware, Virtuozzo, VirtualBox, KVM, Xen, etc... Cada una de ellas tiene sus ventajas e inconvenientes.
De momento empezaremos hablando de KVM (Kernel Virtual Machine) ya que está soportado oficialmente por el kernel de linux y se encuentra disponible en la mayoría de las distribuciones modernas del sistema operativo. Se explicará como instalar un configurar una máquina virtual. Los ejemplos utilizan la distribución Mandriva aunque son aplicables con cambios menores a cualquier otra distribución.
KVM: Instalación y configuración
[editar]Antes de nada es conveniente comprobar que la CPU tiene soporte hardware para virtualización. Si no es así, se puede continuar con la instalación pero el rendimiento final será muy inferior al esperado. También hay que resaltar que las CPUs antiguas (anteriores al 2007 aproximadamente) aun teniendo soporte hardware para virtualización tenían un rendimiento bastante pobre. Para comprobar si la CPU tiene soporte hardware para virtualización:
~ cat /proc/cpuinfo | egrep '(vmx|svm)' >/dev/null && echo "SOPORTE DETECTADO" || echo "SIN SOPORTE HARDWARE"
A continuación instalamos de repositorio el software necesario:
# su - Password:******* # urpmi.update -a # urpmi kvm libvirt0 python-virtinst libvirt-utils virt-manager
(virt-manager en realidad sólo hace falta instalarlo en los clientes que vayan a acceder al sistema, normalmente a través de una conexión remota VNC).
Iniciamos el demonio libvirt:
# /etc/init.d/libvirtd start
Para comprobar que el demonio se ha ejecutado correctamente:
# virsh -c qemu:///system list Id Name State ----------------------------------
El siguiente paso (y el más complicado) es reconfigurar la red. Generalemente, tendremos una tarjeta ethernet asociada a una IP. Sin embargo ahora queremos que la tarjeta ethernet sea compartida de forma transparente tanto por nuestro sistema operativo original como por las máquinas virtuales ejecutandose bajo el mismo. La solución es crear un interfaz virtual "puente". La tarjeta física ethernet y las tarjetas virtuales irán asociadas a dicho puente. Para ello instalamos primero el software bridge-utils:
# urpmi bridge-utils
Creamos/editamos el fichero /etc/sysconfig/network-scripts/ifcfg-br0 (tomar como referencia /etc/sysconfig/network-scripts/ifcfg-eth0):
# cat /etc/sysconfig/network-scripts/ifcfg-br0 DEVICE=br0 TYPE=Bridge BOOTPROTO=static DNS1=194.224.52.4 <--- Reemplazar por nuestro DNS primario DNS2=194.224.52.6 <--- Reemplazar por nuestro DNS secundario GATEWAY=192.168.0.1 IPADDR=192.168.0.10 <--- Esta será la IP asociada al dispositivo de red Bridge0 (br0) NETMASK=255.255.255.0 ONBOOT=yes IPV6INIT=no IPV6TO4INIT=no SEARCH=
A su vez modificamos la configuración de eth0 ya que IPADDR y otros parámetros (BOOTPROTO, DNS1, GATEWAY, NETMASK) deben ser eliminados/comentados. Además hay que añadir la opción BRIDGE=br0 para indicar que se enlace al bridge0:
# cat /etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE=eth0 #BOOTPROTO=static #IPADDR=192.168.0.10 #NETMASK=255.255.255.0 #GATEWAY=192.168.0.1 ONBOOT=yes METRIC=10 MII_NOT_SUPPORTED=no USERCTL=no #DNS1=194.224.52.4 #DNS2=194.224.52.6 RESOLV_MODS=no LINK_DETECTION_DELAY=6 IPV6INIT=no IPV6TO4INIT=no ACCOUNTING=no BRIDGE=br0
Una vez hechos los cambios reiniciamos la red:
# /etc/init.d/network restart
Ahora al mostrar la configuración de red mediante ifconfig debemos ver algo similar a:
# ifconfig
br0 Link encap:Ethernet HWaddr 00:11:22:33:44:55
inet addr:192.168.0.10 Bcast:192.168.0.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:10 errors:0 dropped:0 overruns:0 frame:0
TX packets:15 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1200 (1.17 KiB) TX bytes:1200 (1.17 KiB)
eth0 Link encap:Ethernet HWaddr 00:21:70:9F:07:3A
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:34905 errors:0 dropped:0 overruns:0 frame:0
TX packets:21722 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:121201313 (115.5 MiB) TX bytes:2334243 (2.2 MiB)
Interrupt:28
lo Link encap:Local Loopback
...
A continuación instalamos un sistema huesped (similar a si instalásemos un sistema operativo en una máquina física):
Insertamos el CDROM de instalación en nuestra máquina y a continuación:
# virt-install --connect qemu:///system \ -n servidorWeb \ <--- Nombre de la máquina virtual huesped. -r 256 \ <--- Memoria RAM asignada -f ~/servidorWeb.qcow2 \ <--- Fichero que alogará el disco duro virtual -c /dev/cdrom \ <--- CDROM tal como es visto por el sistema operativo original (puede utilizarse un fichero con una imagen ISO en su lugar) --vnc \ <--- Habilitamos la conexión remota por VNC --accelerate \ <--- Habilitamos la aceleración hardware --network=bridge:br0,mac=33:44:55:66:77:88 \ <--- Nombre del dispositivo de red bridge, dirección MAC --hvm <--- Indica que el huesped tiene soporte hardware Starting install... Creating storage file servidorWeb.qcow2 | 12 GB 00:00 Creating domain... | 0 B 00:00 Domain installation still in progress. You can reconnect to the console to complete the installation process. #
Importante: Si ejecutamos varias máquinas virtuales en paralelo (caso habitual) es importante no olvidar poner una MAC diferente para cada máquina. En caso contrario obtendremos errores aleatoreos de red.
La máquina virtual arrancará y empezará a instalar desde /dev/cdrom. Como hemos indicado la opción --vnc no veremos nada en pantalla. Debemos conectar primero mediante un cliente VNC a nuestra máquina virtual:
# vncviewer 127.0.0.1:5900
El puerto 5900 puede variar si existe más de una máquina virtual en funcionamiento (empieza en 5900 y vá creciendo). El comando :
# virsh vncdisplay servidorWeb :2
nos indicará el puerto correcto. El número anterior hay que sumarlo a 5900 de forma que el comando correcto quedará como:
# vncviewer 127.0.0.1:5902
(Podemos también utilizar el gestor de máquinas virtuales en lugar de vncviewer accediento al menú Aplicaciones -> Herramientas -> Gestor de Máquinas virtuales)
Una vez conectados por vnc podemos continuar la instalación del sistema en nuestro CDROM como lo haríamos habitualmente.
Utilidades
[editar]virsh (virtual shell) permite controlar el proceso kvm, pausándolo, realizando un backup del disco duro virtual,etc.... Para ello:
# virsh --connect qemu:///system
Welcome to virsh, the virtualization interactive terminal.
Type: 'help' for help with commands
'quit' to quit
virsh # help
Commands:
help print help
attach-device attach device from an XML file
attach-disk attach disk device
attach-interface attach network interface
autostart autostart a domain
capabilities capabilities
cd change the current directory
connect (re)connect to hypervisor
console connect to the guest console
create create a domain from an XML file
start start a (previously defined) inactive domain
destroy destroy a domain
detach-device detach device from an XML file
detach-disk detach disk device
detach-interface detach network interface
define define (but don't start) a domain from an XML file
domid convert a domain name or UUID to domain id
domuuid convert a domain name or id to domain UUID
dominfo domain information
domname convert a domain id or UUID to domain name
domstate domain state
domblkstat get device block stats for a domain
domifstat get network interface stats for a domain
domxml-from-native Convert native config to domain XML
domxml-to-native Convert domain XML to native config
dumpxml domain information in XML
edit edit XML configuration for a domain
find-storage-pool-sources discover potential storage pool sources
find-storage-pool-sources-as find potential storage pool sources
freecell NUMA free memory
hostname print the hypervisor hostname
list list domains
migrate migrate domain to another host
net-autostart autostart a network
net-create create a network from an XML file
net-define define (but don't start) a network from an XML file
net-destroy destroy a network
net-dumpxml network information in XML
net-edit edit XML configuration for a network
net-list list networks
net-name convert a network UUID to network name
net-start start a (previously defined) inactive network
net-undefine undefine an inactive network
net-uuid convert a network name to network UUID
iface-list list physical host interfaces
iface-name convert an interface MAC address to interface name
iface-mac convert an interface name to interface MAC address
iface-dumpxml interface information in XML
iface-define define (but don't start) a physical host interface from an XML file
iface-undefine undefine a physical host interface (remove it from configuration)
iface-edit edit XML configuration for a physical host interface
iface-start start a physical host interface (enable it / "if-up")
iface-destroy destroy a physical host interface (disable it / "if-down")
nodeinfo node information
nodedev-list enumerate devices on this host
nodedev-dumpxml node device details in XML
nodedev-dettach dettach node device from its device driver
nodedev-reattach reattach node device to its device driver
nodedev-reset reset node device
nodedev-create create a device defined by an XML file on the node
nodedev-destroy destroy a device on the node
pool-autostart autostart a pool
pool-build build a pool
pool-create create a pool from an XML file
pool-create-as create a pool from a set of args
pool-define define (but don't start) a pool from an XML file
pool-define-as define a pool from a set of args
pool-destroy destroy a pool
pool-delete delete a pool
pool-dumpxml pool information in XML
pool-edit edit XML configuration for a storage pool
pool-info storage pool information
pool-list list pools
pool-name convert a pool UUID to pool name
pool-refresh refresh a pool
pool-start start a (previously defined) inactive pool
pool-undefine undefine an inactive pool
pool-uuid convert a pool name to pool UUID
secret-define define or modify a secret from an XML file
secret-dumpxml secret attributes in XML
secret-set-value set a secret value
secret-get-value Output a secret value
secret-undefine undefine a secret
secret-list list secrets
pwd print the current directory
quit quit this interactive terminal
reboot reboot a domain
restore restore a domain from a saved state in a file
resume resume a domain
save save a domain state to a file
schedinfo show/set scheduler parameters
dump dump the core of a domain to a file for analysis
shutdown gracefully shutdown a domain
setmem change memory allocation
setmaxmem change maximum memory limit
setvcpus change number of virtual CPUs
suspend suspend a domain
ttyconsole tty console
undefine undefine an inactive domain
uri print the hypervisor canonical URI
vol-create create a vol from an XML file
vol-create-from create a vol, using another volume as input
vol-create-as create a volume from a set of args
vol-clone clone a volume.
vol-delete delete a vol
vol-dumpxml vol information in XML
vol-info storage vol information
vol-list list vols
vol-path convert a vol UUID to vol path
vol-name convert a vol UUID to vol name
vol-key convert a vol UUID to vol key
vcpuinfo domain vcpu information
vcpupin control domain vcpu affinity
version show version
vncdisplay vnc display
Ejs:
virsh # list --all Id Name State ---------------------------------- 1 servidorWeb running virsh # suspend servidorWeb virsh # resume servidorWeb virsh # quit #
Resolución de problemas
[editar]Problemas con la MAC de la tarjeta ethernet
[editar]Suele ser bastante habitual utilizar una imágen como plantilla para nuevas máquinas virtuales. En este caso al arrancar una nueva máquina virtual basada en la plantilla con una nueva MAC virtual el sistema udev detecta que eth0 ya está asignado a la MAC utilizada para crear la plantilla y entonces asociará la nueva MAC al dispositivo eth1. Esto puede generar molestias en sistemas en producción. La solución es editar el fichero correspondiente en /etc/udev/rules.d/ y eliminar la línea correspondiente a la antigua MAC. El fichero en concreto puede variar en cada distribución pero basta con hacer algo similar a:
# cd /etc/udev/rules.d
# grep --ignore-case "00:21:70:9F:07:3A" *
61-net_config.rules:SUBSYSTEM=="net", ACTION=="add", ENV{INTERFACE}!="*.*", SYSFS{address}=="00:21:70:9f:07:3a", NAME="eth0", ENV{MDV_CONFIGURED}="yes"
70-persistent-net.rules:# Drakx-net rule for eth0 (00:21:70:9f:07:3a)
70-persistent-net.rules:SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:21:70:9f:07:3a", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"
(En este ejemplo, eliminaríamos las líneas en los ficheros 61-net_config.net y 70-persistent-net.rules)
En el siguiente reinicio de la máquina virtual se asignará la MAC a eth0.
Controlando los recursos de la máquina virtual
[editar]El sistema operativo físico el que esta fuera de la vmware ve cada máquina virtual como un proceso más. Podemos controlar la prioridad de cada máquina virtual mediante los comandos habituales renice e ionice. Por ejemplo una máquina virtual dedicada a correo puede ser ejecutada con mínima prioridad (nice 19, ionice idle).