O princípio “Don’t Repeat Yourself” (DRY) de Andy Hunt e Dave Thomas é um mantra no desenvolvimento de software, e sua aplicação é igualmente vital no universo DevOps para a criação de pipelines eficientes.
Gerenciar múltiplos projetos com stacks tecnológicos semelhantes frequentemente leva à duplicação e complexidade nos pipelines de CI/CD. Minha jornada para otimizar esse cenário me levou a buscar uma estrutura que aliasse padronização e gestão centralizada com flexibilidade e mínima repetição. Após extensa pesquisa e testes no GitLab CI, identifiquei quatro recursos chave que foram cruciais para alcançar esse objetivo.
Estrutura Proposta para CI/CD Centralizado
A organização modular do repositório de CI/CD é fundamental. Veja a estrutura que adotei:
cicd-repository/
├── upstream.gitlab-ci.yml # Modelo base para os .gitlab-ci.yml dos projetos consumidores
├── .gitlab-ci.yml # Pipeline "gateway" no repositório de CI/CD
├── custom/jobs/ # Jobs especializados por projeto
│ ├── projeto1.yml
│ └── projeto2.yml
└── shared/jobs/ # Jobs genéricos e compartilhados
├── build.yml
├── publish.yml
└── deploy.yml
upstream.gitlab-ci.yml
: Este é um template conciso que os projetos consumidores copiarão para seus próprios repositórios como.gitlab-ci.yml
. Sua única função é disparar o pipeline centralizado:
default:
interruptible: true # Permite cancelamento automático
stages:
- trigger
trigger_central_pipeline:
stage: triggers
trigger:
include:
- project: 'SEU_GRUPO/SEU_PROJETO_CICD' # Ex: lvob/devops/cicd
ref: 'main'
file: '/.gitlab-ci.yml'
strategy: depend # Aguarda conclusão do pipeline filho
.gitlab-ci.yml
(no repositório de CI/CD): Atua como um “pipeline gateway”. Ele recebe os triggers dos projetos, processa especificidades e inclui os jobs relevantes deshared/
ecustom/
, orquestrando o fluxo de CI/CD.shared/jobs/
: Contém definições de jobs genéricos (ex: build, test, deploy) reutilizáveis por múltiplos projetos.custom/jobs/
: Contém jobs ou configurações específicas para determinados projetos, especialmente útil para etapas de testes ou QA particulares.
Recursos Essenciais para Pipelines DRY no GitLab CI
Apresento os recursos em ordem crescente de especificidade ao GitLab:
Âncoras YAML (&
e *
)
Nativas do YAML, as âncoras permitem definir um elemento (&nome_ancora
) e reutilizá-lo em outros lugares (*nome_ancora
). São extremamente úteis para evitar repetição de variáveis, scripts ou configurações complexas.
Referenciando valores:
variables:
DEFAULT_INCLUDE_PROJECT: &project "SEU_GRUPO/SEU_PROJETO_CICD"
DEFAULT_INCLUDE_REF: &ref "main"
# [...]
include:
- file: '/shared/jobs/build.yml' # Exemplo de arquivo
project: *project
ref: *ref
Resulta em:
# [...]
include:
- file: '/shared/jobs/build.yml'
project: "SEU_GRUPO/SEU_PROJETO_CICD"
ref: "main"
Referenciando objetos (merge com <<
):
.shared_config: &shared_template
before_script:
- echo "Configuração compartilhada"
variables:
APP_CONFIG1: "valor1"
job1:
<<: *shared_template
script:
- echo "Script do Job 1"
Resulta em:
job1:
before_script:
- echo "Configuração compartilhada"
variables:
APP_CONFIG1: "valor1"
script:
- echo "Script do Job 1"
Usei âncoras principalmente para reutilizar blocos de rules
e conjuntos de variáveis onde extends
não era aplicável.
Jobs ocultos (.
prefixo) e extends
Jobs ocultos (ex: .meu_template_oculto
) não são executados diretamente, servindo como templates. O GitLab CI oferece a diretiva extends
para herdar configurações de um job oculto (ou de um job normal). Esta é a forma preferida pelo GitLab para reutilização de configuração entre jobs, por ser mais legível e integrada
Exemplo com extends
e job oculto para rules
:
# Arquivo: .gitlab-ci.yml (central)
.specific_rules:
rules:
- if: $CI_PROJECT_NAME == "front-end"
variables:
TARGET_URL: site.exemplo.com
- if: $CI_PROJECT_NAME == "back-end"
variables:
TARGET_URL: api.exemplo.com
# ... outras regras
# Arquivo: custom/jobs/meu-job-custom.yml
deploy_specific_app:
extends: .specific_rules # Herda as rules e variáveis condicionais
stage: deploy
script:
- echo "Deploying para $TARGET_URL"
Isso mantém o job deploy_specific_app
genérico, enquanto a lógica de decisão das variáveis fica centralizada e reutilizável.
include:inputs
Este recurso poderoso do GitLab CI permite parametrizar includes
. Ao incluir um arquivo de pipeline, você pode passar inputs
que serão interpolados dentro do arquivo incluído, tornando seus templates de jobs (shared/jobs/
) ainda mais flexíveis. Exemplo:
# Arquivo: shared/jobs/deploy.yml
spec:
inputs:
target_namespace:
description: "Namespace para o deploy"
default: "default" # Opcional: valor padrão
---
deploy_job:
stage: deploy
image: bitnami/kubectl:latest # Imagem de exemplo
script:
- kubectl -n $[[ inputs.target_namespace ]] rollout restart deployment/$(echo $CI_PROJECT_NAME | tr '_' '-')
needs:
- job: publish # Exemplo de dependência
tags:
- docker
# Arquivo: .gitlab-ci.yml (central)
include:
- project: 'SEU_GRUPO/SEU_PROJETO_CICD'
ref: 'main'
file: '/shared/jobs/deploy.yml'
inputs:
target_namespace: $TARGET_NAMESPACE_DO_PROJETO # Variável definida no projeto consumidor ou no CI/CD gateway
rules:
- if: $CI_COMMIT_BRANCH == "main" # Exemplo de regra
A interpolação $[inputs.target_namespace]
no deploy.yml
será substituída pelo valor passado no include
.
Conclusão
A combinação estratégica de âncoras YAML, jobs ocultos, extends
e include:inputs
no GitLab CI permite criar pipelines altamente reutilizáveis, flexíveis e de fácil manutenção. Esses recursos promovem a centralização da lógica de CI/CD, reduzindo drasticamente a duplicação e a complexidade entre projetos. Adotar essas práticas pode transformar significativamente a gestão e a eficiência dos seus pipelines.
Recursos Adicionais
- GitLab CI/CD
include
: https://docs.gitlab.com/ci/yaml/includes/ - GitLab CI/CD
extends
: https://docs.gitlab.com/ci/yaml/yaml_optimization/#use-extends-to-reuse-configuration-sections - GitLab CI/CD
inputs
: https://docs.gitlab.com/ci/inputs/ - Âncoras YAML (especificação YAML, mas relevante): https://documentation.coremedia.com/cmcc-11/current/webhelp/headlessserver-en/content/yaml-anchors-and-aliases.html