O Docker é uma plataforma que vem ganhando bastante adoção nas construção de ambientes dos sistemas, pois suas características de container que proporcionam construir e gerenciar ambientes de diferentes tipos(desenvolvimento, produção, testes, etc), de forma homogênea, automatizando instalações de sistema operacional, servidores, banco de dados e outras dependências necessárias para executar a aplicação.
Com isso, esse post tem como objetivo demonstrar como criar um ambiente de desenvolvimento composto por uma aplicação Spring Boot e o banco de dados Postgres, onde cada um roda em um container Docker.
Pré requisitos
Para realizar os passos desse post é preciso de Maven e Docker instalado.
Iniciando
Como mencionado anteriormente, o sistema é composto por duas partes em termos de infraestrutura: a aplicação e o banco de dados, dessa forma, o ambiente também será dividido em dois containers, como demonstrado na imagem abaixo, onde cada container pode ter as características mais apropriadas para sua execução.
Container 1 – Aplicação
O primeiro passo é criar o arquivo Dockerfile com as configurações do ambiente necessário para executar a aplicação, assim, o arquivo deve seguir basicamente a estrutura demonstrada abaixo:
Dockerfile
FROM openjdk:8-jdk-alpine VOLUME /tmp COPY target/spring-boot-docker-*.jar app.jar ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app.jar"]
- FROM: Configuração de qual imagem será utilizado como base, com isso na Docker hub é disponibilizado uma serie de imagens já prontas para serem utilizadas, como no exemplo a openjdk:8-jdk-alpine que já é uma imagem “slim”, com sistema operacional Debian e openJDK configurado na versão 8;
- VOLUME: Define um diretório para compartilhamento externo a imagem;
- ADD: Adiciona o artefato da aplicação para ser executado, passando dois parâmetros: a origem do arquivo e o destino do arquivo;
- ENTRYPOINT: Comando para executar o artefato adicionado dentro da imagem.
Observação: No atributo “ADD” a origem vai depender de onde se encontra o arquivo Dockerfile, no exemplo acima o arquivo está na raiz do projeto e o artefato gerado pelo maven se encontra dentro da pasta /target, que por padrão em projetos Spring Boot, são empacotados como .jar.
Observação: No atributo “ENTRYPOINT” o comando pode mudar conforme a imagem utilizada no “FROM”, nesse caso, com a imagem do openjdk foi utilizado o comando java.
Para simplificar a criação da imagem Docker em aplicações Java o Spotify disponibiliza um plugin Maven: dockerfile-maven-plugin, que abstrai os comandos docker para geração da imagem local e possibilita a criação da imagem em conjunto com o build da aplicação.
<plugin> <groupId>com.spotify</groupId> <artifactId>dockerfile-maven-plugin</artifactId> <version>1.4.2</version> <configuration> <repository>emmanuelneri/${project.artifactId}-app</repository> </configuration> </plugin>
Após configurado a imagem e adicionado o plugin, executar o comando abaixo para gerar o artefato da aplicação e construir a imagem docker da aplicação.
mvn clean package dockerfile:build
- mvn clean package: O mvn clean package apaga a pasta target e gera novamente um build da aplicação, ou seja, será criado um novo arquivo .jar dentro da pasta do Maven;
- dockerfile:build: Cria a imagem da aplicação com o nome definido no plugin dockerfile-maven-plugin na área de configuration -> repository e instala no docker local, assim sendo visível quando executado o comando
docker images
.
Caso não queira utilizar o plugin do Spotify, a imagem docker pode ser construída facilmente pelo comando build do docker executando o comando abaixo na pasta que se encontra o DockerFile:
docker build -t emmanuelneri/spring-boot-docker-app .
- docker build: Constrói a imagem a partir do Dockerfile no repositório local;
- -t (tag): Informe o nome da imagem.
Container 2 – Banco de dados
Diferente do container das aplicações, que possuem particularidades conforme a tecnologia, os containers de base de dados podem ser mais genéricos, dessa forma, podemos utilizar as imagens disponíveis no Docker Hub e apenas configurar para aplicação como no comando abaixo:
docker run -d \ --name docker-postgres \ -e POSTGRES_DB=db \ -e POSTGRES_USER=postgres \ -e POSTGRES_PASSWORD=postgres \ postgres:10.4
- docker run: Executa a imagem Docker;
- -d: Mantém a execução do container mesmo que fechado a janela (d = ativando Detached mode)
- -e: Define configuração e ambiente (e = environment) ;
- POSTGRES_DB: Configura o nome da base de dados, se não tiver será criado;
- POSTGRES_USER: Configura o usuário de acesso ao banco de dados;
- POSTGRES_PASSWORD: Configura a senha do usuário de acesso ao banco de dados;
- postgres:10.4: Imagem a ser executada, no caso a imagem oficial do Postgres, versão 10.4.
Depois de executado o container do Postgres é preciso configurar a URL de datasource de acesso ao banco de dados para o nome dado ao container da base de dados, então como foi definido docker-postgres no atributo –name da execução é esse o nome a ser inserido no arquivo de configuração”.
application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/db spring.datasource.username=postgres spring.datasource.password=postgres
Executando o ambiente
Feito a construção da imagem da aplicação e executado a base de dados, agora é só executar o container da aplicação com o seguinte comando.
docker run -it \ --link docker-postgres \ -p 8080:8080 \ emmanuelneri/spring-boot-docker-app
- docker run: Executa a imagem Docker;
- -it: Executa o container com o shell iterativo com o container (it = interactive )
- –link: Criar link com outros containers, no caso com o container da base de dados;
- -p: Configura a porta a ser executado o serviço;
- emmanuelneri/exemplo-docker-app : Executa a imagem construída com a aplicação..
Pronto, a aplicação está disponível no endereço http://localhost:8080 e com o comando docker ps
é possível listar os containers em execução.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3b7f0cfeceaf emmanuelneri/spring-boot-docker-app "java -Djava.securit…" 8 minutes ago Up 7 seconds 0.0.0.0:8080->8080/tcp springbootdocker_docker-app_1 7f01ce21cb11 postgres:10.4 "docker-entrypoint.s…" 8 minutes ago Up 7 seconds 5432/tcp springbootdocker_docker-postgres_1
Uma outra alternativa para executar esse cenário seria utilizando com Docker Compose, que é uma ferramenta do próprio Docker para execução de múltiplos containers. Assim, basta criar o arquivo docker-compose.yml com as configurações abaixo e executar o comando docker-compose up.
version: '3' services: docker-app: image: emmanuelneri/spring-boot-docker-app ports: - "8080:8080" depends_on: - docker-postgres docker-postgres: image: postgres:10.4 environment: - POSTGRES_DB=db - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres
- version: Versão do Docker Compose;
- services: Declaração dos serviços, no caso os dois containers docker-app e docker-postgres;
- image: Define a imagem utilizada para o serviço acima;
- ports: Define as portas que podem ser acessadas por fora do containers, mesmo comportamento do parâmetro -p;
- depends_on: Informa que o container depende de outro container, assim chamará a inicialização do container dependente por primeiro;
- environment: Possibilita a configurações das variáveis de ambiente da imagem, mesmo comportamento do parâmetro -e.
Bom é isso, o Docker é uma excelente solução de container para nos auxiliar na construção de ambientes para nossos sistemas, onde cada vez mais podemos criar e recriar nossos cenários com mais rapidez e facilidade, aplicando esses mesmos benefícios para a execução de aplicações com Spring Boot.
Um pouco mais…
Criando um profile específico para o Docker
Nem sempre vamos executar todo o tempo nossas aplicações no docker, com isso, o Spring boot oferece a possibilidade da criação de profiles, onde podemos criar um application-docker.properties específico para ser executado com os containers, como na configuração abaixo:
application-docker.properties
spring.datasource.url=jdbc:postgresql://docker-postgres:5432/db spring.datasource.username=postgres spring.datasource.password=postgres spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
E no Dockerfile configuramos o profile ativo com o parâmetro -Dspring.profiles.active
.
Dockerfile
FROM openjdk:8-jdk-alpine VOLUME /tmp COPY target/spring-boot-docker-*.jar app.jar ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-Dspring.profiles.active=docker", "-jar", "/app.jar"]
Um pouco mais sobre profiles no Spring boot pode ser visto neste post.
Consideração
Na configuração dos plugins, caso não estiver usando o spring-boot-starter-parent no pom da aplicação é preciso configurar spring-boot-maven-plugin para executar o repackage do plugin.
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin>
O código fonte do exemplo acima está disponível no <a href="https://github.com/emmanuelneri-blog/spring-boot-docker.
Muito bom, Neri.
Configurei um projeto seguindo esse tutorial e tudo certo.
Porém, adicionei testes unitários ao projeto e quando executo o comando “mvn clean package dockerfile:build” ele executa os testes. O problema é que o banco está em outra imagem e não sobre junto.
Alguma sugestão?
CurtirCurtir
Obrigado!! Raphael, nesse cenário tem duas opções: A primeira seria deixar o docker do banco sempre ligado e os testes referenciar o docker em execução(o que não seria muito bom, porque teria a premissa do docker do banco estar sempre em execução no momento de realizar o clean package) e a segunda alternativa, seria ir para uma abordagem de inicializar o docker na inicialização dos testes (há alguns frameworks para esse cenário como o docker-compose-rule (https://github.com/palantir/docker-compose-rule). Eu sugiro ir pela segundo abordagem que seria uma solução mais automatizada.
CurtirCurtir