Files
workshop/aula-10
ArgoCD Setup e8c793058c refactor: remover todas as dependências do GitLab no workshop
- Aula 08: nginx-ingress TCP passthrough gitlab→gitea, comments
- Aula 09: add-client.sh API GitLab→Gitea
- Aula 11: node-bugado deployment image registry.kube.quest→gitea.kube.quest
- Aula 12: setup.sh/cleanup.sh API GitLab→Gitea, ArgoCD repoURL
- Aula 13: k8s manifests, benchmarks: registry.kube.quest→gitea.kube.quest,
           gitlab-registry→gitea-registry, GITLAB_TOKEN→GITEA_TOKEN
- Aula 14: comments GitLab→Gitea
- README raiz: arquitetura, tabela, DNS
2026-03-14 02:02:01 -03:00
..

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:

# .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

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.

# 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.

# .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

# 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

docker login gitea.kube.quest
# Username: gitea_admin
# Password: (mesma senha do admin)

Verificar Runner

# 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

# 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

kubectl get events -n gitea --sort-by='.lastTimestamp'
kubectl get pvc -n gitea

SSH não conecta

# Verificar TCP passthrough
kubectl get configmap tcp-services -n ingress-nginx -o yaml

Registry não funciona

curl -v https://gitea.kube.quest/v2/

Runner não executa workflows

# 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:

# 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

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