Introducción a Spring Boot: Dockerización y publicación de un microservicio - Parte II

*NOTA: Este artículo es la continuación del artículo publicado anteriormente con título Introducción a Spring Boot: Creación de un microservicio.

Recordemos que en la primera parte habíamos creado el microservicio para nuestra aerolínea SpringAirline donde publicábamos una api REST basada en Spring Boot con Java 11 y accesible mediante Swagger y OpenAPI 3.0. Accedimos a dicha api localmente y jugamos un poquito con ella y con los datos que introdujimos por defecto. Hasta ahí todo bien, nuestro microservicio teóricamente está terminado. Lo hemos arrancado y hemos visto que funciona pero le falta poder llevárnoslo si fuese necesario a un entorno productivo.

De eso nos vamos a ocupar en esta última parte: Vamos a ver cómo “Dockerizar” nuestro microservicio y crear una imagen docker del mismo para que podamos publicarlo en cualquier entorno basado en Docker como Kubernetes u Openshift. NOTA: Al igual que en el artículo anterior si quieres saltarte toda la explicación que viene a continuación y pasar directamente al código puedes descargártelo de github.

Ante todo vamos a empezar por el principio ¿Qué es Docker? Docker es una tecnología que permite la creación y el uso de contenedores. Es un proyecto open source respaldado por la empresa Docker Inc.

Un contenedor es un conjunto de procesos y librerías separados del resto del sistema. Evolucionando el viejo concepto de virtualización, básicamente lo que hacemos en un contenedor es crear un ecosistema propio e independiente de la máquina local que nos permite ejecutar aplicaciones y procesos en un entorno lo más parecido posible a los entornos productivos donde esa aplicación funcionará en el futuro.

Supongamos que yo trabajo con un equipo basado en Windows y los entornos de mi proyecto, como es habitual, se encuentran basados en Linux. Esta diferencia a la hora del desarrollo, cualquier desarrollador habrá visto en carne propia, que es fuente de errores y quebraderos de cabeza durante un despliegue. Pues si trabajamos utilizando contenedores Docker nunca más tendremos este problema, porque puedo crearme un contenedor en mi equipo local que simule el sistema operativo y todas las características, incluyendo los recursos y cuotas de disco, de los entornos de mi proyecto y desarrollar directamente en ese ecosistema olvidando las características de mi equipo local. Obviamente esto puede hacerse siempre que los recursos de mi máquina física sean superiores a los que voy a necesitar para mi contenedor, algo que en la práctica hoy en día no debería ser ningún problema.

Pero el contenedor es el último eslabón de un proceso que empieza en el código del propio microservicio. Antes de tener un contenedor corriendo es necesario realizar una serie de pasos, cada uno de ellos con sus propios protagonistas.

Como todo esto es un poco lío vamos a intentar explicarlo directamente con un ejemplo práctico, desplegando como un contenedor Docker nuestro microservicio spring-airline.

En mi caso, he desarrollado este microservicio utilizando un sistema operativo Windows 10. Pero sé que si quiero desplegarlo en el futuro lo haré en un entorno que correrá bajo un sistema operativo Linux, por lo tanto antes de hacerlo quiero asegurarme que mi microservicio funcionará correctamente en ese tipo de entornos.

Antes de nada, tenemos que asegurarnos de tener Docker instalado en nuestro equipo. Para ello te recomiendo que visites su página oficial donde explican cómo hacer esta instalación dependiendo de tu sistema operativo.

Definición de la imagen del contenedor mediante el fichero Dockerfile

El primer paso es crear el fichero Dockerfile. Este fichero es el manual de instrucciones que le indicará a Docker como quiero que construya mi imagen y qué componentes quiero que tenga dentro. Para ello, en la raíz de mi proyecto, a la misma altura que el fichero pom.xml, creo un fichero que se llamará Dockerfile (así, sin extensión) y le pondré el siguiente contenido:

FROM openjdk:11-jre-slim
ARG JAR_FILE=target/spring-airline-1.0.0.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

FROM openjdk:11-jre-slim

