Pipeline de CI: build, testes e lint automatizados

1. Fundamentos de uma Pipeline de CI para Containers

Uma pipeline de CI (Continuous Integration) é a espinha dorsal de qualquer estratégia DevOps moderna. Ela automatiza a integração de código, executando build, testes e verificações de qualidade sempre que um desenvolvedor faz push para o repositório. No contexto de Docker e Kubernetes, a CI ganha ainda mais relevância: ela garante que cada alteração produza uma imagem de container confiável, testada e segura antes de chegar ao cluster.

A principal diferença entre CI e CD é clara: CI foca na integração contínua do código e validação automatizada, enquanto CD (Continuous Delivery/Deployment) trata da entrega ou deploy automático para produção. Neste artigo, focamos exclusivamente na fase de CI, que prepara o terreno para uma CD eficiente.

2. Estrutura do Projeto e Preparação do Repositório

Para uma pipeline de CI eficaz, a organização do repositório é fundamental. Considere a seguinte estrutura:

meu-projeto/
├── src/
│   └── app.py
├── tests/
│   ├── test_unit.py
│   └── test_integration.py
├── Dockerfile
├── .hadolint.yaml
├── requirements.txt
└── .github/
    └── workflows/
        └── ci.yml

Adotar uma branch strategy como Trunk-based Development ou Git Flow simplifica o versionamento. No Trunk-based, todas as alterações são integradas na branch principal (main), com releases a partir de tags. Para este artigo, usaremos GitHub Actions como orquestrador da pipeline.

3. Etapa de Lint e Verificação de Qualidade de Código

A etapa de lint é a primeira barreira de qualidade. Ela deve falhar rapidamente para evitar desperdício de recursos.

Exemplo de lint com Hadolint (Dockerfile) e Pylint (código Python):

- name: Lint Dockerfile
  run: |
    docker run --rm -v $(pwd):/code hadolint/hadolint:latest \
      hadolint /code/Dockerfile

- name: Lint Python code
  run: |
    pip install pylint
    pylint src/ --fail-under=8.0

Essas verificações garantem boas práticas de segurança (como não expor portas desnecessárias) e formatação consistente. Se o lint falhar, a pipeline deve ser interrompida imediatamente, economizando minutos preciosos.

4. Etapa de Build da Imagem Docker

O build da imagem Docker deve ser otimizado com cache de camadas e Docker BuildKit.

Exemplo de build com cache e tagging semântico:

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

- name: Build and tag image
  uses: docker/build-push-action@v5
  with:
    context: .
    file: ./Dockerfile
    push: false
    tags: |
      myapp:latest
      myapp:${{ github.sha }}
      myapp:${{ github.ref_name }}
    cache-from: type=gha
    cache-to: type=gha,mode=max

O uso de docker/build-push-action com cache do GitHub Actions (type=gha) acelera builds subsequentes em até 70%. O tagging semântico com latest, SHA do commit e nome da branch facilita a rastreabilidade.

5. Etapa de Testes Automatizados

Testes automatizados dentro do container garantem que o ambiente de execução seja idêntico ao de produção.

Exemplo de testes unitários com pytest:

- name: Run unit tests
  run: |
    docker build -t myapp:test --target=test .
    docker run --rm myapp:test pytest tests/test_unit.py -v

Para testes de integração com banco de dados, use docker-compose para subir dependências:

- name: Integration tests
  run: |
    docker-compose -f docker-compose.test.yml up -d db
    docker build -t myapp:integration .
    docker run --rm --network=test_net myapp:integration \
      pytest tests/test_integration.py -v
    docker-compose down

A validação de saúde da imagem pode ser feita com um healthcheck no Dockerfile e verificação pós-build:

FROM python:3.11-slim AS final
HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:8080/health || exit 1

6. Scaneamento de Segurança e Vulnerabilidades

A segurança não é opcional. Ferramentas como Trivy escaneiam a imagem contra bancos de dados de vulnerabilidades conhecidas.

Exemplo de scan com Trivy:

- name: Scan image for vulnerabilities
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: 'myapp:${{ github.sha }}'
    format: 'sarif'
    output: 'trivy-results.sarif'
    severity: 'CRITICAL,HIGH'
    exit-code: '1'

Se uma vulnerabilidade crítica for encontrada, a pipeline falha. Para dependências Python, adicione pip-audit:

- name: Audit Python dependencies
  run: |
    pip install pip-audit
    pip-audit --requirement requirements.txt --desc

7. Publicação da Imagem no Registry

Após validação, publique a imagem em um registry como GitHub Container Registry (GHCR) ou Docker Hub.

Exemplo de push autenticado para GHCR:

- name: Login to GitHub Container Registry
  uses: docker/login-action@v3
  with:
    registry: ghcr.io
    username: ${{ github.actor }}
    password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push
  uses: docker/build-push-action@v5
  with:
    push: true
    tags: |
      ghcr.io/${{ github.repository }}:latest
      ghcr.io/${{ github.repository }}:${{ github.sha }}

Use tags imutáveis (SHA do commit) para garantir rastreabilidade. Evite sobrescrever tags comuns como latest sem validação.

8. Integração com Kubernetes (K8s) e Próximos Passos

Com a imagem publicada, o próximo passo é gerar manifestos YAML para deploy no Kubernetes. Ferramentas como Kustomize ou Helm facilitam a parametrização.

Exemplo de atualização de deployment via Kustomize:

- name: Update K8s manifest
  run: |
    cd k8s/overlays/production
    kustomize edit set image myapp=ghcr.io/${{ github.repository }}:${{ github.sha }}
    git commit -m "Update image tag to ${{ github.sha }}"
    git push

Isso pode disparar uma pipeline de CD (usando ArgoCD ou Flux) para aplicar as mudanças no cluster. Notificações pós-CI via Slack ou e-mail mantêm a equipe informada:

- name: Notify Slack
  uses: slackapi/slack-github-action@v1
  with:
    payload: |
      {
        "text": "Pipeline de CI concluída para ${{ github.repository }}@${{ github.sha }}"
      }
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

Conclusão

Uma pipeline de CI bem estruturada para Docker e Kubernetes não apenas automatiza builds e testes, mas também incorpora lint, segurança e validação de qualidade desde o primeiro commit. Ao seguir este roteiro, você garante que cada imagem publicada seja confiável, segura e pronta para deploy, reduzindo riscos e acelerando o ciclo de desenvolvimento.

Referências