Introdução
No artigo anterior, vimos o que é o Docker, como os contêineres são úteis como uma nova tendência de devOps e aprendemos a diferença entre contêineres e imagens. Também aprendemos sobre Dockerfile.
Agora, aprenderemos sobre o comando docker-compose
e como executar um aplicativo SpringBoot de um contêiner local. Mas, antes de fazer isso, precisamos empacotar um aplicativo SpringBoot com o Docker. Vamos ver como fazer.
Dockerizing um aplicativo SpringBoot
A primeira coisa que faremos é obter um aplicativo básico baseado em SpringBoot que podemos encaixar. Para esse propósito, podemos usar o aplicativo básico que vem com o andaime Spring ao usar o Spring Initializr no IntelliJ, e podemos adicionar um endpoint muito simples que retornará uma mensagem de saudação no formato JSON.
Não importa se você não tem o Spring Initializr, desde que você possa expor um endpoint e consultá-lo por meio do objeto de teste mockMvc ou de uma ferramenta como o Postman.
Para referência, aqui está nossa estrutura de pacote de aplicativo:
Você pode notar que o Dockerfile é colocado na pasta raiz de todo o projeto. Este será o arquivo usado para controlar todos os detalhes da nossa imagem, sua especificação.
Em seguida, o arquivo será usado para criar uma imagem Docker e, a partir dessa imagem, quando estiver efetivamente em execução, teremos nosso contêiner.
Para encaixar nosso aplicativo SpringBoot (ou qualquer aplicativo), é necessário especificar em nosso Dockerfile tudo o que precisamos para executar nosso aplicativo. Precisa ser, em essência, uma descrição em camadas de todas as etapas que tomaríamos para construir e executar nosso aplicativo, localmente, em um formato que pode ser:
- reproduzível: um Dockerfile deve sempre resultar na mesma imagem depois de executado e totalmente especificado. Qualquer pessoa que usar essa imagem sempre obterá exatamente a mesma funcionalidade.
- independente do ambiente: em essência, o poder do Docker depende do fato de que muitos serviços de infraestrutura e devOps agora o suportam em seus pipelines de desenvolvimento para que, sempre que um envio de novo código para um branch especificado acontecer, ele acione uma reconstrução completa de a imagem do Docker. Em seguida, os desenvolvedores podem usá-lo via
docker-compose
perfeitamente ou a imagem mais recente estará disponível em um registro para ser exposta como um micro-serviço para o mundo via nginx, etc.
Uma vez que a única coisa que realmente precisamos para iniciar o aplicativo SpringBoot após o término da compilação é o arquivo .jar
contendo o código compilado, contanto que usemos uma imagem Java base para construir a nossa, podemos envolver nosso aplicativo SpringBoot em um Contêiner do Docker definindo o ponto de entrada como executando o arquivo .jar
:
FROM openjdk:8
COPY target/greeter-*.jar greeter.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","greeter.jar"]
Portanto, construímos nossa imagem a partir de uma imagem base Java 8 openJDK e, em seguida, copiamos o arquivo JAR (que precisa existir em nossa máquina local no mesmo nível de pasta de destino e o próprio Dockerfile) de nosso sistema local para o diretório raiz do contêiner.
Em seguida, expomos a porta 8080 do contêiner para ficar disponível para o exterior. Esta é a mesma porta do serviço SpringBoot; caso contrário, o contêiner não saberá onde o serviço está sendo executado.
Por fim, chamamos o Java para iniciar nosso serviço a partir do JAR que acabamos de copiar para o contêiner de nossa máquina local. Isso significa que, ao iniciar um contêiner a partir da imagem construída por meio deste Dockerfile, iniciaremos efetivamente o aplicativo SpringBoot.
O comando docker-compose
O Dockerfile nos permite especificar o que é necessário para construir uma imagem.
docker-compose
é um comando que nos permite especificar e iniciar vários serviços compostos de muitas imagens Docker juntos, de forma coordenada, enquanto nos permite especificar qual imagem usar para qual serviço e garantir que os serviços sejam iniciados corretamente. Também nos permite definir as variáveis de ambiente que são expostas como tal no contêiner em execução.
Vamos supor que nosso aplicativo exige autenticação de nome de usuário e senha em nosso endpoint. Para definir essas variáveis, podemos escrever um arquivo docker-compose.yml
que colocamos no mesmo nível que o Dockerfile, e lá, podemos especificar de qual Dockerfile construir o contêiner, qual imagem usar (ambos para local cria e usa imagens remotas), que atende a composição de contêineres que usará e as variáveis de ambiente necessárias.
Um simples docker-compose.yml
poderia ser:
version: '3'
services:
web:
build: .
ports:
- "8080:8080"
environment:
- username="User"
- password="Pass"
Definimos um serviço chamado web que é construído a partir do Dockerfile no diretório atual, expõe a porta 8080 para o exterior e o serviço em execução no contêiner também está na porta 8080.
Em seguida, definimos as variáveis de ambiente disponíveis para nosso serviço, assim como definimos em um arquivo application.properties ou em um shell.
Para usar este arquivo para iniciar um contêiner, simplesmente fazemos:
docker-compose -f <caminho para compor o arquivo> -d up
Isso usará o arquivo de composição para construir e instanciar nosso contêiner e iniciar o serviço configurado com esse ambiente.
-d
garante que o contêiner continue em execução em segundo plano e o inicializa.
O Compose permite aos desenvolvedores extrair uma imagem localmente em suas máquinas, executá-la e testá-la localmente, o que é muito útil.
Conclusão
Vimos o que é docker-compose, como ajuda os desenvolvedores a testar certas funcionalidades e como isola questões em um ambiente controlado. Também vimos o que é necessário para empacotar um aplicativo SpringBoot com Docker.
Fonte: Intro to Docker 2- Docker-compose and packaging a SpringBoot application by Bruno Oliveira
Licença: Creative Commons -Attribution -ShareAlike 4.0 (CC-BY-SA 4.0)