Criando uma pipeline de CI/CD para aplicações Java no Azure DevOps

Criando uma pipeline de CI/CD para aplicações Java no Azure DevOps

No mundo atual de desenvolvimento de software, a entrega rápida e eficiente de código é crucial para as equipes de desenvolvimento. Implementar uma pipeline de CI/CD automatizada garante entregas rápidas e de alta qualidade.

Neste artigo vamos criar um modelo prático de CI/CD para projetos Java, incluindo deploy contínuo em um cluster Kubernetes.

Requisitos Básicos

Para publicar nossa aplicação, utilizaremos os seguintes recursos:

  • Conta no Azure Devops: https://dev.azure.com
  • Conta no Azure Portal: https://portal.azure.com
  • Acesso a um cluster Kubernetes (AKS) ou uma Virtual Machine (VM)
  • Azure Container Registry (ACR): que será usado para armazenar nossas imagens Docker
  • Azure CLI: https://learn.microsoft.com/en-us/cli/azure/install-azure-cli
  • Kubectl: https://kubernetes.io/docs/tasks/tools
  • Projeto Java

Repositório

Para nossa demonstração de hoje, iremos usar o código que está disponível no repositório https://dev.azure.com/toolbox-devops/toolbox-playground/_git/hello-world-java, no qual temos um simples “Hello World” feito em Java.

Se desejar, você pode clonar o código em sua máquina local para seguir os mesmos passos que temos disponíveis aqui. Ou, se preferir, pode clonar o repositório direto no seu Azure Devops clicando em <Seu_Azure_DevOps_Projeto> → Repos →Import repository → cole a URL acima referente ao repositório → import, como mostram as duas imagens abaixo:

Configuração do Ambiente

Como o nosso propósito é demonstrar o uso das pipelines do Azure DevOps, não iremos abordar hoje como gerenciar/publicar os serviços dentro no Azure, então será necessário garantir que existam alguns recursos de infraestrutura para que nossa pipeline tenha onde publicar nossa aplicação.

Dito isso, a primeira coisa que devemos fazer é criar uma conta no Microsoft Azure, se você não possuir, e depois instalar o Azure CLI. Existem diversas formas de fazer isso, contudo, se você estiver executando o Linux (Debian/Ubuntu), incluindo o WSL do Windows, o jeito mais fácil para conseguir isso é rodar o comando:

				
					curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
				
			

Para mais detalhes e/ou saber como instalar o Azure CLI em outros Sistemas Operacionais, você pode consultar https://learn.microsoft.com/pt-br/cli/azure/install-azure-cli.

Em seguida, execute o comando:

				
					az login
				
			

Feito isso será aberto o seu browser pedindo para que você se autentique nos serviços do Azure. Basta inserir suas credenciais que o terminal irá reconhecer as subscriptions e tenants a que o seu usuário tem acesso.

Agora instale a extensão do Azure CLI para gerenciar o Kubernetes usando o comando:

				
					az aks install-cli
				
			

Vamos fazer deploy em um cluster kubernetes. Caso você não tenha um cluster Kubernetes para testes, é possível executar os comandos abaixo para provisionar um:

				
					## Criar novo cluster Kubernetesaz
aks create --resource-group <RESOURCE_GROUP> --name <CLUSTER_NAME> --node-count 1 --enable-addons monitoring --generate-ssh-keys

## Atualizar arquivos de configuração usado pelo Kubectl
az aks get-credentials --resource-group <RESOURCE_GROUP> --name <CLUSTER_NAME>
				
			

Vamos também criar um namespace chamado dev, a fim de garantir o isolamento dos nossos deployments:

				
					kubectl create namespace dev
				
			

Configuração da Service Connection no Azure Devops

No Azure Devops, Service Connection é uma forma de autenticação que ele usa para poder se conectar a outros serviços, podendo ser serviços do próprio Azure ou de terceiros.

Para criarmos essas conexões, dentro do Azure Devops, vamos em Project Settings, depois Service Connections e por fim New Service Connection. Na lista que abre, vamos escolher Docker Registry e fornecer os detalhes que precisamos pro nosso ACR.

Também vamos criar outra Service Connection, mas dessa vez vamos escolher o tipo Kubernetes e inserir o output do comando:

				
					az aks get-credentials
				
			

Perfeito! Com todo o ambiente configurado, agora podemos seguir para as pipelines de CI e CD.

Criação da Pipeline de Integração Contínua (CI)

Para começarmos, vamos usar o arquivo de pipelines azure-pipelines.yaml, que está na raiz do nosso repositório. Ele realiza a criação de uma pipeline através dos stages, jobs e tasks/steps:

				
					trigger:
  branches:
    include:
      - main

