Spring Boot + Docker + AWS EC2
Tags: springboot, docker, aws, development
Published: 2023-04-26 19:23:33


## Introdução
Salve galera! Nesse post vou tentar demostrar como publicar uma Api Spring Boot utilizando Docker, DockerHub e uma instância do EC2 da AWS. Então seguem os passos que vamos seguir para publicar a Api.

1. Criar um projeto Spring Boot
2. Criar uma imagem com Dockerfile
3. Publicar a imagem no DockerHub
4. Configurar uma instância EC2 na AWS e colocar a API em produção

Vamos Lá!

##Criando o projeto

Para criação do projeto vamos utilizar a plataforma [Spring Initializr](https://start.spring.io/) e criar um projeto simples utilizando maven e java 17 e com apenas uma dependência conforme abaixo.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ouamz3i5ly3gohjbjll.png)

O projeto consiste em uma pequena Api que vai consultar dados sobre Pokemons em uma outra Api bem famosa, a [PokéApi](https://pokeapi.co/). Faremos a consulta passando o nome do pokemon para um controller get na nossa api. 

_PokemonControler_
```
@RestController
public class PokemonController {

    @Autowired
    PokemonService pokemonService;

    @GetMapping("pokemon-info")
    public String GetPokemonInfo(@RequestParam String pokemonName){

        return pokemonService.GetPokemonInfo(pokemonName);
    }
}

```
Dentro do nosso controller temos uma injeção de dependência para nossa classe service que por sua vez acionará nosso repository que buscará os dados.

_PokemonService_

```
@Service
public class PokemonService {

    @Autowired
    PokemonRepository pokemonRepository;
    public String GetPokemonInfo(String pokemonName) {
      return pokemonRepository.GetPokemonInfo(pokemonName);
    }
}

```
Por último temos nosso PokemonRepository, geralmente é aqui que fazemos o acesso ao banco de dados, mas como a Api é simples vamos simular esse acesso aos dados com uma chamada para outra Api passando o nome do pokemon recebido pelo controller.

_PokemonRepository_

```
@Repository
public class PokemonRepositoryImplementation implements PokemonRepository {

    @Autowired
    private Environment env;

    @Override
    public String GetPokemonInfo(String pokemonName) {
        try {
          return  new RestTemplate().getForObject(env.getProperty("baseUrl")+pokemonName, String.class);
        }catch (HttpClientErrorException error){
            if (error.getStatusCode() == HttpStatus.NOT_FOUND) {
                throw new ResponseStatusException(HttpStatus.NOT_FOUND);
            } else {
                throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
            }
        }

    }
}

```
O código completo pode ser encontrado no meu [GitHub](https://github.com/thiagosouzasi/Spring-deploy-docker-aws)

## 2. Criar a imagem com Dockerfile

Vamos criar um arquivo na raiz do projeto com o nome Dockerfile sem extensão. Dentro desse arquivos vamos incluir os comandos Docker para geração da imagem que será enviada para o DockerHub e posteriormente carregada dentro de um container na nossa instância EC2 na AWS. 

_Dockerfile_

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u98fsk83dn1brap4dw8u.png)

Dentro do arquivo vamos entender o que faz cada comando:

`FROM openjdk:17` -> Indica que vamos construir nossa imagem a partir do jdk 17 necessário para que a nossa Api seja executada no container.

`WORKDIR /app` -> Cria o repositório app dentro do nosso container para onde vamos copiar nossos arquivos .jar compliados.

`COPY /target/demo-0.0.1-SNAPSHOT.jar /app/api.jar` -> Aqui estamos copiando nosso jar de dentro da pasta onde ele foi gerado, para dentro do repositório criado no nosso comando anterior.

`EXPOSE 8080` -> Expondo a porta onde nossa aplicação ficará disponível.

`ENTRYPOINT ["java","-jar", "api.jar"]` -> Executando nosso arquivo .jar criado para que nossa aplicação seja iniciada.


Antes de utilizarmos o Dockerfile para criar nossa imagem, precisamos construir o nosso .jar com nosso código fonte compilado, para isso utilizaremos o comando `mvn clean install` isso deve gerar nossos arquivos dentro do diretório target do projeto.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kvlfw3uh6oc66zpvz54u.png)

A partir de agora precisaremos estar com o [Docker](https://www.docker.com/) instalado e rodando em nossa máquina, pois vamos fazer build utilizando nosso Dockerfile para efetivamente gerar a imagem que será posteriormente enviada ao DockerHub. É necessário também possuir uma conta no [DockerHub](https://hub.docker.com/) pois vamos precisar efetuar o login na nossa conta para fazer o push da imagem.

Abra o terminal e execute o seguinte comando `docker build  -t pokemon-api .` esse comando vai gerar nossa imagem com o nome pokemon-api utilizando nosso Dockerfile criado anteriormente.

Podemos agora executar o comando `docker images` e verificar que nossa imagem foi criada com. sucesso!

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzswkipzq8ktqn2qcce4.png)

##3. Publicar a imagem no DockerHub

Agora que temos a nossa imagem gerada, vamos subir para a nossa conta no DockerHub. Precisamos fazer login na nossa conta, então vamos rodar o comando `docker login` dentro do terminal, nossas credenciais serão solicitadas após isso já poderemos efetuar o push da imagem para nossa conta.

Vamos marcar a imagem com uma tag que será importante na hora de fazer o pull dentro da nossa instância de EC2. O padrão para esse comando é: `docker tag <image_id> <dockerhub_username>/<image_name>:<tag>`, onde image_id é o ID da imagem que desejamos marcar, no meu caso, o comando ficaria assim: `docker tag c8a7b3f87156  thiagosouzasi/pokemon-api:0.0.1`. Estou utilizando a tag para versionar a nossa imagem com 0.0.1.

Agora que a imagem já possui a tag, vamos fazer o push para nosso repositório. Pra isso vamos utilizar o comando `docker push thiagosouzasi/pokemon-api:0.0.1`. Lembrando que precisamos estar logados na nossa conta para que o processo funcione.

Pronto, agora temos nossa imagem publicada dentro do DockerHub.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8bkszgw3a5m2zcw5xd6i.png)

