Orquestar contenedores con Docker Compose

Este es uno de los capítulos del tutorial Docker. Introducción y primeros pasos. Encontrarás los enlaces a todos los de capítulos, al final de este artículo.

Con lo que has visto en los diferentes capítulos de este tutorial sobre Docker, ya estarás levantando contenedores a diestro y siniestro. Eso si, hasta el momento, solo puedes trabajar con contenedores aislados, contenedores que no se relacionan unos con otros. Ahora, ¿como levantar un WordPress? al fin y al cabo un WordPress está compuesto de diferentes piezas. Por un lado el servidor web, por otro lado la base de datos y por último, el lenguaje de programación. Podrías plantearte levantar un contenedor que contuviera todo esto… Sin embargo, ¿porque no levantar un contenedor para cada uno de estos servicios? En este nuevo capítulo del tutorial sobre docker, verás como puedes levantar varios contenedores y que estos se relacionen entre si. Es decir, vas a orquestar contenedores con docker-compose.

Orquestar contenedores

Orquestar contenedores con Docker Compose

¿Porque varios por separador?

Es posible que te plantees meter todos los servicios que necesitas en un único contenedor. Así, como te indicaba anteriormente, podrías tener tu WordPres, con tu servidor web, tu base de datos, etc, en un solo contenedor. Pero, ¿que pasa si quieres cambiar la versión del servidor web?¿o incluso si quisieras cambiar de servidor de base de datos?

Evidentemente en estos casos, tendrías que detener el contenedor y te quedarías sin servicio. El tiempo que tardes en levantar un nuevo contenedor.

Sin embargo, si cada uno de estos servicios lo tienes en un contenedor separado, la operación es tan sencilla, detener el contenedor docker que te interese y levantar otro.

Chiquito de nuevo

En el capítulo 6, visto como crear una imagen docker paso a paso. Se trataba de una sencilla API realizada en Python, utilizando Flask y SQLite. Pero, ¿y si quisieras utilizar una base de datos externa?. Y ¿si quisieras utilizar MariaDB como tu servidor de bases de datos?

La solución sería por un lado levantar un contenedor con MariaDB, y por el otro lado levantar el contenedor con tu aplicación implementada en Python. De esta manera, por un lado, podrías ser agnóstico, hasta cierto punto, en lo que al servidor de bases de datos se refiere. Por otro lado, puedes actualizar el servidor cuando lo necesites sin excesiva complicación ni problemas. Y por último podrías servir a varias aplicaciones de forma simultánea.

Así, voy a partir del mismo ejemplo que en el caso anterior, sin embargo, las frases de Chiquito de la Calzada irán a parar a un contenedor docker con MariaDB.

Contenedor a contenedor

Primero te voy a mostrar como hacer para levantar los diferentes contenedores y conseguir que se relacionen unos con otros. En seguida te darás cuenta de que esta es una operación tediosa, y tiene muchas posibilidades de error.

El servidor de base de datos

Como te indicaba anteriormente, en lugar de utilizar una base de datos como SQLite, el objetivo es utilizar un servidor de bases de datos en otro contenedor. En este caso, he elegido Mariadb, pero, evidentemente podía elegir cualquier otro.

Tienes que levantar un docker de Mariadb con persistencia de datos, que es lo interesante. De no utilizar persistencia de datos, tendrías que introducir los datos en cada ocasión que quisieras cambiar de servidor de base de datos, o cambiar de versión o lo que fuera. Para levantar el contenedor ejecuta la siguiente instrucción,

docker run -d \
  -e MYSQL_ROOT_PASSWORD=password \
  -v "$(pwd)"/data:/var/lib/mysql \
  -p 3306:3306 \
  --name mariadbserver \
  mariadb

Para llenar la base de datos con las frases de chiquito, he implementado un sencillo script que por supuesto puedes encontrar en el repositorio de GitHub. Pero, para que te hagas una idea, finalmente todo se resume ejecutar la siguiente instrucción en un terminal,

mysql -h $HOST -P $PORT -u $USER --password=$PASSWORD chiquito < frases.sql

Chiquito as a Service (CaaS)

Por otro lado, he modificado la aplicación de chiquito para que pueda utilizar MariaDB, en lugar de utilizar SQLite. De nuevo tienes que construir esta aplicación, a la que le he puesto una etiqueta composer, por lo que podrás ver mas adelante. La construcción es tan sencilla como lo que has visto en el capítulo

docker build -t atareao/chiquito:composer .

Una vez construida la imagen, tan solo tienes que ponerla en producción. Para ello, ejecuta la siguiente instrucción,

docker run -d \
  -p 5000:5000 \
  --name chiquitocomposer \
  atareao/chiquito:composer

Sin embargo, te llevarás una desagradable sorpresa, y es que al ponerlo en funcionamiento, te arroja un error. Y te arroja un error, porque el contenedor que lleva tu aplicación de chiquito es incapaz de ver a la base de datos MariaDB que se encuentra en el otro contenedor. Para corregir este problema, tienes que añadir una opción adicional,

docker run -d \
  -p 5000:5000 \
  --name chiquitocomposer \
  --link=mariadbserver \
  atareao/chiquito:composer

