Introducción a Linux/Virtualización
Introducción
editarCon 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
editarAntes 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
editarvirsh (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
editarProblemas con la MAC de la tarjeta ethernet
editarSuele 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
editarEl 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).