DevOps & CI/CD

Guia Completo de GitLab CI/CD: Pipelines, Runners e Integração com Kubernetes

14 min de leitura

Guia Completo de GitLab CI/CD: Pipelines, Runners e Integração com Kubernetes

Introdução ao GitLab CI/CD: Automação de Ponta a Ponta GitLab CI/CD é um sistema integrado de integração contínua e entrega contínua que permite automatizar testes, builds e deploys diretamente a partir do seu repositório. Diferentemente de ferramentas externas, o GitLab CI/CD funciona nativamente dentro da plataforma, eliminando necessidade de integrações complexas. Cada push para o repositório dispara automaticamente uma sequência de jobs configurados em um arquivo , permitindo que você valide código, execute testes e implante aplicações sem intervenção manual. A beleza do GitLab CI/CD reside na simplicidade e poder combinados. Você não precisa provisionar servidores separados ou gerenciar credenciais complexas — tudo é orquestrado através de uma configuração declarativa. Quando implementado corretamente, elimina erros humanos, acelera o ciclo de desenvolvimento e fornece visibilidade completa sobre o status de cada etapa do pipeline. Entendendo Pipelines: Estrutura e Execução O que é um Pipeline? Um pipeline é uma série de estágios que são executados sequencialmente ou em paralelo. Cada estágio contém

<h2>Introdução ao GitLab CI/CD: Automação de Ponta a Ponta</h2>

<p>GitLab CI/CD é um sistema integrado de integração contínua e entrega contínua que permite automatizar testes, builds e deploys diretamente a partir do seu repositório. Diferentemente de ferramentas externas, o GitLab CI/CD funciona nativamente dentro da plataforma, eliminando necessidade de integrações complexas. Cada push para o repositório dispara automaticamente uma sequência de jobs configurados em um arquivo <code>.gitlab-ci.yml</code>, permitindo que você valide código, execute testes e implante aplicações sem intervenção manual.</p>

<p>A beleza do GitLab CI/CD reside na simplicidade e poder combinados. Você não precisa provisionar servidores separados ou gerenciar credenciais complexas — tudo é orquestrado através de uma configuração declarativa. Quando implementado corretamente, elimina erros humanos, acelera o ciclo de desenvolvimento e fornece visibilidade completa sobre o status de cada etapa do pipeline.</p>

<h2>Entendendo Pipelines: Estrutura e Execução</h2>

<h3>O que é um Pipeline?</h3>

<p>Um pipeline é uma série de estágios que são executados sequencialmente ou em paralelo. Cada estágio contém um ou mais jobs que realizam tarefas específicas. A execução só prossegue para o próximo estágio se todos os jobs do estágio anterior forem bem-sucedidos (comportamento padrão).</p>

<h3>Estrutura Básica do <code>.gitlab-ci.yml</code></h3>

<p>O arquivo de configuração define toda a lógica do pipeline. Você especifica variáveis globais, estágios, imagens Docker, e os jobs que serão executados. GitLab processa este arquivo YAML e cria os jobs automaticamente.</p>

<pre><code class="language-yaml"># Definição global

image: python:3.11

variables:

PIP_CACHE_DIR: &quot;$CI_PROJECT_DIR/.cache/pip&quot;

stages:

  • test
  • build
  • deploy

Job no estágio &quot;test&quot;

unit_tests:

stage: test

script:

  • pip install -r requirements.txt
  • pytest tests/ -v --cov=src

artifacts:

reports:

coverage_report:

coverage_format: cobertura

path: coverage.xml

coverage: &#039;/TOTAL.*\s+(\d+%)$/&#039;

Job no estágio &quot;build&quot;

build_artifact:

stage: build

script:

  • pip install -r requirements.txt
  • python -m PyInstaller --onefile src/main.py

artifacts:

paths:

  • dist/main

expire_in: 30 days

dependencies:

  • unit_tests

Job no estágio &quot;deploy&quot;

deploy_production:

stage: deploy

script:

  • echo &quot;Deploying to production...&quot;
  • ./deploy.sh

environment:

name: production

url: https://app.exemplo.com

only:

  • main</code></pre>

<p>Neste exemplo, o estágio <code>test</code> executa testes unitários com cobertura. O estágio <code>build</code> depende do sucesso do <code>test</code> e gera um artefato. Finalmente, <code>deploy_production</code> só executa em merges para a branch <code>main</code>. O atributo <code>dependencies</code> garante que apenas os artefatos necessários sejam baixados.</p>

<h3>Conditions e Triggers Avançados</h3>

<p>Você pode controlar quando jobs executam usando condições. <code>only</code> restringe a branches ou tags; <code>except</code> faz o inverso. Para lógica mais complexa, use <code>rules</code>:</p>

