Files
workshop/aula-10
ArgoCD Setup 7222d8971e fix(aula-10): configurar bucket único para todos os tipos de objeto GitLab
Adiciona configuração explícita para usar o mesmo bucket S3 para uploads,
artifacts, LFS, packages, externalDiffs, terraformState, ciSecureFiles e
dependencyProxy. O GitLab organiza internamente por pastas/prefixos.
2026-01-24 08:05:25 -03:00
..

Aula 10 - GitLab via Helm (Cluster Hetzner Cloud)

Deploy do GitLab em Kubernetes usando Helm chart oficial, com Container Registry, SSH e TLS configurável.

Arquitetura

                    Hetzner LoadBalancer
                            │
        ┌───────────────────┼───────────────────┐
        │                   │                   │
     :80/:443              :22                  │
        │                   │                   │
        ▼                   ▼                   │
┌───────────────┐    ┌─────────────┐            │
│ NGINX Ingress │    │ TCP Service │            │
└───────┬───────┘    └──────┬──────┘            │
        │                   │                   │
        ▼                   ▼                   │
┌─────────────────────────────────────────┐     │
│              GitLab (namespace: gitlab) │     │
│  ┌──────────┐  ┌────────┐  ┌─────────┐  │     │
│  │Webservice│  │Sidekiq │  │  Shell  │◄─┼─────┘
│  │ (Rails)  │  │ (Jobs) │  │  (SSH)  │  │
│  └────┬─────┘  └───┬────┘  └─────────┘  │
│       │            │                     │
│  ┌────▼────────────▼────┐               │
│  │       Gitaly         │               │
│  │   (Git Storage)      │               │
│  └──────────────────────┘               │
│                                          │
│  ┌──────────┐  ┌───────┐                │
│  │PostgreSQL│  │ Redis │                │
│  │ (10Gi)   │  │(10Gi) │                │
│  └──────────┘  └───────┘                │
└──────────────┬──────────────────────────┘
               │
               ▼
    ┌─────────────────────┐
    │  Hetzner Object     │
    │  Storage (S3)       │
    │  - uploads          │
    │  - artifacts        │
    │  - registry images  │
    │  - lfs objects      │
    └─────────────────────┘

Pré-requisitos

  1. Cluster Talos na Hetzner (aula-08) com:
    • Hetzner CSI Driver (StorageClass: hcloud-volumes)
    • NGINX Ingress Controller com LoadBalancer
  2. Hetzner Object Storage (bucket + credenciais):
  3. kubectl instalado
  4. Helm 3.x instalado

Contexto do Cluster

Esta aula usa o cluster Kubernetes provisionado na aula-08. O kubeconfig foi gerado automaticamente pelo OpenTofu.

# Verificar se o cluster está acessível
export KUBECONFIG=$(pwd)/../aula-08/kubeconfig
kubectl cluster-info

Instalação

cd aula-10

# Configurar acesso ao cluster (kubeconfig da aula-08)
export KUBECONFIG=$(pwd)/../aula-08/kubeconfig

# Executar setup interativo
chmod +x setup.sh
./setup.sh

O script é interativo e pergunta:

  1. Hostname do GitLab (ex: git.kube.quest) - FQDN completo
  2. Usa CloudFlare? (com proxy/CDN) - pode herdar da aula-09
  3. Ativar Let's Encrypt? (se não usar CloudFlare)
  4. Hetzner Object Storage - endpoint, bucket e credenciais

Opções de TLS

Cenário TLS Provider Configuração
CloudFlare (proxy) CloudFlare Edge Sem cert-manager, TLS na borda
Outro DNS + Let's Encrypt cert-manager HTTP-01 challenge automático
Outro DNS + HTTP Nenhum Apenas HTTP (dev/workshop)

CloudFlare e Trusted Proxies (Erro 422)

Quando você usa CloudFlare com proxy ativo (ícone laranja), o tráfego passa pelos servidores do CloudFlare antes de chegar ao GitLab:

