Dockerizing um aplicativo de inicialização Spring
1. Visão geral
Neste artigo, vamos nos concentrar em como encaixar umSpring Boot Application para executá-lo em um ambiente isolado, a.k.a. container.
Além disso, mostraremos como criar uma composição de contêineres, que dependem uns dos outros e estão interligados em uma rede privada virtual. Também veremos como eles podem ser gerenciados em conjunto com comandos únicos.
Vamos começar criando uma imagem base leve habilitada para Java, executandohttps://hub.docker.com//alpine/[_Alpine Linux].
2. Imagem de Base Comum
Estaremos usando o formato de arquivo de construção do próprioDocker’s: aDockerfile.
ADockerfile é, em princípio, um arquivo de lote linewise, contendo comandos para construir uma imagem. Não é absolutamente necessário colocar esses comandos em um arquivo, porque podemos passá-los para a linha de comando também - um arquivo é simplesmente mais conveniente.
Então, vamos escrever nosso primeiroDockerfile:
FROM alpine:edge
MAINTAINER example.com
RUN apk add --no-cache openjdk8
COPY files/UnlimitedJCEPolicyJDK8/* \
/usr/lib/jvm/java-1.8-openjdk/jre/lib/security/
-
FROM: A palavra-chaveFROM diz aDocker para usar uma determinada imagem com sua tag como base de construção. Se esta imagem não estiver na biblioteca local, uma pesquisa online emDockerHub, ou em qualquer outro registro remoto configurado, é realizada
-
MAINTAINER: UmMAINTAINER é geralmente um endereço de e-mail, identificando o autor de uma imagem
-
RUN: com o comandoRUN, estamos executando uma linha de comando shell dentro do sistema de destino. Aqui, estamos utilizando o gerenciador de pacotesAlpine Linux’sapk para instalar oJava 8 OpenJDK
-
COPY: O último comando informaDocker aCOPY alguns arquivos do sistema de arquivos local, especificamente uma subpasta para o diretório de construção, na imagem em um determinado caminho
REQUIREMENTS: Para executar o tutorial com sucesso, você deve baixarJava Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files deOracle. Simplesmente extraia o arquivo baixado em uma pasta local chamada‘files'.
Para finalmente construir a imagem e armazená-la na biblioteca local, precisamos executar:
docker build --tag=alpine-java:base --rm=true .
NOTICE: A opção–tag dará à imagem seu nome e–rm=true removerá as imagens intermediárias depois que ela tiver sido construída com sucesso. O último caractere neste comando shell é um ponto, atuando como um argumento de construção de diretório.
3. Encaixe um aplicativo de inicialização Spring independente
Como exemplo de um aplicativo que podemos encaixar, pegaremosspring-cloud-config/server despring cloud configuration tutorial. Como uma etapa de preparação, temos que montar um arquivo jar executável e copiá-lo para nosso diretório de compilaçãoDocker:
tutorials $> cd spring-cloud-config/server
server $> mvn package spring-boot:repackage
server $> cp target/server-0.0.1-SNAPSHOT.jar \
../../spring-boot-docker/files/config-server.jar
server $> cd ../../spring-boot-docker
Agora vamos criar umDockerfile chamadoDockerfile.server com o seguinte conteúdo:
FROM alpine-java:base
MAINTAINER example.com
COPY files/spring-cloud-config-server.jar /opt/spring-cloud/lib/
COPY files/spring-cloud-config-server-entrypoint.sh /opt/spring-cloud/bin/
ENV SPRING_APPLICATION_JSON= \
'{"spring": {"cloud": {"config": {"server": \
{"git": {"uri": "/var/lib/spring-cloud/config-repo", \
"clone-on-start": true}}}}}}'
ENTRYPOINT ["/usr/bin/java"]
CMD ["-jar", "/opt/spring-cloud/lib/spring-cloud-config-server.jar"]
VOLUME /var/lib/spring-cloud/config-repo
EXPOSE 8888
-
FROM: Como base para nossa imagem, tomaremos oJava-enabledAlpine Linux, criado na seção anterior
-
COPY: deixamosDocker copiar nosso arquivo jar para a imagem
-
ENV: este comando permite definir algumas variáveis de ambiente, que serão respeitadas pela aplicação em execução no container. Aqui, definimos umSpring Boot Application configuration personalizado, para entregar ao executável jar posteriormente
-
ENTRYPOINT/CMD: This will be the executable to start when the container is booting. Devemos defini-los comoJSON-Array, porque usaremos umENTRYPOINT em combinação com umCMD para alguns argumentos do aplicativo
-
VOLUME: Como nosso contêiner será executado em um ambiente isolado, sem acesso direto à rede, temos que definir um ponto de montagem para o nosso repositório de configuração
-
EXPOSE: aqui estamos informandoDocker, em qual porta nosso aplicativo está listado. Essa porta será publicada no host quando o contêiner estiver inicializando
Para criar uma imagem de nossoDockerfile, temos que executar‘docker build', como antes:
$> docker build --file=Dockerfile.server \
--tag=config-server:latest --rm=true .
Mas antes de executar um contêiner a partir de nossa imagem, temos que criar um volume para montagem:
$> docker volume create --name=spring-cloud-config-repo
NOTICE: Embora um contêiner seja imutável, quando não confirmado para uma imagem após a saída do aplicativo, os dados armazenados em um volume serão persistentes em vários contêineres.
Finalmente, somos capazes de executar o contêiner a partir de nossa imagem:
$> docker run --name=config-server --publish=8888:8888 \
--volume=spring-cloud-config-repo:/var/lib/spring-cloud/config-repo \
config-server:latest
-
Primeiro, temos que–name nosso contêiner. Caso contrário, um será escolhido automaticamente
-
Então, devemos–publish nossa porta exposta (consulteDockerfile) para uma porta em nosso host. O valor é fornecido na forma‘host-port:container-port'. Se apenas uma porta de contêiner for fornecida, uma porta de host escolhida aleatoriamente será usada. Se deixarmos essa opção de fora, o contêiner será completamente isolado
-
A opção–volume dá acesso a um diretório no host (quando usado com um caminho absoluto) ou a um volumeDocker criado anteriormente (quando usado comvolume-name). O caminho após os dois pontos especifica omountpoint dentro do contêiner
-
Como argumento, temos que dizer aDocker, qual imagem usar. Aqui temos que darimage-name da etapa anterior de ‘docker build’
-
Algumas opções mais úteis:
-
-it - ativa o modo interativo e aloca umpseudo-tty
-
-d - desconectar do contêiner após a inicialização
-
Se executamos o contêiner no modo desanexado, podemos inspecionar seus detalhes, pará-lo e removê-lo com os seguintes comandos:
$> docker inspect config-server
$> docker stop config-server
$> docker rm config-server
4. Dockerize aplicativos dependentes em um composto
Os comandosDocker eDockerfiles são particularmente adequados para a criação de contêineres individuais. Mas se você quiseroperate on a network of isolated applications, o gerenciamento do contêiner ficará rapidamente desordenado.
To solve that, Docker provides a tool named Docker Compose. Isso vem com um arquivo de construção próprio no formatoYAML e é mais adequado para gerenciar vários contêineres. Por exemplo: é capaz de iniciar ou parar uma composição de serviços em um comando ou mesclar a saída de log de vários serviços em umpseudo-tty.
Vamos construir um exemplo de dois aplicativos em execução em diferentes contêineres do docker. Eles se comunicarão e serão apresentados como "unidade única" ao sistema host. Vamos construir e copiar o exemplospring-cloud-config/client descrito emspring cloud configuration tutorial para nossa pastafiles, como fizemos antes comconfig-server.
Este será o nossodocker-compose.yml:
version: '2'
services:
config-server:
container_name: config-server
build:
context: .
dockerfile: Dockerfile.server
image: config-server:latest
expose:
- 8888
networks:
- spring-cloud-network
volumes:
- spring-cloud-config-repo:/var/lib/spring-cloud/config-repo
logging:
driver: json-file
config-client:
container_name: config-client
build:
context: .
dockerfile: Dockerfile.client
image: config-client:latest
entrypoint: /opt/spring-cloud/bin/config-client-entrypoint.sh
environment:
SPRING_APPLICATION_JSON: \
'{"spring": {"cloud": \
{"config": {"uri": "http://config-server:8888"}}}}'
expose:
- 8080
ports:
- 8080:8080
networks:
- spring-cloud-network
links:
- config-server:config-server
depends_on:
- config-server
logging:
driver: json-file
networks:
spring-cloud-network:
driver: bridge
volumes:
spring-cloud-config-repo:
external: true
-
version: Especifica qual versão de formato deve ser usada. Este é um campo obrigatório. Aqui usamos a versão mais recente, enquantolegacy format é '1'
-
services: cada objeto nesta chave define um contêinerservice, a.k.a. Esta seção é obrigatória
-
build: se fornecido,docker-compose é capaz de construir uma imagem a partir de umDockerfile
-
context: se fornecido, especifica o diretório de construção, onde oDockerfile é pesquisado
-
dockerfile: se fornecido, define um nome alternativo para umDockerfile
-
-
image: Diz aDocker qual nome deve ser dado à imagem quando recursos de construção são usados. Caso contrário, ele está procurando por esta imagem na biblioteca ouremote-registry
-
networks: este é o identificador das redes nomeadas a serem usadas. Um determinadoname-value deve ser listado na seçãonetworks
-
volumes: isto identifica os volumes nomeados para usar e os pontos de montagem para montar os volumes, separados por dois pontos. Da mesma forma na seçãonetworks, umvolume-name deve ser definido em uma seçãovolumes separada
-
links: Isso criará um link de rede interna entre o serviçothis e o serviço listado. O serviçoThis será capaz de se conectar ao serviço listado, onde a parte antes dos dois pontos especifica umservice-name da seçãoservices e a parte após os dois pontos especifica o nome do host no qual o serviço está escutando em uma porta exposta
-
depends_on: Isso diz aDocker para iniciar um serviço apenas, se os serviços listados foram iniciados com sucesso. NOTICE: Isso funciona apenas no nível do contêiner! Para uma solução alternativa para iniciar o dependenteapplication primeiro, consulteconfig-client-entrypoint.sh
-
logging: Aqui estamos usando o driver‘json-file', que é o padrão. Alternativamente,‘syslog' com uma determinada opção de endereço ou‘none' pode ser usado
-
-
networks: nesta seção, estamos especificando onetworks disponível para nossos serviços. Neste exemplo, deixamosdocker-compose criar umnetwork nomeado do tipo‘bridge' para nós. Se a opçãoexternal é definida paratrue, ele vai usar um existente com o nome dado
-
volumes: É muito semelhante à seçãonetworks
Antes de continuarmos, verificaremos nosso arquivo de compilação quanto a erros de sintaxe:
$> docker-compose config
Este será nossoDockerfile.client para construir a imagemconfig-client. Ele difere deDockerfile.server porque também instalamosOpenBSD netcat (que é necessário na próxima etapa) e tornamos oentrypoint executável:
FROM alpine-java:base
MAINTAINER example.com
RUN apk --no-cache add netcat-openbsd
COPY files/config-client.jar /opt/spring-cloud/lib/
COPY files/config-client-entrypoint.sh /opt/spring-cloud/bin/
RUN chmod 755 /opt/spring-cloud/bin/config-client-entrypoint.sh
E este será oentrypoint personalizado para nossoconfig-client service. Aqui usamosnetcat em um loop para verificar se nossoconfig-server está pronto. Você deve notar que podemos alcançar nossoconfig-server por seulink-name, em vez de um endereço IP:
#!/bin/sh
while ! nc -z config-server 8888 ; do
echo "Waiting for upcoming Config Server"
sleep 2
done
java -jar /opt/spring-cloud/lib/config-client.jar
Finalmente, podemos construir nossas imagens, criar os contêineres definidos e iniciá-lo em um comando:
$> docker-compose up --build
Para parar os contêineres, remova-o deDockere remova osnetworksevolumes conectados dele, podemos usar o comando oposto:
$> docker-compose down
Um bom recurso dedocker-compose é oability to scale services. Por exemplo, podemos dizer aDocker para executar um contêiner paraconfig-servere três contêineres paraconfig-client.
Mas para que isso funcione corretamente, temos que remover ocontainer_name de nossodocker-compose.yml, para deixarDocker escolher um, e temos que mudar oexposed port configuration, para evitar confrontos .
Depois disso, podemos dimensionar nossos serviços da seguinte forma:
$> docker-compose build
$> docker-compose up -d
$> docker-compose scale config-server=1 config-client=3
5. Conclusão
Como vimos, agora podemos construir imagensDocker personalizadas, executando umSpring Boot Application como um contêinerDocker e criando contêineres dependentes comdocker-compose.
Para ler mais sobre os arquivos de construção, nos referimos aosDockerfile referenceedocker-compose.yml reference oficiais.
Como de costume, os códigos-fonte para este tutorial podem ser encontradoson Github.