Buenas tardes
Recientemente, me enfrenté a la tarea de lanzar aplicaciones Spring Boot 2 en un clúster de Kubernetes usando una imagen acoplable. Este problema no es nuevo, rápidamente encontré ejemplos en Google y empaqué mi aplicación. Me sorprendió mucho no encontrar la imagen alpina de jdk11 y esperaba que ese tamaño fuera lo suficientemente pequeño, pero cuando envié la imagen al registro de la ventana acoplable, noté que su tamaño era de casi 422 megabytes. Debajo del gato hay una descripción de cómo reduje la imagen de la ventana acoplable con mi bota de primavera y Java 11 a 144 megabytes.

App
Como mencioné anteriormente, mi aplicación está construida usando Spring Boot 2 y es un contenedor API REST sobre una base de datos relacional (usando @RepositoryRestResource). Mis dependencias incluyen:
org.springframework.boot:spring-boot-starter-data-rest org.springframework.boot:spring-boot-starter-data-jpa org.flywaydb:flyway-core org.postgresql:postgresql
El archivo jar recopilado tiene un tamaño de 37,6 megabytes.
Dockerfile:
FROM openjdk:11-jdk-slim WORKDIR /home/demo ARG REVISION COPY target/spring-boot-app-${REVISION}.jar app.jar ENTRYPOINT ["java","-jar","app.jar"]
Como resultado del ensamblaje, obtengo una imagen de tamaño: 422 mb de acuerdo con la salida del comando docker images. Curiosamente, cuando se utiliza la imagen obsoleta de 8-jdk-slim, el tamaño se reduce a 306 mb.
Intento 1: otra imagen básica
El primer paso lógico fue un intento de encontrar una imagen más ligera, preferiblemente basada en alpino. Escaneé los repositorios Java más populares:
(11 como la versión actual de LTS y 8 ya que todavía hay una cantidad suficiente de aplicaciones que no podrían migrar a versiones más modernas)
Una tabla con imágenes y etiquetas (~ 2700), sus tamaños al momento de escribir está disponible aquí
Aquí hay algunos de ellos:
openjdk 8 488MB openjdk 8-slim 269MB openjdk 8-alpine 105MB openjdk 8-jdk-slim 269MB openjdk 8-jdk-alpine 105MB openjdk 8-jre 246MB openjdk 8-jre-slim 168MB openjdk 8-jre-alpine 84.9MB openjdk 11 604MB openjdk 11-slim 384MB openjdk 11-jdk 604MB openjdk 11-jdk-slim 384MB openjdk 11-jre 479MB openjdk 11-jre-slim 273MB adoptopenjdk/openjdk8 alpine 221MB adoptopenjdk/openjdk8 alpine-slim 89.7MB adoptopenjdk/openjdk8 jre 200MB adoptopenjdk/openjdk8 alpine-jre 121MB adoptopenjdk/openjdk11 alpine 337MB adoptopenjdk/openjdk11 alpine-slim 246MB adoptopenjdk/openjdk11 jre 218MB adoptopenjdk/openjdk11 alpine-jre 140MB
Por lo tanto, si cambia la imagen base a adoptopenjdk / openjdk11: alpine-jre, puede reducir la imagen con la aplicación a 177 mb.
Intento 2: tiempo de ejecución personalizado
Desde el lanzamiento de jdk9 y la modularización, fue posible construir su propio tiempo de ejecución que contiene solo los módulos que son necesarios para su aplicación. Puede leer más sobre esta funcionalidad aquí .
Intentemos determinar los módulos necesarios para la aplicación de arranque de prueba de primavera:
~/app ᐅ jdeps -s target/app-1.0.0.jar app-1.0.0.jar -> java.base app-1.0.0.jar -> java.logging app-1.0.0.jar -> not found
Ok, parece que jdeps no puede manejar el fat-jar creado con Spring Boot, pero podemos descomprimir el archivo y escribir el classpath:
~/app ᐅ jdeps -s -cp target/app-1.0.0/BOOT-INF/lib/*.jar target/app-1.0.0.jar.original Error: byte-buddy-1.9.12.jar is a multi-release jar file but --multi-release option is not set ~/app ᐅ jdeps -s --multi-release 11 -cp target/app-1.0.0/BOOT-INF/lib/*.jar target/app-1.0.0.jar.original Error: aspectjweaver-1.9.2.jar is not a multi-release jar file but --multi-release option is set
En este sentido, actualmente hay un error abierto: https://bugs.openjdk.java.net/browse/JDK-8207162
Intenté descargar jdk12 para obtener esta información, pero me encontré con el siguiente error:
Exception in thread "main" com.sun.tools.classfile.Dependencies$ClassFileError ... Caused by: com.sun.tools.classfile.ConstantPool$InvalidEntry: unexpected tag at #1: 53
Por prueba, error y búsqueda de módulo por ClassNotFoundException, determiné que mi aplicación necesita los siguientes módulos:
- java.base
- java.logging
- java.sql
- java.naming
- java.management
- java.instrument
- java.desktop
- java.security.jgss
Rantime para ellos se puede recolectar usando:
jlink --no-header-files --no-man-pages --compress=2 --strip-debug --add-modules java.base,java.logging,java.sql,java.naming,java.management,java.instrument,java.desktop,java.security.jgss --output /usr/lib/jvm/spring-boot-runtime
Intentemos construir una imagen de acoplador básica usando estos módulos:
FROM openjdk:11-jdk-slim RUN jlink --no-header-files --no-man-pages --compress=2 --strip-debug --add-modules java.base,java.logging,java.sql,java.naming,java.management,java.instrument,java.desktop,java.security.jgss --output /usr/lib/jvm/spring-boot-runtime FROM debian:stretch-slim COPY --from=0 /usr/lib/jvm/spring-boot-runtime /usr/lib/jvm/spring-boot-runtime RUN ln -s /usr/lib/jvm/spring-boot-runtime/bin/java /usr/bin/java
y recógelo:
docker build . -t spring-boot-runtime:openjdk-11-slim
Como resultado, el tamaño era de 106 megabytes, que es significativamente más pequeño que la mayoría de las imágenes base encontradas con openjdk. Si lo usa para mi aplicación, el tamaño resultante será de 144 megabytes.
Además, podemos usar spring-boot-runtime:openjdk-11-slim
como imagen base para todas las aplicaciones de arranque de primavera si tienen dependencias similares. En el caso de varias dependencias, es posible utilizar un ensamblaje de imagen de varias etapas para cada una de las aplicaciones en las que se recopilará el tiempo de ejecución de Java en la primera etapa, y el archivo con la aplicación se agregará en la segunda.
FROM openjdk:11-jdk-slim RUN jlink --no-header-files --no-man-pages --compress=2 --strip-debug --add-modules java.base,YOUR_MODULES --output /usr/lib/jvm/spring-boot-runtime FROM debian:stretch-slim COPY --from=0 /usr/lib/jvm/spring-boot-runtime /usr/lib/jvm/spring-boot-runtime WORKDIR /home/demo ARG REVISION COPY target/app-${REVISION}.jar app.jar ENTRYPOINT ["/usr/lib/jvm/spring-boot-runtime/bin/java","-jar","app.jar"]
Conclusión
Actualmente, la mayoría de las imágenes de Docker para Java tienen un volumen lo suficientemente grande, lo que puede afectar negativamente el tiempo de inicio de la aplicación, especialmente si las capas necesarias aún no están en el servidor. Usando etiquetas con jre o usando la modularización de java, puede construir su propio tiempo de ejecución, lo que reducirá significativamente el tamaño de la imagen de la aplicación.