pool:
  vmImage: 'ubuntu-latest'

stages:
  - stage: Build
    jobs:
      - job: Build
        steps:
          - checkout: self
          - task: Maven@3
            inputs:
              mavenPomFile: 'pom.xml'
              mavenOptions: '-Xmx3072m'
              javaHomeOption: 'JDKVersion'
              jdkVersionOption: '1.11'
              jdkArchitectureOption: 'x64'
              goals: 'clean package'
          - task: PublishBuildArtifacts@1
            inputs:
              pathToPublish: '$(System.DefaultWorkingDirectory)/target/*.jar'
              artifactName: 'drop'
				
			

Vamos agora dar uma olhada com um pouco mais de detalhes em cada um dos blocos para garantir nosso entendimento sobre eles:

				
					trigger:
  branches:
    include:
      - main
				
			

Triggers (ou gatilhos) são os responsáveis por informar qual será a ação que fará com que essa pipeline seja executada.

Existem diversas configurações que podemos usar para os mais diferentes cenários. Mas em resumo, uma pipeline pode ser executada quando um Pull Request é aberto contra alguma branch específica, quando ocorre alguma atualização na em determinada branch, ou até mesmo ela pode nunca ser executada de forma automática. O Azure Devops conta com várias configurações de triggers que podem ser encontradas em : https://learn.microsoft.com/pt-br/azure/devops/pipelines/repos/azure-repos-git?view=azure-devops&tabs=yaml#ci-triggers.

				
					pool:
    vmImage: 'ubuntu-latest'
				
			

Uma das grandes vantagens do Azure DevOps, é contar com agentes hospedados pela própria Microsoft, onde temos vários sabores como Ubuntu, Windows e MacOS. Cada um dos agentes vem com diversos softwares já instalados, o que facilita e muito a criação das nossas pipelines. Além do mais, também é possível instalar seus próprios agentes em outras máquinas e conectá-los ao Azure DevOps para poderem ser usados.

Todos os detalhes dos agentes do Azure DevOps podem ser encontrados em https://learn.microsoft.com/pt-br/azure/devops/pipelines/agents/agents?view=azure-devops&tabs=yaml%2Cbrowser.

				
					stages:
  - stage: Build
				
			

Stages são agrupamentos de determinadas funções. Por exemplo, stage de build (responsável por executar todas as tarefas de build), stage de CD (tarefas que precisam ser executadas para que o CD ocorra), etc.É possível criar vários stages na sua pipeline e, por padrão, a execução é feita sequencialmente. Você pode ver todos os detalhes disponíveis sobre o CD em https://learn.microsoft.com/pt-br/azure/devops/pipelines/process/stages?view=azure-devops&tabs=yaml.

				
					jobs: - job: Build steps:
				
			

Jobs também são uma forma de agrupar determinadas tarefas, mas dessa vez dentro dos stages. Por exemplo, para fazer um build de uma aplicação, muitas vezes é necessário instalar aplicações, fazer mudanças em arquivos e depois rodar o comando de build. Tudo isso pode ser organizado dentro dos jobs. No site https://learn.microsoft.com/pt-br/azure/devops/pipelines/process/phases?view=azure-devops&tabs=yaml, você encontra todos os parâmetros disponíveis para jobs.

				
					- checkout: self
- task: Maven@3
  inputs:
    mavenPomFile: 'pom.xml'
    mavenOptions: '-Xmx3072m'
    javaHomeOption: 'JDKVersion'
    jdkVersionOption: '1.11'
    jdkArchitectureOption: 'x64'
    goals: 'clean package'
- task: PublishBuildArtifacts@1
  inputs:
    pathToPublish: '$(System.DefaultWorkingDirectory)/target/*.jar'
    artifactName: 'drop'
				
			

Por fim, chegamos no item mais granular das pipelines do Azure DevOps, que são as Tasks. Elas nada mais são do que comandos já prontos, que podem ser executados informando várias entradas diferentes, que terão a sua saída alterada, de acordo com os comandos inseridos. No exemplo acima, executamos duas tasks: a primeira é a task do Maven, que é a responsável por compilar a aplicação Java (definimos alguns parâmetros do Maven) e a segunda task é a que vai fazer a publicação do arquivo gerado (arquivo *.jar) na pipeline. Mais tarde publicaremos esse arquivo no nosso Kubernetes. 