## 4. Configurar uma instância EC2 na AWS e colocar a API em produção.

Finalmente chegou a hora de publicarmos nossa Api, para isso é preciso inicialmente possuir uma conta na [AWS](https://aws.amazon.com/pt/?nc2=h_lg).

Com a conta criada vamos navegar até o console e digitar EC2 na busca. Dentro do EC2 vamos clicar em Executar Instância para criar uma nova Instância de EC2. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/slfgy7bptq1c3egclp74.png)
Informamos o nome da instância e escolheremos o tipo Amazon Linux. Devemos ainda selecionar o tipo t2.micro, assim, conseguiremos publicar a Api sem custos. 

Posteriormente precisamos configurar o login sem atribuir chaves de acesso, **atenção** esse tipo de configuração **não** é recomendado em projetos reais por deixar brechas de segurança, contudo, por hora isso será o suficiente e facilitará a conexão com a instância que será feita mais a frente. 

Após isso basta clicar em executar instância.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/51h8owzcf0eakwzoj1v5.png)


Adicionaremos ainda outra configuração importante, devemos liberar a porta http(80) da nossa instância para habilitar o acesso externo. Mais a frente faremos o mapeamento dessa porta para a porta do container que deverá rodar nossa APi. 

Selecione a instância criada, e navegue até a aba segurança. 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/twybhtza07zz5psfi9ca.png)

Em grupo de segurança clicamos em _launch-wizard_ vai ser aberta uma tela  onde vamos configurar uma regra de entrada. 

Na tela a seguir vamos selecionar regras de entrada e clicar em _editar regras de entrada_  


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uwev8t23vgub16shfvr0.png)

Vamos adicionar a regra para protocolo http (80) e salvar a configuração.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wxvwhbxmdscbfh3q6ry4.png)

Agora já estamos com o EC2 configurado, selecione a instância criada e vá em conectar. Agora instalaremos o Docker, faremos o pull da imagem do Dockerhub e executaremos nossa Api.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/74hgpw25ckoplgs3lyk1.png)

Uma vez conectados basta executar uma sequência de comandos:

- Primeiro vamos alternar para o modo root no terminal com o comando `sudo -i`

- Agora vamos atualizar os pacotes da máquina com o comando `sudo yum update -y`

- Na sequência vamos instalar o Docker na EC2 com o comando `sudo yum install docker`. Após o download e instalação dos pacotes podemos verificar se a instalação foi bem sucedida executando o comando `docker -v`.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h6qzkbfmps8wzfi76qmv.png)

Vamos agora iniciar o Docker com o comando `sudo service docker start`

Docker rodando, vamos fazer o pull da nossa imagem salva no Dockerhub, para isso precisaremos efetuar novamente o login na nossa conta do Dockerhub com o mesmo comando que utilizamos na nossa máquina local `docker login`. Uma vez logados vamos buscar a imagem, o comando pode ser encontrado no próprio Dockerhub onde salvamos a imagem. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5gdu12xv76m79enscejh.png)

Vamos rodar o comando `docker pull thiagosouzasi/pokemon-api:0.0.1` substituindo o tag name pela tag da versão da imagem que configuramos anteriormente.

A imagem será baixada para nossa instância, ao final do processo podemos ainda conferir se deu tudo certo rodando o comando `docker images`.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aikowb215j4v0m199wqk.png)

Chegamos à última etapa, vamos subir um container utilizando nossa imagem para finalmente colocar nossa Api em produção! 

Rode o comando `docker run -d -p 80:8080 thiagosouzasi/pokemon-api:0.0.1`

Com esse comando estamos subindo nosso container na porta 8080, mesma porta configurada na nossa api spring boot e estamos mapeando essa porta para a porta 80 da instância EC2, justamente por isso configuramos a regra de entrada nas etapas anteriores. Dessa forma todo tráfego que chegar na porta http padrão será direcionado para a api no nosso container. 

Foi utilizada ainda a flag -d para que nosso container suba em modo detached e permaneça rodando mesmo quando fecharmos o terminal do EC2.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o5ixbextu4j0hdg9x6qr.png)  

Pronto, é chegada a hora da verdade! Agora basta pegar DNS IPv4 público da nossa instância e fazer uma requisição passando o nome de um pokemon que vc conhece para ver se está tudo funcionando. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lu0zvsnhfs2cf5ogafp0.png)

Por hoje é isso pessoal!