03 Docker com Aplicações Spring Boot - KleberVales/Dominando-Docker GitHub Wiki

1. Dockerizing a Spring Boot application

2. Creating an efficient Dockerfile for Java

3. Using docker-compose with a database (MySQL, PostgreSQL)

4. Environment variables and configuration profiles (application-dev.yml, application-prod.yml)

5. Testing and debugging in Java containers

6. Integration with Testcontainers for automated testing


||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||


Dockerizando uma aplicação Spring Boot

Dockerizar uma aplicação Spring Boot significa empacotá-la em um container para facilitar a execução, distribuição e escalabilidade. Isso elimina problemas de compatibilidade e garante que a aplicação rode de forma consistente em qualquer ambiente.

Pré-requisitos

Ter o Docker instalado.

Uma aplicação Spring Boot já construída (geralmente um .jar gerado pelo Maven ou Gradle).

Familiaridade com o terminal.

Estrutura do projeto

Após compilar sua aplicação, você terá algo como:

/meu-app
 ├── target/
 │    └── meu-app-0.0.1-SNAPSHOT.jar
 ├── src/
 └── Dockerfile

Criando o Dockerfile

O Dockerfile define como a imagem será construída.

Exemplo básico:

# Usando imagem base do OpenJDK
FROM openjdk:17-jdk-slim

# Definindo diretório de trabalho
WORKDIR /app

# Copiando o JAR para dentro do container
COPY target/meu-app-0.0.1-SNAPSHOT.jar app.jar

# Expondo a porta padrão do Spring Boot
EXPOSE 8080

# Comando para rodar a aplicação
ENTRYPOINT ["java", "-jar", "app.jar"]

Construindo a imagem

No diretório do projeto, execute:

docker build -t meu-springboot-app:1.0 .
  • -t → nome e versão da imagem.

  • . → indica que o Dockerfile está no diretório atual.

Executando o container

docker run -d -p 8080:8080 --name springboot-container meu-springboot-app:1.0
  • -p 8080:8080 → mapeia a porta do container para o host.

  • --name → dá um nome ao container.

Agora, acesse no navegador:

http://localhost:8080

Otimizando a imagem (multi-stage build)

Para reduzir o tamanho da imagem, podemos usar multi-stage builds:

# Etapa 1: build da aplicação
FROM maven:3.9.6-eclipse-temurin-17 AS build
WORKDIR /app
COPY . .
RUN mvn clean package -DskipTests

# Etapa 2: imagem final
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY --from=build /app/target/meu-app-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Publicando a imagem em um registry

Depois de dockerizar, você pode enviar sua imagem para um registry (Docker Hub, GitHub ou AWS ECR):

docker tag meu-springboot-app:1.0 usuario/meu-springboot-app:1.0
docker push usuario/meu-springboot-app:1.0

Criando um Dockerfile eficiente para Java

Um Dockerfile eficiente para aplicações Java deve equilibrar performance, tamanho da imagem e facilidade de manutenção. A ideia é evitar imagens pesadas, reduzir tempo de build e garantir que a aplicação rode de forma consistente em qualquer ambiente.

Estrutura basica de um Dockerfile

  • FROM

Define a imagem base. Exemplo:

FROM ubuntu:22.04
  • LABEL

Adiciona metadados à imagem, como autor ou versão.

LABEL maintainer="[email protected]"
  • RUN

Executa comandos durante a construção da imagem.

RUN apt-get update && apt-get install -y python3
  • COPY / ADD

Copia arquivos do host para dentro da imagem.

COPY app.py /usr/src/app/
  • WORKDIR

Define o diretório de trabalho dentro da imagem.

WORKDIR /usr/src/app
  • ENV

Define variáveis de ambiente.

ENV PORT=5000
  • EXPOSE

Indica a porta que o container vai usar.

EXPOSE 5000
  • CMD

Define o comando padrão que será executado quando o container iniciar.

CMD ["python3", "app.py"]
  • ENTRYPOINT

Similar ao CMD, mas fixa o comando principal, permitindo argumentos adicionais.

ENTRYPOINT ["python3", "app.py"]

Boas práticas gerais

  • Usar imagens oficiais e leves (como openjdk:17-jdk-slim ou eclipse-temurin).

  • Multi-stage builds para separar o processo de compilação da execução.

  • Evitar copiar arquivos desnecessários (usar .dockerignore).

  • Definir variáveis de ambiente para configuração.

  • Expor apenas as portas necessárias.

Exemplo básico de Dockerfile para Java

