DevOps

GitLab CI/CD: A Alternativa Enterprise ao GitHub Actions Já leu

10 min de leitura

GitLab CI/CD: A Alternativa Enterprise ao GitHub Actions
O GitHub é a plataforma padrão para projetos open-source e para a maioria dos times que trabalham com código público ou semi-público. Mas o GitLab ocupa um espaço diferente no mercado: é a escolha preferida de organizaçõ

O GitHub é a plataforma padrão para projetos open-source e para a maioria dos times que trabalham com código público ou semi-público. Mas o GitLab ocupa um espaço diferente no mercado: é a escolha preferida de organizações que precisam de uma plataforma DevOps completa, auto-suficiente e, frequentemente, instalada em infraestrutura própria.

A diferença filosófica é importante. O GitHub nasceu como plataforma de hospedagem de código e foi adicionando funcionalidades ao longo do tempo — Actions, Packages, Security, Projects. O GitLab nasceu com a proposta de ser uma plataforma DevOps completa desde o início: repositório, CI/CD, registro de containers, gerenciamento de issues, wiki, monitoramento, segurança e muito mais, tudo integrado em um único produto.

Para o engenheiro que já domina o GitHub Actions, o GitLab CI/CD é a segunda plataforma mais importante para conhecer — não porque seja "melhor", mas porque ela aparece com frequência em contextos enterprise e em organizações que valorizam soberania sobre seus dados e pipelines.

A Estrutura do GitLab CI/CD

O arquivo de pipeline no GitLab é o .gitlab-ci.yml, que fica na raiz do repositório — equivalente ao .github/workflows/nome.yml do GitHub Actions. Mas há uma diferença estrutural importante: no GitHub Actions, cada arquivo de workflow é independente. No GitLab, há um único arquivo de pipeline por projeto (embora ele possa incluir outros arquivos via include).

O mapeamento de conceitos é o seguinte. No GitHub Actions, um workflow contém jobs que contêm steps. No GitLab CI/CD, um pipeline contém stages que agrupam jobs, e cada job contém um script. Os stages definem a ordem de execução — todos os jobs de um stage executam em paralelo, e o próximo stage só começa quando todos os jobs do stage anterior terminam.

# .gitlab-ci.yml

# Stages definem a ordem de execução
stages:
  - verificacao
  - build
  - testes
  - seguranca
  - deploy-staging
  - deploy-producao

# Variáveis globais (equivalente ao env: global do GitHub Actions)
variables:
  NODE_VERSION: "20"
  DOCKER_DRIVER: overlay2
  # Variáveis sensíveis vêm das CI/CD Variables do projeto (Settings → CI/CD)

# ── Job: Lint ────────────────────────────────────────────────────
lint:
  stage: verificacao
  image: node:20-alpine
  cache:
    key:
      files:
        - package-lock.json
    paths:
      - node_modules/
  script:
    - npm ci
    - npm run lint
  rules:
    # Executar em qualquer branch ou merge request
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_COMMIT_BRANCH == "main"'

# ── Job: Testes Unitários ────────────────────────────────────────
testes-unitarios:
  stage: testes
  image: node:20-alpine
  services:
    # Services são containers auxiliares — equivalente ao services: do GitHub Actions
    - name: postgres:16-alpine
      alias: postgres
      variables:
        POSTGRES_DB: testdb
        POSTGRES_USER: testuser
        POSTGRES_PASSWORD: testpass
    - name: redis:7-alpine
      alias: redis
  variables:
    DATABASE_URL: "postgresql://testuser:testpass@postgres:5432/testdb"
    REDIS_URL: "redis://redis:6379"
  cache:
    key:
      files:
        - package-lock.json
    paths:
      - node_modules/
  script:
    - npm ci
    - npm test -- --coverage
  coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'  # Regex para extrair cobertura do output
  artifacts:
    when: always
    reports:
      # Equivalente ao PublishTestResults do Azure DevOps
      junit: test-results.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
    paths:
      - coverage/
    expire_in: 7 days
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_COMMIT_BRANCH == "main"'

Cache e Artifacts: Conceitos Distintos

O GitLab separa explicitamente dois conceitos que o GitHub Actions trata de maneira mais unificada com actions/cache e actions/upload-artifact.

O cache no GitLab é para acelerar execuções — node_modules, dependências Maven, cache do pip. Ele é armazenado no runner e reutilizado entre jobs e pipelines. A chave do cache pode ser baseada em arquivos (como o package-lock.json) para invalidar automaticamente quando as dependências mudam.

Os artifacts são saídas do job que precisam ser passadas para jobs subsequentes ou preservadas para download. Um job de build gera o binário como artifact; um job de deploy consome esse artifact. A propriedade expire_in define por quanto tempo o artifact é mantido — sem isso, o GitLab usa a configuração padrão da instância.

