# 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 │ │ MinIO │ │ │ │ (10Gi) │ │(10Gi) │ │ (10Gi) │ │ │ └──────────┘ └───────┘ └──────────┘ │ └─────────────────────────────────────────┘ ``` ## Pré-requisitos 1. **Cluster Talos na Hetzner** (aula-08) com: - Hetzner CSI Driver (StorageClass: hcloud-volumes) - NGINX Ingress Controller com LoadBalancer 2. **kubectl** instalado 3. **Helm** 3.x instalado ## Contexto do Cluster Esta aula usa o cluster Kubernetes provisionado na **aula-08**. O `kubeconfig` foi gerado automaticamente pelo OpenTofu. ```bash # Verificar se o cluster está acessível export KUBECONFIG=$(pwd)/../aula-08/kubeconfig kubectl cluster-info ``` ## Instalação ```bash 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) ### 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: ```yaml 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 | | MinIO | 128Mi | 256Mi | 10Gi | | Shell | 64Mi | 128Mi | - | | Toolbox | 256Mi | 512Mi | - | | **Total** | ~5Gi | ~8Gi | 40Gi | ## Pod Anti-Affinity (Distribuicao Inteligente) O GitLab configura **antiAffinity** para garantir que webservice e sidekiq rodem em nos diferentes: ```yaml gitlab: webservice: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: sidekiq topologyKey: kubernetes.io/hostname ``` ### Por que isso importa? Sem antiAffinity, webservice (~2.5Gi) + sidekiq (~2Gi) = 4.5Gi no mesmo no CAX11 (4GB): - **Resultado**: OOMKilled constantes, servico instavel Com antiAffinity: - Kubernetes detecta que nao pode agendar no mesmo no - Pod fica **Pending** - Cluster Autoscaler cria um novo no automaticamente - Pods distribuidos = servico estavel ### Licao do Workshop > "Kubernetes nao e so rodar containers - e **orquestracao inteligente**. > Quando configuramos antiAffinity, o scheduler entendeu que precisava de mais > recursos e o autoscaler provisionou automaticamente." ## 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`: - https://git.kube.quest - https://registry.kube.quest - git@git.kube.quest ## Arquivo de Configuração O `setup.sh` gera um arquivo `.env` (herda configuração de TLS da aula-09): ```bash # 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= ``` ## Acesso ### Web UI ``` URL: https://git.{domain} Usuário: root Senha: (ver abaixo) ``` ### Obter senha do root ```bash kubectl get secret gitlab-gitlab-initial-root-password \ -n gitlab \ -o jsonpath='{.data.password}' | base64 -d; echo ``` ### SSH ```bash # 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 ```bash # 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 ```bash # 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 ```bash # 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= ``` ## Troubleshooting ### Pods não iniciam ```bash # 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 ```bash # 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 ```bash # 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 | Recurso | Custo/mês | |---------|-----------| | Workers (2x CAX11 - antiAffinity) | ~$9.18 | | Volumes (4x 10Gi = 40Gi) | ~$1.94 | | LoadBalancer (compartilhado) | ~$1.80 (1/3) | | **Total GitLab** | ~$12.92 | **Nota:** O antiAffinity requer 2 workers minimos (webservice e sidekiq em nos separados). O Cluster Autoscaler provisiona automaticamente quando necessario. ## Referências - [GitLab Helm Chart](https://docs.gitlab.com/charts/) - [GitLab Helm Chart Values](https://gitlab.com/gitlab-org/charts/gitlab/-/blob/master/values.yaml) - [External Ingress](https://docs.gitlab.com/charts/advanced/external-ingress/) - [NGINX TCP Services](https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/)