# Imagem base leve
FROM openjdk:17-jdk-slim

# Diretório de trabalho
WORKDIR /app

# Copiar o JAR da aplicação
COPY target/minha-app.jar app.jar

# Expor porta padrão
EXPOSE 8080

# Comando de inicialização
ENTRYPOINT ["java", "-jar", "app.jar"]

Esse exemplo é simples, mas gera uma imagem maior porque o JAR já precisa estar pronto.

Dockerfile otimizado com multi-stage build

# Etapa 1: Build da aplicação
FROM maven:3.9.6-eclipse-temurin-17 AS build
WORKDIR /app

# Copiar código e dependências
COPY pom.xml .
COPY src ./src

# Compilar e empacotar
RUN mvn clean package -DskipTests

# Etapa 2: Imagem final
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app

# Copiar apenas o JAR gerado
COPY --from=build /app/target/minha-app.jar app.jar

EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Vantagens:

A imagem final contém apenas o JRE e o JAR da aplicação.

O Maven e dependências de build não ficam na imagem final.

Redução significativa do tamanho da imagem.

Usando .dockerignore

Crie um arquivo .dockerignore para evitar copiar arquivos desnecessários:

target/
.git
README.md
*.log

Isso mantém a imagem mais limpa e rápida de construir.

Ajustes de performance

  • Heap size configurável:
ENV JAVA_OPTS="-Xms512m -Xmx1024m"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
  • Camadas otimizadas: copie primeiro arquivos que mudam menos (como pom.xml) para aproveitar cache de build.

  • Imagens base Alpine: reduzem tamanho, mas podem exigir ajustes em bibliotecas nativas.

Casos de uso

Aplicações web Spring Boot → multi-stage build com Maven.

Microserviços leves → imagens base alpine para reduzir footprint.

Ambientes corporativos → uso de registries privados e tags versionadas (1.0.0, prod, dev).

Usando docker-compose com banco de dados (MySQL, PostgreSQL)

O docker-compose facilita a orquestração de múltiplos containers, permitindo definir serviços (como aplicação e banco de dados) em um único arquivo YAML. Isso é especialmente útil para rodar aplicações que dependem de bancos como MySQL ou PostgreSQL.

O que é docker-compose?

Ferramenta para definir e gerenciar ambientes multi-container.

Usa um arquivo docker-compose.yml para descrever serviços, redes e volumes.

Permite subir todo o ambiente com um único comando:

docker-compose up -d

Exemplo com PostgreSQL

version: '3.8'

services:
  app:
    image: openjdk:17-jdk-slim
    container_name: minha-app
    working_dir: /app
    volumes:
      - ./target:/app
    command: ["java", "-jar", "meu-app.jar"]
    ports:
      - "8080:8080"
    depends_on:
      - postgres

  postgres:
    image: postgres:15
    container_name: meu-postgres
    environment:
      POSTGRES_USER: usuario
      POSTGRES_PASSWORD: senha
      POSTGRES_DB: minha_base
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

O que acontece aqui:

O serviço postgres cria automaticamente o banco minha_base.

Usuário e senha são configurados via variáveis de ambiente.

Os dados ficam persistidos no volume postgres_data.

Comandos úteis

Subir os serviços:

docker-compose up -d

Parar os serviços:

docker-compose down

Ver logs de um serviço específico:

docker-compose logs -f app

Boas práticas

  • Usar volumes para persistência de dados.

  • Definir variáveis sensíveis em arquivos .env (não direto no YAML).

  • Versionar o docker-compose.yml junto ao código da aplicação.

  • Usar depends_on para garantir ordem de inicialização.

Variáveis de ambiente e perfis de configuração (application-dev.yml, application-prod.yml)

Em aplicações modernas, especialmente com Spring Boot, é fundamental separar configurações de acordo com o ambiente (desenvolvimento, testes, produção). Isso garante flexibilidade, segurança e facilidade de manutenção.

O que são variáveis de ambiente?

São valores externos que podem ser usados pela aplicação sem precisar alterar o código.

Permitem configurar credenciais, URLs de banco de dados, portas, entre outros.

Evitam expor informações sensíveis em arquivos versionados.

Exemplo de uso no Docker:

environment:
  SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/minha_base
  SPRING_DATASOURCE_USERNAME: usuario
  SPRING_DATASOURCE_PASSWORD: senha

Perfis de configuração no Spring Boot

O Spring Boot permite definir perfis (dev, test, prod) para carregar configurações específicas.

  • Arquivo padrão: application.yml ou application.properties.

  • Perfis: application-dev.yml, application-prod.yml.