Usuário → CloudFlare (TLS) → LoadBalancer → NGINX → GitLab
                ↓
         Adiciona headers:
         X-Forwarded-For: IP-do-usuario
         X-Forwarded-Proto: https

Problema: Por padrão, o GitLab (Workhorse) não confia nesses headers. Resultado:

  • GitLab pensa que a requisição veio via HTTP (não HTTPS)
  • CSRF token é gerado para HTTPS mas validado como HTTP
  • Erro 422: "The change you requested was rejected"

Solução: O setup.sh configura automaticamente os IPs do CloudFlare como proxies confiáveis:

gitlab.webservice.workhorse.trustedCIDRsForXForwardedFor:
  - 173.245.48.0/20   # CloudFlare
  - 103.21.244.0/22
  - 103.22.200.0/22
  # ... (todos os CIDRs do CloudFlare)

Com isso, o Workhorse confia nos headers X-Forwarded-* vindos do CloudFlare e o login funciona corretamente.

Lição: Proxies reversos (CloudFlare, NGINX, HAProxy) adicionam headers para preservar informações do cliente original. O backend precisa ser configurado para confiar nesses headers, caso contrário terá problemas de protocolo e autenticação.

Componentes Instalados

Da aula-08 (infraestrutura):

  • Hetzner CSI Driver (StorageClass: hcloud-volumes)
  • NGINX Ingress Controller com LoadBalancer

