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

  1. ¿Por qué imágenes por capas?
  2. Configuración del plugin Maven
  3. Creando el Dockerfile

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:

  • dependencies
  • spring-boot-loader
  • snapshot-dependencies
  • application

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:latest es 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: