Files
workshop/aula-10/README.md

453 lines
15 KiB
Markdown

# Aula 10 - Plataforma de Desenvolvimento (Gitea)
Git Server + Container Registry + CI/CD Runner — tudo num único deploy.
## Por que esta aula existe
Nas aulas anteriores construímos um cluster Kubernetes na Hetzner com autoscaling, ingress e storage. Agora precisamos de uma **plataforma de desenvolvimento** para:
1. **Hospedar código** (Git)
2. **Armazenar imagens Docker** (Container Registry)
3. **Automatizar builds e deploys** (CI/CD)
Normalmente, isso exigiria 3 ferramentas separadas. Nesta aula, mostramos como o Gitea entrega as 3 em um único binário.
## Por que Gitea (e não GitLab, Harbor, Tekton)?
A escolha de ferramentas é uma decisão de arquitetura. No ecossistema Kubernetes, existem opções especializadas para cada peça:
### De → Para
| Função | Opção "Enterprise" | O que usamos | Por quê |
|--------|-------------------|--------------|---------|
| **Git Server** | GitLab CE/EE | **Gitea** | GitLab pede ~5Gi RAM, 8 pods, 2 nodes. Gitea pede ~500Mi, 1 pod, 1 node. |
| **Container Registry** | Harbor | **Gitea (integrado)** | Harbor é um projeto separado (~1Gi RAM, PostgreSQL, Redis, storage backend). Gitea já tem registry OCI embutido. |
| **CI/CD** | Tekton / GitLab CI | **Gitea Actions** | Tekton é poderoso mas complexo (CRDs, pipelines, tasks, triggers — curva de aprendizado alta). Gitea Actions usa sintaxe GitHub Actions, que a maioria já conhece. |
### A conta fecha assim
```
Abordagem "enterprise" (GitLab + Harbor + Tekton):
GitLab: ~5Gi RAM, 8 pods, 40Gi storage, 2 nodes ($12.92/mês)
Harbor: ~1Gi RAM, 5 pods, 20Gi storage ($5/mês)
Tekton: ~512Mi RAM, 4 CRDs, curva de aprendizado
Total: ~6.5Gi RAM, 17+ pods (~$18/mês)
Abordagem Gitea (tudo integrado):
Gitea: ~500Mi RAM, 3 pods, 21Gi storage, 1 node ($7.41/mês)
Total: ~500Mi RAM, 3 pods (~$7/mês)
```
**Para um workshop com cluster pequeno na Hetzner (CAX11 com 4GB RAM), a conta não fecha com a abordagem enterprise.**
### Mas e a sintaxe do CI?
Esse é o ponto decisivo. Gitea Actions usa **sintaxe GitHub Actions** — o padrão de fato da indústria:
```yaml
# .gitea/workflows/ci.yml — mesma sintaxe do GitHub Actions
name: Build
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t minha-app .
```
Se você sabe GitHub Actions, sabe Gitea Actions. Se você sabe Gitea Actions, sabe GitHub Actions. Tekton tem sua própria DSL (PipelineRun, TaskRun, etc.) que não transfere pra nenhuma outra plataforma.
### Comparação visual: GitLab vs Gitea
| | GitLab | Gitea |
|---|---|---|
| **RAM** | ~5 Gi (request) | ~500 Mi |
| **Pods** | 8 (webservice, sidekiq, gitaly, shell, postgres, redis, minio, registry) | 3 (gitea, postgresql, valkey) |
| **Storage** | 40 Gi (4 PVCs) | 21 Gi (3 PVCs) |
| **Nodes mínimos** | 2 (antiAffinity webservice/sidekiq) | 1 |
| **Install time** | 10-15 min | 2-3 min |
| **CI Syntax** | `.gitlab-ci.yml` (proprietária) | `.gitea/workflows/*.yml` (GitHub Actions) |
| **Registry** | Componente separado (+ MinIO/S3) | Integrado (OCI nativo) |
| **Runner** | `gitlab-runner` (Helm chart separado) | `act_runner` (Helm chart separado) |
## Arquitetura
```
Hetzner LoadBalancer
┌───────────────────┼──────────────┐
│ │ │
:80/:443 :22 │
│ │ │
▼ ▼ │
┌───────────────┐ ┌─────────────┐ │
│ NGINX Ingress │ │ TCP Service │ │
└───────┬───────┘ └──────┬──────┘ │
│ │ │
▼ ▼ │
┌──────────────────────────────────────────┐
│ Namespace: gitea │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Gitea Pod │ │
│ │ Web UI + API + Git + SSH │◄────┘
│ │ Container Registry (OCI) │
│ │ Gitea Actions (habilitado) │
│ └──────────────┬──────────────────┘
│ │
│ ┌──────────────┼──────────────┐
│ │ │ │
│ ▼ ▼ ▼
│ PostgreSQL Valkey act_runner
│ (10Gi) (1Gi) + DinD
│ (builds Docker)
└──────────────────────────────────────────┘
```
### O act_runner em detalhe
O runner é instalado como um **StatefulSet** com 2 containers:
| Container | O que faz |
|-----------|-----------|
| `act-runner` | Conecta ao Gitea, escuta por jobs, executa workflows |
| `dind` (Docker-in-Docker) | Daemon Docker privilegiado — permite `docker build` e `docker push` dentro dos workflows |
Quando você faz `git push` num repo que tem `.gitea/workflows/ci.yml`, o fluxo é:
```
git push → Gitea recebe → act_runner detecta workflow
→ Runner cria container temporário
→ Executa steps (checkout, build, push)
→ Imagem vai pro Gitea Container Registry
→ Kubernetes puxa a imagem no deploy
```
## 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
## Instalação
```bash
cd aula-10
export KUBECONFIG=$(pwd)/../aula-08/kubeconfig
chmod +x setup.sh
./setup.sh
```
O script é interativo e pergunta:
1. **Hostname do Gitea** (ex: `gitea.kube.quest`) - FQDN completo
2. **Usa CloudFlare?** (com proxy/CDN)
3. **Ativar Let's Encrypt?** (se não usar CloudFlare)
### O que o setup.sh instala
```
1. cert-manager (se Let's Encrypt)
2. Gitea via Helm (gitea-charts/gitea)
- Web UI + API + Git + SSH
- Container Registry (packages)
- PostgreSQL standalone
- Valkey (cache)
3. Gitea Actions Runner via Helm (gitea-charts/actions)
- act_runner + Docker-in-Docker
- Registration token obtido automaticamente
4. TCP passthrough no NGINX para SSH (porta 22)
```
### 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) |
## Componentes e Recursos
| Componente | Chart | Memory Request | Memory Limit | Volume |
|------------|-------|----------------|--------------|--------|
| Gitea | gitea-charts/gitea | 512Mi | 1Gi | 10Gi |
| PostgreSQL | (subchart) | 128Mi | 256Mi | 10Gi |
| Valkey | (subchart) | 32Mi | 64Mi | 1Gi |
| act_runner + DinD | gitea-charts/actions | 128Mi | 256Mi | 5Gi |
| **Total** | | **~800Mi** | **~1.6Gi** | **~26Gi** |
## Funcionalidades
### 1. Container Registry (OCI)
O Gitea inclui um Container Registry OCI integrado. Sem Harbor, sem MinIO, sem componentes extras.
```bash
# Login no registry
docker login gitea.kube.quest
# Push de imagem
docker tag minha-app:v1 gitea.kube.quest/usuario/minha-app:v1
docker push gitea.kube.quest/usuario/minha-app:v1
```
### 2. Gitea Actions (CI/CD)
CI/CD integrado com sintaxe GitHub Actions. Sem Tekton, sem CRDs extras, sem DSL proprietária.
```yaml
# .gitea/workflows/build.yml
name: Build
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login gitea.kube.quest -u ${{ gitea.actor }} --password-stdin
docker build -t gitea.kube.quest/${{ gitea.repository }}:${{ github.sha }} .
docker push gitea.kube.quest/${{ gitea.repository }}:${{ github.sha }}
```
### 3. SSH
```bash
# Clonar via SSH
git clone git@gitea.kube.quest:usuario/projeto.git
# Configurar chave SSH: Settings → SSH/GPG Keys
```
## Acesso
### Web UI
```
URL: https://gitea.kube.quest
Usuário: gitea_admin
Senha: (exibida no final do setup.sh)
```
### Container Registry
```bash
docker login gitea.kube.quest
# Username: gitea_admin
# Password: (mesma senha do admin)
```
### Verificar Runner
```bash
# Pod do runner
kubectl get pods -n gitea -l app.kubernetes.io/name=actions
# Logs
kubectl logs -n gitea -l app.kubernetes.io/name=actions -c act-runner -f
# Workflows executados
# Via UI: https://gitea.kube.quest/{owner}/{repo}/actions
```
## Comandos Úteis
```bash
# Ver todos os pods
kubectl get pods -n gitea
# Ver logs do Gitea
kubectl logs -n gitea -l app.kubernetes.io/name=gitea -f
# Reiniciar Gitea
kubectl rollout restart deployment -n gitea gitea
# Ver certificados (se Let's Encrypt)
kubectl get certificate -n gitea
# Desinstalar
./cleanup.sh
```
## Troubleshooting
### Pods não iniciam
```bash
kubectl get events -n gitea --sort-by='.lastTimestamp'
kubectl get pvc -n gitea
```
### SSH não conecta
```bash
# Verificar TCP passthrough
kubectl get configmap tcp-services -n ingress-nginx -o yaml
```
### Registry não funciona
```bash
curl -v https://gitea.kube.quest/v2/
```
### Runner não executa workflows
```bash
# Verificar se o runner está registrado
kubectl logs -n gitea -l app.kubernetes.io/name=actions -c act-runner --tail=20
# Verificar se DinD está rodando
kubectl logs -n gitea -l app.kubernetes.io/name=actions -c dind --tail=10
# Erro "Docker connection failed" → DinD não compartilha socket
# Verificar se extraVolumeMounts do dind inclui docker-socket
```
### Runner crashando (OOM)
O Gitea pode usar mais memória durante migrações de repos grandes. Se ocorrer OOM:
```bash
# Aumentar limit temporariamente
helm upgrade gitea gitea-charts/gitea -n gitea \
--reuse-values \
--set resources.limits.memory=1Gi
```
## Custos
| Recurso | Custo/mês |
|---------|-----------|
| Worker (1x CAX11) | ~$4.59 |
| Volumes (~26Gi) | ~$1.26 |
| LoadBalancer (compartilhado) | ~$1.80 (1/3) |
| **Total** | **~$7.65** |
## Storage: Disco Local vs S3 (Object Storage)
Por padrão, o Gitea armazena tudo em **disco local** (PVC). Isso é simples e funciona pro workshop. Mas em produção com muitas imagens Docker, o PVC de 10Gi enche rápido.
O Gitea suporta **S3-compatible storage** (MinIO, AWS S3, Hetzner Object Storage, etc.) como backend para **tudo**: container images, LFS, avatars, attachments, actions artifacts.
### O que usamos (default)
```
Gitea Pod → PVC 10Gi (hcloud-volumes)
├── /data/git/repositories (código)
├── /data/gitea/packages (container images)
├── /data/gitea/lfs (large files)
└── /data/gitea/avatars (fotos)
```
Simples, sem dependências externas. Suficiente pra clusters pequenos.
### Quando migrar pra S3
- PVC passando de 80% (o pvc-autoresizer da aula-12 ajuda, mas tem limite)
- Muitas imagens Docker grandes (container registry crescendo)
- Precisa de storage "ilimitado" sem gerenciar volumes
### Como configurar S3 (Hetzner Object Storage)
1. Criar bucket no [Hetzner Cloud Console](https://console.hetzner.cloud) → Object Storage
2. Gerar Access Key + Secret Key
3. Adicionar no `gitea-values.yaml`:
```yaml
gitea:
config:
storage:
STORAGE_TYPE: minio
MINIO_ENDPOINT: nbg1.your-objectstorage.com
MINIO_ACCESS_KEY_ID: "sua-access-key"
MINIO_SECRET_ACCESS_KEY: "sua-secret-key"
MINIO_BUCKET: gitea
MINIO_LOCATION: nbg1
MINIO_USE_SSL: "true"
SERVE_DIRECT: "true"
```
4. `helm upgrade` pra aplicar
### SERVE_DIRECT: como funciona (e por que é seguro)
Com `SERVE_DIRECT: true`, o Gitea **não** faz proxy dos downloads. Em vez disso, gera **URLs pré-assinadas** com expiração:
```
docker pull gitea.kube.quest/org/app-privada:v1
├─ 1. Client autentica no Gitea (token/password)
├─ 2. Gitea verifica permissão (repo privado? user tem acesso?)
│ → Se não tem acesso: 403 Forbidden. Para aqui.
├─ 3. Gitea gera URL S3 pré-assinada (expira em ~10 minutos)
│ https://s3.example.com/gitea/packages/sha256:abc...
│ ?X-Amz-Signature=xxx&X-Amz-Expires=600
└─ 4. Client baixa direto do S3 via URL assinada
→ URL expira, não pode ser reutilizada
→ Repo privado permanece privado
```
Sem `SERVE_DIRECT` (default):
```
Client → Gitea (proxy) → S3 → Gitea (proxy) → Client
^^^^^^^^^^^^ ^^^^^^^^^^^^
CPU + RAM + bandwidth passam pelo Gitea
```
**Repos privados continuam privados** — a URL assinada só é gerada após autenticação e verificação de permissão. É o mesmo mecanismo usado por AWS S3, GitHub Packages e Google Cloud Storage.
Único cenário onde **não** usar `SERVE_DIRECT`: se o bucket S3 está em rede privada e os clients não têm acesso direto à rede do S3.
### Ou só pra packages (container images)
Se quiser manter repos git em disco local mas imagens no S3:
```yaml
gitea:
config:
storage.packages:
STORAGE_TYPE: minio
MINIO_ENDPOINT: nbg1.your-objectstorage.com
MINIO_ACCESS_KEY_ID: "sua-access-key"
MINIO_SECRET_ACCESS_KEY: "sua-secret-key"
MINIO_BUCKET: gitea
MINIO_USE_SSL: "true"
MINIO_BASE_PATH: "packages/"
SERVE_DIRECT: "true"
```
### Custo S3 vs PVC
| Storage | Custo | Limite |
|---------|-------|--------|
| PVC (Hetzner Volume) | ~$0.048/GB/mês | 10TB max |
| Hetzner Object Storage | ~€0.006/GB/mês (~8x mais barato) | Ilimitado |
**Para o workshop usamos PVC** — zero configuração extra, zero secrets de S3.
## Lições do Workshop
1. **Nem sempre precisa da ferramenta "enterprise"** — Gitea substitui GitLab + Harbor + parcialmente Tekton com 10x menos recursos
2. **Registry integrado** é suficiente para a maioria dos casos — Harbor justifica-se quando você precisa de vulnerability scanning, replicação multi-site, ou compliance (assinatura de imagens)
3. **Sintaxe GitHub Actions é portável** — o que você aprende aqui transfere direto pro GitHub, e vice-versa
4. **Docker-in-Docker no Kubernetes** requer namespace com PodSecurity `privileged` — é o trade-off pra ter builds Docker no cluster
## Referências
- [Gitea Docs](https://docs.gitea.com/)
- [Gitea Helm Chart](https://gitea.com/gitea/helm-chart)
- [Gitea Container Registry](https://docs.gitea.com/usage/packages/container)
- [Gitea Actions](https://docs.gitea.com/usage/actions/overview)
- [act_runner](https://docs.gitea.com/usage/actions/act-runner)
- [ArtifactHub - Gitea](https://artifacthub.io/packages/helm/gitea/gitea)
- [ArtifactHub - Gitea Actions](https://artifacthub.io/packages/helm/gitea/actions)