Esta línea indica que para construir nuestro contenedor vamos a partir de una imagen pública. Por regla general las imágenes que creamos habitualmente no son más que la extensión de una o más imágenes públicas que combinamos como más nos interesa. En un momento concreto, la comunidad Docker para simplificar el trabajo de los futuros desarrolladores creó instantáneas de los contenedores más utilizados y los publicó en un repositorio público para el uso de cualquier desarrollador que lo necesitase. En este enlace puede consultarse este catálogo de imágenes y coger como base de nuestro contenedor la que más nos interese. En nuestro caso partimos de una versión ligera de Linux con openjdk11 instalado.>

ARG JAR_FILE=target/spring-airline-1.0.0.jar

Aquí simplemente estamos declarando una constante llamada JAR_FILE donde indicamos la ruta relativa dentro del proyecto donde se encuentra el jar del microservicio.

COPY ${JAR_FILE} app.jar

Copiamos dentro del contenedor el jar del microservicio, cuya ruta declaramos en la línea anterior, en el directorio $HOME del sistema operativo que estamos usando como base.

ENTRYPOINT ["java","-jar","/app.jar"]

Por último indicamos, cuando se arranque el contenedor, que comando queremos que se ejecute. En este caso queremos que arranque nuestro microservicio con el comando habitual en las aplicaciones java (Recordemos que Spring Boot ya tiene un tomcat embebido que al ejecutar este comando levantará por lo que no es necesario que lo instalemos directamente en el contenedor a través del Dockerfile). Es muy importante tener en cuenta que el contenedor estará corriendo mientras este comando esté activo.

Con esto, para un microservicio tan sencillo como el que tenemos es suficiente. Nuestro fichero Dockerfile está terminado y ya tenemos todas las instrucciones necesarias para ejecutar el microservicio dentro del contenedor.

Construcción de la imagen del contenedor

Para ello tenemos que construirnos la imagen del contenedor. Recordemos que una imagen es una instantánea fija e inmutable de un momento concreto de nuestro contenedor y es lo que obtenemos cuando ejecutamos un build a un fichero Dockerfile. Como entre las instrucciones que ejecuta está copiar el jar de nuestro microservicio vamos a asegurarnos que en /target está la versión más actualizada de este jar. Para ello ejecutamos el siguiente comando Maven:

mvn clean install

Y luego construimos la imagen situándonos en la consola en el directorio en el que se encuentra el fichero Dockerfile.

docker build -t springairline .

Este comando ejecuta el build y crea una imagen que llamaremos springairline. El ‘.’ final es necesario porque estamos en local.

Existen plugins de maven que permiten integrar este paso en el ciclo de vida del microservicio. En este caso dada la simplicidad del microservicio que estamos desarrollando se ha optado por hacerlo manualmente y así explicar qué comando docker es el que nos permite realizar esta operación, pero para microservicios más complejos te recomiendo que les eches un vistazo a dichos plugins y sus posibilidades.

Arranque del contenedor

Por fin tenemos nuestra imagen y con ella podemos ejecutar e iniciar nuestro contenedor con el siguiente comando:

docker run -p 8080:8085 springairline

Si recordamos, habíamos configurado nuestro microservicio para que se levantase en el puerto 8085. Con este comando le decimos que el puerto 8085 de nuestro contenedor lo publique en el puerto 8080 de nuestra máquina local para que podamos acceder al microservicio aunque este esté encapsulado dentro del contenedor.

Por lo tanto si accedemos a http://localhost:8080/airline/swagger-ui/index.html podremos acceder al Swagger del microservicio y probarlo.

Ya tenemos nuestro contenedor corriendo con docker, lo bueno de esto es que una vez construida la imagen con el comando docker build puedo llevármela tal cual a cualquier entorno y desplegarla con docker run sin ningún tipo de problema.

Casi sin darnos cuenta tenemos nuestro servicio funcionando y corriendo en cualquier máquina que tenga Docker instalado.

 

Guía de Posibilidades Profesionales en el Ecosistema de Java