<pre><code class="language-yaml">deploy_staging:

stage: deploy

script:

  • echo &quot;Deploying to staging...&quot;

rules:

Executa em merge requests

  • if: &#039;$CI_PIPELINE_SOURCE == &quot;merge_request_event&quot;&#039;

Executa em pushes para branches que começam com &quot;feature/&quot;

  • if: &#039;$CI_COMMIT_BRANCH =~ /^feature\/.*/&#039;

Caso nenhuma condição seja atendida, não executa

environment:

name: staging

url: https://staging.exemplo.com</code></pre>

<h2>GitLab Runners: Executores do Pipeline</h2>

<h3>O que é um Runner?</h3>

<p>Um Runner é um agente que executa os jobs definidos no pipeline. GitLab fornece runners compartilhados, mas em ambientes corporativos você frequentemente provisiona seus próprios runners para ter controle total sobre recursos, segurança e dependências.</p>

<h3>Instalação e Configuração de um Runner</h3>

<p>Um runner pode ser instalado em Linux, macOS ou Windows. Vamos usar Linux como exemplo. O processo envolve instalar o binário, registrá-lo com sua instância GitLab e deixá-lo rodando como serviço.</p>

<pre><code class="language-bash"># 1. Baixar e instalar o runner (Ubuntu/Debian)

curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash

sudo apt-get install gitlab-runner

2. Registrar o runner

sudo gitlab-runner register \

--url https://gitlab.seu-dominio.com/ \

--registration-token &lt;seu-token&gt; \

--executor docker \

--docker-image alpine:latest \

--description &quot;Docker runner principal&quot; \

--docker-volumes /var/run/docker.sock:/var/run/docker.sock

3. Iniciar o runner

sudo gitlab-runner start</code></pre>

<p>Após o registro, o runner aparecerá na interface do GitLab e estará pronto para executar jobs. Cada job é isolado em um container Docker (se você escolher o executor <code>docker</code>), garantindo ambiente limpo e reproduzível.</p>

<h3>Executores Disponíveis</h3>

<p>GitLab oferece vários executores. O <code>docker</code> é padrão em CI/CD moderno, mas <code>shell</code> executa diretamente na máquina host (útil para builds nativos), <code>kubernetes</code> integra com clusters K8s, e <code>docker-machine</code> escala automaticamente com máquinas virtuais.</p>

<pre><code class="language-yaml"># Exemplo usando executor shell

build_native:

stage: build

tags:

  • shell
  • macos

script:

  • xcodebuild -scheme MyApp -configuration Release

Exemplo usando docker

build_containerized:

stage: build

image: node:18

tags:

  • docker

script:

  • npm install
  • npm run build</code></pre>

<p>O atributo <code>tags</code> conecta o job a runners específicos. Se você tem múltiplos runners registrados com tags diferentes, o GitLab escolhe o runner apropriado baseado nas tags do job.</p>

<h3>Caching e Artefatos</h3>

<p>Caching acelera pipelines reutilizando dependências entre execuções. Artefatos armazenam outputs que precisam passar entre estágios ou serem disponibilizados para download.</p>

<pre><code class="language-yaml">stages:

  • dependencies
  • build
  • test

Job que popula o cache

install_deps:

stage: dependencies

image: node:18

script:

  • npm install

cache:

key: npm-cache-${CI_COMMIT_REF_SLUG}

paths:

  • node_modules/

Job que usa o cache

build_app:

stage: build

image: node:18

script:

  • npm run build

cache:

key: npm-cache-${CI_COMMIT_REF_SLUG}

paths:

  • node_modules/

artifacts:

paths:

  • dist/

expire_in: 1 week

Job que usa o artefato do build

test_app:

stage: test

image: node:18

script:

  • npm run test

cache:

key: npm-cache-${CI_COMMIT_REF_SLUG}

paths:

  • node_modules/

dependencies:

  • build_app</code></pre>

<p>A chave do cache inclui a branch (<code>CI_COMMIT_REF_SLUG</code>) para isolar caches por branch. O <code>build_app</code> job gera um artefato na pasta <code>dist/</code>, que <code>test_app</code> pode acessar via <code>dependencies</code>.</p>

<h2>Integração com Kubernetes</h2>

<h3>Arquitetura: GitLab e Kubernetes</h3>

<p>Integrar GitLab CI/CD com Kubernetes permite deploys automáticos e escaláveis. A abordagem moderna usa o executor <code>kubernetes</code> para runners, executando jobs diretamente em pods do cluster. Para isso, você registra um runner que se conecta ao seu cluster K8s via kubeconfig.</p>

<h3>Registrando um Runner em Kubernetes</h3>

<pre><code class="language-bash"># 1. Criar namespace para runners

kubectl create namespace gitlab-runners

