Dockerfile para Imagen por Capas con Spring Boot
Por Martinez Henry
Resumen (TL;DR)
Empaquetar una aplicación Spring Boot en capas dentro de Docker permite reutilizar las capas que no cambiaron entre builds, reduciendo tiempos de compilación y el tamaño de los artefactos. En este artículo vemos cómo configurarlo paso a paso.
📑 Tabla de Contenido
Dockerfile para Imagen por Capas con Spring Boot
Una recomendación a la hora de crear imágenes Docker de aplicaciones Spring Boot es empaquetar la aplicación por capas.
Al crear imágenes por capas evitamos construcciones y empaquetados pesados como los jar. Adicionalmente ganamos tiempo de compilación, ya que podemos reutilizar las capas que no sufrieron cambios y procesar solo las capas afectadas por nuestras modificaciones.
Configuración
Lo primero es incluir en el plugin spring-boot-maven-plugin la configuración de capas:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin>
Creando el Dockerfile
Para obtener las capas de nuestra aplicación empaquetada en un jar debemos ejecutar java -Djarmode=layertools -jar app.jar extract. Esto extraerá las capas de Spring Boot.
Spring Boot tiene la siguiente estructura para sus capas:
dependenciesspring-boot-loadersnapshot-dependenciesapplication
En el Dockerfile creamos una sección builder donde ejecutamos la extracción:
# the first stage of our build will extract the layers
FROM openjdk:14-ea-jdk-alpine as builder
ARG WORKDIR_BASE=/home/application
WORKDIR $WORKDIR_BASE/bin
COPY ./target/my-app.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
Luego, en una nueva sección generamos la imagen final con las variables de entorno y configuraciones necesarias:
## Builder App Image
FROM openjdk:14-ea-jdk-alpine
LABEL maintainer="Henry Martinez"
ARG USER_GROUP=app-group
ARG USER=app-user
ARG WORKDIR_BASE=/home/application
WORKDIR $WORKDIR_BASE/bin
RUN mkdir -p $WORKDIR_BASE/logs && addgroup $USER_GROUP && adduser \
--disabled-password \
--gecos "" \
--ingroup $USER_GROUP \
--no-create-home \
$USER
EXPOSE 8080
ENV TZ="America/Bogota"
COPY --from=builder $WORKDIR_BASE/bin/dependencies ./
RUN true
COPY --from=builder $WORKDIR_BASE/bin/spring-boot-loader ./
RUN true
COPY --from=builder $WORKDIR_BASE/bin/snapshot-dependencies ./
RUN true
COPY --from=builder $WORKDIR_BASE/bin/application ./
RUN chown -R $USER:$USER_GROUP $WORKDIR_BASE
USER $USER
ENTRYPOINT java org.springframework.boot.loader.JarLauncher
Las líneas clave son las que incluyen las capas de Spring Boot y la línea de ejecución ENTRYPOINT java org.springframework.boot.loader.JarLauncher.
Para construir la imagen ejecutamos:
docker build -t my-app:latest .
my-app:latestes el nombre con el que deseamos que la imagen se cree.
Cierre
Las imágenes por capas son una práctica recomendada para cualquier aplicación Spring Boot en contenedores. El costo de configurarlas es mínimo y el beneficio en tiempos de build y tamaño de imagen es inmediato, especialmente en pipelines de CI/CD donde los builds son frecuentes.
Referencias e Inspiraciones:
- Spring. Layered Jars — Spring Boot Maven Plugin. docs.spring.io
- Docker. Best practices for writing Dockerfiles. docs.docker.com