使用 multi-stage builds 优化 Docker 镜像
Source: Dev.to
Fala, devs! Hoje vim compartilhar com vocês uma dica de como reduzir o tamanho das suas imagens Docker em projetos Java + Maven: multi‑stage builds!
O problema que eu tinha
No início do meu aprendizado com Docker, era assim que eu construía minhas imagens:
FROM maven:3.9.10-eclipse-temurin-21
WORKDIR /app
COPY . .
RUN mvn clean package -DskipTests
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "target/app.jar"]
O problema é que essas poucas linhas geram uma imagem de mais de 600 MB! Isso acontece porque muitas coisas que não são necessárias em tempo de execução acabam sendo incluídas na imagem final.
A solução
Multi‑stage builds permitem usar múltiplas instruções FROM no Dockerfile, cada uma com uma base diferente, e compartilhar artefatos entre os estágios. Assim, podemos escolher apenas o que for necessário para a execução, reduzindo o tamanho da imagem final.
Um Dockerfile para um projeto Maven com Java 21 usando multi‑stage fica assim:
# ---------- Build stage ----------
FROM maven:3.9.10-eclipse-temurin-21-alpine AS build
WORKDIR /app
# Copia apenas o pom.xml e resolve dependências (cacheável)
COPY pom.xml .
RUN mvn dependency:go-offline -B
# Copia o código fonte e compila
COPY src ./src
RUN mvn clean package -DskipTests
# ---------- Runtime stage ----------
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
# Copia apenas o JAR gerado no estágio de build
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Entendendo cada parte
Processo de build
FROM maven:3.9.10-eclipse-temurin-21-alpine AS build
Utiliza a imagem do Maven com JDK 21 baseada em Alpine (mais leve) e nomeia o estágio como build。
COPY pom.xml .
RUN mvn dependency:go-offline -B
Copiamos apenas o pom.xml e baixamos as dependências. Se o pom.xml não mudar, essa camada fica em cache, acelerando builds subsequentes。
COPY src ./src
RUN mvn clean package -DskipTests
Depois copiamos o código fonte e compilamos o projeto, pulando os testes。
A cartada final
FROM eclipse-temurin:21-jre-alpine
Usamos uma imagem Alpine contendo apenas o JRE (não o JDK), suficiente para executar a aplicação。
COPY --from=build /app/target/*.jar app.jar
Copiamos apenas o JAR gerado no estágio build para a nova imagem, descartando tudo o que não é necessário em tempo de execução。
O resultado
Com esses ajustes, uma imagem que antes tinha cerca de 800 MB passa para aproximadamente 280 MB, uma redução de quase 65 %。
$ docker images
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
multi-stage-build:latest 339813c13178 283MB 73.3MB
single-stage-build:latest 0aadd77f8d9c 834MB 258MB
E por hoje é isso. Espero que esse exemplo tenha te ajudado. 🚀