02 - GitLab CI
video: https://youtu.be/... no frontmatter da nota.GitLab CI
GitLab CI é o sistema de integração e entrega contínua embutido no GitLab, acionado pela presença de um arquivo .gitlab-ci.yml na raiz do repositório e executado por runners que podem ser compartilhados ou dedicados. A integração com a plataforma é o diferencial central: o pipeline nasce junto com o merge request, o resultado aparece na própria solicitação de revisão, os ambientes e review apps são expostos na interface e os artefatos ficam disponíveis por commit. Em produção, essa coesão reduz o atrito entre escrever código e validar mudanças, porque não existe ferramenta externa para conectar, autenticar ou manter sincronizada. Times que vivem no GitLab tendem a obter fluxo de entrega maduro mais rápido justamente pela ausência de costura entre repositório e automação.
Conceito e modelo de execução
O modelo tem três peças. O repositório contém a definição do pipeline em YAML; o GitLab interpreta esse arquivo a cada evento, push, merge request, tag ou agendamento; e os runners executam os jobs em containers, shells ou máquinas virtuais. Cada job roda em um ambiente fresco por padrão, e a comunicação entre repositório e runner acontece por credenciais temporárias emitidas pelo GitLab, o que elimina a gestão manual de segredos de longa duração que atormenta servidores de CI clássicos.
- Job: unidade básica de execução, com script, imagem de container e regras de entrada; um job falho marca o pipeline e aparece imediatamente no merge request.
- Stage: agrupamento lógico de jobs que rodam em paralelo; os estágios avançam em sequência, e a falha de um job interrompe os seguintes.
- Runner: processo executor registrado no GitLab, com etiquetas que descrevem capacidades; o job pede etiquetas, o GitLab escolhe runner livre compatível.
- Pipeline: conjunto completo de estágios disparado por um evento, visível como grafo na interface e associado ao commit que o originou.
A configuração é distribuída por hierarquia: arquivos de .gitlab-ci.yml em grupos e na instância definem templates e variáveis herdadas pelos projetos, o que permite governança central sem impedir autonomia local de cada time.
O problema que resolve
O GitLab CI elimina o custo de integração entre repositório, automação e visibilidade. Em arquiteturas com ferramentas separadas, cada projeto carrega configuração duplicada, webhooks frágeis, permissões a manter em dois sistemas e um vão de visibilidade entre o pull request e o log de build. Com o CI embutido, o merge request se torna a única superfície de trabalho: o revisor vê testes falhos, cobertura, resultado do scan e preview do ambiente no mesmo lugar onde lê o diff. Para a operação de produção, o ganho aparece em auditoria e em rastreabilidade, pois cada artefato, log e ambiente é ligado nativamente ao commit e à pessoa que originou a mudança.
O segundo problema atacado é a deriva de configuração entre projetos. Sem uma plataforma comum, cada time acaba inventando o próprio modo de cachear dependências, nomear estágios e proteger o deploy de produção, e a organização perde a capacidade de evoluir o processo de entrega como um todo. Os templates hierárquicos do GitLab atacam esse problema na raiz: a plataforma define o esqueleto, os projetos preenchem os detalhes, e uma melhoria de segurança aplicada no template de plataforma se propaga para todos os serviços na próxima execução.
Estrutura do .gitlab-ci.yml
O arquivo segue a lógica de estágios e jobs com regras declarativas. O exemplo abaixo descreve um pipeline de serviço típico, com cache por lockfile, revisão de merge request e deploy por ambiente:
stages: [lint, build, test, deploy]
default:
image: node:20-alpine # imagem padrão para todos os jobs
cache:
key:
files:
- package-lock.json # cache invalidado por mudança de lockfile
paths:
- .npm/
lint:
stage: lint
script: npm run lint # falha rápida antes de qualquer build
build:
stage: build
script:
- npm ci --cache .npm --prefer-offline
- npm run build
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
test:
stage: test
needs: [build] # sobrepõe a ordem de estágios
services:
- postgres:16 # dependência real levantada junto do job
script: npm run test:integration
deploy-staging:
stage: deploy
environment: staging # registrado na interface com URL
script: ./deploy.sh staging $CI_COMMIT_SHORT_SHA
rules:
- if: $CI_COMMIT_BRANCH == "main"
deploy-prod:
stage: deploy
environment: production
script: ./deploy.sh prod $CI_COMMIT_SHORT_SHA
rules:
- if: $CI_COMMIT_TAG # produção só por tag
when: manual # gate de aprovação humana
O mecanismo de needs merece atenção: ele transforma o pipeline de sequência rígida de estágios em grafo de dependências, permitindo que o deploy comece assim que os jobs dos quais realmente depende terminarem, e não quando todos os estágios anteriores fecharem. Em pipelines longos, essa distinção vale minutos por execução.
Runners compartilhados e próprios
O GitLab oferece runners compartilhados hospedados para instâncias SaaS, com cotas por projeto, e a opção de registrar runners próprios em qualquer infraestrutura. A decisão tem consequências operacionais diretas: runners compartilhados dispensam manutenção, mas compartilham capacidade com a comunidade e impõem limites de tempo e concorrência; runners próprios garantem previsibilidade, acesso a redes internas e controle de custo, ao preço de administrar as máquinas. Em organizações com cargas pesadas, o desenho comum combina os dois mundos, runners próprios para o volume de base e compartilhados como válvula de escape em picos. As recomendações de operação concentram-se em três pontos:
- Executar o runner em modo container com imagens por job, garantindo ambiente limpo e reprodutível em cada execução.
- Usar etiquetas de capacidade com o mesmo critério de agentes de qualquer CI, separando cargas leves de builds pesados de imagens.
- Monitorar concorrência e fila do runner, porque o limite configurado no config.toml é o número que determina a latência percebida pelo time.
Ambientes e review apps
O conceito de environment conecta cada job de deploy a um ambiente nomeado, com URL, histórico de versões e botões de rollback na interface do GitLab. Sobre essa base, os review apps elevam a prática a outra camada: a cada merge request, o pipeline implanta uma instância temporária do serviço, acessível por URL única, e destrói o ambiente quando o branch é removido. O efeito no processo de revisão é profundo, porque o revisor deixa de avaliar apenas código e passa a validar comportamento real antes do merge. O trecho abaixo registra o ciclo de vida completo de um review app:
review:
stage: deploy
environment:
name: review/$CI_COMMIT_REF_SLUG # ambiente por merge request
url: https://$CI_ENVIRONMENT_SLUG.exemplo.dev
on_stop: stop-review # ação de destruição registrada
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
script: ./deploy.sh review $CI_COMMIT_SHORT_SHA
stop-review:
stage: deploy
environment:
name: review/$CI_COMMIT_REF_SLUG
action: stop # remove o ambiente de revisão
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: manual
script: ./destroy.sh review $CI_ENVIRONMENT_SLUG
Auto DevOps
Auto DevOps é o conjunto de pipelines pré-configurados do GitLab, que detecta a linguagem do projeto e aplica build, testes, scan de segurança, empacotamento e deploy sem configuração inicial. Para projetos pequenos e times iniciando, é uma rampa de entrada rápida com práticas razoáveis de fábrica. Em serviços de produção maduros, o padrão costuma ser a adoção seletiva: herdar os templates base e sobrescrever estágios específicos, mantendo a governança central sem herdar decisões que não conversam com a arquitetura do serviço. O mecanismo de includes permite exatamente esse meio-termo, pipelines de projeto compostos a partir de templates versionados em um repositório de plataforma.
Comparações: GitLab CI versus Jenkins e GitHub Actions
| Aspecto | GitLab CI | Jenkins | GitHub Actions |
|---|---|---|---|
| Integração com o repositório | Nativa, no mesmo produto | Via plugins e webhooks | Nativa, centrada no GitHub |
| Configuração | .gitlab-ci.yml declarativo | Jenkinsfile em Groovy | Workflow em YAML |
| Review apps e ambientes | Recurso nativo de primeira classe | Exige plugins e montagem manual | Recursos de environment no produto |
| Runners | Compartilhados, próprios ou Kubernetes | Agentes próprios sempre | Hospedados, próprios ou scale sets |
| Reuso entre projetos | Includes e templates hierárquicos | Shared libraries em Groovy | Actions e workflows reutilizáveis |
| Manutenção da plataforma | Zero no SaaS; baixa com runners próprios | Alta, controller e plugins | Zero no SaaS; baixa com self-hosted |
| Governança de permissões | Unificada com o repositório | Separada da ferramenta de código | Unificada com o repositório |
A leitura comparativa objetiva é direta: o GitLab CI ganha quando a plataforma de código é o GitLab e o fluxo valoriza ambientes efêmeros por mudança; o Jenkins permanece onde o controle total do runtime é requisito; o GitHub Actions vence por ecossistema e simplicidade quando o código já vive no GitHub.
Erros comuns e boas práticas
Os erros recorrentes em times que adotam a ferramenta sem governança são conhecidos e têm correção barata:
- Pipeline serial completo: estágios em fila quando o grafo permitiria paralelismo; revisar needs é a otimização de melhor retorno por hora investida.
- Rules sem política clara: condições de execução espalhadas e contraditórias entre jobs; centralizar regras em templates evita pipeline que se comporta diferente a cada projeto.
- Runner compartilhado como infraestrutura crítica: depender do pool público para builds com prazo de produção é aposta; capacidade própria para o caminho crítico evita fila no pior momento.
- Variáveis secretas expostas em echo: segredo impresso no log fica visível para qualquer pessoa com acesso ao job; mascaramento e uso direto em comandos minimizam o risco.
- Review apps sem destruição garantida: ambientes de revisão que sobrevivem ao merge acumulam custo e dados; a regra de stop precisa disparar sempre, inclusive em falhas.
- Cache compartilhado entre branches incompatíveis: chave genérica restaura dependências de outro contexto e produz falha intermitente; chave por lockfile e por branch evita a contaminação.
Entre as boas práticas que sustentam a operação em produção, três se destacam. Tratar o .gitlab-ci.yml como produto de plataforma, com templates versionados, revisão e changelog, em vez de permitir que cada projeto reinvente o próprio pipeline. Medir a duração de cada job e do pipeline completo, com alerta quando a mediana passa da meta, porque regressão silenciosa de velocidade é o modo padrão de degradação. E manter os ambientes declarados, com histórico e rollback visíveis, transformando o GitLab em fonte única de verdade sobre o que está implantado em cada ambiente e a partir de qual commit.