Files
workshop/aula-10/README.md
ArgoCD Setup 12f6f48c64 feat(aula-10): migrar para Hetzner Object Storage + melhorias
Mudanças principais:
- Substituir MinIO bundled por Hetzner Object Storage (S3)
- Secrets YAML com envsubst para credenciais S3
- Anti-affinity preferencial (adapta a qualquer topologia)
- Detecção automática de cert-manager existente
- Menu TLS simplificado (Let's Encrypt/CloudFlare/HTTP)
- Perguntar hostname do registry separadamente
- Exibição unificada de TLS (não mais CloudFlare: true/false)
2026-01-23 22:52:43 -03:00

375 lines
12 KiB
Markdown

# 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):
- Criar bucket em: https://console.hetzner.cloud → Object Storage
- Gerar Access Key e Secret Key
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.
```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)
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:
```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 |
| 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:
```yaml
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`:
- 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=
# 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
```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=<token>
```
## 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
| 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
- [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/)