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).