Criando uma aplicação Restful Dockerizada com Spring Boot

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.

Gerando um aplicação Spring Boot pelo start.spring.io

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:

Imagem publicada no DockerHub

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!

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *