Utilizando Docker em App WEB Spring Boot 
Tags: docker, spring, java
Published: 2021-08-16 22:51:38

Certo, muito se fala hoje em dia em Docker... A minha experiência recente é de puro fascínio, como demorei tanto para utilizá-lo? Realmente não sei... 
Neste artigo tentarei sintetizar meus entendimentos de Docker e como utilizá-lo para "rodar" um sisteminha Web bem básico. Assim divido tudo em partes:
1. Conceito do Docker e o que ele tenta resolver;
2. Um pequeno sistema WEB utilizando Spring Boot;
3. Juntando tudo: o sistema WEB rodando dentro do Docker;

## O que é o Docker?
Analise estes cenários:
* Lembra aquela vez em que você queria usar um aplicativo no seu computador e instalando dependências necessárias para tal acabou por "quebrar" outros aplicativos no computador?
* Ou quando para evitar esse tipo de coisa descrita acima você tentou levantar um outro Sistema Operacional virtualizado rodando por cima do seu S.O e no final tudo isso rodando junto ficou tão lento e ocupou tanto espaço que você desistiu!
* Lembra aquele sistema que você desenvolveu, roda legal na sua máquina mas quando você coloca em produção você passa a noite inteira para fazer funcionar?

Para facilitar a resolução dos três cenários acima a resposta é Docker, Docker e Docker! Vamos primeiro tentar entender a diferença entre um sistema rodando no Docker e rodando numa virtualização de sistema operacional olhando a figura abaixo:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sc1cri7osbt8sibkdse8.png)

Para entender Docker precisamos entender o conceito de Containers. Pelo próprio [site](https://www.docker.com/resources/what-container#:~:text=A%20container%20is%20a%20standard,one%20computing%20environment%20to%20another.&text=Secure%3A%20Applications%20are%20safer%20in,isolation%20capabilities%20in%20the%20industry) do Docker, um container é: *"Uma peça padrão de software que empacota código e suas dependências de forma que as aplicações rodem rapidamente e de forma confiável de um ambiente computacional para o outro."* Neste [site](https://www.simplilearn.com/tutorials/docker-tutorial/docker-vs-virtual-machine) há uma comparação legal, que compara o Docker a um condomínio de apartamentos e um sistema virtualizado a uma casa alugada.
Enquanto que em um condomínio os recursos (a piscina, a academia, garagem, água...) são compartilhados entre os vizinhos. Em uma casa alugada nada é compartilhado, ou seja tudo é exclusivo, e sabemos que essa exclusividade tem um **custo**. 
Resumindo: Containers são menores que imagens virtuais, podem ser mais facilmente construídos (e destruídos) e eles compartilham recursos com o S.O em que estão rodando. Em contrapartida, sistemas virtualizados não compartilham nada um com o outro e também não compartilham recursos no S.O em que estão rodando.

OBS: Informações sobre como instalar o Docker podem ser encontradas no [site oficial](https://docs.docker.com/get-docker/).

# 1. Docker
## 1.1. Dockerfile
O Docker entende uma receitinha de bolo que explica como construir o Container, essa receita é um arquivo sem terminação chamado Dockerfile. Se você vai usar um Linux bem pequeno chamado alpine para rodar o seu programa, você tem que dizer isso no Dockerfile. Se vai precisar instalar uma ferramenta/ dependência que o seu sistema precisa dizer isso também no Dockerfile. 
## 1.2 Docker Image
O Dockerfile é só a receita do Container. É necessário construí-lo fisicamente em uma *imagem* (na verdade aquele Linux chamado Alpine que é pequeno e que você pode usar também é uma imagem), ou seja, imagens específicas são compostas de outras mais genéricas.
## 1.3 Container
Depois que temos o Dockerfile e a imagem construída, colocamos o Container baseado naquela imagem para rodar. Se fizermos a analogia com JAVA, imagine o Dockerfile como se fosse o arquivo *.java, a imagem como se fosse o *.class ou o *. jar gerado e o Container como o aquivo jar rodando. E como sabemos, para rodar um jar muitas vezes precisamos passar parâmetros, com os Containers não é diferente, por exemplo, se queremos acessar uma porta do Container, precisamos passar isso como parâmetro.

# 2. Sistema WEB com SpringBoot
## 2.1. http://localhost:8080
Crie uma pasta chamada docker e abra o VSCode nela e pressione CTRL+SHIFT+p
```
$mkdir docker
/docker$ code .
```
Escolha Spring Initializr: Create a Maven Project
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zuja2rcr40ek9lqfghdn.png)
Depois escolha a versão mais recente que houver do SpringBoot, no caso, estou usando 2.5.3
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aam9knjjveiu27nf8g8c.png)
Escolha a linguagem JAVA e mantenha as opções que vem sendo apresentadas (com.example) e (demo). No packaging type, escolha Jar e na versão JAVA eu estou trabalhando com a 8.
Por fim escolha apenas a dependência do Spring Web (a última da foto)
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xpj6f1p60t6uwx9w84dq.png)
 Após isso o seu VSCode deve estar assim com a classe DemoApplication.java criada:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d0hsi1hx794tv0h82255.png)
 O programa ainda não tem muita função, precisamos de um acesso para este retorne informações quando o sistema receber requisições externas. Vamos criar uma classe chamada Serviço (Servico.java) dentro de uma package chamada service que é bem simples, ela só retorna "Hello World":
```
package com.example.demo.service;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Servico {

    @RequestMapping("/hello")
	public String hello() {
		return "Hello World";
	}
    
}
```
Volte na classe DemoApplication e no método main clique em Run para executar a aplicação. ![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b0trawx83qgwud52fzp9.png)
 Quando ela acabar de levantar, vá no browser e escreva http://localhost:8080/hello
