Invocando contenedores Docker desde el contexto de Spring

0
6882

En este tutorial veremos como asociar el ciclo de vida de un contenedor docker al ciclo de vida de un contexto de Spring mediante el uso de beans.

Índice de contenidos

1. Introducción

En el presente tutorial vamos a mostrar una de las múltiples maneras mediante las cuales podemos gestionar el ciclo de vida de uno o varios contenedores de Docker, en concreto mediante un objeto asociado al ciclo de vida del contexto de Spring.

Como veíamos en el tutorial de Jorge Pacheco, Docker for dummies, Docker es una herramienta que nos proporciona un entorno mediante el cual realizar «virtualizaciones ligeras».

Como pre-requisito necesitaremos tener el entorno de docker instalado en nuestro equipo, así como algunas nociones básicas de la tecnología, así que si aún no te has pasado por el anterior tutorial, este es el momento.

Nota: A lo largo del tutorial se va a hacer referencia a la IP asignada a la máquina virtual que genera docker-machine (192.168.99.100), está puede cambiar dependiendo de la configuración propia de cada sistema. Para obtener dicha IP solo hace falta ejecutar el comando «docker-machine ip default», donde «default» es el nombre de la máquina virtual de la que se va a hacer uso.

2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: MacBook Pro 17′ (2.66 GHz Intel Core i7, 8GB DDR3 SDRAM).
  • Sistema Operativo: Mac OS X Lion 10.10.3.
  • NVIDIA GeForce GT 330M 512Mb.
  • Crucial MX100 SSD 512 Gb.
  • Eclipse Mars 4.5.0.
  • Java 1.8
  • MySQL 5.7
  • Docker Toolbox 1.8.2
  • Docker 1.7.1
  • Spring Framework v4.2.1.RELEASE

    • spring-core
    • spring-context
    • spring-jdbc
    • spring-tx
    • spring-test
  • JUnit 4.12
  • MyBatis 3.3.0

3. Preparando el entorno

Para demostrar el funcionamiento de esta configuración vamos a proceder con un ejemplo sencillo: pruebas de integración contra una base de datos MySQL «dockerizada«.

La preparación del entorno no tiene misterio alguno. Hemos procedido con la creación de un proyecto Maven y definido las dependencias para Spring, JUnit, MyBatis y la base de datos, como puede verse a continuación.

Además de esto, nos hemos hecho con la última versión del contenedor de MySQL a través del repositorio de MySQL en DockerHub.

4. Configurando el contexto de Spring

Procedemos con la configuración del contexto de Spring que utilizaremos en nuestras pruebas de integración, y que alojaremos en la carpeta src/test/resources de nuestro proyecto.

applicationContext-test.xml

La mayoría de las declaraciones se corresponden con configuración de lo que sería una aplicación Java que hace uso de MyBatis (podéis repasar este tutorial de Rubén Aguilera por si algo no os cuadra MyBatis con Maven y Spring).

Además introducimos un bean de la clase DockerManager, la explicamos a continuación.

5. La clase DockerManager

Esta es la clase que hemos creado para proceder con las operaciones necesarias para poner en funcionamiento el contenedor Docker de MySQL.

Nos hemos ayudado de las anotaciones @PostConstruct y @PreDestroy, que nos permiten establecer lógica que se ejecutará en la inicialización de un bean y en la destrucción del mismo.

DockerManager.java

Como vemos, la lógica de los métodos consta de la ejecución de un script de línea de comandos. Estos scripts mantienen las instrucciones necesarias para poner en marcha el contenedor de Docker y pararlo, respectivamente.

dockerInitScript.sh

En este script de bash realizamos tareas rutinarias como son:

  • Levantar la máquina virtual con docker-machine.
  • Compilar una nueva imagen de mysql a partir de la original.
  • Lanzar el contenedor docker con la imagen de mysql, en caso de ser la primera vez volcamos un dump con la estructura base de la base de datos, en caso contrario «despausamos» contenedor.

Cada una de las acciones establece previamente una comprobación para ser realizada solo cuando sea necesario.

Como se puede ver en el script, ha sido necesario establecer un tiempo de pausa en la ejecución del script puesto que es necesario esperar a que el contenedor y el servidor MySQL estén listos para trabajar antes de continuar con la recuperación del dump de la base de datos, así como con las pruebas de integración. De otra manera, al intentar ejecutar las pruebas de integración, recibiríamos un mensaje informando de que no se puede conectar contra la base de datos ó que la base de datos no mantiene la estructura adecuada.

Para compilar la nueva imagen de docker hacemos uso del siguiente Dockerfile, en el que establecemos algunos valores básicos para el uso del contenedor:

Dockerfile

Por último el script que parará la ejecución de docker, cuando el contexto de Spring sea destruido:

dockerDestroyScript.sh

En este tutorial, hemos optado por pausar el contenedor de docker para que las ejecuciones sucesivas de las pruebas de integración sean lo más rápidas posibles y por ello se utilizan las instrucciones docker pause y docker unpause.

Siempre se puede optar por una aproximación más conservadora y, modificando el script dockerDestroyScript.sh, parar el contenedor, destruir la imagen y forzar que cada vez que se ejecute una prueba se compile una imagen nueva y levante la nueva instancia desde 0.

6. La prueba

DockerManagerExampleTest.java

En el código se puede ver un mapper de MyBatis con el que realizamos dos operaciones (inserción y recuperación) contra la base de datos y posteriormente comprobamos si coincide el número de registros que se espera.

Si todo esta bien, al ejecutar la prueba podrá verse en el terminal la salida por pantalla referente a las instrucciones que se van ejecutando desde el script.

7. Conclusiones

Como hemos visto, se puede enlazar de una manera fácil y rápida el ciclo de vida de un contenedor Docker con el ciclo de vida del contexto de Spring.

En ocasiones en las que los tiempos de respuesta no sean cruciales esta forma de trabajar puede ventajosa, como por ejemplo en el caso de querer realizar pruebas de integración específicas.

Sin embargo, se desaconseja por completo en situaciones donde:

  • los tiempos sean importantes, ya que la recarga de contextos los penalizará considerablemente
  • haya sincronización de eventos, ya que hay que tener en cuenta los tiempos de arranque de cada servicio

8. Referencias

Dejar respuesta

Please enter your comment!
Please enter your name here