Para resumir todo esse código YAML em uma imagem, uma pipeline CI/CD vai conter as seguintes etapas e visão geral arquitetural (baseado em https://learn.microsoft.com/pt-br/azure/devops/pipelines/get-started/key-pipelines-concepts?view=azure-devops):

(baseado em https://learn.microsoft.com/pt-br/azure/devops/pipelines/get-started/key-pipelines-concepts?view=azure-devops)

Criação da Pipeline de Entrega Contínua (CD)

A parte do CD também está no arquivo azure-pipelines.yaml, logo após o código que vimos anteriormente:

				
					- stage: Deploy
    dependsOn: Build
    jobs:
      - job: Deploy
        pool:
          vmImage: 'ubuntu-latest'
          steps:
            - checkout: self
            - download: current
              artifact: drop
            - task: Docker@2
              inputs:
              containerRegistry: '<DOCKER_REGISTRY_SERVICE_CONNECTION>'
              repository: 'java-app'
              command: 'buildAndPush'
              Dockerfile: '**/Dockerfile'
              tags: |
                latest
            - task: KubernetesManifest@0
              inputs:
              action: 'deploy'
              kubernetesServiceEndpoint: '<KUBERNETES_SERVICE_CONNECTION>'
              namespace: 'default'
              manifests: 'deployment.yaml'
              containers: |
                java-app: <DOCKER_REGISTRY>/java-app:latest
				
			

Mais uma vez, temos o mesmo formato de comandos que vimos na parte de CI porém, dessa vez estamos usando algumas tarefas (ou tasks) diferentes, como a do Docker (responsável por compilar e publicar a imagem Docker da nossa aplicação e salvá-la no nosso Container Registry) e as tarefas do Kubernetes (que irá usar o arquivo deployment.yaml do nosso repositório para publicar no Kubernetes em si).

Testando e validando a pipeline

Vamos começar fazendo o setup da nossa pipeline. Para isso, vamos na opção Pipelines do Azure DevOps, em seguida vamos criar uma nova pipeline baseada em um repositório e em um arquivo de pipeline já existente. Para isso, no Azure DevOps clique no menu Pipelines e em seguida no botão “Create Pipeline”:

Em seguida, selecione onde está o seu código. Neste caso, o repositório está hospedado no próprio Azure DevOps, então selecionaremos Azure Repos Git:

Na aba seguinte, selecione o seu repositório hello-world-java, onde se encontra o projeto Java e o arquivo de configuração da pipeline que acabamos de ver.

Por fim, selecione “Configure your pipeline from an Existing Azure Pipeline Yaml file”, selecione a branch main e o path como sendo o arquivo Yaml de configuração da pipeline, denominado como azure-pipeline.yaml, e clique em continue:

Por último, clicando no botão de Run, o Azure DevOps irá criar e executar a pipeline a partir do nosso arquivo YAML, que contém nossas stages, jobs e tarefas  CI e CD.

Finalizando estas etapas, sua primeira pipeline no Azure DevOps será executada com sucesso:

Você pode fazer vários testes, alterando o código da aplicação e fazendo commits. Verifique se o build é executado automaticamente, se a imagem Docker foi criada no ACR e refletida no Kubernetes.

Acelere sua carreira conosco

A Mentoria Next Level DevOps é um programa de mentoria de 12 meses com encontros semanais ao vivo, com um grupo seleto e restrito, onde estaremos do seu lado para mantê-lo relevante e atualizado no mercado de tecnologia, aprendendo e implementando as melhores práticas e ferramentas de DevOps.

Clique aqui para entrar na prioridade pela melhor oferta de lançamento

Dicas Adicionais

Um dos segredos da cultura DevOps é falhar o mais rápido possível, evitando que o seu cliente final seja impactado. Dessa forma, algumas boas práticas são extremamente importantes de se adicionar na sua pipeline, tais como:

Testes Unitários e de Integração: Conjunto mínimo de testes para ser adicionado no seu step build;

Análise Estática de Código e Segurança: Ferramentas como SonarQube/Sonnarcloud são importantes aliados para análise estática de código, te ajudando a descobrir problemas de qualidade. Já o Snyk é capaz de varrer seu código em busca de vulnerabilidades de segurança;

Monitoramento: É importante que toda aplicação seja monitorada para garantir que qualquer problema seja rapidamente detectado e resolvido. No Azure, existe um recurso chamado Application Insights que pode te ajudar com isso.

Conclusão

Com essa pipeline de CI/CD automatizada para aplicações Java usando Azure DevOps, você pode agilizar seus processos de desenvolvimento e entrega contínua, dessa forma, a equipe pode se concentrar mais em desenvolvimento e menos em tarefas operacionais. Automatização com CI/CD é chave para ganhar mais agilidade no mundo de desenvolvimento de software DevOps!