Como usar um Dockerfile multi-stage para empacotar aplicações Java EE

DockerBeginner
Pratique Agora

Introdução

Os Dockerfiles multi-stage do Docker oferecem uma solução poderosa para otimizar o processo de empacotamento de aplicações Java EE. Este tutorial guiará você pelas etapas para construir e otimizar sua aplicação Java EE usando um Dockerfile multi-stage, garantindo um deploy e distribuição eficientes.

Compreendendo Dockerfiles Multi-Stage

Os Dockerfiles multi-stage são um recurso poderoso introduzido no Docker 17.05 que permite criar imagens Docker otimizadas usando múltiplos estágios num único Dockerfile. Esta abordagem ajuda a reduzir o tamanho final da imagem e a melhorar o processo de construção, separando o ambiente de construção do ambiente de execução.

O que é um Dockerfile Multi-Stage?

Um Dockerfile multi-stage é um Dockerfile que contém múltiplas instruções FROM, cada uma representando um estágio diferente no processo de construção. Cada estágio pode usar uma imagem base diferente, e o estágio final é usado para criar a imagem Docker final.

graph TD
    A[Imagem Base] --> B[Estágio de Construção]
    B --> C[Estágio de Execução]
    C --> D[Imagem Final]

Benefícios do Uso de Dockerfiles Multi-Stage

  1. Redução do Tamanho da Imagem: Separando o ambiente de construção do ambiente de execução, você pode criar uma imagem Docker final menor, contendo apenas os componentes necessários para a execução da sua aplicação.
  2. Melhoria do Processo de Construção: Os Dockerfiles multi-stage podem simplificar o processo de construção, permitindo que você utilize imagens base diferentes para diferentes estágios, reduzindo a necessidade de scripts de construção complexos ou Dockerfiles separados.
  3. Aumento da Segurança: Ao reduzir o tamanho da imagem final, você pode minimizar a superfície de ataque e reduzir o risco de vulnerabilidades de segurança.

Como Usar Dockerfiles Multi-Stage

Para usar um Dockerfile multi-stage, você precisa definir múltiplas instruções FROM no seu Dockerfile, cada uma representando um estágio diferente. O estágio final é usado para criar a imagem Docker final.

## Estágio de Construção
FROM maven:3.8.2-openjdk-11 AS build
COPY . /app
WORKDIR /app
RUN mvn clean package

## Estágio de Execução
FROM openjdk:11-jdk-slim
COPY --from=build /app/target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

Neste exemplo, o primeiro estágio utiliza a imagem maven:3.8.2-openjdk-11 para construir a aplicação Java EE, e o segundo estágio utiliza a imagem openjdk:11-jdk-slim para criar a imagem Docker final, que contém apenas os componentes de execução necessários.

Construindo uma Aplicação Java EE com Dockerfiles Multi-Stage

Nesta seção, vamos percorrer o processo de construção de uma aplicação Java EE utilizando um Dockerfile multi-stage.

Preparando a Aplicação Java EE

Vamos assumir que você possui uma aplicação Java EE que deseja empacotar usando um Dockerfile multi-stage. A aplicação está estruturada da seguinte forma:

my-java-ee-app/
├── pom.xml
└── src/
    ├── main/
    │   ├── java/
    │   │   └── com/
    │   │       └── example/
    │   │           └── MyApp.java
    │   └── webapp/
    │       └── WEB-INF/
    │           └── web.xml
    └── test/
        └── java/
            └── com/
                └── example/
                    └── MyAppTest.java

Criando o Dockerfile Multi-Stage

Aqui está um exemplo de um Dockerfile multi-stage para construir e empacotar a aplicação Java EE:

## Estágio de Construção
FROM maven:3.8.2-openjdk-11 AS build
COPY . /app
WORKDIR /app
RUN mvn clean package

