Automatizando o Build e Envio da Aplicação para Docker Hub com GitHub Actions

Roberto Sarquis e Diego Mello | conteudo@hmit.com.br

Publicado em 19 de novembro de 2024

Você tem uma aplicação do tipo Spring Web executada em um contêiner Docker e deseja automatizar o processo da geração de uma nova versão sempre que ocorrer um push na branch master, incluindo o build com Maven, a criação da imagem Docker e o envio da imagem versionada para o Docker Hub.
Em outras palavras, você pretende implementar um fluxo de CI/CD (Continuous Integration / Continuous Deployment) para obter agilidade, confiabilidade e escalabilidade, um processo que reflete as práticas de DevOps.

Se esse é o seu cenário, este artigo lhe mostrará como como montar uma automação deste processo utilizando o GitHub Actions. A seguir, veremos como realizar esse processo de maneira prática. Antes, porém, vamos começar com um exemplo básico, uma espécie de “Hello World”.

Siga os passos: abra o repositório no GitHub, clique em “Actions” e, em seguida, em “set up a workflow yourself”, conforme mostrado na imagem abaixo.

Ao fazer isso, o GitHub abrirá um arquivo YAML (.yml) em branco, onde será configurado o workflow. Insira os comandos mostrados no print abaixo ou, se preferir, copie o código diretamente através do link para o arquivo de exemplo, disponível logo abaixo.
https://github.com/sarquis/blockchain/blob/master/.github/workflows/main.yml

Vamos analisar os comandos. Na primeira seção, estamos nomeando o workflow como “GitHub Actions Demo”; na segunda, definimos o evento que disparará a execução, neste caso, um push na branch master. Por fim, estamos definindo o trabalho (job) a ser realizado, que, neste exemplo, será apenas exibir a mensagem “It works!”, utilizando o ambiente (runner) Ubuntu.

Após finalizar a edição do arquivo de workflow, faça o commit no repositório utilizando a opção “Commit changes”. Com isso, o processo estará concluído. Agora, para realizar um teste, faça um commit na branch master. Em seguida, acesse o seu repositório no GitHub e clique novamente na aba “Actions” para verificar a execução do workflow.

Na página “Actions”, você verá um painel com os workflows do seu repositório. Na seção “workflow runs”, estão listadas todas as execuções dos workflows, com detalhes como a data de execução, o tempo necessário para conclusão, se ocorreu uma falha, etc. Para visualizar mais detalhes da execução clique na última execução do seu workflow. Em seguida, será exibido o job executado, neste caso, “my_job_demo”. Clique no nome do job para visualizar os detalhes da execução.

Pronto! Nesta página temos os detalhes da execução do nosso job confirmando que foi executado com sucesso e que a nossa mensagem “It works!” foi exibida. Agora que adquirimos o conhecimento básico sobre o funcionamento do GitHub Actions, vamos seguir para o workflow do cenário inicial.

Vamos configurar o arquivo de workflow em partes, descrevendo o que cada seção faz. Logo abaixo, você encontrará um link para o arquivo completo de exemplo:

https://github.com/sarquis/bridge/blob/master/.github/workflows/ci-cd.yml

Começaremos definindo o nome do workflow,o evento que disparará a execução do job e o runner. Similar ao exemplo básico anterior, até aqui não há nada de novo.

name: CI/CD Pipeline
on:
  push:
    branches: [ “master” ]
jobs:
  build:
    runs-on: ubuntu-latest

Continuando, vamos definir os primeiros passos da execução. Inicialmente, realizamos o checkout do projeto, baixando o código da branch. Em seguida, configuramos a JDK que será utilizada, neste caso, a distribuição OpenJDK 17. Por último, executamos o build com o Maven. O comando “working-directory: ./bridge” indica o diretório onde está localizado o projeto Java que a ser compilado.

steps:

– name: Checkout code
uses: actions/checkout@v4

– name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: ’17’
distribution: ‘temurin’
cache: maven

– name: Build with Maven
run: mvn -B clean package -DskipTests
working-directory: ./bridge


Nos próximos passos, criaremos a imagem Docker versionada, obtendo a versão diretamente do projeto Java. A versão é obtida do pom.xml e salva na variável de ambiente “VERSION”, para ser utilizada na criação da imagem.

– name: Extract project version
id: extract_version
run: echo “VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)” >> $GITHUB_ENV
working-directory: ./bridge

– name: Build Docker image
run: docker build -t robertosarquis/bridge:${{ env.VERSION }} .
working-directory: ./bridge


Agora vamos fazer o login no Docker Hub e remover a tag “latest” antes de enviar a nova imagem, pois deve existir apenas uma única versão com essa tag. A ideia aqui é a seguinte: sempre que você enviar uma imagem, crie duas tags: uma com a versão e outra com ‘latest’ para indicar qual é a versão mais recente. Isso ajudará na automação da atualização das imagens no servidor. Por exemplo, se você utilizar o ‘Watchtower’ (https://containrrr.dev/watchtower/) para esse fim, poderá configurá-lo para sempre atualizar o contêiner pela tag ‘latest’. Para criar os “secrets”, acesse o repositório, vá em “Settings”, clique em “Secrets and variables” e depois em “Actions”. Você encontrará a seção “Repository secrets”.

– name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

– name: Remove old latest tag if exists
run: |
if docker images –format ‘{{.Repository}}:{{.Tag}}’ | grep -q ‘robertosarquis/bridge:latest’; then
docker rmi robertosarquis/bridge:latest || true
fi


Para finalizar, vamos enviar as imagens para o Docker Hub e criar as tags (uma com a versão e outra com “latest”).

– name: Push Docker image
run: docker push robertosarquis/bridge:${{ env.VERSION }}

– name: Tag and push Docker image as latest
run: |
docker tag robertosarquis/bridge:${{ env.VERSION }} robertosarquis/bridge:latest
docker push robertosarquis/bridge:latest

Com isso, finalizamos a configuração do workflow para o nosso cenário. Espero que isso possa ajudá-lo em seus projetos de automação futuros. Observe que o cenário apresentado é bastante simples, focado em fins didáticos, mas facilmente adaptável para cenários mais complexos. Por exemplo, se você utiliza o modelo Gitflow, pode configurar workflows diferentes, como realizar um disparo quando uma nova release for criada ou uma nova tag de versão. Abaixo segue o link para o documento completo com toda a sintaxe para a montagem de workflows.

https://docs.github.com/pt/actions/writing-workflows/workflow-syntax-for-github-actions#about-yaml-syntax-for-workflows

Autores
Roberto Sarquis é Analista de Sistemas e PO NFe Master na HMIT Tecnologia
Diego Mello é Chief Technology Officer na HMIT Tecnologia | CTO