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

AWS na Prática: EC2, VPC e IAM em Profundidade
AWS na Prática: EC2, VPC e IAM em Profundidade

Os artigos anteriores introduziram EC2, VPC e IAM como parte da prática com T...

Navegando pelo Sistema de Arquivos com Confiança
Navegando pelo Sistema de Arquivos com Confiança

No artigo anterior foram apresentados os primeiros movimentos no terminal. Ag...

Compliance e Auditoria: LGPD, SOC2 e Evidências Automatizadas
Compliance e Auditoria: LGPD, SOC2 e Evidências Automatizadas

Existe uma diferença fundamental entre uma organização que é segura e uma org...