diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f2699ce --- /dev/null +++ b/Dockerfile @@ -0,0 +1,127 @@ +#=================================== MULTISTAGE -> build ==================================== +# FROM [nome da imagem]:[versão/tag da imagem] +# Referência: https://docs.docker.com/engine/reference/builder/#from +# +# Define uma imagem local ou pública do Docker Store. Aqui é utilizado uma imagem oficial do +# Maven (baseada na distribuição linux Debian Stretch Slim), cujo objetivo é servir de imagem +# base para os demais estágios do processo de build da imagem final, reduzindo seu tamanho e +# permitindo total independência à ferramentas externas ao Docker. +#============================================================================================ +FROM maven:3.5.2-jdk-8 AS build + +#============================================================================================ +# COPY [arquivo a ser copiado] [destino do arquivo copiado] +# Referência: https://docs.docker.com/engine/reference/builder/#copy +# +# Copia os arquivos de código fonte da aplicação para dentro do container. +#============================================================================================ +COPY src /usr/src/app/src +COPY pom.xml /usr/src/app + +#============================================================================================ +# RUN [comandos a serem executados] +# Referência: https://docs.docker.com/engine/reference/builder/#run +# +# A instrução RUN executará qualquer comando sobre uma nova camada da imagem atual e +# confirmará os resultados. A imagem resultante será usada para o próximo passo no Dockerfile. +#============================================================================================ +RUN \ + mvn -f /usr/src/app/pom.xml clean package -DskipTests + +#================================== MULTISTAGE -> release =================================== +# FROM [nome da imagem]:[versão/tag da imagem] +# Referência: https://docs.docker.com/engine/reference/builder/#from +# +# Define uma imagem local ou pública do Docker Store. Aqui é utilizado uma imagem oficial do +# Debian (baseada na distribuição linux Debian Stretch Slim). Em sua primeira execução, ela +# será baixada para o computador e usada no build para criar a imagem da aplicação. +#================================ +FROM debian:stretch + +#============================================================================================ +# LABEL maintainer=[nome e e-mail do mantenedor da imagem] +# Referência: https://docs.docker.com/engine/reference/builder/#label +# +# Indica o responsável/autor por manter a imagem. +#============================================================================================ +LABEL maintainer="Raphael F. Jesus " + +#============================================================================================ +# ARG [=] +# Referência: https://docs.docker.com/engine/reference/builder/#arg +# +# A instrução ARG define uma variável que os usuários podem passar no tempo de compilação +# para o construtor com o comando docker build. +#============================================================================================ +ARG PORT + +#============================================================================================ +# ENV [nome da variável de ambiente] +# Referência: https://docs.docker.com/engine/reference/builder/#env +# +# Variáveis de ambiente com o path da aplicação dentro do container. +#============================================================================================ +ENV \ + PORT=${PORT:-8090} \ + JAVA_OPTS='-Xms256m -Xmx256m' \ + DEBUG_OPTS= + +#============================================================================================ +# VOLUME [nome do volume] +# Referência: https://docs.docker.com/engine/reference/builder/#volume +# +# Cria um ponto de montagem com o nome especificado e marca-o como um volume persistente +# montado a partir de hospedeiros nativos ou outros containers. +#============================================================================================ +VOLUME /tmp + +#============================================================================================ +# EXPOSE [número da porta] +# Referência: https://docs.docker.com/engine/reference/builder/#expose +# +# Irá expor a porta para a máquina host (hospedeira). É possível expor múltiplas portas, como +# por exemplo: EXPOSE 80 443 8080 +#============================================================================================ +EXPOSE ${PORT} + +#============================================================================================ +# RUN [comandos a serem executados] +# Referência: https://docs.docker.com/engine/reference/builder/#run +# +# A instrução RUN executará qualquer comando sobre uma nova camada da imagem atual e +# confirmará os resultados. Aqui será executado a atualização dos pacotes instalado no sistema +# operacional, bem como instalar o openjdk e o pacote Curl para usarmos no healthcheck. +#============================================================================================ +RUN \ + apt -y update \ + && apt-get -y install openjdk-8-jre-headless curl --no-install-recommends \ + && rm -rf /var/lib/apt/lists/* + +#============================================================================================ +# COPY [arquivo a ser copiado] [destino do arquivo copiado] +# Referência: https://docs.docker.com/engine/reference/builder/#copy +# +# Copia o arquivo da aplicação para dentro do container sob o nome app.jar. +#============================================================================================ +COPY --from=build /usr/src/app/target/spring-boot-sample-hateoas-2.0.1.RELEASE.jar /app.jar + +#============================================================================================ +# ENTRYPOINT [executável seguido dos parâmetros] +# Referência: https://docs.docker.com/engine/reference/builder/#entrypoint +# +# Inicia o container como um executável a partir da inicialização da aplicação. Essa instrução +# é muito útil, pois caso a aplicação caia, o container cai junto, indicando ao orquestrador +# de containers aplicar a política de restart configurada para a aplicação. +#============================================================================================ +ENTRYPOINT exec java ${JAVA_OPTS} ${DEBUG_OPTS} -Djava.security.egd=file:/dev/./urandom -jar /app.jar + +#============================================================================================ +# HEALTHCHECK --interval=[duração em segundos] --timeout=[duração em segundos] +# Referência: https://docs.docker.com/engine/reference/builder/#healthcheck +# +# Diz ao Docker como testar um container para verificar se ele ainda está funcionando. Isso +# pode detectar casos como um servidor web que está preso em um loop infinito e incapaz de +# lidar com novas conexões, mesmo que o processo do servidor ainda esteja em execução. +#============================================================================================ +HEALTHCHECK --interval=30s --timeout=30s CMD curl -f http://127.0.0.1:8090/actuator/health || exit 1 + diff --git a/README.md b/README.md index 10a2c06..19ce802 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,197 @@ -# Example application # +# Bravi Software - Teste para vaga de Arquiteto DevOps -A simple Spring Boot application that sports a REST API and does persistence using JPA. +Este documento tem como objetivo demonstrar os passos necessários para validar as implementações submetidas pelos candidados à vaga de Arquiteto DevOps na Bravi Software. -This project is based on Spring Boot's [HATEOAS example](https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples/spring-boot-sample-hateoas). +Tabela de conteúdo +================== + +- [Pré-requisitos](#pré-requisitos) +- [Tarefas](#tarefas) + - [Tarefa 1: Migrar a persistência para Postgresql ou MySQL](#tarefa-1:-migrar-a-persistência-para-postgresql-ou-mysql) + - [Tarefa 2: Empacotar a aplicação usando Docker e implantá-la usando uma ferramenta de orquestração compatível com Docker](#tarefa-2:-empacotar-a-aplicação-usando-docker-e-implantá-la-usando-uma-ferramenta-de-orquestração-compatível-com-Docker) + - [Tarefa 3: Implementar atualização sem afetar disponibilidade usando Docker em uma ferramenta de orquestração compatível com Docker](#tarefa-3:-implementar-atualização-sem-afetar-disponibilidade-usando-docker-em-uma-ferramenta-de-orquestração-compatível-com-docker) +- [Referências](#referências) + +## Pré-requisitos + +Antes de iniciar a execução das tarefas descritas no teste em pauta, certifique-se que as seguintes ferramentas estão disponíveis em seu ambiente: + +- [Git](https://git-scm.com/) +- [JDK 8+](https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) +- [Maven 3.3+](https://maven.apache.org/download.cgi) +- [Docker 1.17+](https://docs.docker.com/install/) +- [Docker Swarm](https://docs.docker.com/engine/swarm/swarm-tutorial/) +- [cURL](https://curl.haxx.se/docs/manpage.html) +- [watch](https://linux.die.net/man/1/watch) + +Para assegurar que as ferramentas citadas acima estão devidamente instaladas no ambiente, execute os comandos abaixo: + +```shell +# Printa a versão do Git instalado na máquina +git --version + +# Printa a versão da JDK instalada na máquina +java -version + +# Printa a versão do Maven instalado na máquina +mvn --version + +# Printa a versão do Docker instalado na máquina +docker --version +``` + +![Console com a confirmação da instalação das ferramentas](docs/images/Tarefa1-Console_com_a_confirmacao_da_instalacao_das_ferramentas.png) + +## Tarefas + +## Tarefa 1: Migrar a persistência para Postgresql ou MySQL + +Para execução dessa tarefa, será utilizado o banco de dados PostgreSQL. + +1) Nesta etapa, será demonstrado os passos necessários para criação do banco de dados que será utilizado pela aplicação. + +```shell +# Execute o comando abaixo para criar um container Docker a partir da imagem oficial do PostgreSQL expondo-o na porta 5432 +docker run --name postgres-test -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres -d postgres:10.6-alpine +``` + +![Console com a criação do banco de dados PostgreSQL](docs/images/Tarefa1-Console_com_a_criacao_do_banco_de_dados_PostgreSQL.png) + +2) A partir do diretório raiz do projeto, empacote (formato .jar) a aplicação e execute-a a partir de um terminal, conforme descrito abaixo. + +```shell +# Execute o empacotamento da aplicação utilizando o Maven +mvn clean package +``` + +![Console com o finalização do empacotamento da aplicação](docs/images/Tarefa1-Console_com_o_finalizacao_do_empacotamento_da_aplicacao.png) + +```shell +# Inicie a aplicação empacotada via terminal +java -jar target/spring-boot-sample-hateoas-2.0.1.RELEASE.jar +``` + +![Console com a inicialização da aplicação via terminal](docs/images/Tarefa1-Console_com_a_inicializacao_da_aplicacao_via_terminal.png) + +3) Com aplicação inicializada, acesse a URL `http://localhost:8090/swagger-ui.html` a partir do seu navegador favorito e teste a aplicação utilizando os recursos do Swagger. + +![Página inicial do Swagger gerado pela aplicação](docs/images/Tarefa1-Pagina_inicial_do_Swagger_gerado_pela_aplicacao.png) + +>**Nota:** Tenha certeza que as portas `8090` e `5432` utilizadas pela aplicação e banco de dados respectivamente, não estão em uso por outros processos em seu ambiente. + +### Tarefa 2: Empacotar a aplicação usando Docker e implantá-la usando uma ferramenta de orquestração compatível com Docker + +Para execução dessa tarefa, será utilizado o Docker Swarm como ferramenta de orquestração de containers Dockers. + +1) Inicie um docker swarm em seu ambiente, executando os comandos abaixo: + +```shell +# Inicializa o orquestrador de containers na máquina +docker swarm init +``` + +2) Com o orquestrador de containers inicializado, inicie o container do PostgreSQL como serviço. + +```shell +# Execute o comando abaixo a partir do diretório raiz do projeto +docker stack deploy --compose-file docker-stack-infra.yml BRAVI +``` + +![Console com a criação do container PostgreSQL como serviço](docs/images/Tarefa2-Console_com_a_criacao_do_container_PostgreSQL_como_servico.png) + +>**Nota:** Garanta que a porta `5432` utilizada pelo PostgreSQL não esteja em uso. + +2) Em seguida, a partir do diretório raiz do projeto, gere a imagem docker da aplicação e implante-a como serviço, conforme configuração contida no arquivo `docker-stack.yml`. + +```shell +# Constrói a imagem docker da aplicação +docker build --tag spring-boot-sample-hateoas:1.0.0 . + +# Execute o comando abaixo a partir do diretório raiz do projeto para implantar a aplicação como serviço +docker stack deploy --compose-file docker-stack.yml BRAVI + +# Confirme se o serviço foi criado corretamente +docker service ls +``` + +![Console com a confirmação da criação do serviço da aplicação](docs/images/Tarefa2-Console_com_a_confirmacao_da_criacao_do_servico_da_aplicacao.png) + +>**Nota:** Para visualizar os logs gerados pela aplicação implantada como serviço, utilize o comando `docker service logs BRAVI_spring-boot-sample-hateoas` a partir do terminal. + +3) Na sequência, usando seu navegador favorito, acesse a URL `http://127.0.0.1:8090/swagger-ui.html` e teste a aplicação utilizando os recursos do Swagger. + +![Página inicial do Swagger da aplicação](docs/images/Tarefa2-Pagina_inicial_do_Swagger_da_aplicacao.png) + +4) Agora, simule a queda de um dos containers associados ao serviço da aplicação e veja como a aplicação se comporta para o usuário final. Para executar esse passo, todos os comandos descritos abaixo devem ser executados em terminais diferentes. + +```shell +# Diminua a réplica do serviço de 2 para 1 +docker service scale BRAVI_spring-boot-sample-hateoas=1 + +# Lista todos os containers em execução a meio segundo +watch -n 0.5 docker ps + +# Testa o endpoint da aplicação a cada 1 segundo +watch -n 1 curl -I http://127.0.0.1:8090/swagger-ui.html +``` + +![Console com a saída do monitoramento da aplicação quando simulado uma queda](docs/images/Tarefa2-Console_com_a_saida_do_monitoramento_da_aplicacao_quando_simulado_uma_queda.png) + +### Tarefa 3: Implementar atualização sem afetar disponibilidade usando Docker em uma ferramenta de orquestração compatível com Docker + +Para realizar essa tarefa, será preciso ajustar alguns parâmetros de configurações do **.yml**. + +**Atenção**: Tenha certeza que o container do PostgreSQL está em execução, conforme descrito na tarefa 2. + +1) A partir do diretório raiz do projeto, execute os comandos abaixo para gerar a imagem da aplicação e na sequência implantá-la como serviço. + +```shell +# Constrói a imagem docker da aplicação +docker build --tag spring-boot-sample-hateoas:1.0.0 . + +# Execute o comando abaixo para implantar a aplicação como serviço +docker stack deploy --compose-file docker-stack.yml BRAVI +``` + +![Console com a confirmação da criação do serviço da aplicação](docs/images/Tarefa3-Console_com_a_confirmacao_da_criacao_do_servico_da_aplicacao.png) + +2) Antes de prosseguir com a atualização da aplicação, execute os comandos a seguir em terminais diferentes, permitindo o monitoramento do estado da aplicação. + +```shell +# Lista todos os containers em execução (refresh a cada segundo) +watch -n 1 docker service ps BRAVI_spring-boot-sample-hateoas + +# Testa o endpoint da aplicação a cada segundo +watch -n 1 curl -I http://127.0.0.1:8090/swagger-ui.html +``` + +![Console com a saída do monitoramento da aplicação](docs/images/Tarefa3-Console_com_a_saida_do_monitoramento_da_aplicacao.png) + +3) Com os terminais de monitoramento em execução, abra outro terminal e execute os comandos listados abaixo para gerar uma nova imagem que contemple alguma alteração da aplicação e na sequência, implante-a como serviço. + +```shell +# Edite o arquivo docker-stack.yml para alterar a versão da imagem De "1.0.0" Para "2.0.0" +vi docker-stack.yml +:wq + +# Construa a imagem docker da aplicação, versionando-a em 2.0.0 +docker build --tag spring-boot-sample-hateoas:2.0.0 . + +# Implanta a atualiza a aplicação já em execução +docker stack deploy --compose-file docker-stack.yml BRAVI +``` + +![Console com a saída do monitoramento da aplicação durante a atualização](docs/images/Tarefa3-Console_com_a_saida_do_monitoramento_da_aplicacao_durante_a_atualizacao.png) + +>**Nota:** Ignore o warning exibido durante a atualização do serviço, pois isso é devido a não configuração de um registry (fora do escopo da tarefa), sendo necessário em ambiente clusterizado. + +## Referências + +- [Git - Documentação](https://git-scm.com/doc) +- [JDK - Documentação](https://docs.oracle.com/javase/8/docs/) +- [Maven - Documentação](https://maven.apache.org/guides/index.html) +- [Docker - Documentação](https://docs.docker.com/) +- [Docker Swarm - Documentação](https://docs.docker.com/engine/swarm/) +- [PostgreSQL - Documentação](https://www.postgresql.org/docs/10/index.html) +- [PostgreSQL - Imagem oficial no Docker Store](https://docs.docker.com/samples/library/postgres/) diff --git a/docker-stack-infra.yml b/docker-stack-infra.yml new file mode 100644 index 0000000..57c66c4 --- /dev/null +++ b/docker-stack-infra.yml @@ -0,0 +1,16 @@ +version: '3.5' +services: + postgres-test: + image: postgres:10.6-alpine + environment: + - POSTGRES_DB=postgres + - POSTGRES_USER=postgres + - "POSTGRES_PASSWORD=postgres" + ports: + - 5432:5432 + networks: + - rede + +networks: + rede: + driver: overlay diff --git a/docker-stack.yml b/docker-stack.yml new file mode 100644 index 0000000..4a29e0c --- /dev/null +++ b/docker-stack.yml @@ -0,0 +1,36 @@ +version: '3.5' +services: + spring-boot-sample-hateoas: + image: spring-boot-sample-hateoas:1.0.0 + environment: + - JAVA_OPTS=-Xms256m -Xmx256m + - DATASOURCE_URL=jdbc:postgresql://postgres-test:5432/postgres + - DATASOURCE_USERNAME=postgres + - DATASOURCE_PASSWORD=postgres + ports: + - 8090:8090 + networks: + - rede + stop_grace_period: 30s + deploy: + resources: + limits: + memory: 512M + reservations: + memory: 256M + replicas: 2 + restart_policy: + condition: on-failure + delay: 15s + max_attempts: 5 + window: 120s + update_config: + parallelism: 1 + delay: 10s + failure_action: rollback + monitor: 60s + max_failure_ratio: 0.5 + +networks: + rede: + driver: overlay diff --git a/docs/images/Tarefa1-Console_com_a_confirmacao_da_instalacao_das_ferramentas.png b/docs/images/Tarefa1-Console_com_a_confirmacao_da_instalacao_das_ferramentas.png new file mode 100644 index 0000000..97ca781 Binary files /dev/null and b/docs/images/Tarefa1-Console_com_a_confirmacao_da_instalacao_das_ferramentas.png differ diff --git a/docs/images/Tarefa1-Console_com_a_criacao_do_banco_de_dados_PostgreSQL.png b/docs/images/Tarefa1-Console_com_a_criacao_do_banco_de_dados_PostgreSQL.png new file mode 100644 index 0000000..1248013 Binary files /dev/null and b/docs/images/Tarefa1-Console_com_a_criacao_do_banco_de_dados_PostgreSQL.png differ diff --git a/docs/images/Tarefa1-Console_com_a_inicializacao_da_aplicacao_via_terminal.png b/docs/images/Tarefa1-Console_com_a_inicializacao_da_aplicacao_via_terminal.png new file mode 100644 index 0000000..060ba33 Binary files /dev/null and b/docs/images/Tarefa1-Console_com_a_inicializacao_da_aplicacao_via_terminal.png differ diff --git a/docs/images/Tarefa1-Console_com_o_finalizacao_do_empacotamento_da_aplicacao.png b/docs/images/Tarefa1-Console_com_o_finalizacao_do_empacotamento_da_aplicacao.png new file mode 100644 index 0000000..9f07b9c Binary files /dev/null and b/docs/images/Tarefa1-Console_com_o_finalizacao_do_empacotamento_da_aplicacao.png differ diff --git a/docs/images/Tarefa1-Pagina_inicial_do_Swagger_gerado_pela_aplicacao.png b/docs/images/Tarefa1-Pagina_inicial_do_Swagger_gerado_pela_aplicacao.png new file mode 100644 index 0000000..3d47270 Binary files /dev/null and b/docs/images/Tarefa1-Pagina_inicial_do_Swagger_gerado_pela_aplicacao.png differ diff --git a/docs/images/Tarefa2-Console_com_a_confirmacao_da_criacao_do_servico_da_aplicacao.png b/docs/images/Tarefa2-Console_com_a_confirmacao_da_criacao_do_servico_da_aplicacao.png new file mode 100644 index 0000000..268613f Binary files /dev/null and b/docs/images/Tarefa2-Console_com_a_confirmacao_da_criacao_do_servico_da_aplicacao.png differ diff --git a/docs/images/Tarefa2-Console_com_a_criacao_do_container_PostgreSQL_como_servico.png b/docs/images/Tarefa2-Console_com_a_criacao_do_container_PostgreSQL_como_servico.png new file mode 100644 index 0000000..3f51ef1 Binary files /dev/null and b/docs/images/Tarefa2-Console_com_a_criacao_do_container_PostgreSQL_como_servico.png differ diff --git a/docs/images/Tarefa2-Console_com_a_saida_do_monitoramento_da_aplicacao_quando_simulado_uma_queda.png b/docs/images/Tarefa2-Console_com_a_saida_do_monitoramento_da_aplicacao_quando_simulado_uma_queda.png new file mode 100644 index 0000000..7b1c6b3 Binary files /dev/null and b/docs/images/Tarefa2-Console_com_a_saida_do_monitoramento_da_aplicacao_quando_simulado_uma_queda.png differ diff --git a/docs/images/Tarefa2-Pagina_inicial_do_Swagger_da_aplicacao.png b/docs/images/Tarefa2-Pagina_inicial_do_Swagger_da_aplicacao.png new file mode 100644 index 0000000..cdfd30b Binary files /dev/null and b/docs/images/Tarefa2-Pagina_inicial_do_Swagger_da_aplicacao.png differ diff --git a/docs/images/Tarefa3-Console_com_a_confirmacao_da_criacao_do_servico_da_aplicacao.png b/docs/images/Tarefa3-Console_com_a_confirmacao_da_criacao_do_servico_da_aplicacao.png new file mode 100644 index 0000000..6bab6fb Binary files /dev/null and b/docs/images/Tarefa3-Console_com_a_confirmacao_da_criacao_do_servico_da_aplicacao.png differ diff --git a/docs/images/Tarefa3-Console_com_a_saida_do_monitoramento_da_aplicacao.png b/docs/images/Tarefa3-Console_com_a_saida_do_monitoramento_da_aplicacao.png new file mode 100644 index 0000000..a983567 Binary files /dev/null and b/docs/images/Tarefa3-Console_com_a_saida_do_monitoramento_da_aplicacao.png differ diff --git a/docs/images/Tarefa3-Console_com_a_saida_do_monitoramento_da_aplicacao_durante_a_atualizacao.png b/docs/images/Tarefa3-Console_com_a_saida_do_monitoramento_da_aplicacao_durante_a_atualizacao.png new file mode 100644 index 0000000..403871a Binary files /dev/null and b/docs/images/Tarefa3-Console_com_a_saida_do_monitoramento_da_aplicacao_durante_a_atualizacao.png differ diff --git a/pom.xml b/pom.xml index 517346c..60a85ae 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,10 @@ + + org.springframework.boot + spring-boot-starter-actuator + org.springframework.boot spring-boot-starter-hateoas @@ -35,8 +39,9 @@ - com.h2database - h2 + org.postgresql + postgresql + runtime diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 33cc6dd..069af1b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,14 +1,12 @@ server.port=8090 spring.datasource.name=customerdb +spring.datasource.url=${DATASOURCE_URL:jdbc:postgresql://127.0.0.1:5432/postgres} +spring.datasource.username=${DATASOURCE_USERNAME:postgres} +spring.datasource.password=${DATASOURCE_PASSWORD:postgres} +spring.datasource.driver-class-name=org.postgresql.Driver - -spring.datasource.url=jdbc:h2:file:~/${spring.datasource.name} -spring.datasource.username=sa -spring.datasource.password= -spring.datasource.driver-class-name=org.h2.Driver - -spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect spring.jpa.hibernate.ddl-auto=none flyway.schemas=customer @@ -18,6 +16,4 @@ logging.level.org.hibernate.SQL=DEBUG logging.level.org.hibernate.tool.hbm2ddl=DEBUG logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE logging.level.org.flywaydb.core.internal.dbsupport=DEBUG - logging.level.sample=DEBUG -