5.3 Construindo o Projeto Utilizando Contêiner
A construção de um projeto de software, em geral, demanda uma série de atividades até que o código em baixo nível esteja disponível para execução.
Quando se inicia no mundo da programação, nas matérias introdutórias, não nos preocupamos muito com isso pois desenvolvemos projetos simples que podem ser compilados facilmente dentro de um ambiente de desenvolvimento por meio de um clique. Entretanto, quando pensamos em grandes projetos que precisam ser construídos de forma automatizada passa a ser de fundamental importância as chamadas ferramentas de construção (build) de software.
Existem inúmeras delas disponíveis para as mais diferentes linguagens de programação. Por exemplo, no caso de C/C++ temos o famoso Make, para Ruby tem-se o Rake, para JavaScript tem-se o Grunt, para .Net tem-se o NAnt, e para Java têm-se o Ant, o Maven, o Gradle, dentre outros.
Todos eles apresentam funções bem similares e têm a missão de, a partir de um determinado conjunto de código fonte e demais recursos, resolver as dependências, ou seja, encontrar todas as bibliotecas externas que esse código fonte referencia, e construir o código de baixo nível da aplicação. Durante esse processo, é possível também executar os testes no código para se ter certeza de que tudo está operacional, gerar relatórios sobre o produto, gerar a documentação, dentre outras tarefas, de modo que, se todas forem completadas com sucesso, conseguiremos chegar ao resultado final que é a aplicação em um nível de abstração que possa ser executada.
No caso do nosso projeto exemplo, ele utiliza o Maven como ferramenta de build. O processo de construção da aplicação está configurado no arquivo denominado pom.xml
, localizado na raiz do repositório do projeto. É nesse arquivo que se determina quais são, por exemplo, as dependências que a loja precisa pra ser compilada, testada, e empacotada.
Um cenário típico de construção da aplicação até o seu empacotamento, passa por por uma série de objetivos (goals) na terminologia do Maven. Alguns objetivos comuns são:
mvn compile
: tem o objetivo de compilar todo código fonte da aplicação;mvn test-compile
: tem o objetivo de compilar todo o código de teste da aplicação;mvn test
: tem o objetivo de executar os testes na aplicação;mvn integration-test
: tem o objetivo de executar os teste de integração na aplicação;mvn verify
: tem o objetivo de executar tanto os testes unitários quanto de integração na aplicação. Equivalente a ummvn test integration-test
;mvn package
: tem o objetivo de empacotar a aplicação, produzindo ou o arquivo .jar ou .war, dependendo do tipo de aplicação. No nosso caso, será gerado o arquivocombined/target/devopsnapratica.war
mvn clean
: limpa todo código gerado durante o processo de build acima, mantendo apenas os fontes originais.
Esses são alguns dos objetivos disponíveis para uso com o Maven, que pode ser utilizado na linha de comando pela invocação do comando mvn
, conforme ilustrado abaixo:
No exemplo acima, o comando mvn foi executado com dois objetivos distintos, clean
, para limpar todos os arquivos gerados e compile
, para construir o código objeto da aplicação.
Um dos aspectos fundamentais para a garantia da qualidade dos produtos de software e ambém para alertar o desenvolvedor sobre possíveis problemas em relação às mudanças realizadas é o teste. Em DevOps é comum que os produtos contenham não apenas o código da aplicação em si, mas também código para o teste unitário, de integração, e outros tipos de testes automatizados, que visam a garantir a qualidade do produto e podem ser também utilizados para garantir o sucesso do build após um determinado commit ser realizado.
No caso do nosso código exemplo, ele não possui muito código de teste associado, mas existem alguns poucos que podem ser compilados e executados com o comando mvn test-compile test
, conforme ilustrado a seguir.
No exemplo, foram localizados dois testes na classe br.com.devopsnapratica.admin.controller.AdminLoginControllerTest
, que tem por objetivo verificar problemas no pacote admin
. Se algum teste falar, o resultado final não seria o de BUILD SUCCESS
, mas sim de BUILD FAILURE
, de modo que isso poderia impedir a entrega do produto caso alguma falha fosse detectada.
Os comandos acima foram possíveis de serem executados pois tenho o Maven instalado na minha máquina local, na qual fiz o clone do projeto disponível em https://gitlab.com/aurimrv/loja-virtual-devops. Entretanto, para implementarmos o pipeline DevOps teremos a necessidade de encontrar um contêiner que permita a construção desse projeto na nuvem.
Contêiner Docker com Maven e JDK 1.8
Analisando a estrutura de nosso projeto, sabemos que ele demanda uma versão específica do Java para ser compilado, Java 1.8 da Oracle e o Maven. Em minha máquina a tentativa de compilação do projeto com o OpenJDK 1.8 foi frustrada e resultou em BUILD FAILURE
.
Pesquisando no Hub Docker, localizei um repositório no Hub Docker com imagens ideais para quem trabalha com JDK e Maven: https://registry.hub.docker.com/_/microsoft-java-maven. Nesse repositório existem inúmeras imagens com diferentes versões do Maven e do JDK disponíveis. No nosso caso, optei pela imagem nomeada mcr.microsoft.com/java/maven:8-zulu-debian9
.
Para testar a imagem basta executar os comandos abaixo:
Observem que o resultado da criação do contêiner a partir desta imagem foi a execução simples do comando mvn
sem qualquer objetivo definido. Desse modo, para utilizá-la para o nosso propósito necessitamos realizar duas ações:
Compartilhar o código de nossa aplicação com o contêiner
Executar o comando
mvn
de forma personalizada, indicando a localização dopom.xml
de nossa aplicação e os objetivos desejados para a construção da aplicação.
Para realizar as ações acima, basta alterar ligeiramente o comando docker run
incluindo os seguintes parâmetros, considerando a configuração de minha máquina. No caso de vocês, façam as alterações de diretórios correspondentes.
A primeira alteração foi a inclusão do parâmetro -v /home/auri/temp/devops-extra/cap-05/maven-jdk/loja-virtual-devops:/home/loja-virtual-devops
, que basicamente compartilha o diretório com a cópia local do repositório da loja virtual com o contêiner. Desse modo, o diretório /home/loja-virtual-devops
dentro do contêiner está refletido no diretório local /home/auri/temp/devops-extra/cap-05/maven-jdk/loja-virtual-devops
.
Em seguida, após o nome da imagem, foi incluída a personalização do comando mvn
que desejamos executar. Nesse caso, mvn -f /home/loja-virtual-devops/pom.xml clean package
. Ou seja, peço ao Maven que utilize o arquivo pom.xml
da aplicação de dentro do contêiner e execute os objetivos clean package
. Desse modo, se tudo correr bem, será gerado o arquivo no diretório combined/target/devopsnapratica.war
.
A execução do comando acima levará algum tempo mas, como será observado, permitiu a construção da aplicação exemplo sem utilizar o Maven e o compilador locais. Tudo foi feito dentro do contêiner escolhido. Ao término da execução do comando mvn -f /home/loja-virtual-devops/pom.xml clean package
, o contêiner encerra sua execução e é possível verificar que o arquivo .war
está também presente no diretório local, compartilhado com o contêiner.
Importante observar acima que o proprietário e o grupo associados aos arquivos acima são root:root
pois foram gerados dentro do contêiner e copiados para a pasta local pelo processo do docker
. Caso deseje escrever nesse diretório ou realizar alguma alteração qualquer é necessário, primeiro, restaurar ao mesmo a posse para o usuário local que pode ser feito com o comando abaixo:
Chegando até aqui com sucesso estamos aptos a iniciar a configuração de nosso servidor de intetração contínua.
Last updated