Anatomia de um pipeline CI
Esta aula detalha a estrutura de um pipeline de Integração Contínua (CI), abordando estágios, triggers, artefatos e cache. O aluno aprenderá a projetar pipelines eficientes com exemplos práticos em YAML.
Um pipeline de Integração Contínua (CI) é uma sequência automatizada de etapas que validam e entregam código de forma confiável. Compreender sua anatomia é essencial para qualquer profissional DevOps, pois permite construir fluxos eficientes, detectar erros rapidamente e acelerar o ciclo de desenvolvimento.
Nesta aula, exploraremos os quatro componentes fundamentais de um pipeline CI: estágios, triggers, artefatos e cache. Cada um desempenha um papel crucial na automação, desde a execução ordenada das tarefas até a otimização de desempenho. Utilizaremos exemplos em YAML, formato comum em ferramentas como GitLab CI, GitHub Actions e Jenkins.
Estágios
Estágios (stages) são as fases lógicas de um pipeline, executadas sequencialmente. Cada estágio contém um ou mais jobs que rodam em paralelo. A definição de estágios permite organizar o pipeline em etapas como build, teste e deploy, garantindo que jobs de um estágio só iniciem após a conclusão bem-sucedida do anterior.
Por exemplo, em um pipeline de CI típico, temos os estágios: build, test, e deploy. Jobs dentro do estágio 'test' podem rodar em paralelo (testes unitários, de integração, lint), mas todos devem passar para que o pipeline avance ao próximo estágio. Se um job falha, o pipeline para, evitando que código defeituoso prossiga.
Exemplo de definição de estágios e jobs em YAML (GitLab CI):
stages:
- build
- test
- deploy
build_job:
stage: build
script:
- echo "Compilando..."
test_unit:
stage: test
script:
- echo "Rodando testes unitários"
test_integration:
stage: test
script:
- echo "Rodando testes de integração"
deploy_job:
stage: deploy
script:
- echo "Fazendo deploy..."
Triggers
Triggers (gatilhos) definem quando um pipeline deve ser executado. Eles podem ser baseados em eventos como push em um branch, criação de tags, merge requests, ou agendamento (cron). Triggers também podem ser manuais ou acionados por outros pipelines (chain).
Configurar triggers corretamente evita execuções desnecessárias e economiza recursos. Por exemplo, é comum executar o pipeline apenas para pushes em branches principais (main, develop) ou para merge requests. Também é possível filtrar por padrões de caminho (path) para ignorar alterações em documentação.
Exemplo de triggers em YAML (GitLab CI):
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_TAG =~ /^v\d+/'
- when: never # ignora outros casos
job:
script: echo "Executando job"
Artefatos
Artefatos são arquivos gerados durante a execução do pipeline que são preservados para uso em estágios posteriores ou para download. Eles incluem binários compilados, relatórios de teste, pacotes, imagens Docker, etc. A definição de artefatos permite que jobs compartilhem resultados sem precisar refazer o trabalho.
Por exemplo, o job de build pode gerar um artefato (como um JAR ou uma imagem) que será usado pelo job de deploy. Artefatos podem ter expiração configurada para liberar espaço. É importante definir corretamente os caminhos e a expiração.
Exemplo de artefatos em YAML (GitLab CI):
build_job:
stage: build
script:
- mkdir build
- echo "Conteúdo do artefato" > build/output.txt
artifacts:
paths:
- build/
expire_in: 1 week
deploy_job:
stage: deploy
script:
- cat build/output.txt
- echo "Deploy realizado"
needs:
- build_job
Cache
Cache é um mecanismo para armazenar dependências (como pacotes npm, gems, bibliotecas) entre execuções de pipeline, acelerando a instalação. Diferente de artefatos, o cache é usado para otimizar o tempo de setup, não para compartilhar resultados entre estágios. O cache é restaurado no início do job e atualizado ao final.
Por exemplo, em um projeto Node.js, o diretório node_modules pode ser cacheado. A chave do cache geralmente inclui o arquivo de lock (package-lock.json) para invalidar quando as dependências mudam. É crucial configurar a política de cache para evitar inconsistências.
Exemplo de cache em YAML (GitLab CI):
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
install_deps:
script:
- npm ci
Boas Práticas
Para pipelines CI eficientes, siga estas recomendações:
- Mantenha estágios enxutos: cada estágio deve ter um propósito claro.
- Use triggers específicos para evitar execuções desnecessárias.
- Defina artefatos apenas para o que é necessário em estágios posteriores.
- Cacheie dependências de forma correta, utilizando chaves baseadas em arquivos de lock.
- Monitore o tempo de execução e otimize jobs lentos.
Referências
- GitLab CI: Pipelines
- GitHub Actions: Workflows
- Jenkins Pipeline
- CircleCI Docs
- AWS CodePipeline
- Martin Fowler: Continuous Integration
Exercícios
- Defina um pipeline com três estágios (build, test, deploy) e dois jobs no estágio test (test_unit, test_integration). Use YAML.
- Configure um trigger para executar o pipeline apenas em pushes para a branch main e em merge requests.
- Crie um job que gera um artefato (um diretório 'dist/') e outro job que o utiliza, definindo expiração de 2 semanas.
- Configure cache para dependências Python (diretório 'venv') com chave baseada em requirements.txt.
- Explique a diferença entre artefatos e cache, e cite um cenário onde cada um é mais adequado.
stages:
- build
- test
- deploy
build_job:
stage: build
script:
- echo "Building..."
test_unit:
stage: test
script:
- echo "Unit tests"
test_integration:
stage: test
script:
- echo "Integration tests"
deploy_job:
stage: deploy
script:
- echo "Deploying..."
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == "main"'
- when: never
build_job:
stage: build
script:
- mkdir dist
- echo "build output" > dist/output.txt
artifacts:
paths:
- dist/
expire_in: 2 weeks
deploy_job:
stage: deploy
script:
- cat dist/output.txt
- echo "Deploy using artifacts"
needs:
- build_job
cache:
key: ${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHA}
paths:
- venv/
policy: pull-push
install_deps:
script:
- python -m venv venv
- source venv/bin/activate
- pip install -r requirements.txt
Artefatos são usados para passar resultados entre jobs (ex.: binário compilado do build para deploy), enquanto cache armazena dependências para acelerar instalações futuras (ex.: node_modules). Artefatos são essenciais para o fluxo do pipeline; cache é otimização de desempenho.