Esteira DevOps - CI/CD pipeline
O modelo DevOps (união das palavras development e operations) combina filosofia de trabalho, ferramentas e práticas que agilizam o processo de entregas e implantação de softwares. Todo o fluxo ocorre em cima de uma estrutura conhecida como Esteira DevOps ou CI/CD (continuous integration / continuous delivery) pipeline.
*Figura 1 - Etapas da Esteira DevOps
Nesse tutorial veremos como configurar o Jenkins para obter um projeto criado no Cronapp e executar as seguintes etapas do pipeline:
Obter o código do repositório GitHub,
Compilar o projeto,
Criar uma imagem Docker,
Salvar essa imagem no Docker Hub.
Pré-requisitos
Esse tutorial não prevê os passos iniciais para instalação e configuração do Jenkins e Docker. Porém, para executar o Jenkins, será necessário instalar:
Configurar as variáveis de ambiente:
Git,
Jenkins
Clique aqui para mais detalhes sobre a instalação do Jenkins e como vincular o Docker ao Jenkins.
No Cronapp, o modo Modo Avançado deve estar habilitado;
Usaremos como repositório:
GitHub: para o repositório de código;
Docker Hub: para o repositório de imagem.
Caso pretenda usar o GitHub e Docker Hub como seus repositórios de código e imagens respectivamente, certifique-se de que os dados sensíveis não sejam disponibilizados em repositórios públicos.
Ao criar repositório nesses sistemas, o padrão é estarem configurados como públicos.
Para esse tutorial utilizamos uma imagem Docker com o Jenkins, sendo executado no Linux.
Pipeline
No arquivo Jenkinsfile encontramos uma estrutura que utiliza o Pipeline, uma linguagem de domínio específico (DSL) com base na linguagem de programação Groovy. É utilizada pelo Jenkins para criar e personalizar os jobs.
Abaixo temos o conteúdo do Jenkinsfile que encontramos ao criar um projeto no Cronapp.
Arquivo Jenkinsfile
pipeline {
agent any
parameters{
choice(choices: ['true', 'false'], description: 'Selecione se quer usar conexões de banco e parâmetros dentro do conteiner.', name: 'CRONAPP_USE_CONTEXT')
choice(choices: ['DEV', 'PROD'], description: 'Selecione o perfil do banco de dados (TIER).', name: 'CRONAPP_TIER')
choice(choices: ['true', 'false'], description: 'Incluir projeto mobile.', name: 'CRONAPP_MOBILE_APP')
string(defaultValue: 'https://index.docker.io/v1/', description: 'URL do Registry (padrão é o docker hub).', name: 'CRONAPP_DOCKER_REGISTRY', trim: false)
string(defaultValue: 'INFO_SEU_USUARIO/NOMEDAIMAGEM', description: 'Informe o nome de sua imagem (se for registry privado, informe o caminho completo).', name: 'CRONAPP_DOCKER_IMAGE_NAME', trim: false)
string(defaultValue: 'INFO_ID_CREDENCIAL_DOCKERHUB', description: 'Informe a credencial (secret) usada para acesso ao registry.', name: 'CRONAPP_DOCKERHUB_ACCESS', trim: false)
string(defaultValue: 'INFO_URL_REPO_GIT_HTTPS', description: 'Informe o endereço HTTPS do repositório Git.', name: 'CRONAPP_GIT_URL', trim: false)
string(defaultValue: 'INFO_ID_CREDENCIAL_GITHUB', description: 'Informe a credencial (secret) usada para acesso ao reposotório Git.', name: 'CRONAPP_GIT_USERPASS', trim: false)
}
stages {
stage('Git Clone') {
steps {
checkout([$class: 'GitSCM', branches: [[name: '${CRONAPP_TAG_VERSION}']], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'CloneOption', noTags: false, reference: '', shallow: false]], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${CRONAPP_GIT_USERPASS}", url: "${CRONAPP_GIT_URL}"]]])
}
}
stage('Maven and Docker Build') {
steps {
sh '''
docker build -t ${CRONAPP_DOCKER_IMAGE_NAME}:${CRONAPP_TAG_VERSION}-${CRONAPP_TIER} --build-arg TIER=${CRONAPP_TIER} --build-arg CONTEXT_USE=${CRONAPP_USE_CONTEXT} --build-arg MOBILE_APP=${CRONAPP_MOBILE_APP} .
'''
}
}
stage('Docker Push') {
steps {
withDockerRegistry(credentialsId: "${CRONAPP_DOCKERHUB_ACCESS}", url: "${CRONAPP_DOCKER_REGISTRY}") {
sh '''
docker push ${CRONAPP_DOCKER_IMAGE_NAME}:${CRONAPP_TAG_VERSION}-${CRONAPP_TIER}
'''
}
}
}
stage('Docker Clean') {
steps {
sh '''
docker image prune -f
'''
}
}
}
}
Declarações usadas:
agent any: o Jenkins permite distribuir a carga de compilação para diferentes nós, porém, o "agent any" define que qualquer nó disponível pode realizar essa ação.
parameters: parâmetros passados para o Jenkins com informações dos repositórios e configurações de banco de dados. Acesse o tópico Executar pipeline para mais detalhes.
stages: define os passos do pipeline do Jenkins:
Git Clone: clona o projeto do repositório de código informado;
Maven and Docker Build: faz o build do projeto e gera uma imagem Docker com base nas configurações do Dockerfile do projeto;
Docker Push: envia a imagem recém-criada para o Docker Hub;
Docker Clean: apaga todas as imagens sem tags, incluindo as que foram geradas no build.
Passos
Para os passos abaixo é necessário acessar a aplicação do Jenkins em seu navegador.
Configuração inicial
Esse tópico apresentará os passos iniciais ao acessar o Jenkins pela primeira vez, caso já possua um Jenkins configurado, pule para o tópico Criar credenciais.
A primeira coisa solicitada pelo Jenkins é a Senha de administrador, essa senha é exibida no log de instalação e também está em um arquivo no diretório apontado na própria tela (destaque 1 da figura abaixo). Informe a senha e clique em Continuar.
Figura 2 - Janela ao executar o Jenkins pela primeira vez
Selecione a opção Install suggested plugins para que o Jenkins adicione automaticamente os plugins mais comuns.
Figura 2.1 - Opção para adicionar os plugins mais utilizados
Aguarde o Jenkins instalar os plugins (Figura 2.2).
Figura 2.2 - Instalação dos plugins Jenkins
Após a instalação dos plugins será exibido uma janela para cadastrar um usuário administrador, informe os campos solicitados e clique em Save and Continue.
Figura 2.3 - Criação do primeiro usuário administrador
Em seguida o Jenkins exibirá o domínio e porta em que estará rodando, Clique em Save and Finish.
Figura 2.4 - Criação do primeiro usuário administrador
No último passo o Jenkins apenas informa que a instalação está completa, clique em Start using Jenkins para ser direcionado até a página de Bem-vindo da aplicação Jenkins (Figura 2.5).
Figura 2.5 - Página inicial da aplicação Jenkins
Criar credenciais
Vamos usar o Jenkins para armazenar as credenciais de acesso do GitHub e Docker Hub, impedindo que esses dados fiquem expostos.
No link Painel de controle (canto superior esquerdo), posicione o cursor do mouse até exibir o ícone do menu e clique para exibir o menu (destaque 1 da figura 3), acesse Gerenciar Jenkins > Manager Credentials para abrir a página de Credentials. Na lista Stores scoped to Jenkins, posicione o cursor do mouse em (global) (coluna Domains) até exibir o ícone do menu e clique para exibir as opções (destaque 2 na figura 2.1), selecione a opção Add Credentials.
Figura 3 - Acesso ao gerenciador de Credenciais do Jenkins
Alimente os campos com os seus dados do Github e clique no botão Create para cadastrar, após isso, clique em Credentials no breadcrumbs (destaque 1 da figura 3.1) para retornar a tela do gerenciar de credenciais (Figura 3) e criar um nova credencial, dessa vez para preencher com os dados do Dockerhub.
Figura 3.1 - Acesso ao gerenciador de Credenciais do Jenkins
GitHub
Kind: deixe a opção padrão Username with password.
Scope: selecione a opção Global.
Username do GitHub.
Password: informe o token gerado a partir da sua conta do Github.
ID: para facilitar a identificação, sugerimos adicionar o nome do projeto e serviço da credencial (ex.: "nome_do_projeto-github").
Description: adicione uma descrição própria ou informe o mesmo valor do ID (ex.: "Credencial do Github.").
Docker Hub
Kind: deixe a opção padrão Username with password.
Scope: selecione a opção Global.
Username do Docker Hub.
Password do Docker Hub.
ID: para facilitar a identificação, sugerimos adicionar o nome do projeto e serviço da credencial (ex.: "nome_do_projeto-dockerhub").
Description: adicione uma descrição própria ou informe o mesmo valor do ID (ex.: "Credencial do Dockerhub.").
Anote os ID's utilizados nas 2 credenciais, usaremos eles ao executar o pipeline.
Ao final, teremos 2 credenciais globais (Figura 3.2).
Figura 3.2 - Após finalizar o cadastro do Github e Dockerhub
Adicionar plugins
O próximo passo será a instalação do Plugin Docker Pipeline.
Figura 4 - Instalação do plugin Docker pipeline
No link Painel de controle (canto superior esquerdo), posicione o cursor do mouse até exibir o ícone do menu e clique para exibir o menu (destaque 1 da figura 4), acesse Gerenciar Jenkins > Gerenciar extensões para abrir a página de Gerenciador de extensões.
Selecione a aba Disponíveis.
Digite o nome do plugin: Docker pipeline
Marque a caixa de seleção do plugin.
Clique em Baixar agora e instalar após o reinício.
O Jenkins mudará para a página de instalação do plugin, aguarde esse procedimento e, em seguida, logue novamente na aplicação.
Criar o Job
No link Painel de controle (canto superior esquerdo), posicione o cursor do mouse até exibir o ícone do menu e clique para exibir o menu (destaque 1 da figura 5), acesse Nova tarefa para abrir a página inicial de configuração. Informe um nome para o job (destaque 2), selecione a opção Pipeline (3) e clique em Tudo certo (4)
Figura 4.1 - Criando um Job do tipo Pipeline
Parâmetro da Branch
Antigamente recomendávamos o uso de um plugin para listar todas as Branchs e Versões tags do Git do projeto, permitindo apenas selecionar a versão do projeto que iria ser utilizada no pipeline. Porém, esse plugin deixou de ser mantido e apresenta falha de segurança, assim, vamos criar um parâmetro para informar manualmente a branch ou versão tag que será utilizada.
Marque a opção Esta construção é parametrizada e na caixa de seleção exibida, selecione a opção Parâmetro de texto (Figura 5),
Figura 5 - Novo parâmetro do tipo texto
Será exibido uma área com alguns campos, preencha como informado abaixo.
Figura 5.1 - Configuração do parâmetro CRONAPP_TAG_VERSION
Nome: nome do parâmetro utilizado no arquivo Jenkinsfile, informe exatamente o valor "CRONAPP_TAG_VERSION".
Valor padrão: nome da branch ou tag version que será utilizado, esse valor poderá ser alterado depois, ao executar o pipeline.
Descrição do campo.
Script do pipeline
Ainda nessa página, desça até a área de configuração do Pipeline e preencha os campos abaixo. Nessa área, os campos identados serão exibidos a medida que os campos superiores forem selecionados.
Figura 6 - Configuração do sistema de controle de código do pipeline
Definition: selecione a opção "Pipeline script from SCM".
SCM: selecione a opção "Git".
Repositories / Repository URL: endereço Git (https) do seu projeto.
Repositories / Credentials: selecione a credencial de acesso Git criado no primeiro passo desse tutorial.
Script Path: nesse campo deve ser informado o endereço do arquivo Jenkinsfile dentro do projeto. Como esse arquivo fica na pasta raiz, informe apenas o nome do arquivo “
Jenkinsfile”.Demais campos: deixe a configuração padrão do Jenkins nos outros campos.
Finalizada as configurações, clique em Salvar.
Obter demais Parâmetros do pipeline
Para executar o pipeline são necessários outros parâmetros além do que cadastramos no tópico Parâmetro da Branch. Esses parâmetros podem ser obtidos automaticamente a partir do arquivo Jenkinsfile do projeto. Assim, será necessário executar o pipeline pela primeira vez, durante a execução, o Jenkins irá obter os parâmetros definidos e apresentará uma falha, já que os parâmetros ainda não foram preenchidos.
Na página do Job criado, clique em Construir com parâmetros (destaque 1 da figura 7) e na tela de Construção será exibido apenas o parâmetro criado anteriormente: CRONAPP_TAG_VERSION. Nesse momento não é necessário informar um valor correto neste campo, clique em Construir (2).
Figura 7 - Configuração do sistema de controle de código do pipeline
Após o erro de execução (figura 7.1), o Jenkins já possui todos os parâmetros necessários.
Figura 7.1 - Erro gerado na primeira execução do pipeline
Executar o pipeline
Clique novamente em Construir com parâmetros (destaque 1 da figura 8) para voltar a tela de execução do pipeline. Verifique que agora são exibidos todos os parâmetros contidos no arquivo Jenkinsfile, mais o parâmetro "CRONAPP_TAG_VERSION" que incluímos no tópico Parâmetro da Branch.
Configure os campos como descritos abaixo.
Figura 8 - Tela de execução do Pipeline
CRONAPP_USE_CONTEXT: permite incluir no pacote final do projeto as configurações usadas no perfil do banco de dados (arquivo
context.xml). Para mais informações, acesse o tópico "Exportando war via comando (High-code)" em Importar e exportar projetos.true: adiciona as configurações do perfil de banco de dados;
false: não adiciona as configurações do perfil de banco de dados.
CRONAPP_TIER: informe qual perfil de banco de dados será usado.
DEV: desenvolvimento;
PROD: produção.
CRONAPP_MOBILE_APP: permite incluir na raiz do pacote um diretório (
mobileapp/) com a aplicação mobile do projeto.true: inclui a aplicação mobile;
false: não inclui a aplicação mobile.
CRONAPP_DOCKER_REGISTRY: endereço do Registry usado da imagem, o valor padrão é o Dockerhub.
CRONAPP_DOCKER_IMAGE_NAME: nome da
<conta>/<repositório>da imagem.CRONAPP_DOCKERHUB_ACCESS: identificador da credencial de acesso ao Docker Hub.
CRONAPP_GIT_URL: endereço (HTTPS) do repositório Git.
CRONAPP_GIT_USERPASS: identificador da credencial de acesso ao GitHub,
CRONAPP_TAG_VERSION: tag version ou branch que será usada na construção.
Clique em Construir para iniciar o processo.
Na página Stage View é possível acompanhar o andamento de cada etapa do processo (destaque 1 da figura 8.1). O tempo de execução do pipeline pode variar bastante, mas em média costuma levar de 8 a 20min, sendo influenciado por fatores como velocidade da internet e o hardware do equipamento que executa o Jenkins.
Para reduzir esse tempo, use métodos para adicionar seu .m2/repository no Dockerfile, evitando a necessidade do download das dependências a cada execução do job, ou realize o build da aplicação fora do Dockerfile.
Figura 8.1 - Job do pipeline em execução
Em caso de falha no processo, acesse o Console Output (2 da figura 8.1) e verifique o que causou o erro.
Resultado
Finalizado a execução do pipeline, acesse o Docker Hub para verificar a imagem criada do seu projeto.
Figura 9 - Imagem Docker gerada através do pipeline deste tutorial
Problemas e soluções
Abaixo descrevemos alguns problemas que podem ocorrer durante a execução do pipeline e como solucioná-los.
Antes de tudo, é preciso ter ciência que o ambiente que for executar o Jenkins necessita do Java SDK 11, Tomcat 9, Git, Maven, Docker e de suas respectivas variáveis de ambiente configuradas. Clique aqui para mais detalhes sobre a instalação do Jenkins.
Problema | Sugestão de solução |
|---|---|
| Dependendo da forma como de como o Git foi instalado, talvez seja necessário alterar as configurações de variáveis de ambiente. clique aqui para mais detalhes. |
| Verifique se o cliente Docker está aberto e em execução. |
| Verifique se o plugin Docker Pipeline está instalado no Jenkins. |