En entradas anteriores: Dockerfile: Creación de imágenes docker y Ejemplos de ficheros Dockerfile, creando imágenes docker, hemos estudiado la utilización de la herramiento docker build para construir imágenes docker a partir de fichero Dockerfile.

En esta entrada vamos a utilizar la instrucción VOLUME, para crear volúmenes de datos en los contenedores que creemos a partir de la imagen que vamos a crear.

Creación de una imagen con un servidor web

Vamos a repetir el ejemplo que vimos en la entrada Ejemplos de ficheros Dockerfile, creando imágenes docker, pero en este caso, al crear nuestro contenedor se van a crear dos volúmenes de datos: en uno se va a guardar el contenido de nuestro servidor (/var/www) y en otro se va a guardar los logs del servidor (/var/log/apache2). En este caso si tengo que eliminar el contenedor, puedo crear uno nuevo y la información del servidor no se perderá.

En este caso el fichero Dockerfile quedaría:

FROM ubuntu:14.04
MAINTAINER José Domingo Muñoz "josedom24@gmail.com"

RUN apt-get update && apt-get install -y apache2 && apt-get clean && rm -rf /var/lib/apt/lists/*

ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2

VOLUME /var/www /var/log/apache2
EXPOSE 80
ADD ["index.html","/var/www/html/"]

ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

Además del fichero Dockerfile, tenemos el fichero index.html en nuestro contexto. con la siguiente instrucción construimos la nueva imagen:

~/apache$ docker build -t josedom24/apache2:1.0 .

Y podemos crear nuestro contenedor:

$ docker run -d -p 80:80 --name servidor_web josedom24/apache2:1.0
78033d752c8f163576e5ef1a7435613a16954f4c138cf62f4d47a635fc5eb374sss

Nuestro contenedor está ofreciendo la página web, pero la información del servidor está guardad de forma permanente en los volúmenes. Podemos comprobar que se han creado dos volúmenes:

$ docker volume ls
DRIVER              VOLUME NAME
local               8dc51c65f164b25854dac01257d3074de0a35bfd202d2d6b94de5c9e97884249
local               a611141be3434229ed22acab6a69fd591dc7ddd39c6321784c05100065ddb266

Y obteniendo información del contenedor, podemos obtener:

$ docker inspect servidor_web 
..."Mounts": [
    {
        "Name": "8dc51c65f164b25854dac01257d3074de0a35bfd202d2d6b94de5c9e97884249",
        "Source": "/mnt/sda1/var/lib/docker/volumes/8dc51c65f164b25854dac01257d3074de0a35bfd202d2d6b94de5c9e97884249/_data",
        "Destination": "/var/log/apache2",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
    },
    {
        "Name": "a611141be3434229ed22acab6a69fd591dc7ddd39c6321784c05100065ddb266",
        "Source": "/mnt/sda1/var/lib/docker/volumes/a611141be3434229ed22acab6a69fd591dc7ddd39c6321784c05100065ddb266/_data",
        "Destination": "/var/www",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
    }
],
...

Si accedemos al Docker Engine podemos comprobar los ficheros que hay en cada uno de los volúmenes:

$ docker-machine ssh nodo1
docker@nodo1:~$ sudo su
root@nodo1:/home/docker# cd /mnt/sda1/var/lib/docker/volumes/8dc51c65f164b25854dac01257d3074de0a35bfd202d2d6b94de5c9e97884249/_data
root@nodo1:/mnt/sda1/var/lib/docker/volumes/8dc51c65f164b25854dac01257d3074de0a35bfd202d2d6b94de5c9e97884249/_data# ls
access.log               error.log                other_vhosts_access.log

En el primer volumen vemos los ficheros correpondiente al log del servidor, y en el segundo tenemos los fichero del document root:

cd /mnt/sda1/var/lib/docker/volumes/a611141be3434229ed22acab6a69fd591dc7ddd39c6321784c05100065ddb266/_data
root@nodo1:/mnt/sda1/var/lib/docker/volumes/a611141be3434229ed22acab6a69fd591dc7ddd39c6321784c05100065ddb266/_data# ls
html

Finalmente indicar que si borramos el contenedor, y creamos uno nuevo desde la misma imagen la información del servidor (logs y document root) no se habrá eliminado y la tendremos a nuestra disposición en el nuevo contenedor.

Cuando un contenedor es borrado, toda la información contenida en él, desaparece. Para tener almacenamiento persistente en nuestros contenedores, que no se elimine al borrar el contenedor, es necesario utilizar volúmenes de datos (data volume). Un volumen es un directorio o un fichero en el docker engine que se monta directamente en el contenedor. Podemos montar varios volúmenes en un contenedor y en varios contenedores podemos montar un mismo volumen.

Tenemos dos alternativas para gestionar el almacenamiento en docker:

  • Usando volúmenes de datos
  • Usando contenedores de volúmenes de datos

Volúmenes de datos

Los volúmenes de datos tienen las siguientes características:

  • Son utilizados para guardar e intercambiar información de forma independientemente a la vida de un contenedor.
  • Nos permiten guardar e intercambiar información entre contenedores.
  • Cuando borramos el contenedor, no se elimina el volumen asociado.
  • Los volúmenes de datos son directorios del host montados en un directorio del contenedor, aunque también se pueden montar ficheros.
  • En el caso de montar en un directorio ya existente de un contenedor un volumen de datos , su contenido no será eliminado.

Añadiendo volúmenes de datos

Vamos a empezar creando una contenedor al que le vamos a asociar un volumen:

$ docker run -it --name contenedor1 -v /volumen ubuntu:14.04 bash

Como podemos comprobar con la opción -v hemos creado un nuevo volumen que se ha montado en el directorio /volumen del contenedor. Vamos a crear un fichero en ese directorio:

root@d50f89458659:/# cd /volumen/
root@d50f89458659:/volumen# touch fichero.txt
root@d50f89458659:/volumen# exit

Podemos comprobar los puntos de montajes que tiene nuestro contnedor con la siguiente instrucción:

$ docker inspect contenedor1
...
"Mounts": [
            {
                "Name": "c7665edfb4505d6ac85fb0f3db118f6c7bb63958157ec722d6d3ee15ca8f3427",
                "Source": "/mnt/sda1/var/lib/docker/volumes/c7665edfb4505d6ac85fb0f3db118f6c7bb63958157ec722d6d3ee15ca8f3427/_data",
                "Destination": "/volumen",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
...

Continue reading

machine

Docker Machine es una herramienta que nos ayuda a crear, configurar y manejar máquinas (virtuales o físicas) con Docker Engine. Con Docker Machine podemos iniciar, parar o reiniciar los nodos docker, actualizar el cliente o el demonio docker y configurar el cliente docker para acceder a los distintos Docker Engine. El propósito principal del uso de esta herramienta es la de crear máquinas con Docker Engine en sistemas remotos y centralizar su gestión.

Docker Machine utiliza distintos drivers que nos permiten crear y configurar Docker Engine en distintos entornos y proveedores, por ejemplo virtualbox, AWS, VMWare, OpenStack, …

Las tareas fundamentales que realiza Docker Machine, son las siguientes:

  • Crea una máquina en el entorno que hayamos indicado (virtualbox, openstack,…) donde va a instalar y configurar Docker Engine.
  • Genera los certificados TLS para la comunicación segura.

También podemos utilizar un driver genérico (generic) que nos permite manejar máquinas que ya están creadas (físicas o virtuales) y configurarlas por SSH.

Instalación de Docker Machine

Para instalar la última versión (0.7.0) de esta herramienta ejecutamos:

$ curl -L https://github.com/docker/machine/releases/download/v0.7.0/docker-machine-`uname -s`-`uname -m` > /usr/local/bin/docker-machine && \
chmod +x /usr/local/bin/docker-machine

Y comprobamos la instalación:

$ docker-machine -version
docker-machine version 0.7.0, build a650a40

Continue reading

Al igual que en la entrada anterior, sobre copias de seguridad de volúmenes en OpenStack, en esta entrada vamos a mostrar otra funcionalidad que nos ofrece OpenStack cuando trabajamos con volúmenes con su componente Cinder.

Transferencia de volúmenes

Esta operación nos permite transferir un volumen de un proyecto a otro.

Crear una transferencia

Vamos a trabajar con el usuario demo, que tiene creado un volumen.

$ source demo-openrc.sh	

$ cinder list
+--------------------------------------+-------------------+--------+------+-------------+----------+-------------+
|                  ID                  |       Status      |  Name  | Size | Volume Type | Bootable | Attached to |
+--------------------------------------+-------------------+--------+------+-------------+----------+-------------+
| 917ef4cc-784d-4803-a19a-984b847b9f1e | awaiting-transfer | disco1 |  1   | lvmdriver-1 |  false   |             |
+--------------------------------------+-------------------+--------+------+-------------+----------+-------------+

Vamos a crear una transferencia, con las siguientes instrucciones:

$ cinder transfer-create 917ef4cc-784d-4803-a19a-984b847b9f1e
+------------+--------------------------------------+
|  Property  |                Value                 |
+------------+--------------------------------------+
|  auth_key  |           408af7d4a1441587           |
| created_at |      2016-01-08T17:07:03.412265      |
|     id     | d24d5659-40e9-446c-9437-238fc8868571 |
|    name    |                 None                 |
| volume_id  | 917ef4cc-784d-4803-a19a-984b847b9f1e |
+------------+--------------------------------------+

$ cinder transfer-list
+--------------------------------------+--------------------------------------+------+
|                  ID                  |              Volume ID               | Name |
+--------------------------------------+--------------------------------------+------+
| d24d5659-40e9-446c-9437-238fc8868571 | 917ef4cc-784d-4803-a19a-984b847b9f1e | None |
+--------------------------------------+--------------------------------------+------+

Continue reading

En esta entrada voy a explicar una característica muy específica que nos proporciona el componente Cinder de OpenStack, que es el encargado de gestionar el almacenamiento persistente con el concepto de volumen. La característica a la que me refiero es la posibilidad de hacer copias de seguridad del contenido de nuestro volúmenes. El estudio de esta opción la hemos llevado a cabo durante la realización del último curso sobre OpenStack que he impartido con Alberto Molina. Además si buscas información de este tema, hay muy poco en español, así que puede ser de utilidad.

El cliente cinder proporciona las herramientas necesarias para crear una copia de seguridad de un volumen. Las copias de seguridad se guardar como objetos en el contenedor de objetos swift. Por defecto se utiliza swift como almacén de copias de seguridad, aunque se puede configurar otros backend para realizar las copias de seguridad, por ejemplo una carpeta compartida por NFS.

Configurando devstack de forma adecuada

Podemos configurar nuestra instalación de OpenStack con devstack para habilitar la característica de copia de seguridad de volúmenes. En artículo anteriores he hecho una introducción al uso de devstack para realizar una instalación de OpenStack en un entorno de pruebas: Instalar Open Stack Juno con devstack.

Al crear nuestro fichero local.conf, tenemos que tener en cuenta dos cosas:

  • Habilitar el componente de swift (almacenamiento de objetos) donde vamos a realizar las copias de seguridad.
 enable_service s-proxy s-object s-container s-account
 SWIFT_REPLICAS=1
 SWIFT_HASH=password
  • Habilitar la característica de copia de seguridad de los volúmenes.
enable_service c-bak

Continue reading

wordpress-mysql-db-merge-180x180

En los artículos anteriores hemos estudiado como trabajar con imágenes y contenedores docker. En todos los ejemplos que hemos mostrado, los contenedores han trabajado ofreciendo uno o varios servicios, pero no se han comunicado o enlazado con ningún otro. En realidad sería muy deseable trabajar con el paradigma de “microservicio” donde cada contenedor ofrezca un servicio que funcione de forma autónoma y aislada del resto, pero que tenga cierta relación con otro contenedor (que ofrezca también un sólo servicio) para que entre todos ofrezcan una infraestructura más o menos compleja. En esta entrada vamos a mostrar un ejemplo de como podemos aislar servicios en distintos contenedores y enlazarlos para que trabajen de forma conjunta.

Instalación de wordpress en docker

Más concretamente vamos a crear un contenedor con un servidor web con wordpress instalado que lo vamos a enlazar con otro contenedor con un servidor de base de datos mysql. Para realizar el ejemplo vamos a utilizar las imágenes oficiales de wordpress y mysql que encontramos en docker hub.

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
wordpress           latest              55f2580b9cc9        5 days ago          516.5 MB
mysql               latest              e13b20a4f248        5 days ago          361.2 MB
debian              latest              256adf7015ca        5 days ago          125.1 MB
ubuntu              14.04               14b59d36bae0        5 days ago          187.9 MB

Docker nos permite un mecanismo de enlace entre contenedores, posibilitando enviar información de forma segura entre ellos y pudiendo compartir información entre ellos, por ejemplo las variables de entorno. Para establecer la asociación entre contenedores es necesario usar el nombre con el que creamos el contenedor, el nombre sirve como punto de referencia para enlazarlo con otros contenedores.

Por lo tanto, lo primero que vamos a hacer es crear un contenedor desde la imagen mysql con el nombre servidor_mysql, siguiendo las instrucción del repositorio de docker hub:

$ docker run --name servidor_mysql -e MYSQL_ROOT_PASSWORD=asdasd -d mysql

En este caso sólo hemos indicado la variable de entrono MYSQL_ROOT_PASSWORD, que es obligatoria, indicando la contraseña del usuario root. Si seguimos las instrucciones del repositorio de docker hub podemos observar que podríamos haber creado más variables, por ejemplo:  MYSQL_DATABASE, MYSQL_USER, MYSQL_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD.

A continuación vamos a crear un nuevo contenedor, con el nombre servidor_wp, con el servidor web a partir de la imagen wordpress, enlazado con el contenedor anterior.

$ docker run --name servidor_wp -p 80:80 --link servidor_mysql:mysql -d wordpress

Para realizar la asociación entre contenedores hemos utilizado el parámetro --link, donde se indica el nombre del contenedor enlazado y un alias por el que nos podemos referir a él. Continue reading