Boa tarde
Recentemente, enfrentei a tarefa de iniciar aplicativos de boot 2 da primavera em um cluster kubernetes usando uma imagem do docker. Esse problema não é novo, encontrei rapidamente exemplos no Google e empacotei meu aplicativo. Fiquei muito surpreso ao não encontrar a imagem alpina do jdk11 e esperava que o fino fosse pequeno o suficiente, mas quando enviei a imagem para o registro do docker, notei que seu tamanho era quase 422 megabytes. Sob o gato, há uma descrição de como reduzi a imagem do docker com minha bota de mola e java de 11 a 144 megabytes.

App
Como mencionei anteriormente, meu aplicativo é criado usando a inicialização por mola 2 e é um wrapper da API REST sobre um banco de dados relacional (usando @RepositoryRestResource). Minhas dependências incluem:
org.springframework.boot:spring-boot-starter-data-rest org.springframework.boot:spring-boot-starter-data-jpa org.flywaydb:flyway-core org.postgresql:postgresql
O arquivo jar coletado tem um tamanho 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 da montagem, recebo uma imagem de tamanho: 422 mb de acordo com a saída do comando docker images. Curiosamente, ao usar a imagem 8-jdk-slim desatualizada, o tamanho é reduzido para 306 mb.
Tentativa 1: Outra imagem básica
O primeiro passo lógico foi uma tentativa de encontrar uma imagem mais leve, preferencialmente baseada em alpinos. Examinei os repositórios Java mais populares:
(11 como a versão atual do LTS e 8 como ainda há um número suficiente de aplicativos que não puderam migrar para versões mais modernas)
Uma tabela com imagens e tags (~ 2700), seus tamanhos no momento da escrita, está disponível aqui
Aqui estão alguns deles:
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
Portanto, se você alterar a imagem base para adoptopenjdk / openjdk11: alpine-jre, poderá reduzir a imagem com o aplicativo para 177 mb.
Tentativa 2: tempo de execução personalizado
Desde o lançamento do jdk9 e da modularização, tornou-se possível criar seu próprio tempo de execução que contém apenas os módulos necessários para o seu aplicativo. Você pode ler mais sobre esta funcionalidade aqui .
Vamos tentar determinar os módulos necessários para o aplicativo de inicialização da mola de teste:
~/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 o jdeps não pode lidar com o fat-jar criado com o spring boot, mas podemos descompactar o arquivo e escrever o caminho de classe:
~/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
Nesta ocasião, um bug está aberto no momento: https://bugs.openjdk.java.net/browse/JDK-8207162
Tentei fazer o download do jdk12 para obter essas informações, mas o seguinte erro ocorreu:
Exception in thread "main" com.sun.tools.classfile.Dependencies$ClassFileError ... Caused by: com.sun.tools.classfile.ConstantPool$InvalidEntry: unexpected tag at #1: 53
Por tentativa, erro e pesquisa de módulo por ClassNotFoundException, determinei que meu aplicativo precisa dos seguintes módulos:
- java.base
- java.logging
- java.sql
- java.naming
- java.management
- java.instrument
- java.desktop
- java.security.jgss
O tempo de espera para eles pode ser coletado 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
Vamos tentar criar uma imagem básica do docker usando estes 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
e colecione:
docker build . -t spring-boot-runtime:openjdk-11-slim
Como resultado, o tamanho era de 106 megabytes, o que é significativamente menor do que a maioria das imagens de base encontradas com o openjdk. Se você o usar no meu aplicativo, o tamanho resultante será 144 megabytes.
Além disso, podemos usar o spring-boot-runtime:openjdk-11-slim
como a imagem base para todos os aplicativos de inicialização do Spring se eles tiverem dependências semelhantes. No caso de várias dependências, é possível usar um conjunto de imagens de vários estágios para cada um dos aplicativos em que o Java Runtime será coletado no primeiro estágio, e o arquivo com o aplicativo será adicionado no segundo.
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"]
Conclusão
Atualmente, a maioria das imagens de janela de encaixe para java possui um volume grande o suficiente, o que pode afetar negativamente a hora de início do aplicativo, especialmente se as camadas necessárias ainda não estiverem no servidor. Usando tags com jre ou usando modularização java, é possível criar seu próprio tempo de execução, o que reduzirá significativamente o tamanho da imagem do aplicativo.