Esta opción, --link, añade una nueva entrada en el /etc/hosts que apunta al IP del contenedor identficado por --link=CONTAINER_NAME.

Una vez en marcha, verás que todo se comporta como se espera. Vas a poder obtener frases aleatorias de Chiquito de la Calzada

Un paso mas…

Sin embargo, como tu mismo has podido comprobar, esto no es ni mucho menos la forma mas sencilla e intuitiva de trabajar con varios contenedores. Lo suyo sería disponer de una herramienta que permitiera orquestar la puesta en marcha y funcionamiento de varrios contenedores. Y efectivamente tienes a tu disposición esta herramienta y se llama docker compose

Instalar docker-compose para orquestar contenedores

Instalar docker-compose es tan sencillo como ejecutar las siguientes instrucciones en un terminal. Aunque debes tener en cuenta en la versión actual de docker-compose, porque esta es la que estaba vigente en el momento de escribir el artículo, y con lo rápido que avanza todo esto, vete a saber.

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose

Una vez descargado tienes que preparar el archivo .yml. En este archivo definirás cada uno de los contenedores que constituyen el puzzle. En este caso, como has visto anteriormente se trata de un contenedor para el servicio, y un segundo contenedor para la base de datos. Así, el archivo, docker-compose.yml en cuestión, tendrá la siguiente estructura,

version: '3'
services:
    chiquito:
        image: atareao/chiquito:composer
        ports:
            - "5000:5000"
    mariadbserver:
        image: mariadb
        ports:
            - "3306:3306"
        volumes:
            - ./data:/var/lib/mysql
        environment:
            MYSQL_ROOT_PASSWORD: password

Como ves, cada uno de los servicios se corresponde con una de las imágenes que formará tu red de contenedores. El primero, se trata de la imagen de tu aplicación, en este caso Chiquito, particularizada para el caso que utiliza MariaDB como base de datos, y el segundo, es precisamente la base de datos en cuestión. Aquí, al igual que en el caso de la imagen anterior, tienes que definir los distintos parámetros que normalmente defines directamente en la línea de comandos. Así tienes que indicar los puertos que compartes, los volúmenes, y en su caso las variables de entorno.

También es posible construir la imagen de chiquito directamente en lugar de utilizar la imagen que construiste previamente. Para hacer esto, tan solo tienes que sustituir la línea,

image: atareao/chiquito:composer

Por la línea,

build: .

El problema de esta segunda opción es que cada vez que levantes tu red de contenedores tendrás que construir la imagen previamente, y esto se realizará en cada ocasión. Si ya construiste la imagen en su momento, el proceso de levantar los contenedores será mucho mas rápido

Utilizando docker-compose para orquestar contenedores

Una vez definido el archivo docker-compose.yml te queda ponerlo en marcha, y cuando hayas terminado con él detenerlo. Así, las instrucciones son las siguientes,

  • docker-compose up levanta los contenedores
  • docker-composer up -d hace lo mismo que la instrucción pero en modo desvinculado, detached mode.
  • docker-compose ps te permite ver los contenedores que están en funcionamiento.
  • docker compose stop es la herramienta encargada de detener los diferentes contenedores una vez hayas terminado de trabajar con ellos.

Así por ejemplo, en el caso que estamos viendo, lo que te encontrarías al ejecutar las distintas instrucciones sería algo como lo que te muestros a continuación,

  • Para levantar los contenedores, utilizaremos docker-compose para orquestar contenedores
$ docker-compose up -d

Creating chiquito-compose_chiquito_1      ... done
Creating chiquito-compose_mariadbserver_1 ... done
  • Para ver el estado de los contenedores
$ docker-composer ps

              Name                           Command             State           Ports         
-----------------------------------------------------------------------------------------------
chiquito-compose_chiquito_1        python3 app.py                Up      0.0.0.0:5000->5000/tcp
chiquito-compose_mariadbserver_1   docker-entrypoint.sh mysqld   Up      0.0.0.0:3306->3306/tcp

Y por último, para detener los contenedores, de nuevo recurrimos a nuestro docker-compose para orquestar contenedores, la instrucción es tan sencilla como ejecutar,

$ docker-composer down

Conclusión

Recordarte que tanto la imagen como el script, y el resto de archivos que he utilizado tanto para crear la base de datos, como para la imagen, los tienes disponibles en GitHub y Docker Hub. En este caso, evidentemente, te tienes que descargar la imagen etiquetada como composer.

Una vez, te he indicado donde puedes encontrar los recursos para montar todo esto, viene el momento de la reflexión. Aunque, después de lo que te he mostrado, creo que no voy a necesitar mucho esfuerzo para convencerte de la ventaja de esto de orquestar contenedores como docker-compose para realizar el trabajo. Desde luego desde punto de vista de la sencillez, la simplicidad y la elegancia, este procedimiento está muy por delante de los primeros pasos que has dado. Así, sin lugar a dudas, es bien sencillo levantar tus contenedores y detenerlos en su caso. Y todo ello sin casi ninguna preocupación.


Más información,

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *