<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: "$CI_PROJECT_DIR/.cache/pip"
stages:
- test
- build
- deploy
Job no estágio "test"
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: '/TOTAL.*\s+(\d+%)$/'
Job no estágio "build"
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 "deploy"
deploy_production:
stage: deploy
script:
- echo "Deploying to production..."
- ./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 "Deploying to staging..."
rules:
Executa em merge requests
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
Executa em pushes para branches que começam com "feature/"
- if: '$CI_COMMIT_BRANCH =~ /^feature\/.*/'
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 <seu-token> \
--executor docker \
--docker-image alpine:latest \
--description "Docker runner principal" \
--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 <seu-token> \
--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 "K8s runner"</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 <seu-cluster-context>
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 "s|image: .*|image: $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHA|" deployment.yaml
Commit e push
- git config user.email "ci@seu-dominio.com"
- git config user.name "CI Pipeline"
- git add deployment.yaml
- git commit -m "Update image to $CI_COMMIT_SHA"
- 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 > CI/CD > 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 "Usando registry user: ${REGISTRY_USER:0:5}***"
- kubectl config view --raw > /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 "CI_PROJECT_DIR: $CI_PROJECT_DIR"
- echo "CI_COMMIT_SHA: $CI_COMMIT_SHA"
- 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 "test"
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 "test"
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><!-- FIM --></p>