## Estágio de Execução
FROM tomcat:9.0-jdk11-openjdk-slim
COPY --from=build /app/target/*.war /usr/local/tomcat/webapps/app.war

Neste Dockerfile, temos dois estágios:

  1. Estágio de Construção: Este estágio utiliza a imagem maven:3.8.2-openjdk-11 para construir a aplicação Java EE. Os comandos COPY e RUN são usados para copiar o código-fonte da aplicação e construir a aplicação usando Maven.
  2. Estágio de Execução: Este estágio utiliza a imagem tomcat:9.0-jdk11-openjdk-slim como imagem base. O comando COPY --from=build é usado para copiar o arquivo WAR construído do estágio anterior para o diretório webapps do Tomcat.

Construindo e Executando a Imagem Docker

Para construir a imagem Docker, execute o seguinte comando no diretório que contém o Dockerfile:

docker build -t my-java-ee-app .

Após a construção da imagem, você pode executar o contêiner:

docker run -p 8080:8080 my-java-ee-app

Isso iniciará o servidor Tomcat e implantará a aplicação Java EE. Você poderá então acessar a aplicação em http://localhost:8080/app.

Otimizando Dockerfiles Multi-Stage para Aplicações Java EE

Ao construir aplicações Java EE com Dockerfiles multi-stage, existem várias técnicas de otimização que podem ser usadas para reduzir ainda mais o tamanho da imagem Docker final e melhorar o processo de construção.

Aproveitando as Dependências de Construção

Uma otimização comum é aproveitar as dependências de construção apenas no estágio de construção e excluí-las do estágio de execução final. Por exemplo, você pode usar uma imagem de construção separada que inclui todas as ferramentas de construção necessárias, como uma imagem Maven ou Gradle, e, em seguida, copiar os artefatos construídos para uma imagem de execução menor.

## Estágio de Construção
FROM maven:3.8.2-openjdk-11 AS build
COPY . /app
WORKDIR /app
RUN mvn clean package

## Estágio de Execução
FROM openjdk:11-jdk-slim
COPY --from=build /app/target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

Utilizando o Cache Multi-Stage

O recurso de construção multi-stage do Docker também permite aproveitar o cache de construção para acelerar as construções subsequentes. Ao organizar seu Dockerfile de forma a maximizar a reutilização do cache, você pode reduzir significativamente o tempo de construção.

## Estágio Base
FROM maven:3.8.2-openjdk-11 AS base
WORKDIR /app

## Copiar dependências
COPY pom.xml .
RUN mvn dependency:go-offline

## Estágio de Construção
FROM base AS build
COPY . .
RUN mvn clean package

## Estágio de Execução
FROM openjdk:11-jdk-slim
COPY --from=build /app/target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

Neste exemplo, o arquivo pom.xml é copiado e as dependências são baixadas no estágio base. Isso garante que a etapa de resolução de dependências seja armazenada em cache, acelerando as construções subsequentes.

Otimizando a Imagem de Execução

Outra técnica de otimização é usar uma imagem base menor para o estágio de execução. Por exemplo, você pode usar a imagem openjdk:11-jdk-slim em vez da imagem completa openjdk:11-jdk, o que pode reduzir significativamente o tamanho da imagem final.

## Estágio de Execução
FROM openjdk:11-jdk-slim
COPY --from=build /app/target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

Além disso, você pode usar técnicas como builds multi-stage, imagens baseadas em Alpine ou imagens distroless para otimizar ainda mais o tamanho da imagem de execução.

Aplicando essas técnicas de otimização, você pode criar imagens Docker altamente otimizadas para suas aplicações Java EE, reduzindo o tamanho da imagem e melhorando o processo de construção.

Resumo

Ao final deste tutorial, você terá um sólido entendimento de como aproveitar Dockerfiles multi-stage para empacotar suas aplicações Java EE de forma eficaz. Você aprenderá técnicas para otimizar o processo de construção, reduzir o tamanho da imagem e criar um pipeline de implantação confiável e reproduzível usando Docker. Este conhecimento o capacitará a aprimorar a eficiência e a escalabilidade das implantações de sua aplicação Java EE.