VMware: Mejores prácticas en la sobresuscripción de CPU

Como es bien sabido, una de las ventajas de la virtualización es la de poder asignar mas recursos virtuales de los que realmente disponemos sobre un determinado hardware. Esto hace que el uso de los recursos sea mas eficiente y los aprovechemos mejor. Si bien es posible hacer una sobresuscripción con un ratio elevado, esto nos puede llevar a tener problemas de rendimiento en las máquinas virtuales. He encontrado una guía muy completa donde se establecen algunas mejores prácticas a la hora de dimensionar las máquinas virtuales.

pCPU vs vCPU

Lo primero a tener en cuenta es que se entiende por pCPU y vCPU:
  • pCPU se refiere a las CPU físicas. El número de CPUs físicas presentes en un determinado host depende de varios factores como son el número de sockets, el número de cores y si tiene hiperthreading y está habilitado. 
  • vCPU son las CPU que se asignan a una máquina virtual y no tiene porque está ligado al número de pCPU. Cuando se crea una máquina virtual las vCPU se asignan a una pCPU. Debe haber un número de pCPU disponibles para soportar el número de vCPU asignadas a una máquina virtual o esta no podrá arrancar.

En vSphere 5 el número máximo de vCPU por pCPU es de 25:1. Aunque se recomienda que por temas de rendimiento esta relación no sobrepase el 5:1. No obstante esto siempre dependerá del tipo de carga de trabajo que ejecuten las máquinas virtuales.

    Cuando se supera la relación 1:1 debe entrar en funcionamiento el “scheduler” para distribuir los tiempos de procesador entre las máquinas que lo necesiten.

    A la hora de dimensionar el ratio vCPU:pCPU la guía recomienda seguir dos estrategias:

    1. Empezar con una vCPU en la máquina virtual. Una vez analizada la carga de la máquina virtual ir añadiendo vCPU según la necesidad. Cuando la máquina virtual necesite realizar una operación, esta tendrá que esperar a que haya disponibles un número de pCPU igual al número de vCPU. Con lo que, al añadir mas vCPU se incrementa el riesgo de que el tiempo de espera de pCPU aumente provocando un rendimiento peor.
    2. Como la relación vCPU:pCPU depende de la carga de trabajo, la relación puede ser mayor en hosts que ejecuten máquinas virtuales con necesidades de procesamiento marginal. Por otro lado, en hosts que requieran intensivas cargas de trabajo, el ratio deberá ser inferior.

    Métricas

    Existirán ciertas métricas que nos servirán para determinar si el host está teniendo algún problema de rendimiento y para hacer un uso mas eficiente de los recursos mientras se mantienen las cargas de trabajo.

    • Uso de CPU de la máquina virtual: Se deberán añadir vCPU adicionales cuando el uso medio de CPU se mantenga en altos niveles.
    • CPU Ready en el host: Desde el punto de vista de la salud del host, esta es la métrica mas importante. Esta métrica determina el tiempo que una máquina virtual esta esperando para obtener vCPU disponibles que necesita. Por ejemplo, si una máquina virtual tiene asignadas cuatro vCPU, esta métrica indica el tiempo que una máquina virtual ha esperado para que cuatro pCPU estén disponibles en el mismo instante de tiempo.
    • Uso de CPU del host: permite analizar la carga que está soportando el host.

    En el White paper se establecen estos criterios:

    • Una relación entre 1:1 y 3:1 no provoca problemas
    • Entre 3:1 y 5:1 puede empezar a causar alguna degradación de rendimiento
    • Si la relación es superior a 6:1 se suelen tener problemas.

    Adicionalmente se recomienda que el CPU Ready este por debajo del 5%.

    Calculo del CPU Ready

    Como hemos comentado esta métrica es realmente importante para determinar si estamos teniendo problemas de rendimiento. En el cliente de vSphere esta métrica se representa en segundos, mientras que con un ESXTOP se devuelve en porcentaje.

    Para realizar la conversión debemos tener en cuenta el tiempo de muestreo de la gráfica, 20 segundos en la gráfica de real time. La fórmula será la siguiente:
    (x ms  / 20.000 ms) * 100 = %rdy
    Por ejemplo, con esta formula sabremos que:

    • 1% = 200ms
    • 5% = 1000ms
    • 10% = 2000ms
    • 100% = 20000ms

    Hay que tener en cuenta que estas medidas se dan por vCPU, co lo que una máquina con 4 vCPU podrá llegar a tener un 400% de tiempo de planificación. La misma máquina con un 10% de CPU Ready implicará que cada vCPU tenga un CPU Ready de 2.5%.

    Caso Real

    En el siguiente ejemplo tenemos un host HP Prolian DL580 G7 con 4 CPU octacore y con Hyperthreading activado:

    En dicho host hay máquinas virtuales de todo tipo, pero especialmente hay varias “Monster Machines” con hasta 128GB de RAM y 32 vCPU. Como se puede ver en la siguiente gráfica, estas máquinas están sobredimensionadas, ya que el uso de CPU medio es muy bajo:

    Otras tienen un uso de CPU mas elevado, la media en torno al 65%.

    Sin embargo en momentos puntuales llegamos a tener contención y el CPU ready se dispara como podemos ver en esta gráfica donde aparece el acumulado en el host. Esto es debido a que estas máquinas forman parte de un servicio que hace que todas tengan una carga intensiva de CPU en momentos puntuales haciendo que el CPU Ready suba tanto que al final el rendimiento de las máquinas es peor del esperado:

     Como vemos en las gráficas de rendimiento de estas  “Monster Machines”, en momentos puntuales el CPU Ready se dispara a la vez, llegando a los 13 segundos:

     
    Según la fórmula de conversión al %rdy esos 13 segundos corresponden a un 65% de CPU Ready. Para la máquina de 32 vCPU es un 2% por CPU y para la máquina de 8 vCPU corresponde a un 8%. Esta última es la que realmente está teniendo un peor rendimiento y la que se vé mas afectada. Lo ideal sería bajar el número de vCPU asignadas a estas máquinas virtuales para adecuarse mas al consumo real que están teniendo.


    Algunos enlaces interesantes respecto a este tema:

    Anuncios

    Fallo al arrancar máquinas virtuales despues de migrar oVirt a 3.5

    Recientemente he actualizado una instalación de oVirt de la versión 3.4 a la versión 3.5. El proceso es bastante trasparente y básicamente consiste en actualizar el repositorio y seguir los mismos pasos que durante la instalación. Una vez con la nueva versión podemos actualizar la compatibilidad del cluster que tengamos configurado a la versión 3.5. Para ello es recomendable poner los hosts en mantenimiento. Una vez actualizado se pueden actualizar también los Hosts. Ya con todo actualizado vemos que hay funcionalidades nuevas, como la gestión de memoria con NUMA. 
    Esta funcionalidad puede ser interesante en entornos que requieren un uso intensivo de CPU y Memoria, pero debe estar bien configurado. Si, como en mi caso, tenemos un entorno mas estándar no es necesario configurarlo, pero al intentar arrancar las máquinas de nuevo podemos tener el siguiente error:
    VM compute1 is down with error. Exit message: internal error internal error NUMA memory tuning in ‘preferred’ mode only supports single node.
    Para solucionarlo basta seguir los pasos que se indican en la lista de distribución:
    Resumiendo, hay que editar la máquina virtual que presenta los problemas y acceder a la opción Host:
    Allí veremos que el “Tune Mode” esta deshabilitado y configurado por defecto en “Preferred”. Para poder cambiarlo seleccionaremos un host sobre el que correrá esta máquina y deshabilitaremos las migraciones. En ese momento ya podemos seleccionar la opción “Interleave” como “Tune Mode”:
    Una vez cambiado podemos volver los parámetros por defecto. Una vez aplicados podremos arrancar la máquina

    Agilizando la Infraestructura IV: Trabajando con oVirt

    En el post anterior vimos como instalar y configurar oVirt dejándolo preparado para el despliegue de máquinas virtuales. En este post explicaré como configurar el repositorio de imágenes ISO, de forma que podamos arrancar una máquina virtual y asignarle una imagen de instalación por ejemplo. A continuación veremos como integrarlo con Foreman de forma que el despliegue de las máquinas virtuales lo hagamos desde Foreman como si fuese una máquina física.

    Repositorio de imágenes

    Durante la instalación se ha creado un Storage Domain que debemos configurar y que aparece como “Unattached”. Al seleccionarlo, en la consola inferior seleccionamos la pestaña “Data Center” y a continuación lo activamos con el boton de “Attach”:

    A continuación nos aparecerá una ventana donde indicaremos al “Data Center” donde lo queremos añadir, en nuestro caso, al solo tener uno no tendremos mas opción:

    En este punto ya podremos subir imágenes. No he encontrado otra forma de subirlas, y al parecer hay que hacerlo desde la línea de comandos con la herramienta engine-iso-uploader:


    [root@ovirt ~]# engine-iso-uploader upload -i ISO_DOMAIN /var/lib/exports/iso/ubuntu-14.04-desktop-amd64.iso
    Please provide the REST API password for the admin@internal oVirt Engine user (CTRL+D to abort):
    Uploading, please wait…
    INFO: Start uploading /var/lib/exports/iso/ubuntu-14.04-desktop-amd64.iso  

    A los pocos minutos la imagen estará subida y disponible para añadirla a alguna máquina virtual. Así mismo, podemos ver el listado de imágenes que tenemos subidas desde la interfaz web:

    Integración con Foreman

    La integración con Foreman es bidireccional. Por una parte una vez integrado, desde Foreman, seremos capaces de provisionar máquinas virtuales y aplicarle las clases que hayamos definido como si de una máquina física se tratara. Por otro lado, desde oVirt, podremos acceder al inventario de máquinas de Foreman para el caso de que quisieramos añadir mas hosts, por ejempo.
    Para el primer caso, desde la consola de Foreman accederemos al menú “Infraestructure/Compute Resources” y configuraremos la URL de la API de oVirt. También deberemos especificar el usuario administrador y su password. Finalmente probaremos la conexión para poder obtener el certificado e importarlo:
    Para la integración en el otro sentido, desde la consola de oVirt añadiremos en este caso la URL de la API de Foreman desde la rama “External Providers” y con el botón de “Add”:

    Creación de máquinas virtuales

    Para la creación de máquinas virtuales podríamos seguir el método clásico de crear una nueva máquina e instalarle el sistema operativo desde el CD que le hayamos asignado. Sin embargo, aprovechando que tenemos nuestra instalación de Foreman, realizaremos el aprovisionamiento de las máquinas virtuales con el.
    En la interfaz de Foreman, iremos a añadir un nuevo Host, y tras ponerle el nombre y asignarle el Host Group le pondremos que el despliegue se realice sobre oVirt. Esta opción se ha habilitado gracias a la integración que hemos configurado anteriormente. Veremos además que aparece una nueva pestaña de “Virtual Machine” donde podremos configurar parámetros específicos:

    En la pestaña “Virtual Machine” le asignaremos los recursos que queremos que tenga nuestra máquina virtual: número de cores y memoria. También deberemos añadirle una interfaz de red y uno o mas discos, marcando que uno de ellos sea bootable:

    En este punto ya veremos en la interfaz de oVirt que se ha creado una nueva máquina con el nombre que le hemos especificado y empezará la instalación de la misma.
    Para poder ver la consola, desde Ubuntu deberemos instalar el paquete virt-viewer. Al acceder a la consola con Firefox nos pedirá con que programa abrirlo, a lo que deberemos seleccionar “Remote viewer”

    julian@ubuntu:~$ sudo apt-get install virt-viewer  

    Agilizando la Infraestructura III: oVirt 3.4 con glusterFS

    En esta entrada voy a explicar como he instalado un entorno de virtualización basado en oVirt 3.4 y utilizando como almacenamiento compartido volúmenes de glusterFS. oVirt requiere de un nodo de control y varios nodos de computación. La arquitectura viene a ser bastante similar a la que podríamos tener con VMware vSphere. He instalado el motor de gestión en un servidor físico con 2 CPU y 1 GB de RAM. Si bien los requisitos son mucho mayores, para la demo que he preparado va bastante bien aunque le puede llegar a faltar algo de fluidez.

    Instalación de oVirt

    La instalación, como en todos los productos en los que Red Hat está por detras, se realiza desde un RPM. Se instala primero un rpm que nos configurará los repositorios oficiales de ovirt y a continuación se instala el paquete ovirt-engine:

    [root@ovirt ~]# yum install http://plain.resources.ovirt.org/pub/yum-repo/ovirt-release34.rpm
    [root@ovirt ~]# yum -y install ovirt-engine

     Una vez instalado se lanza la configuración donde deberemos responder algunas preguntas:

    [root@ovirt ~]# engine-setup
    [ INFO ] Stage: Initializing
    [ INFO ] Stage: Environment setup
    Configuration files: [‘/etc/ovirt-engine-setup.conf.d/10-packaging.conf’]
    Log file: /var/log/ovirt-engine/setup/ovirt-engine-setup-20140704130518-cgito9.log
    Version: otopi-1.2.1 (otopi-1.2.1-1.el6)
    [ INFO ] Stage: Environment packages setup
    [ INFO ] Yum Downloading: repomdSYOvZitmp.xml (0%)
    [ INFO ] Stage: Programs detection
    [ INFO ] Stage: Environment setup
    [ INFO ] Stage: Environment customization

    –== PRODUCT OPTIONS ==–


    –== PACKAGES ==–

    [ INFO ] Checking for product updates…
    [ INFO ] No product updates found

    –== NETWORK CONFIGURATION ==–

    Host fully qualified DNS name of this server [ovirt.enterprise.com]:
    Setup can automatically configure the firewall on this system.
    Note: automatic configuration of the firewall may overwrite current settings.
    Do you want Setup to configure the firewall? (Yes, No) [Yes]: yes
    The following firewall managers were detected on this system: iptables Firewall manager to configure (iptables): iptables
    [ INFO ] iptables will be configured as firewall manager.

    –== DATABASE CONFIGURATION ==–

    Where is the Engine database located? (Local, Remote) [Local]:
    Setup can configure the local postgresql server automatically for the engine to run. This may conflict with existing applications.
    Would you like Setup to automatically configure postgresql and create Engine database, or prefer to perform that manually? (Automatic, Manual) [Automatic]:

    –== OVIRT ENGINE CONFIGURATION ==–

    Application mode (Both, Virt, Gluster) [Both]:
    Default storage type: (NFS, FC, ISCSI, POSIXFS, GLUSTERFS) [NFS]: GLUSTERFS
    Engine admin password:
    Confirm engine admin password:
    –== PKI CONFIGURATION ==–

    Organization name for certificate [enterprise.com]:

    –== APACHE CONFIGURATION ==–

    Setup can configure apache to use SSL using a certificate issued from the internal CA.
    Do you wish Setup to configure that, or prefer to perform that manually? (Automatic, Manual) [Automatic]:
    Setup can configure the default page of the web server to present the application home page. This may conflict with existing applications.
    Do you wish to set the application as the default page of the web server? (Yes, No) [Yes]:

    –== SYSTEM CONFIGURATION ==–

    Configure WebSocket Proxy on this machine? (Yes, No) [Yes]:
    Configure an NFS share on this server to be used as an ISO Domain? (Yes, No) [Yes]:
    Local ISO domain path [/var/lib/exports/iso]:
    Local ISO domain ACL [0.0.0.0/0.0.0.0(rw)]:
    Local ISO domain name [ISO_DOMAIN]:

    –== MISC CONFIGURATION ==–


    –== END OF CONFIGURATION ==–

    [ INFO ] Stage: Setup validation
    –== CONFIGURATION PREVIEW ==–

    Engine database name : engine
    Engine database secured connection : False
    Engine database host : localhost
    Engine database user name : engine
    Engine database host name validation : False
    Engine database port : 5432
    NFS setup : True
    PKI organization : enterprise.com
    Application mode : both
    Firewall manager : iptables
    Update Firewall : True
    Configure WebSocket Proxy : True
    Host FQDN : ovirt.enterprise.com
    NFS export ACL : 0.0.0.0/0.0.0.0(rw)
    NFS mount point : /var/lib/exports/iso
    Datacenter storage type : glusterfs
    Configure local Engine database : True
    Set application as default page : True
    Configure Apache SSL : True

    Please confirm installation settings (OK, Cancel) [OK]:
    [ INFO ] Stage: Transaction setup
    [ INFO ] Stopping engine service
    [ INFO ] Stopping websocket-proxy service
    [ INFO ] Stage: Misc configuration
    [ INFO ] Stage: Package installation
    [ INFO ] Stage: Misc configuration
    [ INFO ] Initializing PostgreSQL
    [ INFO ] Creating PostgreSQL ‘engine’ database
    [ INFO ] Configuring PostgreSQL
    [ INFO ] Creating Engine database schema
    [ INFO ] Creating CA
    [ INFO ] Configuring WebSocket Proxy
    [ INFO ] Generating post install configuration file ‘/etc/ovirt-engine-setup.conf.d/20-setup-ovirt-post.conf’
    [ INFO ] Stage: Transaction commit
    [ INFO ] Stage: Closing up

    –== SUMMARY ==–SSH fingerprint: 83:1F:BB:B9:DC:7B:94:D3:41:38:67:AC:33:E9:58:AE Internal CA 7E:BE:5C:B7:34:DD:E3:EB:2D:44:18:1B:E4:B8:3B:BE:33:BB:99:14 Web access is enabled at:
    http://ovirt.enterprise.com:80/ovirt-engine https://ovirt.enterprise.com:443/ovirt-engine
    Please use the user “admin” and password specified in order to login

    –== END OF SUMMARY ==–

    [ INFO ] Starting engine service
    [ INFO ] Restarting httpd
    [ INFO ] Restarting nfs services
    [ INFO ] Stage: Clean up
    Log file is located at /var/log/ovirt-engine/setup/ovirt-engine-setup-20140704130518-cgito9.log
    [ INFO ] Generating answer file ‘/var/lib/ovirt-engine/setup/answers/20140704131454-setup.conf’
    [ INFO ] Stage: Pre-termination
    [ INFO ] Stage: Termination


    [ INFO ] Execution of setup completed successfully

    Con las pocas preguntas que hemos respondido ya nos instala todo lo necesario y nos habilita el acceso a la consola web. También nos ha configurado un repositorio de imágenes ISO que mas adelante mostraré como usar y terminar de configurar.

    Interfaz web

    Si accedemos a la URL que nos ha devuelto el proceso de configuración veremos que nos ha habilitado varios portales, entre ellos el de administración:

    Accedemos al portal y lo primero que haremos será añadir dos hosts al cluster. Para crear los hosts existen dos opciones:

    • utilizar una imagen desde la web de oVirt con la que se instala el sistema operativo y todo lo necesario
    • utilizar un sistema operativo ya preinstalado al que se le instalará el software necesario al agregarlo al cluster.

    En mi caso ya disponia de dos servidores instalados con lo que los he añadido al cluster. Previamente he configurado los repositorios de oVirt al igual que hice para instalar el motor. A continuación desde la interfaz web añadomos el host e introduciremos los datos del usuario root para que pueda instalar el software:

    En la parte inferior podemos ver los eventos y tareas que se están ejecutando. En el se puede ver que se está ejecutando YUM en los servidores para instalar el software de oVirt.

    Almacenamiento

    Una vez instalado los nodos sobre los que correrán las máquinas virtuales es necesario la configuración de un almacenamiento compartido. He elegido GlusterFS por las bondades de HA y performance que tiene. GlusterFS lo configuro sobre dos servidores adicionales, aunque bien podría instalarse en los mismos servidores donde van a correr las máquinas virtuales.

    La instalacioń es secilla:

    [root@storage01 ~]# yum install -y glusterfs glusterfs-fuse glusterfs-server

    Una vez instalado se configuran los pares y ya se puede crear un volumen e iniciarlo:

    [root@storage01 ~]# gluster peer probe storage02
    peer probe: success.[root@storage01 ~]# gluster peer status
    Number of Peers: 1

    Hostname: storage02
    Uuid: 3e4056d0-2ab0-4e83-b273-ceb565b7458c
    State: Peer in Cluster (Connected)
    [root@storage01 #]# gluster volume create DATA replica 2 transport tcp storage01:/opt/gluster/data storage02:/opt/gluster/data
    volume create: DATA: success: please start the volume to access data
    [root@storage01 ~]# gluster volume start DATA


    volume start: DATA: success  

    He creado un volúmen con dos réplicas, de forma que si perdemos una de las dos no habría pérdida de datos. Para poder utilizarlo en oVirt se deben realizar algunas modificaciones:

    [root@storage01 ~]# gluster volume set DATA allow-insecure on[root@storage01 ~]# gluster volume set DATA server.allow-insecure on
    [root@storage01 ~]# gluster volume set DATA storage.owner-uid=36
    [root@storage01 ~]# chown 36:36 /opt/gluster/data
    [root@storage01 ~]# gluster volume info

    Volume Name: DATA
    Type: Replicate
    Volume ID: 6361b068-7601-4903-88c4-5cdc50f28236
    Status: Started
    Number of Bricks: 1 x 2 = 2
    Transport-type: tcp
    Bricks:
    Brick1: storage01:/opt/gluster/data
    Brick2: storage02:/opt/gluster/data
    Options Reconfigured:
    server.allow-insecure: on


    storage.owner-uid: 36

    También se debe modificar el fichero /etc/glusterfs/glusterd.vol añadiendo la opción rcp-auth-allow-insecure:

    [root@storage01 ~]# vi /etc/glusterfs/glusterd.volvolume management
    type mgmt/glusterd
    option working-directory /var/lib/glusterd
    option transport-type socket,rdma
    option transport.socket.keepalive-time 10
    option transport.socket.keepalive-interval 2
    option transport.socket.read-fail-log off
    # option base-port 49152
    option rpc-auth-allow-insecure on


    end-volume

    Una vez realizado estos cambios se debe reiniciar el demonio en los nodos:

    [root@storage01 ~]# service glusterd restart
    Starting glusterd: [ OK ]

    Por último, en los Hosts de oVirt es necesario instalar el paquete vdsm-gluster para que puedan montar los volúmenes:

    [root@compute01 ~]# sudo yum install vdsm-gluster

    Con todo ya configurado se puede crear el dominio de almacenamiento y mapear el volúmen que hemos creado anteriormente:

    Ya está todo preparado para poder empezar a crear máquinas virtuales. En la siguiente entrada explicaré como crear el repositorio de imágenes y como integrar oVirt con Foreman.

    Jugando con Docker

    Últimamente se esta oyendo mucho hablar de Docker. Se trata de una tecnología similar a la que ya existía en Solaris con las zonas, y en Linux con LXC (de hecho utiliza LXC por debajo). Parece que viene patrocinado por algunas de las grandes en internet como eBay, Spotify e incluso Google parece que venia utilizando algo parecido desde hace tiempo.
    Docker
    La gran ventaja, como en el caso de las zonas, es que te permite “virtualizar” sin depender de un hipervisor y con un overhead mínimo en comparación a la virtualización tradicional.

    Instalación

    Como todo en Linux es muy fácil de instalar y ya viene preparado en la mayoría de distribuciones. En el caso de CentOS hay que habilitar el repositorio EPEL editando el fichero de configuración del repositorio y poniendo la variable enabled a 1:

    [root@centos1 ~]# vi /etc/yum.repos.d/epel.repo

    [epel]
    name=Extra Packages for Enterprise Linux 6 – $basearch
    #baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch
    mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-6&arch=$basearch
    failovermethod=priority
    enabled=1
    gpgcheck=1
    gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
    A continuación ya podemos instalar docker con yum. Como veremos, una de las dependencias es LXC:
    [root@centos1 ~]# yum install docker-io
    Dependencies Resolved

    ================================================================================
     Package            Arch            Version                 Repository     Size
    ================================================================================
    Installing:
     docker-io          x86_64          1.0.0-3.el6             epel          4.5 M
    Installing for dependencies:
     lxc                x86_64          0.9.0-2.el6             epel           78 k
     lxc-libs           x86_64          0.9.0-2.el6             epel          116 k

    Transaction Summary
    ================================================================================
    Install       3 Package(s)

    Total download size: 4.7 M
    Installed size: 24 M
    Is this ok [y/N]: y

    Así de sencillo es instalarlo. Y como vemos, la versión es la primera estable que se ha publicado recientemente:
    [root@centos1 ~]# docker -v
    Docker version 1.0.0, build 63fe64c/1.0.0
    Para empezar a utilizarlo se debe iniciar el servicio y configurarlo para que arranque en el runlevel predeterminado:
    [root@centos1 ~]# service docker start
    Starting cgconfig service:                                 [  OK  ]
    Starting docker:                                                [  OK  ]
    [root@centos1 ~]# chkconfig docker on

    Imágenes y contenedores

    Con el servicio ya en marcha podemos descargar las imágenes que docker mantiene, así como imágenes que la comunidad ha creado y ha subido. Por ejemplo descargaremos las últimas para centos y para ubuntu:

    [root@centos1 ~]# docker pull centos:latest
    Pulling repository centos
    0c752394b855: Download complete
    511136ea3c5a: Download complete
    34e94e67e63a: Download complete

    [root@centos1 ~]# docker pull ubuntu:latest
    Pulling repository ubuntu
    e54ca5efa2e9: Download complete
    511136ea3c5a: Download complete
    d7ac5e4f1812: Download complete
    2f4b4d6a4a06: Download complete
    83ff768040a0: Download complete
    6c37f792ddac: Download complete
    Para ver las imágenes que tenemos descargadas y disponibles para utilizar bastará con ejecutar el subcomando “images”:
    [root@centos1 ~]# docker images
    REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
    ubuntu              latest              e54ca5efa2e9        41 hours ago        276.1 MB
    centos              latest              0c752394b855        10 days ago         124.1 MB
    Pasando el parámetro -a podemos ver el historial de versiones de cada imagen:
    [root@centos1 ~]# docker images -a
    REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
    ubuntu              latest              e54ca5efa2e9        41 hours ago        276.1 MB
                                6c37f792ddac        41 hours ago        276.1 MB
                                83ff768040a0        41 hours ago        192.7 MB
                                2f4b4d6a4a06        41 hours ago        192.7 MB
                                d7ac5e4f1812        41 hours ago        192.5 MB
    centos              latest              0c752394b855        10 days ago         124.1 MB
                                34e94e67e63a        2 weeks ago         0 B
                                511136ea3c5a        12 months ago       0 B
    Podremos arrancar instancias o contenedores con las imágenes que queramos de la siguiente manera:
    [root@centos1 ~]# docker run -i -t centos /bin/bash
    bash-4.1# more /etc/centos-release
    CentOS release 6.5 (Final)
    bash-4.1# hostname
    3ce460cbb9d8

    [root@centos1 ~]# docker run -i -t ubuntu /bin/bash
    root@60d6e0455be0:/# more /etc/lsb-release
    DISTRIB_ID=Ubuntu
    DISTRIB_RELEASE=14.04
    DISTRIB_CODENAME=trusty
    DISTRIB_DESCRIPTION=”Ubuntu 14.04 LTS”
    root@60d6e0455be0:/# hostname
    60d6e0455be
    Al pasar el parámetro -i al comando run se ejecutará el contenedor de forma interactiva, de forma que, en cuanto termine el comando que pasamos como parámetro la ejecución del contenedor terminará.
    Mientras estemos ejecutando la shell, veremos que el contenedor está ejecutándose:
    [root@centos1 ~]# docker ps
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
    49087a343e7f        centos:latest       /bin/bash           12 seconds ago      Up 11 seconds                           dreamy_curie   
    Si quisiesemos ver un historial de las instancias ejecutadas pasaríamos el parámetro -a:
     [root@centos1 ~]# docker ps -a
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
    15dc7c59189c        centos:latest       /bin/bash           27 seconds ago      Up 26 seconds                                   compassionate_fermi  
    49087a343e7f        centos:latest       /bin/bash           12 minutes ago      Exited (0) 17 seconds ago                       dreamy_curie         
    9e69f5b64254        ubuntu:latest       uname -r            15 minutes ago      Exited (0) 15 minutes ago                       high_engelbart       
    f5aa352a5ce9        centos:latest       uname -r            15 minutes ago      Exited (0) 15 minutes ago                       jovial_meitner       
    60d6e0455be0        ubuntu:latest       /bin/bash           17 minutes ago      Exited (0) 16 minutes ago                       cocky_leakey         
    961b10b9bbbf        ubuntu:latest       hostname            17 minutes ago      Exited (0) 17 minutes ago                       insane_mayer         
    3ce460cbb9d8        centos:latest       /bin/bash           19 minutes ago      Exited (0) 17 minutes ago                       loving_yonath  
    Si por contra, quisiesemos ejecutar un contenedor en background o demonizado, como sucede en Solaris con las zonas, al comando run le pasaríamos el parámetro -d. En el siguiente ejemplo arrancaríamos una instancia de la imagen ubuntu y dos instancias de la imagen de centos y finalmente veríamos que se están ejecutando las tres:
    [root@centos1 ~]# docker run -d -t ubuntu  /bin/bash
    abc254ab2e8eb392ce80938c8f2b8fa8cb1603ab6935b2121b7cb0cddbdd64c3
    [root@centos1 ~]# docker run -d -t centos  /bin/bash
    59adfe1998cc678cf2c9db964f1b57e60a301ffa2139c7a4635ebe315db2b718
    [root@centos1 ~]# docker run -d -t centos  /bin/bash
    6714217732adadec19f47399fe5f7252823787869a96e8fed72457cd5106589c
    [root@centos1 ~]# docker ps
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
    6714217732ad        centos:latest       /bin/bash           3 seconds ago       Up 2 seconds                            romantic_shockley 
    59adfe1998cc        centos:latest       /bin/bash           7 seconds ago       Up 6 seconds                            thirsty_bartik    
    abc254ab2e8e        ubuntu:latest       /bin/bash           14 seconds ago      Up 13 seconds                           naughty_yonath 
    Al arrancar cada contenedor nos devuelve un número muy largo. Este número es el container_id que mas adelante vemos en la primera columna del comando ps. La última columna es un nombre que se asigna automáticamente y servirá para identificar las instancias de una forma mas amigable. Para parar las zonas que están corriendo podríamos pasar como identificador tanto el container_id como el nombre:

    [root@centos1 ~]# docker stop romantic_shockley
    romantic_shockley
    [root@centos1 ~]# docker stop thirsty_bartik
    thirsty_bartik
    [root@centos1 ~]# docker stop naughty_yonath
    naughty_yonath
    [root@centos1 ~]# docker ps
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 

    Como vemos, el kernel que se está ejecutando es el del host:

    [root@centos1 ~]# uname -r
    2.6.32-431.el6.x86_64
    [root@centos1 ~]# docker run -i -t centos uname -r
    2.6.32-431.el6.x86_64
    [root@centos1 ~]# docker run -i -t ubuntu uname -r
    2.6.32-431.el6.x86_64

    Otra forma de arrancar un contenedor es con el comando start y pasando como parámetro el id del contenedor o el nombre que docker le asigno en su momento (lo podemos encontrar con docker ps -a)
     [root@centos1 ~]# docker start romantic_shockley
    romantic_shockley
    [root@centos1 ~]# docker ps
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
    6714217732ad        centos:latest       /bin/bash           15 minutes ago      Up 3 seconds                            romantic_shockley
    Para acceder a la consola del contenedor ejecutaríamos el comando attach:
     [root@centos1 ~]# docker attach romantic_shockley

    Obtener información

    Existe una serie de comandos que nos servirán para obtener información, tanto de docker, como de las imagenes o contenedores que tenemos creados. El comando info nos devolverá información general de la instalación actual de docker:
    [root@centos1 ~]# docker info
    Containers: 6
    Images: 8
    Storage Driver: devicemapper
     Pool Name: docker-8:1-132310-pool
     Data file: /var/lib/docker/devicemapper/devicemapper/data
     Metadata file: /var/lib/docker/devicemapper/devicemapper/metadata
     Data Space Used: 784.9 Mb
     Data Space Total: 102400.0 Mb
     Metadata Space Used: 1.4 Mb
     Metadata Space Total: 2048.0 Mb
    Execution Driver: native-0.2
    Kernel Version: 2.6.32-431.el6.x86_64
    Con el comando history veremos el historial de versiones de la imagen que le hayamos especificado:
    [root@centos1 ~]# docker history centos
    IMAGE               CREATED             CREATED BY                                      SIZE
    0c752394b855        10 days ago         /bin/sh -c #(nop) ADD file:ce8fdb737386beb5fd   124.1 MB
    34e94e67e63a        2 weeks ago         /bin/sh -c #(nop) MAINTAINER The CentOS Proje   0 B
    511136ea3c5a        12 months ago                                                       0 B
    También tendremos el comando inspect, que nos devolverá un objeto JSON con información detallada de la imagen. Tambien sirve para obetner información de un contenedor.

    [root@centos1 ~]# docker inspect centos
    [{
        “Architecture”: “amd64”,
        “Author”: “The CentOS Project \u003ccloud-ops@centos.org\u003e – ami_creator”,
        “Comment”: “”,
        “Config”: {
            “AttachStderr”: false,
            “AttachStdin”: false,
            “AttachStdout”: false,
            “Cmd”: null,
            “CpuShares”: 0,
            “Cpuset”: “”,
            “Domainname”: “”,
            “Entrypoint”: null,
            “Env”: [
                “HOME=/”,
                “PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin”
            ],
            “ExposedPorts”: null,
            “Hostname”: “b2d0f1281acd”,
            “Image”: “34e94e67e63a0f079d9336b3c2a52e814d138e5b3f1f614a0cfe273814ed7c0a”,
            “Memory”: 0,
            “MemorySwap”: 0,
            “NetworkDisabled”: false,
            “OnBuild”: [],
            “OpenStdin”: false,
            “PortSpecs”: null,
            “StdinOnce”: false,
            “Tty”: false,
            “User”: “”,
            “Volumes”: null,
            “WorkingDir”: “”
        },
        “Container”: “b2d0f1281acd040292b36f6623feca10a89df1637c8d86e54079d473da4d05e3”,
        “ContainerConfig”: {
            “AttachStderr”: false,
            “AttachStdin”: false,
            “AttachStdout”: false,
            “Cmd”: [
                “/bin/sh”,
                “-c”,
                “#(nop) ADD file:ce8fdb737386beb5fd9aff7c9bbe9e6c9e60db290809dd6407c61b377e444b59 in /”
            ],
            “CpuShares”: 0,
            “Cpuset”: “”,
            “Domainname”: “”,
            “Entrypoint”: null,
            “Env”: [
                “HOME=/”,
                “PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin”
            ],
            “ExposedPorts”: null,
            “Hostname”: “b2d0f1281acd”,
            “Image”: “34e94e67e63a0f079d9336b3c2a52e814d138e5b3f1f614a0cfe273814ed7c0a”,
            “Memory”: 0,
            “MemorySwap”: 0,
            “NetworkDisabled”: false,
            “OnBuild”: [],
            “OpenStdin”: false,
            “PortSpecs”: null,
            “StdinOnce”: false,
            “Tty”: false,
            “User”: “”,
            “Volumes”: null,
            “WorkingDir”: “”
        },
        “Created”: “2014-06-09T21:38:41.281490617Z”,
        “DockerVersion”: “0.10.0”,
        “Id”: “0c752394b855e8f15d2dc1fba6f10f4386ff6c0ab6fc6a253285bcfbfdd214f5”,
        “Os”: “linux”,
        “Parent”: “34e94e67e63a0f079d9336b3c2a52e814d138e5b3f1f614a0cfe273814ed7c0a”,
        “Size”: 124078508
    }

    Para obtener informacion específica de un contenedor especificaremos al comando inspect la clave que queremos que nos devuelva, como por ejemplo el hostname o la dirección IP:
    [root@centos1 ~]# docker run -d -t centos  /bin/bash
    d7e7938ab7e98ff8e2413179d5222201de6a0c66716b99aafb35a0805fbf279e
    [root@centos1 ~]# docker  ps
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
    d7e7938ab7e9        centos:latest       /bin/bash           11 seconds ago      Up 11 seconds                           nostalgic_newton   

    [root@centos1 ~]# docker inspect -f ‘{{ .Config.Hostname }}’ nostalgic_newton
    d7e7938ab7e9
    [root@centos1 ~]# docker inspect -f ‘{{ .NetworkSettings.IPAddress }}’ nostalgic_newton
    172.17.0.22

    Borrado de contenedores

    Para norrar contenedores utilizaremos el comando rm. Le podremos pasar un único identificador de contenedor o varios en el mismo comando:
    [root@centos1 ~]# docker ps -a
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                       PORTS               NAMES
    abc254ab2e8e        ubuntu:latest       /bin/bash           22 minutes ago      Exited (0) 18 minutes ago                        naughty_yonath      
    90ae553f4384        centos:latest       /bin/bash           23 minutes ago      Exited (-1) 22 minutes ago                       ecstatic_mcclintock

    [root@centos1 ~]# docker rm ecstatic_mcclintock naughty_yonath
    ecstatic_mcclintock
    naughty_yonath

    Reconfiguración de interfaces en entornos virutales con máquinas virtuales Linux

    En entornos virtuales es habitual clonar servidores. En Linux el sistema udev, al detectar un nuevo hardware con una MAC diferente, le asigna un número de interfaz secuencial. Da igual si se le ha borrado alguna interfaz previa, la nueva que se configure seguirá la numeración. Con este comportamiento, es posible que haya cosas que no funcionen, por ejemplo si tenemos un script que haga referencia al nombre de interfaz eth0. Si queremos recuperar la numeración deberemos modificar el fichero de reglas de udev y luego además revisar los scripts de configuración de las interfaces ya que probablemente las MACs no coincidan.
    He creado un nuevo script, que se puede poner en el .bashrc del usuario root por ejemplo. Dicho script detectará si existe la interfaz eth0, en caso de no existir realizará la reconfiguración de udev, limpiará configuraciones de red previas y finalmente creará los nuevos ficheros de configuración. Para crear estos ficheros tendrá en cuenta si queremos utilizar un direccionamiento estático o dinámico (DHCP). 
    Utilizando este script, siempre que clonemos la máquina y accedamos con el usuario root nos preguntará la configuración de red que queremos.
    Ejemplo de uso (desde una consola ya que baja las interfaces):
    Verificamos que el nombre de la interfaz eth0 no existe:

    Configuración de red tras el clonado

    Lanzamos el script, donde veremos que baja las inerfaces de red y pide si queremos borrar configuraciones previas:

    Llamada al script netconfig.sh

    Pide que tipo de configuración queremos en todas las interfaces:

    Reconfiguración de las interfaces

    Una vez terminada la ejecución del script vemos que la interfaz eth0 ya aparece y mantiene la MAC de la interfaz que veiamos en la primer imágen:

    Configuración de red final

    El script:

    #!/bin/bash
    function static {
    hostname=`hostname`
    read -r -p “Hostname: [${hostname}] ” hostname
    if [ -z “$hostname” ]; then hostname=`hostname`; fi
    read -r -p “IP address: ” ip
    read -r -p “Netmask: ” mask
    grep dhcp /etc/sysconfig/network-scripts/ifcfg-eth* 2>/dev/null >/dev/null
    if [ $? -eq 0 ] ; then
    echo “there is a network configured with dhcp which should configure your gateway”
    else
    read -r -p “Gateway: ” gate
    if [ $? -eq 0 ] ; then
    sed -i -e ‘s#^\(GATEWAY=\).*$#\1′”${gate}”‘#’ /etc/sysconfig/network
    else
    echo “GATEWAY=\”${gate}\”” >>/etc/sysconfig/network
    fi
    fi
    #echo “HOSTNAME: ${hostname}\tIP: ${ip}\tNETMASK: ${mask}”
    mac=`ifconfig ${iface} | grep eth | awk ‘{ print $5}’`
    if [ -f /etc/sysconfig/network-scripts/ifcfg-${iface} ]; then
    rm -r /etc/sysconfig/network-scripts/ifcfg-${iface}
    fi
    echo “DEVICE=\”${iface}\”” >> /etc/sysconfig/network-scripts/ifcfg-${iface}
    echo “BOOTPROTO=\”static\”” >> /etc/sysconfig/network-scripts/ifcfg-${iface}
    echo “NM_CONTROLLED=\”yes\”” >> /etc/sysconfig/network-scripts/ifcfg-${iface}
    echo “ONBOOT=\”yes\”” >> /etc/sysconfig/network-scripts/ifcfg-${iface}
    echo “TYPE=\”Ethernet\”” >> /etc/sysconfig/network-scripts/ifcfg-${iface}
    echo “IPADDR=\”${ip}\”” >> /etc/sysconfig/network-scripts/ifcfg-${iface}
    echo “NETMASK=\”${mask}\”” >> /etc/sysconfig/network-scripts/ifcfg-${iface}
    echo “HWADDR=\”${mac}\”” >> /etc/sysconfig/network-scripts/ifcfg-${iface}
    echo “DNS1=\”X.X.X.X\”” >> /etc/sysconfig/network-scripts/ifcfg-${iface}
    echo “DNS2=\”Y.Y.Y.Y\”” >> /etc/sysconfig/network-scripts/ifcfg-${iface}
    echo “DOMAIN=\”enterprise.com\”” >> /etc/sysconfig/network-scripts/ifcfg-${iface}
    sed -i -e ‘s#^\(HOSTNAME=\).*$#\1′”${hostname}”‘#’ /etc/sysconfig/network
    grep GATEWAY /etc/sysconfig/network 2>/dev/null >/dev/null
    }

    function dhcp {
    echo “Configuring DHCP for ${iface}”
    hostname=`hostname`
    read -r -p “Hostname: [${hostname}] ” hostname
    if [ -z “$hostname” ]; then hostname=`hostname`; fi
    mac=`ifconfig ${iface} | grep eth | awk ‘{ print $5}’`
    if [ -f /etc/sysconfig/network-scripts/ifcfg-${iface} ]; then
    rm -r /etc/sysconfig/network-scripts/ifcfg-${iface}
    fi
    echo “DEVICE=\”${iface}\”” >> /etc/sysconfig/network-scripts/ifcfg-${iface}
    echo “BOOTPROTO=\”dhcp\”” >> /etc/sysconfig/network-scripts/ifcfg-${iface}
    echo “HWADDR=\”${mac}\”” >> /etc/sysconfig/network-scripts/ifcfg-${iface}
    echo “NM_CONTROLLED=\”yes\”” >> /etc/sysconfig/network-scripts/ifcfg-${iface}
    echo “ONBOOT=\”yes\”” >> /etc/sysconfig/network-scripts/ifcfg-${iface}
    echo “TYPE=\”Ethernet\”” >> /etc/sysconfig/network-scripts/ifcfg-${iface}
    sed -i -e ‘s#^\(HOSTNAME=\).*$#\1′”${hostname}”‘#’ /etc/sysconfig/network
    }


    ifconfig eth0 2>/dev/null >/dev/null
    if [ $? -ne 0 ] ; then
    echo “eth0 not found. Interface configuration…”
    echo “Recreating interfaces”
    # Rename eth1 with eth0    
    echo “Stopping network”
    service network stop

    #Clearing devices and renaming
    echo “UDEV Config…”
    rm -f /etc/udev/rules.d/70-persistent-net.rules
    #ls -l  /etc/udev/rules.d/70-persistent-net.rules
    udevadm trigger
    sleep 2
    #ls -l  /etc/udev/rules.d/70-persistent-net.rules
    ifdevs=`udevadm info –export-db | grep “INTERFACE=eth” | cut -d “=” -f2`
    #echo “Interfaces ${ifdevs}”
    count=0
    for ifdev in ${ifdevs}; do
    sed -i -e “s/${ifdev}/eth${count}/g” /etc/udev/rules.d/70-persistent-net.rules
    # echo “sed -i -e ‘s/${ifdev}/eth${count}/g’ /etc/udev/rules.d/70-persistent-net.rules”
    count=$((count+1))
    # echo “count: ${count}”
    done
    udevadm trigger –attr-match=subsystem=net
    #echo “udev Net rules”
    #grep eth /etc/udev/rules.d/70-persistent-net.rules
        echo “—————————————————“
        
    #Clearing configuration    
    echo “Remove previous configuration”
    for cfgfile in `ls /etc/sysconfig/network-scripts/ifcfg-eth*`; do
    read -r -p “Remove previous configuration file ${cfgfile}? [y/N] ” response
    case $response in
    [yY])
    rm -f ${cfgfile}
    ;;
    *)
    echo “Not removing ${cfgfile}”
    ;;
    esac
    done
        
    #Setup ehternet devices
    sleep 2
    ifaces=`ifconfig -a | grep eth | awk ‘{ print $1}’`
    #echo ${ifaces}
    for iface in ${ifaces}; do
    read -r -p “Do you want to configure a static IP for ${iface}? [y/N] ” response
    case $response in
    [yY])
    static
    ;;
    *)
    read -r -p “Do you want to configure DHCP for ${iface}? [y/N] ” respdhcp
    case $respdhcp in
    [yY])
    dhcp
    ;;
    *)
    echo “Not configuring ${iface}.”
    #rm -r /etc/sysconfig/network-scripts/ifcfg-${iface}
    ;;
    esac
    ;;
    esac
    done
    #Start networking
    echo “Starting networking”
    service network start
    fi

    Identificar zona global

    En Solaris es posible crear zonas que no son mas que “particiones” del sistema operativo. Desde la zona Global, el equivalente a un ESXi en VMware, puede acceder a los procesos y sistemas de ficheros de las zonas. No así desde una zona, que están aisladas unas de otras y es imposible acceder a la zona global desde una zona. Es mas, es muy difícil identificar la zona global sobre la que esta corriendo una zona en concreto.

    Para hacer mas fácil esta identificación he montado un script que lo que hace es generar un fichero /etc/GLOBALZONE en cada una de las zonas con el nombre de la zona:

    #!/usr/bin/python

    #
    # zoneadm list output
    # zoneadm list -cv
    #  ID NAME             STATUS     PATH                           BRAND    IP    
    #   0 global           running    /                              native   shared
    #   5 zona1      running    /zonas/zona1             native   shared
    #   7 zona2      running    /zonas/zona2             native   shared
    #   – zona3      installed  /zonas/zona3             native   shared
    #
    #
    from subprocess import *

    p = Popen([‘hostname’], stdout=PIPE, stderr=PIPE)
    hostname, err = p.communicate() 

    p = Popen([‘zoneadm’, ‘list’, ‘-cv’], stdout=PIPE, stderr=PIPE)
    zoneList, err = p.communicate()

    for zone in zoneList.splitlines():
    name = zone.split()[1]
    if (name ==’NAME’) or (name == ‘global’): continue
    cfgfile = “/zonas/%s/root/etc/GLOBALZONE” % name
    try:
    f = open(cfgfile, “w”)
    f.write(hostname)
    f.close()
    except:
    pass
    El script lo he configurado en el cron para que se ejecute cada hora por ejemplo. De esta forma, si creo alguna zona nueva, o si muevo una zona de un servidor a otro, este fichero se actualizará.

    También se puede modificar el perfil del usuario, de modo que el nombre de la zona global aparezca en la variable PS1:
    PS1 = "\u@\h \w (`cat /etc/GLOBALZONE`) $ "

    Openstack: Prueba de concepto – Parte 2

    En relación a la entrada anterior, he querido mostrar una demostración en un vídeo en YouTube. El vídeo esta grabado sobre la prueba de concepto que monté. En el siguiente gráfico se puede ver la arquitectura:


    Existen cuatro nodos de computación, y uno de ellos además, lleva el resto de elementos de Openstack. Todo está funcionando sobre Ubuntu 12.04 y montado con servidores con una única interfaz física.

    Para las instancias he descargado las imágenes UEC que ya tiene preparadas Canonical.

    El vídeo se podría dividir en dos partes. En la primera parte se muestran los principales comandos para trabajar con imagenes, “flavors”, arrancar instancias y terminarlas.

    A continuación se hace un repaso por la Dashboard viendo como es sumamente sencillo la creación y despliegue de nuevas instancias. También he aprovechado para trabajar con volúmenes y demostrando que se pueden mover de una instancia a otra manteniendo la información que contienen.

    Aquí se puede ver el vídeo:

    OpenStack: Prueba de concepto – Parte 1

    Estos días he tenido la opción de montar una pequeña prueba de concepto sobre Openstack. La idea es ver como funciona y analizar si en un futuro se podría implementar en un entorno productivo.

    Me he basado en la documentación de Cloud – IES Gonzalo Nazareno concretamente en el documento bk-admin-openstack.pdf. También he seguido el libro de “recetas”: OpenStack Cloud Computing Cookbook.

    Lo que he montado es básicamente una arquitectura de cuatro nodos. Uno de ellos es el Cloud Controller, donde estará todo el software necesario de Openstack: nova-compute, nova-network, nova-volume, glance y keystone. En los otros tres nodos únicamente se ha instalado el modulo de computación, nova-compute.

    El hipervisor elegido para las pruebas ha sido KVM, con la idea de poder tener imagenes de otros sistemas operativos, además de imágenes de distribuciones GNU/Linux.

    El esquema lógico sería el siguiente:

    Por ciertas limitaciones únicamente he podido disponer de una interfaz de red cableada, por ello he apostado por el diseño mas sencillo con FlatDHCP. Aún así, las instancias son capaces de verse entre si a pesar de correr en diferentes hosts.

    Ficheros de configuración:
    Tal y como se explica en la documentación, el único fichero que hay que ir tocando es el /etc/nova/nova.conf. Yo copié el que indica en el documento  bk-admin-openstack.pdf y lo adapté a mi entorno. Básicamente hay que cambiar la IP que aparece por cada servicio por la del nodo que yo utilizaba como controlador. El apartado que me dió mas problemas fue el de red. Hay que tener mucho cuidado con los segmentos que se definen para las redes privadas, ya que es posible que se den problemas de routing. El apartado de configuración de red quedó de la siguiente manera:

    # NETWORK
    network_manager=nova.network.manager.FlatDHCPManager
    force_dhcp_release=True
    dhcpbridge_flagfile=/etc/nova/nova.conf
    dhcpbridge=/usr/bin/nova-dhcpbridge
    firewall_driver=nova.virt.libvirt.firewall.IptablesFirewallDriver
    my_ip=X.X.3.91
    public_interface=eth0
    flat_network_bridge=br100
    flat_interface=eth1
    fixed_range=192.168.221.0/27
    floating_range=192.168.221.32/27
    routing_source_ip=X.X.3.91
    start_guests_on_host_boot=true
    resume_guests_state_on_host_boot=true
    network_size=10
    flat_network_dhcp_start=192.168.221.10
    flat_injected=False
    force_dhcp_release=True
    root_helper=sudo nova-rootwrap

    El fichero nova.conf será el mismo en todos los nodos, lo único que habrá que cambiar es la variable my_ip. También hay que tener en cuenta la configuración de la consola vnc para poder acceder desde la interfaz web:

    # VNC
    novnc_enabled=true
    vnc_keymap=es
    novncproxy_base_url=http://10.150.3.91:6080/vnc_auto.html
    vncserver_proxyclient_address=10.150.3.92
    vncserver_listen=10.150.3.92
    vnc_console_proxy_url=http://10.150.3.91:6080

    Aquí habrá que modificar las IPs en las variables vncserver_proxyclient_address y vncserver_listen. En las otras dos variables se dejará la IP del nodo donde se instala la Dashboard.

    Como punto importante, ya en la configuración de Keystone, es necesario configurar las variables de entorno del usuario que vamos a autilizar para manejar los servicios de Nova. Para ello, en el fichero .bahsrc hay que introducir las siguientes líneas (en mi caso en el de root):

    export SERVICE_ENDPOINT=”http://X.X.3.91:35357/v2.0″
    export SERVICE_TOKEN=PASSWORD
    export OS_TENANT_NAME=admin
    export OS_USERNAME=admin
    export OS_PASSWORD=PASSWORD
    export OS_AUTH_URL=”http://X.X.3.91:5000/v2.0/”

    Una vez con esto configurado ya podemos empezar a hacer pruebas con Openstack.

    Problemas encontrados:
    Hasta encontrar la configuración que funciona he tenido que ir jugando con los diferentes flags de los ficheros de configuración. Alguna vez, después de arrancar instancias, se han quedado en un estado inconsistente, siendo imposible el borrado de de las instancias de forma convencional. Para ello hay que conectarse a la base de datos y borrar los datos que hacen referencia a las instancias. Para hacerlo mas sencillo me monté un pequeño script en python que saca un listado de las instancias y permite borrar una o el borrado de todas. El script es el siguiente:

    #!/usr/bin/python
    #coding: iso-8859-15
    import MySQLdb
    import sys

    db = MySQLdb.connect(host=’HOST’,user=’nova’, passwd=’PASSWORD’, db=’nova’)
    cursor = db.cursor()
    consulta = ‘select id, uuid from instances’
    cursor.execute(consulta)
    instances = cursor.fetchall()

    for inst in instances:
        print “Instancia: %s UUID: %s” % (str(inst[0]), str(inst[1]))



    if “–all” in sys.argv:
        print ‘Limpiando todas las instancias’
        cont = raw_input(‘¿Continuar? (s/n): ‘)
        if cont == ‘s’ or cont == ‘S’:
            for inst in instances:
                id=str(inst[0])
                print ‘Borrando instancia %s’ % id
                consulta = ‘delete from instance_info_caches where id=%s’ % id
                cursor.execute(consulta)
                            consulta = ‘delete from security_group_instance_association where id=%s’ % id
                            cursor.execute(consulta)
                            consulta = ‘delete from instances where id=%s’ % id
                            cursor.execute(consulta)
                db.commit()
            print ‘Todas las instancias borradas’
    else:
        id = raw_input(‘Selecciona la instancia a borrar: ‘)
       
        for inst in instances:
            if str(inst[0]) == id:
                print “Se va a borrar la instancia %s con UUID %s” % (str(id), str(inst[1]))
                cont = raw_input(‘¿Continuar? (s/n): ‘)
                if cont == ‘s’ or cont == ‘S’:
                    print ‘Borrando instancia %s’ % id
                    consulta = ‘delete from instance_info_caches where id=%s’ % id
                    cursor.execute(consulta)
                    consulta = ‘delete from security_group_instance_association where id=%s’ % id
                    cursor.execute(consulta)
                    consulta = ‘delete from instances where id=%s’ % id
                    cursor.execute(consulta)
                    print ‘Ejecute “nova list | grep %s” para confirmar que se ha borrado la instancia’ % str(inst[1])
                    db.commit()

    En una ocasión no pude borrar la instancia con el script y era porque a la instancia le había asignado un volumen. Al borrar daba un error de clave referenciada en una tabla, con lo que tuve que borrar dicha entrada en la tabla que indicaba el error. Una vez limpiado se pudo borrar con el script.

    Es importante, en caso de tener algún problema, revisar los logs. Allí encontraremos muchas pistas de que puede estar fallando y es muy probable que a alguien le haya pasado antes. Los logs de nova se encuentran en /var/logs/nova y los de KVM, no menos importantes, están en /var/logs/libvirt.

    Agrandar disco en VirtualBox OSE

    Agrandar un disco en VirtualBox OSE no es tan trivial como en otras plataformas de virtualización como VMware ESX. No existe la opción de agrandar un disco directamente pero siempre existen formas alternativas de clonado. El método que presento a continuación lo he encontrado en el siguiente link. Básicamente se trata de crear un disco nuevo de mayor capacidad, crear una estructura igual al disco original y hacer una copia palabra a palabra de un disco a otro. Una vez tengamos la copia hacer que el nuevo disco sea bootable y eliminar el original.

    Para crear un nuevo disco en VirtualBox iremos al “Virtual Media Manager” y seleccionaremos un nuevo disco. A continuación aparecerá un wizard donde elegiremos el tipo de disco, su capacidad y dónde estará almacenado:



    A continuación añadimos el nuevo disco a nuestra máquina virtual y arrancamos con un CD Live de Linux que incluya el programa de edición de particiones Gparted (Ubuntu por ejemplo).
    Una vez arrancado abrimos un terminal y mostraremos la tabla de particiones de del disco original. Para ello utilizaremos el comando fdisk sobre el disco /dev/sda. Deberemos anotar el último cilindro de la partición y el tipo de sistema de ficheros:

    A continuación pasamos a particionar el nuevo disco. También con fdisk crearemos una nueva partición primaria, con el mismo tamaño en cilindros y el mismo sistema de ficheros:


    Certificamos que el particionado del disco ha quedado igual que el disco original:

    Escribimos los cambios en el disco:

    Una vez tengamos los dos discos con el mismo particionado haremos una copia byte a byte del disco original al nuevo disco con el comando dd:
    Con el programa gparted, en Ubuntu en el menu Sistema –> Administración –> Administrador de Particionas, aumentaremos el tamaño de la partición del nuevo disco. Para ello lo seleccionaremos y veremos que hay espacio sin asignar. Seleccionaremos la partición activa y con el boton resize la aumentaremos. Una vez configurado deberemos aplicar los cambios:


    Los cambios en los discos ya están realizados, con lo que se puede reiniciar la máquina virtual y arrancar con el sistema operativo que teníamos originalmente instalado. Si se trata de windows iremos al administrador de discos para marcar el nuevo disco como activo:
    El último paso será apagar la máquina virtual y eliminar el disco original.