2. Criar service account

kubectl create serviceaccount gitlab-runner -n gitlab-runners

3. Dar permissões

kubectl create clusterrolebinding gitlab-runner \

--clusterrole=cluster-admin \

--serviceaccount=gitlab-runners:gitlab-runner

4. Obter token

RUNNER_TOKEN=$(kubectl create token gitlab-runner -n gitlab-runners --duration=87600h)

5. Registrar runner

gitlab-runner register \

--url https://gitlab.seu-dominio.com/ \

--registration-token &lt;seu-token&gt; \

--executor kubernetes \

--kubernetes-host https://seu-k8s-api:6443 \

--kubernetes-cert-file /path/to/ca.crt \

--kubernetes-key-file /path/to/key.pem \

--kubernetes-cert-file /path/to/cert.pem \

--kubernetes-namespace gitlab-runners \

--description &quot;K8s runner&quot;</code></pre>

<p>Agora, quando um job é disparado, GitLab cria um pod ephemeral no cluster, executa o job, e o destrói após conclusão. Isso é extremamente eficiente para CI/CD em larga escala.</p>

<h3>Deploy em Kubernetes via GitLab CI/CD</h3>

<pre><code class="language-yaml">stages:

  • build
  • deploy

variables:

REGISTRY: registry.seu-dominio.com

IMAGE_NAME: minha-app

IMAGE_TAG: ${CI_COMMIT_SHA}

build_image:

stage: build

image: docker:24

services:

  • docker:24-dind

script:

  • docker login -u $REGISTRY_USER -p $REGISTRY_PASSWORD $REGISTRY
  • docker build -t $REGISTRY/$IMAGE_NAME:$IMAGE_TAG .
  • docker push $REGISTRY/$IMAGE_NAME:$IMAGE_TAG

deploy_k8s:

stage: deploy

image: bitnami/kubectl:latest

script:

Configurar kubectl

  • kubectl config use-context &lt;seu-cluster-context&gt;

Criar namespace se não existir

- kubectl create namespace production --dry-run=client -o yaml | kubectl apply -f -

Fazer deploy usando kubectl apply

  • kubectl set image deployment/minha-app minha-app=$REGISTRY/$IMAGE_NAME:$IMAGE_TAG -n production

Aguardar rollout

  • kubectl rollout status deployment/minha-app -n production

environment:

name: production

url: https://app.seu-dominio.com

kubernetes:

namespace: production

only:

  • main</code></pre>

<p>O job <code>build_image</code> constrói a imagem Docker e a envia para um registry. O job <code>deploy_k8s</code> então atualiza o deployment do Kubernetes com a nova imagem. O atributo <code>environment.kubernetes.namespace</code> integra a informação do K8s diretamente no GitLab, fornecendo visualização sobre pods e resources.</p>

<h3>Exemplo Avançado: GitOps com ArgoCD</h3>

<p>Para uma abordagem GitOps verdadeira, integre GitLab CI/CD com ArgoCD. GitLab dispara builds e atualiza manifests Git; ArgoCD sincroniza automaticamente o cluster com os manifests.</p>

<pre><code class="language-yaml">stages:

  • build
  • update-manifest

build_and_push:

stage: build

image: docker:24

services:

  • docker:24-dind

script:

  • docker build -t $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHA .
  • docker push $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHA

update_manifest:

stage: update-manifest

image: alpine:latest

script:

Clonar repositório de manifests

  • apk add --no-cache git
  • git clone https://oauth2:${DEPLOY_TOKEN}@gitlab.seu-dominio.com/seu-usuario/k8s-manifests.git
  • cd k8s-manifests

Atualizar tag da imagem no manifest

- sed -i &quot;s|image: .*|image: $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHA|&quot; deployment.yaml

Commit e push

  • git config user.email &quot;ci@seu-dominio.com&quot;
  • git config user.name &quot;CI Pipeline&quot;
  • git add deployment.yaml
  • git commit -m &quot;Update image to $CI_COMMIT_SHA&quot;
  • git push origin main

only:

  • main</code></pre>

<p>Quando o manifesto é atualizado, ArgoCD detecta a mudança e sincroniza automaticamente com o cluster. Isso desacopla a execução do pipeline (push de imagem) da aplicação (deploy no cluster), oferecendo mais segurança e auditoria.</p>

<h2>Boas Práticas e Troubleshooting</h2>

<h3>Segurança: Variáveis e Secrets</h3>

<p>Nunca commit credenciais no repositório. Use variáveis do GitLab com escopo limitado.</p>

<pre><code class="language-yaml"># Em Settings &gt; CI/CD &gt; Variables, crie:

REGISTRY_USER (Protected, Masked)

REGISTRY_PASSWORD (Protected, Masked, não exibir em logs)