# Job de build que gera artifact para o job de deploy
build-imagem:
  stage: build
  image: docker:24
  services:
    - docker:24-dind  # Docker-in-Docker
  variables:
    IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
  before_script:
    # Login no GitLab Container Registry (equivalente ao ECR/ACR)
    # CI_REGISTRY, CI_REGISTRY_USER e CI_REGISTRY_PASSWORD são variáveis automáticas
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker build -t $IMAGE_TAG .
    - docker push $IMAGE_TAG
    # Também taggear como latest na branch main
    - |
      if [ "$CI_COMMIT_BRANCH" == "main" ]; then
        docker tag $IMAGE_TAG $CI_REGISTRY_IMAGE:latest
        docker push $CI_REGISTRY_IMAGE:latest
      fi
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

Variáveis Automáticas do GitLab

O GitLab injeta dezenas de variáveis de ambiente automaticamente em cada job. Para quem vem do GitHub Actions (onde as variáveis equivalentes ficam no contexto github.*), é útil conhecer as mais usadas:

CI_COMMIT_SHA é o SHA completo do commit — equivalente ao ${{ github.sha }}. CI_COMMIT_SHORT_SHA são os primeiros 8 caracteres — muito usado para taggear imagens Docker. CI_COMMIT_BRANCH é o nome da branch atual — equivalente ao ${{ github.ref_name }}. CI_COMMIT_TAG é a tag do commit, se existir — equivalente ao ${{ github.ref }} quando é uma tag. CI_PIPELINE_SOURCE indica o que disparou o pipeline: push, merge_request_event, schedule, web e outros — equivalente ao ${{ github.event_name }}. CI_REGISTRY_IMAGE é a URL da imagem no GitLab Container Registry deste projeto — sem equivalente direto no GitHub, que precisa construir a URL manualmente. CI_PROJECT_PATH é o caminho do projeto no formato grupo/subgrupo/projeto. CI_ENVIRONMENT_URL é a URL do environment de deploy, configurada no job.

Segurança no Pipeline: SAST, DAST e Dependency Scanning

Um diferencial significativo do GitLab em relação ao GitHub Actions é a integração nativa de ferramentas de segurança. O GitLab oferece SAST (Static Application Security Testing), DAST (Dynamic Application Security Testing), Dependency Scanning e Container Scanning como templates prontos para incluir no pipeline — sem precisar configurar actions de terceiros ou criar scripts customizados.

# Incluir templates de segurança do GitLab
include:
  - template: Security/SAST.gitlab-ci.yml
  - template: Security/Dependency-Scanning.gitlab-ci.yml
  - template: Security/Container-Scanning.gitlab-ci.yml
  - template: Security/Secret-Detection.gitlab-ci.yml

# Os jobs de segurança são adicionados automaticamente pelo include
# e executam no stage 'test' por padrão

# Configuração do Container Scanning (scan da imagem Docker)
container_scanning:
  stage: seguranca
  variables:
    CS_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
    CS_SEVERITY_THRESHOLD: HIGH  # Falhar apenas em HIGH ou CRITICAL
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'

# Configuração do SAST
sast:
  stage: seguranca
  variables:
    SAST_EXCLUDED_PATHS: "spec,test,tests,tmp,node_modules"

Os resultados de todas as ferramentas de segurança aparecem na interface do GitLab no painel de Security do projeto e nos Merge Requests — o revisor vê os findings de segurança diretamente no MR, sem precisar navegar para outro sistema.

Deploy com Environments e Revisão Manual

O GitLab tem um sistema de Environments robusto que rastreia o histórico de deploys, permite rollback visual e suporta revisão manual antes de deploys em ambientes críticos.

# ── Deploy em Staging ────────────────────────────────────────────
deploy-staging:
  stage: deploy-staging
  image: bitnami/kubectl:latest
  environment:
    name: staging
    url: https://staging.minha-app.com
  variables:
    KUBECONFIG: $KUBECONFIG_STAGING  # Secret file configurada nas CI/CD Variables
  script:
    - kubectl set image deployment/minha-app
        app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
        -n staging
    - kubectl rollout status deployment/minha-app -n staging --timeout=5m
    - |
      # Smoke test
      HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://staging.minha-app.com/health)
      if [ "$HTTP_STATUS" != "200" ]; then
        echo "Smoke test falhou: $HTTP_STATUS"
        exit 1
      fi
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'