Exemplo application.yml (arquivo base):

spring:
  profiles:
    active: dev

Exemplo application-dev.yml:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/dev_db
    username: dev_user
    password: dev_pass
  jpa:
    hibernate:
      ddl-auto: update

Exemplo application-prod.yml:

spring:
  datasource:
    url: jdbc:postgresql://postgres:5432/prod_db
    username: prod_user
    password: prod_pass
  jpa:
    hibernate:
      ddl-auto: none

Ativando perfis

  • Via variável de ambiente:
export SPRING_PROFILES_ACTIVE=prod
  • Via linha de comando:
java -jar minha-app.jar --spring.profiles.active=prod
  • Via docker-compose:
environment:
  SPRING_PROFILES_ACTIVE: prod

Boas práticas

  • Nunca versionar credenciais sensíveis → use variáveis de ambiente ou serviços de segredos (Vault, AWS Secrets Manager).

  • Separar ambientes claramente → cada perfil deve ter seu próprio arquivo.

  • Automatizar seleção de perfil → pipelines de CI/CD podem definir o perfil correto.

  • Manter consistência → nomes de variáveis e perfis devem seguir padrão (dev, staging, prod).


Testes e debug em containers Java

Rodar aplicações Java dentro de containers é ótimo para padronizar ambientes, mas quando chega a hora de testar e depurar (debug), precisamos de estratégias específicas para não perder visibilidade do que acontece dentro do container.

Estratégias de Testes em Containers

  • Testes unitários e de integração:

    • Devem ser executados antes de empacotar a aplicação no container (via Maven/Gradle).

    • Exemplo:

mvn test
  • Testes de integração com banco de dados:

    • Use docker-compose para subir aplicação + banco e rodar testes contra esse ambiente.
  • Testcontainers:

    • Biblioteca Java que cria containers temporários para testes automatizados.

    • Exemplo: subir um PostgreSQL em container apenas durante os testes.

Debugging em Containers Java

1. Logs

Capturar logs da aplicação:

docker logs -f meu-container

Redirecionar logs para arquivos montados em volumes, facilitando análise.

2. Acesso ao container

Entrar no container para inspecionar arquivos ou processos:

docker exec -it meu-container bash

3. Debug remoto (JDWP)

Para depurar código Java rodando dentro de um container, habilite o Java Debug Wire Protocol (JDWP):

No Dockerfile:

ENTRYPOINT ["java", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005", "-jar", "app.jar"]

No docker-compose.yml:

ports:
  - "8080:8080"
  - "5005:5005"

Integração com Testcontainers para testes automatizados

O Testcontainers é uma biblioteca Java que permite criar e gerenciar containers Docker diretamente nos testes automatizados. Ele é amplamente usado em projetos Spring Boot e Java para rodar testes de integração com bancos de dados, mensageria e outros serviços sem precisar configurar manualmente o ambiente.

O que é Testcontainers?

Biblioteca que cria containers temporários durante a execução dos testes.

Permite rodar serviços reais (MySQL, PostgreSQL, Kafka, Redis, etc.) sem depender de instalações locais.

Containers são iniciados e destruídos automaticamente, garantindo isolamento e repetibilidade.

Dependência Maven/Gradle

Maven:

<dependency>
  <groupId>org.testcontainers</groupId>
  <artifactId>testcontainers</artifactId>
  <version>1.20.0</version>
  <scope>test</scope>
</dependency>

<dependency>
  <groupId>org.testcontainers</groupId>
  <artifactId>postgresql</artifactId>
  <version>1.20.0</version>
  <scope>test</scope>
</dependency>

Gradle:

testImplementation 'org.testcontainers:testcontainers:1.20.0'
testImplementation 'org.testcontainers:postgresql:1.20.0'

Exemplo com PostgreSQL

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.testcontainers.containers.PostgreSQLContainer;

@SpringBootTest
class MeuTeste {

    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
            .withDatabaseName("testdb")
            .withUsername("usuario")
            .withPassword("senha");

    static {
        postgres.start();
        System.setProperty("spring.datasource.url", postgres.getJdbcUrl());
        System.setProperty("spring.datasource.username", postgres.getUsername());
        System.setProperty("spring.datasource.password", postgres.getPassword());
    }

    @Test
    void contextoCarregado() {
        // Aqui você pode rodar testes que usam o banco real em container
    }
}
⚠️ **GitHub.com Fallback** ⚠️