miércoles, 16 de mayo de 2018

Usando Docker de manera práctica - Parte IV

Hola de nuevo, hoy vamos con una nueva entrada dedicada a Docker y como podemos usarlo para crear servicios complejos.

Hasta ahora, en todos los ejemplos que hemos visto, hemos utilizado la potencia de Docker para crear y desplegar de forma rápida servicios simples basados en aplicaciones, como podía ser un servidor web o una base de datos. En alguno de los ejemplos hemos creado servicios un poco más complejos enlazando dos contenedores entre sí, comunicando nuestro servidor web con nuestra base de datos. Las funcionalidades que proporciona Docker lo hacen ideal precisamente para casos como este, en el que nuestro servicio está basado por múltiples aplicaciones individuales que proprocionan un servicio a nuestros clientes.

Con esto último no quiero decir que no se pueda usar en otros casos, los contenedores por sus características nos permiten diseñar múltiples arquitecturas en las que, aunque nuestro servicio sea pequeño y quizás nada complejo, nos puede ser de gran utilidad. Así que, ahora que sabemos un poco más sobre Docker, podemos pensar en usarlo en casos como los siguientes:
  • Migración de aplicaciones antiguas y no soportadas a un entorno más moderno. Con Docker podremos crear una imagen con nuestra aplicación y migrarla directamente a un nuevo host o entorno virtual.
  • Entornos de pruebas. En aquellos casos en los que necesitamos probar nuevas versiones o funcionalidades de software, pero no queremos instalarlo en nuestras máquinas, si existe la imagen correspondiente podremos crear un contenedor a partir de dicha imagen y realizar todas las pruebas que necesitemos.
  • Reducción del número de hosts o entornos virtuales. Si tenemos varios servicios ligeros desplegados en diferentes hosts o entornos virtuales, los cuales no interactuan entre sí, podemos convertirlos en contenedores con sus dependencias y unificarlos todos en un solo servidor, ya sea físico o virtual simplificando así nuestra infraestructura.
Estos ejemplos son casos sencillos pero las aplicaciones son innumerables y dependerán del entorno en el cual nos encontremos y sobre todo, de nuestras necesidades. En un post posterior intentaré desarrollar un poco más todo esto con ejemplos.

Ahora, siguiendo con la idea de servicios formados por diferentes aplicaciones que se comunican entre sí vamos a investigar Docker Compose o compose a partir de este punto, que nos permite definir un servicio a partir de todos sus componentes y lanzarlo con un solo comando.

Compose es una herramienta que, a partir de la definición de un servicio nos permite lanzarlo y gestionarlo de forma sencilla, olvidándonos así de los contenedores individuales y centrándonos en la imagen completa del servicio que queremos proporcionar así como las interacciones entre los contenedores que lo forman.

Para empezar lo primero es instalar el comando docker-compose, ya que por defecto puede no estar incluido en la instalación de Docker. Siguiendo las instrucciones de instalación dadas en https://docs.docker.com/compose/install/ instalamos compose en nuestro sistema.

Si ejecutamos compose sin argumentos, veremos que nos devuelve una ayuda muy similar a la que nos devuelve el comando docker que hemos venido utilizando hasta ahora:

Salida del comando docker-compose.
Como vemos, con compose podemos realizar muchas tareas que en apariencia son similares a las que realizamos con el comando docker estándar. La verdadera diferencia es que compose usará un fichero de definición de servicio, el cual contendrá la descripción de los contenedores que definen el servicio así como la relación existente entre ellos.

Para describir o declarar un servicio crearemos un fichero YAML en el cual estableceremos de forma muy simple los elementos que forman el servicio y la relación entre los mismos. Como creo que con un ejemplo quedará mucho más claro, vamos a repetir nuestro ejemplo de servidor web enlazado con una base de datos mariaDB del post anterior. En aquel caso nuestro servicio era un servidor web en el cual suponemos que corre un servicio que necesita consultar una base de datos externa, dada por otro contenedor que ejecuta una base de datos mariaDB. Lo que entonces hicimos  podemos describirlo con YAML del siguiente modo:

Definición en YAML de un servicio web simple.
Analizando el fichero es fácil ver como mediante YAML hemos definido cada uno de nuestros contenedores, un servidor web basado en Apache y una base de datos basada en mariaDB y cual es la relación entre ambos contenedores.

Con este fichero, con solo ejecutar el comando docker-compose especificando cual es nuestro ficheo de definición, arrancaremos el servicio formado por nuestros dos contenedores:

Lanzando un servicio con docker-compose.
Si comprobamos que contenedores hay corriendo, pero usamos docker-compose, la salida es la siguiente:

Contenedores en ejecución de un servicio.
Podemos ver de forma sencilla los contenedores que forman el servicio, su estado, los puertos que tienen publicados así como el comando ejecutado en la imagen.

Para controlar nuestro servicio usaremos diferentes subcomandos de compose, los cuales me permitirán parar el servicio, inspeccionar los logs de los contenedores, etc...

Mostrando los logs de un contenedor miembro del servicio.
Parando un contenedor miembro del servicio.
Arrancando un contenedor miembro del servicio.
Parando un servicio de forma completa.

Eliminando completamente un servicio.
Por tanto, compose nos permite controlar nuesros servicios, así como los contenedores que lo forman, de una manera muy similar a como lo hacíamos con el comando docker.

Un punto muy importante que debemos tener en cuenta al usar compose es que el comando siempre busca el fichero de definición del servicio, el cual por defecto es docker-compose.yml, con lo que si nuestro fichero de definición se llama de otra manera, tendremos que especificarlo con la opción -f. Para ahorrarnos esto en todos nuestros comandos, un enlace simbólico nos permitirá especificarlo y además tener nosmbres más descriptivos en nuestros fichero YAML. 

Debe quedar claro que un ejemplo tan sencillo como este no permite apreciar la potencia que proporciona compose. Para desarrollarlo un poco más, en el próximo post, intentaré complicar esto un poco más y estudiar más funcionalidades de compose.