KUBE_CONFIG (Protected, Masked, conteúdo completo do kubeconfig)

deploy:

stage: deploy

script:

A variável é injetada no ambiente e mascarda em logs

  • echo &quot;Usando registry user: ${REGISTRY_USER:0:5}***&quot;
  • kubectl config view --raw &gt; /tmp/config</code></pre>

<p>Marque variáveis como <code>Protected</code> (executam apenas em branches protegidas) e <code>Masked</code> (ocultam valor em logs). Para grandes secrets como kubeconfig, use arquivos ao invés de variáveis de texto simples.</p>

<h3>Debugging: Logs e Failed Pipelines</h3>

<p>Quando um job falha, o GitLab mostra logs completos. Use variáveis de debug para aumentar verbosidade:</p>

<pre><code class="language-yaml">debug_job:

stage: test

script:

  • set -x # Bash: mostra cada comando antes de executar
  • echo &quot;CI_PROJECT_DIR: $CI_PROJECT_DIR&quot;
  • echo &quot;CI_COMMIT_SHA: $CI_COMMIT_SHA&quot;
  • pwd
  • ls -la

artifacts:

paths:

  • debug-logs/

when: always # Coleta artefatos mesmo se job falha</code></pre>

<p>O comando <code>set -x</code> em bash mostra cada linha antes de executar. O atributo <code>when: always</code> garante que artefatos sejam coletados mesmo em caso de falha, essencial para debugging.</p>

<h3>Paralelização e Otimização</h3>

<p>Reduce tempo total do pipeline executando jobs em paralelo:</p>

<pre><code class="language-yaml">stages:

  • test
  • build

Estes três jobs rodam em paralelo no estágio &quot;test&quot;

unit_tests:

stage: test

script:

  • pytest tests/unit/ -v

integration_tests:

stage: test

script:

  • pytest tests/integration/ -v

lint:

stage: test

script:

  • flake8 src/
  • black --check src/

Só inicia após todos os jobs do estágio &quot;test&quot;

build:

stage: build

script:

  • python setup.py sdist bdist_wheel</code></pre>

<p>Os três jobs do estágio <code>test</code> executam simultaneamente, reduzindo o tempo total de pipeline. Para jobs muito rápidos (como lint), execute-os em paralelo com testes para maximizar eficiência.</p>

<h2>Conclusão</h2>

<p>Dominando GitLab CI/CD você adquire três competências críticas: <strong>primeira</strong>, automação completa de pipelines eliminando deploy manual e reduzindo erros, usando <code>.gitlab-ci.yml</code> como fonte única da verdade; <strong>segunda</strong>, provisionar e gerenciar runners adequados ao seu contexto, seja docker, shell ou kubernetes, entendendo tradeoffs entre simplicidade e poder; <strong>terceira</strong>, integrar completamente com Kubernetes para deploys escaláveis e reproducíveis, aproveitando GitOps para auditoria e reversão automática.</p>

<h2>Referências</h2>

<ol>

<li><a href="https://docs.gitlab.com/ee/ci/" target="_blank" rel="noopener noreferrer">GitLab CI/CD Documentation - Official</a></li>

<li><a href="https://docs.gitlab.com/runner/" target="_blank" rel="noopener noreferrer">GitLab Runner - Installation and Configuration</a></li>

<li><a href="https://docs.gitlab.com/runner/executors/kubernetes.html" target="_blank" rel="noopener noreferrer">Kubernetes Executor for GitLab Runner</a></li>

<li><a href="https://argo-cd.readthedocs.io/" target="_blank" rel="noopener noreferrer">ArgoCD and GitOps Best Practices</a></li>

<li><a href="https://docs.docker.com/build/ci/github-actions/docker-build/" target="_blank" rel="noopener noreferrer">Docker-in-Docker for CI/CD</a></li>

</ol>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em DevOps & CI/CD

Kubernetes Fundamentos: Arquitetura, Componentes e kubectl na Prática: Do Básico ao Avançado
Kubernetes Fundamentos: Arquitetura, Componentes e kubectl na Prática: Do Básico ao Avançado

Entendendo Kubernetes: O Orquestrador de Contêineres Kubernetes é uma platafo...

Compliance como Código: OPA, Conftest e Policy Enforcement em Kubernetes na Prática
Compliance como Código: OPA, Conftest e Policy Enforcement em Kubernetes na Prática

O Que é Compliance como Código Compliance como Código é um paradigma que tran...

Como Usar Helm: Gerenciador de Pacotes Kubernetes na Prática em Produção
Como Usar Helm: Gerenciador de Pacotes Kubernetes na Prática em Produção

O que é Helm e Por Que Você Precisa Dele Helm é um gerenciador de pacotes par...