O resultado será o abaixo, e isto quer dizer que o seu sistema WEB está rodando localmente com acesso ao serviço /hello na porta 8080.
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sqksnwi0mx4jd4pmtfk9.png)
Falamos antes de Docker e agora temos uma aplicação WEB rodando. Como criar um Container rodando esta aplicação? É o próximo desafio.

# 3. Aplicativo Rodando no Container
## 3.1. Construindo Dockerfile
Temos o nosso aplicativo construído na seção 2, vamos agora pensar em como seria o ambiente para executá-lo. Primeiramente, vamos executá-lo em um Container Linux, então vamos considerar o Alpine, mais informações sobre ele [aqui](https://www.alpinelinux.org/). Lembrem que eu escolhi o Java 8 quando estava criando o aplicativo? Então este é outro ponto a se considerar.
Crie um arquivo Dockerfile (sem extensão) no mesmo diretório criado (docker) e no mesmo local onde o projeto JAVA está criado e irei comentar linha a linha este arquivo Dockerfile. 
{% gist
https://gist.github.com/ferfox1981/06eafa153b82ecfb1bda238e445f3bcb
%}
Linha 1: Diz que precisaremos de uma imagem do Linux Alpine e que deve vir com um jdk versão 8 já instalado (prático, não precisamos instalar JAVA);
Linha 3: Diz qual será o diretório de trabalho no Container, no caso será o `/home`;
Linha 5: Diz para copiar *tudo* que estiver neste diretório em que estamos e mandar para o WORKDIR definido (no caso `/home`);
Linha 7: Como a versão Alpine do Linux é bem básica, precisamos instalar o Maven (lembrem que criamos um Maven Project lá no VSCode), é isto que este comando faz;
Linha 9: Após a linha 3 executar passamos a ter um diretório `/demo` dentro de `/home`, setamos então `/home/demo` como sendo o novo diretório de trabalho;
Linha 11: Rodamos o comando para fazer o MAVEN gerar o Jar do App que desenvolvemos, este jar é criado em uma pasta `/home/demo/target`
Linha 13: Este comando expõe a porta 8080 (que é a porta que o App Web utiliza para receber requisições
Linha 15: Este comando representa o que deverá ser rodado no prompt deste Container quando ele for iniciado, que são o comando java, com os parâmetros "-jar" e o arquivo jar a ser executado `/home/demo/target/demo-0.0.1-SNAPSHOT.jar`
 
## 3.2. Gerando Imagem
Lembrem que o Dockerfile é como se fosse o arquivo .java, então temos que gerar o .jar dele para podermos rodá-lo. Para fazer isso, utilizamos o comando `build` e com isso geraremos a imagem com tudo que está descrito no Dockerfile. Assim, na mesma pasta em que estão o Dockerfile e a pasta `/demo` com o nosso projeto Spring, digite:
`docker build . -t java-spring`
O que isso vai fazer? Vai ler o Dockerfile que está nesta pasta, vai ler o projeto Spring criado e gerar a imagem que terá o nome de "java-spring". Aguarde a imagem ser criada e depois digite o comando `docker image list` para constatar que a imagem foi criada.
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1kopo6v6p36fmvrh7sum.png)
Isso quer dizer que a imagem (o nosso jar) foi criado. Temos que pô-lo para rodar.
## 3.3. Criando Container
Como foi dito, da mesma forma como temos que passar parâmetros para rodar um arquivo jar, para rodar um Container também temos a mesma lógica. Para executarmos o Container e conseguirmos acessá-lo do browser da mesma forma que acessamos quando rodamos localmente precisaremos fazer um mapeamento de portas. Recorde-se que na Linha 13 foi exposta a porta 8080, precisamos agora mandar informações para esta porta mapeando uma porta no S.O host para a porta do Container. Veja o comando abaixo para gerar o Container
`docker run --name java-container -d -p 8080:8080 java-spring`
traduzindo os parâmetros para:
* `--name`: o nome do Container (java-container);
* `-d`: Modo dettached (isso quer dizer que após o container começar a rodar, ele ficará rodando em background e o terminal ficará livre para o usuário);
* `-p`: é o mapeamento de portas PORTA_HOST:PORTA_CONTAINER, quer dizer que uma requisição no host para a porta 8080 irá ser enviada para o host na porta 8080.

O parâmetro a seguir é justamente a imagem `java-spring` que foi criada, seria como se fosse o nome do jar que vamos rodar.

Após solicitar que o Container inicie-se pode ir no Browser e digitar http://localhost:8080/hello, o efeito será o mesmo de quando executávamos o app web no host, justamente a palavra Hello World.
## 3.4. Comandos Adicionais Docker
#### Listar Containers rodando
`$docker ps`
#### Listar Containers existentes
`$docker ps -a`
#### Iniciar um Container
`$docker start <nome_container>`
#### Derrubar um Container
`$docker stop <nome_container>`
#### Reiniciar um Container (stop seguido de um start)
`$docker restart <nome_container>`
#### Listar Imagens Máquina Local
`$docker image list`
#### Remover Imagem
`$docker rmi <nome_imagem>`
#### Enviar um comando para um Container rodando
`$docker exec -it <nome_container> <comando>`

# Considerações
O Docker é um aliado poderoso, vale a pena aprender e usá-lo. O app e o Dockerfile estão [aqui](https://github.com/ferfox1981/docker-spring) para quem quiser baixar.