Instalados por este script:

  • cert-manager (se Let's Encrypt ativado)
  • GitLab com todos os componentes
Componente Memory Request Memory Limit Volume
Webservice 2Gi 2.5Gi -
Sidekiq 1.5Gi 2Gi -
Gitaly 512Mi 1Gi 10Gi
PostgreSQL 512Mi 1Gi 10Gi
Redis 256Mi 512Mi 10Gi
Shell 64Mi 128Mi -
Toolbox 256Mi 512Mi -
Total ~5Gi ~8Gi 30Gi

Object Storage (externo):

  • Hetzner Object Storage (S3-compatible)
  • Uploads, artifacts, LFS, registry images
  • Custo: €0.006/GB/mês (paga por uso)

Distribuição Inteligente de Pods

O GitLab usa anti-affinity preferencial para se adaptar automaticamente ao cluster:

gitlab:
  webservice:
    affinity:
      podAntiAffinity:
        preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchLabels:
                  app: sidekiq
              topologyKey: kubernetes.io/hostname

Comportamento Adaptativo

Cenário Comportamento
1 nó grande (8GB+) Tudo roda junto no mesmo nó
Múltiplos nós pequenos Distribui automaticamente
Sem recursos suficientes Cluster Autoscaler cria nós novos

Por que preferencial ao invés de obrigatório?

  • Obrigatório (required): pods ficam Pending se não houver nós separados
  • Preferencial (preferred): funciona com qualquer topologia, otimiza quando possível

Lição do Workshop

"Configurações adaptativas são melhores que rígidas. O anti-affinity preferencial permite que o GitLab funcione em um nó grande ou distribua em vários pequenos, sem intervenção manual."

Padrão de Domínios

Serviço Domínio
Web UI {GITLAB_HOST} (ex: git.kube.quest)
Registry registry.{DOMAIN} (ex: registry.kube.quest)
SSH {GITLAB_HOST}:22

Exemplo com hostname git.kube.quest:

Arquivo de Configuração

O setup.sh gera um arquivo .env (herda configuração de TLS da aula-09):

# aula-10/.env (gerado automaticamente)
GITLAB_HOST=git.kube.quest
REGISTRY_HOST=registry.kube.quest
DOMAIN=kube.quest
USE_CLOUDFLARE=true
USE_LETSENCRYPT=false
LETSENCRYPT_EMAIL=

# Hetzner Object Storage (use a mesma região do cluster)
S3_ENDPOINT=nbg1.your-objectstorage.com
S3_ACCESS_KEY=xxxxx
S3_SECRET_KEY=xxxxx
S3_BUCKET=gitlab-workshop
S3_REGION=nbg1

Acesso

Web UI

URL: https://git.{domain}
Usuário: root
Senha: (ver abaixo)

Obter senha do root

kubectl get secret gitlab-gitlab-initial-root-password \
  -n gitlab \
  -o jsonpath='{.data.password}' | base64 -d; echo

SSH

# Clonar repositório via SSH
git clone git@git.kube.quest:grupo/projeto.git

# Configurar chave SSH no GitLab
# Settings → SSH Keys → Adicionar sua chave pública

Container Registry

# Login no registry
docker login registry.kube.quest

# Push de imagem
docker tag minha-app:v1 registry.kube.quest/grupo/projeto:v1
docker push registry.kube.quest/grupo/projeto:v1

Comandos Úteis

# Ver todos os pods
kubectl get pods -n gitlab

# Ver logs do webservice
kubectl logs -n gitlab -l app=webservice -f

# Ver logs do sidekiq
kubectl logs -n gitlab -l app=sidekiq -f

# Reiniciar componente
kubectl rollout restart deployment -n gitlab gitlab-webservice-default

# Rails console
kubectl exec -it -n gitlab \
  $(kubectl get pod -n gitlab -l app=toolbox -o name | head -1) \
  -- gitlab-rails console

# Backup
kubectl exec -it -n gitlab \
  $(kubectl get pod -n gitlab -l app=toolbox -o name | head -1) \
  -- backup-utility

# Ver certificados (se Let's Encrypt)
kubectl get certificate -n gitlab

# Desinstalar (apenas GitLab)
./cleanup.sh

Nota: O cleanup.sh remove apenas GitLab, clientes e cert-manager. A infraestrutura (CSI Driver, NGINX Ingress, LoadBalancer) é mantida pois pertence à aula-08.

Configurações Adicionais

Adicionar GitLab Runner

# Obter registration token
kubectl get secret gitlab-gitlab-runner-secret \
  -n gitlab \
  -o jsonpath='{.data.runner-registration-token}' | base64 -d; echo

# Instalar runner
helm install gitlab-runner gitlab/gitlab-runner \
  --namespace gitlab \
  --set gitlabUrl=https://git.kube.quest \
  --set runnerRegistrationToken=<token>

Troubleshooting

Pods não iniciam

# Verificar eventos
kubectl get events -n gitlab --sort-by='.lastTimestamp'

# Verificar PVCs
kubectl get pvc -n gitlab

# Verificar se CSI driver está funcionando
kubectl get pods -n kube-system -l app=hcloud-csi

SSH não conecta

# Verificar se porta 22 está no ConfigMap
kubectl get configmap tcp-services -n ingress-nginx -o yaml

# Verificar gitlab-shell pod
kubectl get pods -n gitlab -l app=gitlab-shell

Registry não funciona

# Verificar pod do registry
kubectl get pods -n gitlab -l app=registry
kubectl logs -n gitlab -l app=registry

# Testar internamente
kubectl run test --rm -it --image=busybox -- wget -qO- http://gitlab-registry:5000/v2/

Custos

Cenário Workers Custo/mês
1x CAX21 (8GB) 1 nó ~$8.49
2x CAX11 (4GB) 2 nós (distribuído) ~$9.18
Recurso Custo/mês
Volumes (3x 10Gi = 30Gi) ~$1.45
Object Storage (~10GB uso) ~€0.06
LoadBalancer (compartilhado) ~$1.80 (1/3)

Nota: O anti-affinity preferencial permite ambos os cenários. O Cluster Autoscaler provisiona nós automaticamente quando necessário.

Vantagem Object Storage: Sem volume MinIO (economia de ~$0.50/mês) + storage ilimitado.

Referências