GitHub Actions: criando seu primeiro workflow

1. Introdução ao GitHub Actions no contexto DevOps

GitHub Actions é a plataforma de automação nativa do GitHub que permite construir, testar e implantar código diretamente a partir do repositório. No ecossistema DevOps, ela se destaca por sua integração profunda com o versionamento, eliminando a necessidade de ferramentas externas para gatilhos de CI/CD.

Comparada a outras ferramentas como Jenkins (que exige infraestrutura dedicada e plugins complexos), GitLab CI (poderosa, mas vinculada ao ecossistema GitLab) ou CircleCI (excelente performance, porém custo elevado em escala), o GitHub Actions oferece vantagens significativas para pipelines com Docker e Kubernetes: execução em runners gerenciados, marketplace rico em actions prontas para containerização e orquestração, e integração nativa com secrets e ambientes.

Para equipes que já utilizam GitHub como repositório central, a adoção do Actions reduz a complexidade operacional — um único gatilho de push pode construir uma imagem Docker, publicá-la em um registry e atualizar um deployment no Kubernetes sem intervenção manual.

2. Estrutura de um workflow: conceitos fundamentais

Um workflow é definido em um arquivo YAML dentro do diretório .github/workflows/. Cada workflow é composto por três elementos principais:

Eventos determinam quando o workflow é executado. Os mais comuns são push (dispara em commits), pull_request (abre ou atualiza PR) e schedule (execução cron). Exemplo:

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 6 * * 1'  # toda segunda-feira às 6h UTC

Jobs são unidades de trabalho que rodam em runners (máquinas virtuais com Linux, Windows ou macOS). Cada job pode conter múltiplos steps — comandos ou actions reutilizáveis. Boas práticas incluem separar jobs por responsabilidade (build, teste, deploy) e usar dependências entre eles com needs.

Runners podem ser os fornecidos pelo GitHub (ubuntu-latest, windows-latest) ou auto-hospedados para maior controle.

3. Criando seu primeiro workflow: build e push de imagem Docker

Vamos criar um workflow que constrói uma imagem Docker e a publica no Docker Hub. Primeiro, configure os secrets no repositório: DOCKER_USERNAME e DOCKER_PASSWORD.

Arquivo .github/workflows/docker-build.yml:

name: Build and Push Docker Image

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

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

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ secrets.DOCKER_USERNAME }}/my-app:latest

Este workflow utiliza docker/setup-buildx-action para habilitar builds multi-plataforma e docker/build-push-action que gerencia o cache e o push automaticamente. A autenticação via secrets garante que credenciais nunca apareçam nos logs.

4. Integração com testes e lint automatizados

Antes de publicar a imagem, é prudente executar testes e verificar a qualidade do código. Adicione um job de teste ao workflow:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run unit tests in container
        run: docker compose run --rm app pytest

  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Lint Dockerfile
        uses: hadolint/hadolint-action@v3.1.0
        with:
          dockerfile: Dockerfile

      - name: Lint YAML files
        uses: ibiqlik/action-yamllint@v3
        with:
          config_file: .yamllint.yml

O hadolint analisa boas práticas em Dockerfiles (como ordem de instruções, uso de usuários não-root). O yamllint garante consistência nos arquivos YAML do Kubernetes e do próprio workflow. Essas validações evitam problemas em estágios avançados do pipeline.

5. Deploy automatizado em Kubernetes via workflow

Com a imagem publicada, o próximo passo é atualizar um deployment no cluster Kubernetes. O workflow abaixo utiliza kubectl autenticado via kubeconfig armazenado em secret:

deploy:
  runs-on: ubuntu-latest
  needs: [build, test, lint]
  steps:
    - name: Configure kubectl
      run: |
        mkdir -p $HOME/.kube
        echo "${{ secrets.KUBE_CONFIG }}" > $HOME/.kube/config

    - name: Update deployment image
      run: |
        kubectl set image deployment/my-app \
          my-container=${{ secrets.DOCKER_USERNAME }}/my-app:${{ github.sha }}

    - name: Verify rollout
      run: |
        kubectl rollout status deployment/my-app --timeout=120s

Para projetos que usam Helm, a action deliverybot/helm facilita o deploy com charts versionados. Já com Kustomize, é possível usar kustomize edit set image antes de aplicar. Estratégias de rollback podem ser implementadas com kubectl rollout undo caso a verificação de status falhe.

6. Gerenciamento de variáveis, secrets e ambientes

O GitHub Actions oferece três níveis de armazenamento seguro:

  • Secrets (Settings > Secrets and variables > Actions): para tokens, senhas e chaves SSH. São criptografados e mascarados nos logs.
  • Variáveis de ambiente: podem ser definidas no nível do repositório, ambiente ou workflow.
  • Ambientes (Environments): permitem criar regras de proteção, como aprovação manual antes do deploy em produção.

Exemplo de uso com ambientes:

deploy-staging:
  runs-on: ubuntu-latest
  environment:
    name: staging
    url: https://staging.myapp.com
  steps:
    - name: Deploy to staging
      run: echo "Deploying to staging"

Boas práticas incluem nunca hardcodar valores sensíveis, usar ${{ secrets.NOME }} em vez de variáveis de ambiente para credenciais, e limitar o escopo de secrets por ambiente.

7. Monitoramento e otimização do workflow

Logs centralizados: Para depuração em produção, integre o workflow com Loki via Promtail. Uma action customizada pode enviar logs estruturados para um endpoint:

- name: Send logs to Loki
  run: |
    curl -X POST http://loki:3100/loki/api/v1/push \
      -H "Content-Type: application/json" \
      -d '{"streams": [{"stream": {"job": "github-actions"}, "values": [["'$(date +%s%N)'", "Build completed"]]}]}'

Cache de camadas Docker: Para acelerar builds recorrentes, utilize o cache nativo do docker/build-push-action:

- name: Build and push with cache
  uses: docker/build-push-action@v5
  with:
    cache-from: type=gha
    cache-to: type=gha,mode=max

Isso reduz o tempo de build de minutos para segundos em execuções subsequentes.

Limitação de tempo e paralelismo: Defina timeout-minutes por job (padrão 360) e utilize strategy.matrix para paralelizar testes em múltiplas versões:

strategy:
  matrix:
    python-version: ['3.10', '3.11', '3.12']
steps:
  - uses: actions/setup-python@v5
    with:
      python-version: ${{ matrix.python-version }}

Conclusão

Criar um workflow no GitHub Actions para pipelines DevOps com Docker e Kubernetes é um processo direto, mas que exige atenção a detalhes como segurança, cache e validações. A estrutura apresentada — build com Buildx, testes em container, lint automatizado, deploy via kubectl e monitoramento — forma uma base sólida para projetos de qualquer escala. Comece com um workflow simples e evolua gradualmente, adicionando ambientes, matrizes de teste e integrações com Helm ou Kustomize conforme a necessidade.

Referências