Cómo ejecutar una aplicación estándar de Java con Spring Boot como un contenedor de Docker
1. Introducción
En este tutorial, exploraremos formas de ejecutar una aplicación estándar de Java creada con Spring Boot como un contenedor de Docker. Más específicamente, utilizaremos Liberica JDK sobre Alpaquita Linux para crear la imagen de Docker que ejecutará nuestra aplicación. Liberica JDK y Alpaquita Linux son parte de la oferta de productos de BellSoft. BellSoft es una organización que tiene la visión de hacer de Java el lenguaje preferido para aplicaciones nativas de la nube. A través de sus ofertas específicas, prometen una mejor experiencia a costos más bajos.
2. Una aplicación simple de Spring Boot
Comencemos creando una aplicación sencilla en Java que procederemos a contenerizar. Utilizaremos Spring Boot para crear esta aplicación. Spring Boot facilita la creación de aplicaciones independientes y de producción basadas en Spring con configuraciones mínimas.
La forma más sencilla de inicializar una aplicación Spring Boot es usar el Spring Boot CLI. Nos permite crear un nuevo proyecto utilizando start.spring.io directamente desde nuestra línea de comandos favorita:
$ spring init --build=gradle --dependencies=web spring-bellsoft
Aquí, estamos agregando web como una dependencia que nos permite construir aplicaciones con APIs RESTful y Apache Tomcat como el contenedor embebido predeterminado. Hemos seleccionado Gradle como la herramienta de construcción aquí, sin embargo, el lenguaje usado es Java y muchas otras cosas se eligen como predeterminadas.
Luego, podemos importar el proyecto generado en nuestro IDE favorito, como IntelliJ IDEA, y comenzar a desarrollar la aplicación. Como se mencionó anteriormente, mantendremos esto muy simple. Por lo tanto, añadiremos una API REST simple que tome un número como entrada y devuelva la serie de Fibonacci igual o menor que ese número:
@RestController
public class DemoController {
@GetMapping("/api/v1/fibs")
public List getFibonacciSeriesBelowGivenInteger(Integer input) {
List result;
if (input == 0)
result = List.of(0);
else {
int n = 0; int m = 1;
result = new ArrayList<>(Arrays.asList(n));
while (m <= input) {
result.add(m);
m = n + m; n = m - n;
}
}
return result;
}
}
Construir la aplicación en Gradle es tan simple como ejecutar el siguiente comando, que usa el wrapper de Gradle generado anteriormente:
./gradlew clean build
El empaquetado predeterminado para el proyecto generado es JAR, lo que implica que el comando anterior, al tener éxito, resultará en el JAR final ejecutable creado en el directorio de salida “./build/libs“. Podemos iniciar la aplicación usando este archivo JAR:
java -jar ./build/libs/spring-bellsoft-0.0.1-SNAPSHOT.jar
Luego, podemos llamar a nuestra API y ver si funciona bien:
$ curl http://localhost:8080/api/v1/fibs?input=5
[0,1,1,2,3,5]
Esto concluye nuestro esfuerzo por crear una simple aplicación para el resto del tutorial. Usaremos esta aplicación para contenerizar la aplicación.
3. Contenerizando nuestra aplicación
Un contenedor es una unidad estándar de software que empaqueta el código y todas sus dependencias. Es una forma de virtualización del sistema operativo que ofrece una manera consistente de implementar aplicaciones. Hoy en día, se ha convertido en la opción predeterminada para ejecutar cualquier aplicación en entornos de nube.
Necesitamos una plataforma de contenedor para ejecutar nuestra aplicación simple como un contenedor. Una plataforma de contenedor, entre otras cosas, proporciona un motor de contenedor para crear y administrar contenedores. Docker es la plataforma más popular diseñada para construir, compartir y ejecutar aplicaciones en contenedor.
El motor de contenedor crea un contenedor a partir de la imagen del contenedor. Una imagen de contenedor es un archivo estático inmutable que incluye todo lo que un contenedor necesita para ejecutarse. Sin embargo, comparte el núcleo del sistema operativo del host. Por lo tanto, ofrece un aislamiento completo pero sigue siendo ligero.
Uno de los medios para crear una imagen de Docker es describir la receta para crear la imagen como un Dockerfile. Luego, podemos usar el daemon de Docker para crear una imagen a partir del Dockerfile. El formato de imagen original de Docker ahora se ha convertido en el Open Container Initiative (OCI) Especificación de Imagen.
Una de las ventajas clave de ejecutar una aplicación como un contenedor es que proporciona una experiencia de implementación consistente en múltiples entornos. Por ejemplo, imagina que entregamos nuestra simple aplicación construida usando Java 17 pero desplegada en un entorno con tiempo de ejecución Java 11.
Para evitar esta sorpresa, las imágenes de contenedor nos permiten empaquetar todas las dependencias críticas para nuestra aplicación, como los binarios/librerías del sistema operativo y el tiempo de ejecución de Java. Al hacerlo, podemos estar seguros de que nuestra aplicación se comportará de la misma manera, sin importar en qué entorno se implemente.
4. Contenedores de ejecución de Liberica
Una imagen de contenedor está compuesta por múltiples capas apiladas una encima de la otra. Cada capa representa una modificación específica al sistema de archivos. Típicamente, comenzamos con una imagen base que mejor se ajuste a los requisitos de nuestra aplicación y construimos capas adicionales sobre ella.
BellSoft ofrece varias imágenes altamente optimizadas para ejecutar aplicaciones Java en entornos basados en la nube. Están construidas con Alpaquita Linux y Liberica JDK. Antes de usar estas imágenes, examinemos los beneficios de sus componentes de BellSoft.
4.1. Beneficios de Alpaquita Linux
Alpaquita Linux es un sistema operativo ligero basado en Alpine Linux. Está hecho a medida para Java y optimizado para la implementación de aplicaciones nativas de la nube. Resulta en un tamaño de imagen base de 3.22 MB y requiere pocos recursos para ejecutarse.
Alpaquita Linux viene en dos versiones, una basada en un optimizado musl libc y la otra en glib libc. Aquí, libc se refiere a la biblioteca estándar para el lenguaje de programación C, según lo especificado en la norma ISO C. Proporciona macros, definiciones de tipos y funciones para varias tareas.
Además de estar optimizado para aplicaciones Java, Alpaquita Linux ofrece varias características de seguridad para nuestra implementación. Estas incluyen características de red, opciones de compilación personalizadas y aislamiento de procesos. También incluye endurecimiento del núcleo como bloqueo del núcleo y firma de módulos del núcleo.
Además, Alpaquita Linux está optimizado para la implementación ya que utiliza la compresión de módulos del núcleo para reducir el tamaño de los paquetes. Proporciona una pila confiable y rápida para ejecutar aplicaciones con características de rendimiento como optimizaciones del núcleo y gestión de la memoria.
Alpaquita Linux solo empaqueta un pequeño número de componentes del sistema operativo. Sin embargo, podemos instalar módulos adicionales y paquetes adicionales desde el repositorio APK de Alpaquita. Más importante aún, Alpaquita Linux tiene un ciclo de soporte de cuatro años para sus versiones LTS.
4.2. Beneficios de Liberica JDK
Regresar a Liberica JDK es un tiempo de ejecución de Java de código abierto para implementaciones modernas de Java. Proviene de BellSoft, un contribuyente clave a OpenJDK, y promete un solo tiempo de ejecución para la nube, el servidor y uso de escritorio para aplicaciones Java. También se recomienda para aplicaciones Java basadas en Spring.
Liberica JDK admite varias arquitecturas como x86 64/32 bits, ARM, PowerPC y SPARC. También admite varios sistemas operativos como Windows, macOS y la mayoría de las distribuciones de Linux. Además, admite casi todas las versiones de Java en uso en la actualidad.
Una de las ventajas clave de Liberica JDK es que es bastante ligero. El contenedor de ejecución de Liberica basado en Alpaquita Linux para Java 17 pesa menos de 70 MB. Promete un mejor rendimiento mientras reduce el tráfico, el almacenamiento, el consumo de memoria y los costos totales.
También cuenta con diversas herramientas para trabajar con el tiempo de ejecución de JDK. Existe acceso completo a herramientas para el monitoreo y actualización. Además, los usuarios también pueden obtener acceso al Centro de Administración Liberica (LAC), una herramienta empresarial para el monitoreo de tiempo de ejecución, control de licencias y actualizaciones de seguridad.
Liberica JDK está verificado por TCK para las especificaciones de Java SE y se prueba exhaustivamente antes de cada lanzamiento. Además, BellSoft garantiza al menos ocho años de vida útil de Liberica JDK con correcciones de errores, parches de seguridad y otras mejoras según sea necesario.
5. Creando la imagen del contenedor
Ahora, estamos listos para contenerizar nuestra simple aplicación utilizando Alpaquita Linux y Liberica JDK. El primer paso es elegir una imagen base con estas dependencias. Siempre podemos crear nuestra imagen base, pero afortunadamente, BellSoft mantiene varias imágenes en Docker Hub para elegir.
5.1. Elegir un contenedor de ejecución de Liberica
Estas son imágenes de Alpaquita Linux con diferentes opciones de Liberica JDK lite y Liberica JRE para elegir. Podemos identificar esto típicamente a partir de la etiqueta, que puede contener uno de los siguientes:
- jdk: imagen de Alpaquita Linux con la versión Liberica JDK Lite.
- jdk-all: paquetes de Liberica JDK que se pueden usar para crear un tiempo de ejecución personalizado con la herramienta jlink.
- jre: contiene solo el Liberica JRE para ejecutar aplicaciones Java.
Aquí, la etiqueta de la imagen revela mucha otra información sobre la imagen además de la versión de JDK que contiene. Veamos la convención para la etiqueta de imagen utilizada por BellSoft:
[JDK tipo]-Java versión-[slim]-[OS release]-[libc tipo]-[fecha]
Aquí, las diferentes partes de la etiqueta cuentan un aspecto específico de la imagen y nos ayudan a elegir la correcta entre muchas imágenes disponibles:
- Tipo de JDK: el tipo de JDK (JDK, jdk-all, y jre, como hemos visto anteriormente)
- Versión de Java: la versión de Java a la que se conforma el JDK
- slim: indica si la imagen es una imagen slim
- Versión de OS: la versión del sistema operativo (actualmente solo es stream)
- Tipo de libc: el tipo de biblioteca estándar C (glibc o musl, como hemos visto anteriormente)
- Fecha: la fecha de lanzamiento de la imagen.
5.2. Contenerizando nuestra aplicación
Ahora, tenemos toda la información en la etiqueta de imagen para elegir la adecuada. Por ejemplo, si queremos Java 17 con glibc para nuestra aplicación, debemos elegir la etiqueta “jdk-17-glibc”.
Una vez que hayamos elegido la etiqueta correcta de la imagen base, el siguiente paso es crear un Dockerfile para definir cómo queremos crear la imagen del contenedor para nuestra aplicación:
FROM bellsoft/liberica-runtime-container:jdk-17-glibc
VOLUME /tmp
ARG JAR_FILE=build/libs/java-bellsoft-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
Este Dockerfile bastante simple indica que deseamos comenzar con el contenedor de ejecución Liberica mencionado y copiar nuestro JAR de aplicación fat. También definimos el punto de entrada con instrucciones para ejecutar nuestra aplicación una vez instanciado el contenedor.
Debemos colocar este Dockerfile en la raíz del directorio del código de la aplicación. Luego, podemos usar el siguiente comando para crear la imagen del contenedor en nuestro repositorio local:
docker buildx build -t spring-bellsoft .
Esto extraerá la imagen base del registro, Docker Hub, por defecto y creará la imagen de contenedor para nuestra aplicación. Luego, podemos ejecutar esta imagen como un contenedor:
docker run --name fibonacci -d -p 8080:8080 spring-bellsoft
Tenga en cuenta que hemos mapeado el puerto local 8080 con el puerto 8080 del contenedor. Por lo tanto, podemos acceder a nuestra aplicación como lo hicimos anteriormente en el tutorial:
$ curl http://localhost:8080/api/v1/fibs?input=5
[0,1,1,2,3,5]
Esto concluye nuestro esfuerzo por contenerizar la aplicación simple que creamos anteriormente en el tutorial con contenedores de ejecución de Liberica publicados por BellSoft. Por supuesto, sería interesante probar aplicaciones más complejas y otras variaciones de los contenedores de ejecución de Liberica que tenemos a nuestra disposición.
6. Conclusión
En este tutorial, pasamos por lo básico de crear un contenedor para una simple aplicación Java basada en Spring Boot. Exploramos la opción de elegir los contenedores de ejecución Liberica de BellSoft para hacerlo. En el proceso, creamos una aplicación simple y la contenizamos.
Esto nos ayudó a entender los beneficios de Alpaquita Linux y Liberica JDK, los componentes de los contenedores de ejecución de Liberica. Estas son algunas de las ofertas clave de BellSoft, que se compromete a optimizar aplicaciones Java para entornos basados en la nube.