453 lines
15 KiB
Markdown
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)
|