# ── Deploy em Produção com Revisão Manual ─────────────────────────
deploy-producao:
  stage: deploy-producao
  image: bitnami/kubectl:latest
  environment:
    name: producao
    url: https://minha-app.com
  variables:
    KUBECONFIG: $KUBECONFIG_PRODUCAO
  # when: manual — o job só executa quando alguém clicar no botão na interface
  # Equivalente ao environment com required reviewers do GitHub Actions
  when: manual
  # allow_failure: false garante que o pipeline fica em pausa esperando a aprovação
  allow_failure: false
  script:
    - kubectl set image deployment/minha-app
        app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
        -n producao
    - kubectl rollout status deployment/minha-app -n producao --timeout=10m
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      when: manual

# ── Rollback Manual ───────────────────────────────────────────────
rollback-producao:
  stage: deploy-producao
  image: bitnami/kubectl:latest
  environment:
    name: producao
    action: stop  # Marca o environment como parado no histórico
  when: manual
  script:
    - kubectl rollout undo deployment/minha-app -n producao
    - kubectl rollout status deployment/minha-app -n producao
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      when: manual

Pipelines Agendados e Multi-projeto

O GitLab suporta pipelines agendados (equivalente ao schedule trigger do GitHub Actions) configurados via interface ou API — úteis para testes noturnos, relatórios periódicos e rotações de credenciais.

Para projetos que precisam disparar pipelines em outros projetos (por exemplo, um repositório de infraestrutura que, ao mudar, deve redesenhar aplicações dependentes), o GitLab oferece o trigger:

# Pipeline que dispara outro pipeline em outro projeto
notificar-downstream:
  stage: deploy-producao
  trigger:
    project: minha-empresa/infraestrutura-kubernetes
    branch: main
    strategy: depend  # Aguardar o pipeline downstream terminar
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'

GitLab Runners: Onde o Pipeline Executa

Os GitLab Runners são os agentes que executam os jobs — equivalente aos runners hospedados do GitHub (ubuntu-latest, windows-latest) e aos self-hosted runners. O GitLab.com oferece runners compartilhados gratuitos para projetos públicos e com limites para projetos privados. Para ambientes on-premises ou com requisitos específicos de segurança e performance, é comum instalar runners próprios.

Um runner próprio é instalado em qualquer máquina Linux, Windows ou macOS com o gitlab-runner e registrado no projeto ou grupo GitLab com um token. O executor (como o job executa) pode ser Shell (diretamente na máquina), Docker (em containers isolados — o mais comum), Kubernetes (em pods do cluster — excelente para escalonamento automático) ou VirtualBox/Parallels (em VMs).

Para registrar um runner Kubernetes no cluster:

# helm values para o GitLab Runner no Kubernetes
# helm install gitlab-runner gitlab/gitlab-runner -f values.yml

gitlabUrl: https://gitlab.com/  # Ou a URL do GitLab self-hosted
runnerToken: "TOKEN-DO-RUNNER"  # Obtido em Settings → CI/CD → Runners

rbac:
  create: true

runners:
  config: |
    [[runners]]
      [runners.kubernetes]
        namespace = "gitlab-runners"
        image = "ubuntu:22.04"
        privileged = false
        [[runners.kubernetes.volumes.empty_dir]]
          name = "docker-certs"
          mount_path = "/certs/client"
          medium = "Memory"

O Que Vem a Seguir

Este artigo cobriu o GitLab CI/CD como plataforma SaaS e o padrão de pipelines para times que hospedam código no gitlab.com. O próximo artigo vai mais fundo: o GitLab instalado em infraestrutura própria (self-hosted), que é onde a plataforma mostra todo o seu potencial para organizações com requisitos de soberania de dados, compliance rigoroso ou simplesmente custos de licença mais previsíveis.

Referências para Aprofundamento

— Documentação do GitLab CI/CD: https://docs.gitlab.com/ee/ci/ — Variáveis Predefinidas do GitLab CI/CD: https://docs.gitlab.com/ee/ci/variables/predefined_variables.html — GitLab Security Dashboard: https://docs.gitlab.com/ee/user/application_security/security_dashboard/ — GitLab Runner — Executor Kubernetes: https://docs.gitlab.com/runner/executors/kubernetes/ — Templates de Segurança do GitLab: https://docs.gitlab.com/ee/user/application_security/

Comentários

Mais em DevOps

Azure DevOps: Pipelines e Repositórios
Azure DevOps: Pipelines e Repositórios

O GitHub Actions foi a plataforma de CI/CD do currículo principal desta série...

Introdução ao Terraform: Infraestrutura que Você Pode Versionar
Introdução ao Terraform: Infraestrutura que Você Pode Versionar

Imagine a seguinte situação, comum em empresas que não adotaram Infraestrutur...

Projeto Capstone: Arquitetura do Sistema Completo
Projeto Capstone: Arquitetura do Sistema Completo

Os quarenta e sete artigos anteriores cobriram, em progressão cuidadosa, cada...