Neste artigo, vamos demonstrar como criar uma imagem Docker a partir de uma uma aplicação Spring Boot RESTFul. Além disso, vamos publicar essa imagem no DockerHub, o repositório de imagens Docker na web.
Antes de mais nada
Para este tutorial, vamos precisar de uma conta no DockerHub e da configuração de login na instalação do Docker.
Para checar se o Docker está funcionando perfeitamente, abra um terminal e digite:
$ docker --version Docker version 18.09.1, build 4c52b90
Aqui, a aplicação que vamos subir é um serviço bem simples em Spring, apenas um Controller REST com os métodos GET, POST, PUT e DELETE.
Vamos criar esse serviço a partir de um template do Spring Initializr, com um WEB Starter.

Também vamos criar uma pasta para colocar nossos arquivos necessários para montar a imagem Docker.
No fim, este é o resultado final da estrutura do projeto:
├── README.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main │ ├── java │ │ └── br.com.template.dockerize │ │ ├── Application.java | | └── br.com.template.dockerize.controller │ │ └── UserController.java │ ├── resources │ └── application.yml | ├── docker └── Dockerfile run.sh
UserController é um recurso com uma rota para cada método HTTP usado em um CRUD:
@RestController @RequestMapping("/users") public class UserController { @GetMapping public String getUsers(){ return "Retorno do metodo getUser - HTTP GET"; } @PostMapping public String createUser(){ return "Retorno do metodo createUser - HTTP POST"; } @PutMapping public String updateUser(){ return "Retorno do metodo updateUser - HTTP PUT"; } @DeleteMapping public String deleteUser(){ return "Retorno do metodo deleteUser - HTTP DELETE"; } }
Imagens e Containers Docker
Para relembrar, uma imagem Docker é um template de um ambiente virtual, que provê um sistema operacional mínimo independente do Host, além de pacotes e aplicativos que justifiquem a existência dessa imagem, como um banco de dados ou um servidor WEB.
Um container Docker é, simplesmente, uma instância de uma imagem Docker.
No nosso exemplo, temos uma aplicação Spring Boot empacotada em um JAR, que subirá um servidor Tomcat embarcado.
Para a construção de uma imagem, usamos um arquivo chamado Dockerfile com um script gravado como este:
FROM alpine:latest RUN apk update && apk upgrade && apk add bash
Basicamente, estamos usando a estrutura de camadas inerente ao Docker, adicionando novos elementos através do Dockerfile.
FROM significa a imagem base (ou pai) que fornece a camada inicial.
Esse uso de camadas favorece a reutilização e, consequentemente, economia de espaço e no tráfego de rede. Isso porque quando uma imagem é recuperada de um repositório remoto para uma máquina local, apenas aquelas camadas que ainda não foram baixadas, o serão.
Vamos construir nosso Dockerfile
Neste artigo, vamos construir uma imagem Docker.
Como imagem base, vamos usar uma simplificada que já venha com o Java previamente configurado:
FROM openjdk:8-jdk-alpine
Normalmente, eu atualizo os pacotes baixados da imagem pai:
RUN apk update && apk upgrade
Fazemos isso com o Alpine Linux Package Manager, já que estamos usando uma imagem base Alpine, populares por serem pequenas, rápidas e seguras.
Agora precisamos adicionar algo que justifique a existência de nossa imagem. Então, vamos copiar o pacote da aplicação para uma pasta apropriada:
RUN mkdir -p /usr/local/dockerize
ADD @project.build.finalName@.jar /usr/local/dockerize/
Finalmente, vamos copiar o script utilizado para subir o serviço:
ADD run.sh run.sh
RUN chmod +x run.sh
CMD ./run.sh
O arquivo run.sh contem apenas o comando para levantar o container:
#!/bin/sh
echo "********************************************************"
echo "Starting Template Server with Docker "
echo "********************************************************"
java -jar /usr/local/dockerize/@project.build.finalName@.jar
Agora é com o Maven
Nosso projeto utiliza o Maven para gerenciar as dependências e automatizar os builds.
Primeiramente, precisamos definir algumas propriedades:
<properties> <java.version> 1.8 </java.version> <project.build.sourceEncoding> UTF-8 </project.build.sourceEncoding> <start-class> br.com.template.dockerize.Application </start-class> <docker.image.name> marcelogarcia/dockerize-spring </docker.image.name> <docker.image.tag> latest </docker.image.tag> </properties>
E uns bons plugins.
Sempre vamos usar o plugin de suporte do Spring Boot para o Maven, permitindo o empacotamento de jar’s executáveis.
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin>
Também precisamos agrupar todos os arquivos que serão utilizados para compor a imagem:
<plugin> <artifactId>maven-resources-plugin</artifactId> <executions> <execution> <id>copy-resources</id> <phase>validate</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <outputDirectory>${basedir}/target/dockerfile</outputDirectory> <resources> <resource> <directory>src/main/docker</directory> <filtering>true</filtering> </resource> </resources> </configuration> </execution> </executions> </plugin>
E, finalmente, do plugin responsável por montar a imagem e publicar no repositório local:
<plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>0.4.10</version> <configuration> <imageName>${docker.image.name}:${docker.image.tag}</imageName> <dockerDirectory>${basedir}/target/dockerfile</dockerDirectory> <resources> <resource> <targetPath>/</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> </resource> </resources> </configuration> </plugin>
A geração da imagem pelo maven é feita com o comando:
mvn clean package docker:build
Analise da imagem e criação do container
Se tudo acontecer conforme o esperado, teremos nossa imagem gerada e publicada no repositório local:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
marcelogarcia/dockerize-spring latest 03dfe9135ff5 23 minutes ago 191MB
Localmente, já podemos criar um container baseado nessa imagem:
docker run -t -p 8000:8080 --name dockerize marcelogarcia/dockerize-spring
Deste comando, destacamos:
- -t para exibir o log de eventos do container no nosso console
- -p 8000:8080 para expor a porta 8080 da aplicação na porta 8000 do host
Para o DockerHub
Antes de enviar esta imagem para o DockerHub, precisamos colocar uma TAG. Basicamente, vamos usar o IMAGE ID da listagem anterior e o nome do usuário no DockerHub.
docker tag 03dfe9135ff5 marcelogarcia/dockerize-spring:latest
Meu usuário é marcelogarcia.
A TAG é formada por nome e versão, resultando em dockerize-spring:latest.
Agora podemos publicar a imagem para o repositório remoto:
docker push marcelogarcia/dockerize-spring:latest
Você verá um console semelhante ao que ocorre quando fazemos um pull.
The push refers to repository [docker.io/marcelogarcia/dockerize-spring]
ae5a4a9e2a58: Pushed
6cfa708eec0e: Pushed
d259a26b94f6: Pushed
68bec0aced8b: Pushed
2bc6b069b00b: Pushed
685fdd7e6770: Mounted from library/openjdk
c9b26f41504c: Mounted from library/openjdk
cd7100a72410: Mounted from gliderlabs/logspout
latest: digest: sha256:cb78cfa806ded3eb587b6f0b5aa82c7a2d469bf7c78aac827b617a95ccfb5bc5 size: 1992
Para finalizar, confirme que a imagem está disponível para outros usuários:

Como vimos, usando as ferramentas adequadas, passa a ser bem simples de montar a imagem e publicar uma imagem Docker.
O próximo passo é gerenciar esse processo em um contexto de entrega contínua. Mas, isso fica uma próxima oportunidade!