Las tecnologías de contenedores son prácticamente el estándar sobre el que construimos y desplegamos nuestras aplicaciones hoy en día, y es que las ventajas que nos ofrecen son muchas: Portabilidad, seguridad, facilidades para replicar despliegues y asegurar alta disponibilidad, etc.

Tradicionalmente nuestro flujo de trabajo implicaba construir fuentes y desplegar en un servidor de aplicaciones fijo. Pero que pasa si el día de mañana queremos movernos de host o escalar los recursos? Con las ventajas que nos entregan las tecnologías de contenedor plus las de los cloud providers como GCP o AWS, podemos manipular nuestras aplicaciones con una agilidad superior.

En este post se mostrará el flujo completo de empaquetado y despliegue de una aplicación Java en la nube. Para este caso usaremos Google: GCP, pero fácilmente puedes usar otro proveedor ya que en el fondo el procedimiento es el mismo, solo cambian las herramientas.

Crear la aplicación Java

Para no complicarnos, basaremos nuestra aplicación en un repositorio de ejemplo. Lo clonamos y generamos el build:

git clone https://github.com/felipeleivav/javalin-sample-rest.git
cd javalin-sample-rest
mvn package

Lo único que hace está aplicación es crear una API REST que devuelve un hola mundo. Para arrancarla invoca el comando java pasándole el Jar y la clase principal:

cd target
java -jar java -cp javalin-sample-rest-1.0-SNAPSHOT-jar-with-dependencies.jar app.Main

Entonces, ya sabemos como compilar y arrancar la aplicación, ahora vamos a la dockerización.

Empaquetando con Docker

El siguiente paso es crear el Dockerfile. Este archivo le dará las instrucciones a Docker sobre como debe empaquetar nuestra aplicación.

En la raíz de nuestro proyecto, crear el archivo Dockerfile con lo siguiente:

FROM java:8

WORKDIR /app/

# Copiamos el JAR de nuestra aplicación a la imagen Docker
COPY target/javalin-sample-rest-1.0-SNAPSHOT-jar-with-dependencies.jar .

# Corremos el archivo JAR
CMD ["java", "-cp", "javalin-sample-rest-1.0-SNAPSHOT-jar-with-dependencies.jar", "app.Main"]

Resumen:

  1. Le decimos que el directorio de trabajo (dentro del contenedor) será /app
  2. Cuando generemos la imagen Docker, se copiará el Jar de nuestro directorio target a la imagen
  3. Cuando arranquemos el contenedor, se ejecutará el comando java que inicia nuestra API

Ahora que ya tenemos el Dockerfile, en el mismo directorio ejecutamos:

docker build -t api-rest .

Con el flag -t nombramos nuestra imagen como api-rest, y con . le decimos que busque el Dockerfile en el directorio actual.

Validamos que la imagen se haya creado ejecutando docker images:

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
api-rest            latest              1d8fb7bd6dcc        34 seconds ago      647MB

Arrancar nuestro contenedor

Ahora que ya tenemos nuestra imagen lista la arrancamos ejecutando docker run -p 7000:7000 api-rest.

El flag -p te mapea el puerto 7000 del contenedor al puerto 7000 de tu máquina

Entonces ahora sí puedes acceder a tu API: http://localhost:7000.

Si queremos dejar corriendo el contenedor en background, agrega el flag -d:

docker run -d -p 7000:7000 api-rest

Para detenerlo, primero obtén el ID del contenedor y luego lo pasas a un docker stop:

docker ps | grep api-rest
docker stop <container id>

Desplegar en la nube

Pero en un ambiente real no vamos a ejecutar estos comandos para dejar nuestra aplicación corriendo, lo que normalmente hacemos es correrla dentro de un cluster que orquestará a nuestro contenedor.

Y de donde va a sacar este cluster nuestra imagen Docker? Bueno, se la daremos a través de un Docker Registry, que es básicamente un repositorio de imágenes de Docker. Los hay públicos como el Docker Hub así como los hay privados, como los que ofrecen los proveedores cloud (ECR de Google o ECS de Amazon).

Para recuperar o enviar imágenes a estos repositorios simplemente utilizamos los subcomandos de docker push y pull. Partamos!

Instalar utilidades

Antes que nada necesitamos:

  1. Tener una cuenta GCP, si eres nuevo te dan $300USD para iniciar.
  2. kubectl, CLI tools para kubernetes (instrucciones instalación)
  3. gcloud sdk, CLI tools para GCP (instrucciones instalación)

En el último paso de instalación de gcloud linkeaste la utilidad a tu cuenta con gcloud init, si ya hiciste eso, ahora solo debes autenticar tu docker a tu cuenta cloud, ejecutar:

gcloud auth configure-docker

Preparando la imagen

Antes de enviar la imagen a Google Cloud, le asignamos un tag:

docker images | grep api-rest
docker tag <IMAGE ID> gcr.io/<ID de proyecto GCP>/api-rest

Ahora si podemos hacer el push de la imagen:

docker push gcr.io/<ID proyecto>/api-rest

Si todo sale bien, podrás ver la imagen en la sección Container Registry de la consola de GCP:

Correr aplicación

Para desplegar la imagen, nos vamos a la sección Cargas de trabajo de Kubernetes Engine en la consola de GCP.

Allí le damos Crear despliegue, seleccionamos la imagen que subimos y Continuar. El proceso es bastante intuitivo.

Se tardará un par de minutos en desplegar. Si no tenías un cluster creado, se creará automáticamente en este paso.

Cuando ya esté, en la misma pantalla podrás ver una opción para crear el servicio de balanceador de carga. Este te dará la IP con la redirección de puerto para acceder a tu API.

So, creamos el balanceador apuntando al puerto 7000:

Después de esto la IP del balanceador ya estará asignada y podrás consultar tu API:

Conclusiones

En este post se ha presentado un ejemplo de como desplegar una aplicación Java en GCP. Así como se mencionó que podemos alternar fácilmente el proveedor cloud a AWS por ejemplo, también podríamos haberlo hecho sin problemas con una aplicación desarrollada en otra tecnología como Node o Ruby.

En el fondo, lo importante de todo esto es reconocer las ventajas de aplicar containerización a nuestros desarrollos, y finalmente como podemos sacarle partido a ello aprovechando las capacidades de los servicios en la nube como GCP o AWS.

Características como la alta disponibilidad o auto escalabilidad ya no son propias unicamente del mundo enterprise, si no que están al alcance de cualquier desarrollador gracias a tecnologías abiertas como Docker o Kubernetes y a las plataformas en la nube que las disponibilizan abstrayéndonos de la complejidad de implementar y mantener estos sistemas.