- Aula 10: Gitea + Registry + Actions + Runner (substituiu GitLab) - gitea-values.yaml: PostgreSQL standalone, Valkey standalone, ~800Mi RAM - setup.sh/cleanup.sh: namespace gitea, Helm gitea-charts/gitea + actions - README.md: documentação completa com de→para (GitLab/Harbor/Tekton vs Gitea) - Aula 11: ArgoCD (GitOps) — removido GitLab Runner (runner vive na aula-10) - setup.sh: só ArgoCD, integração SSH com Gitea - node-bugado/.gitea/workflows/ci.yml: pipeline convertida - Aula 13: Container Factory — atualizado para Gitea - setup.sh/cleanup.sh: referências GitLab → Gitea - pipelines/postgresql/ci.yml: Gitea Actions workflow - README.md: conexão com act_runner explicada - CLAUDE.md: tabela de aulas atualizada
410 lines
14 KiB
Markdown
410 lines
14 KiB
Markdown
# Aula 13 - Container Factory + Lazy Pulling (eStargz)
|
|
|
|
Crie suas próprias imagens Docker e aprenda **quando** usar eStargz para lazy pulling.
|
|
|
|
## Conexão com as aulas anteriores
|
|
|
|
```
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ Aula 10: Gitea │
|
|
│ Git server + Container Registry + Gitea Actions (habilitado) │
|
|
│ Chart: gitea-charts/gitea │ Chart: gitea-charts/actions │
|
|
│ Pods: gitea, postgresql, │ Pod: act-runner + DinD │
|
|
│ valkey │ (executa os workflows) │
|
|
└────────────────────┬───────────────────────┬──────────────────┘
|
|
│ │
|
|
│ push código │ roda workflow
|
|
│ │ (.gitea/workflows/)
|
|
▼ ▼
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ Aula 13: Container Factory │
|
|
│ │
|
|
│ 1. git push → Gitea recebe o código │
|
|
│ 2. act_runner detecta .gitea/workflows/ci.yml │
|
|
│ 3. Runner executa: docker buildx build --compression=estargz │
|
|
│ 4. Runner faz push da imagem pro Gitea Container Registry │
|
|
│ 5. kubectl apply → Kubernetes puxa do registry │
|
|
└────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
O **act_runner** (Gitea Actions Runner) é a peça que conecta o `git push` ao build da imagem.
|
|
Ele foi instalado na aula-10 via `gitea-charts/actions` — um StatefulSet com dois containers:
|
|
|
|
| Container | Função |
|
|
|-----------|--------|
|
|
| `act-runner` | Escuta o Gitea por jobs, executa workflows (sintaxe GitHub Actions) |
|
|
| `dind` (Docker-in-Docker) | Daemon Docker privilegiado que o runner usa para `docker build`, `docker push` |
|
|
|
|
Sem o runner, os arquivos `.gitea/workflows/*.yml` existem no repo mas nunca executam.
|
|
Nesta aula usamos o runner para **build automatizado de imagens eStargz** — o ciclo completo de Container Factory.
|
|
|
|
## Visão Geral
|
|
|
|
| Parte | Tema | Valor |
|
|
|-------|------|-------|
|
|
| **1** | Container Factory | Prático - criar imagens independentes |
|
|
| **2** | eStargz e Lazy Pulling | Educacional - quando usar e quando NÃO usar |
|
|
| **3** | Alternativas para Cold Start | Prático - soluções para KEDA/auto-scaling |
|
|
|
|
---
|
|
|
|
# Parte 1: Container Factory
|
|
|
|
## Por que criar suas próprias imagens?
|
|
|
|
### O Problema Bitnami
|
|
|
|
A partir de **28 de agosto de 2025**, a Broadcom descontinuou imagens gratuitas:
|
|
|
|
- Imagens movidas para repositório **legacy** (sem atualizações de segurança)
|
|
- Tags estáveis (`postgresql:13.7.0`) não disponíveis no tier gratuito
|
|
- Imagens atualizadas só via **Bitnami Secure** (~$72k/ano)
|
|
|
|
Referência: [Bitnami Issue #83267](https://github.com/bitnami/containers/issues/83267)
|
|
|
|
### A Solução: Container Factory
|
|
|
|
Criar imagens próprias baseadas nas imagens oficiais:
|
|
- Gratuitas e mantidas pela comunidade
|
|
- Menores e mais seguras
|
|
- Totalmente customizáveis
|
|
|
|
## Estrutura do Projeto
|
|
|
|
```
|
|
aula-13/
|
|
├── README.md
|
|
├── setup.sh
|
|
├── cleanup.sh
|
|
│
|
|
├── images/
|
|
│ ├── postgresql/ # Exemplo principal
|
|
│ │ ├── Dockerfile
|
|
│ │ └── postgresql.conf
|
|
│ └── devops-toolbox/ # Demonstração eStargz
|
|
│ ├── Dockerfile
|
|
│ └── entrypoint.sh
|
|
│
|
|
├── pipelines/
|
|
│ └── postgresql/
|
|
│ ├── ci.yml # Gitea Actions workflow
|
|
│ └── .gitlab-ci.yml # (legado - referência)
|
|
│
|
|
├── k8s/
|
|
│ ├── postgresql/
|
|
│ │ ├── deployment.yaml
|
|
│ │ ├── service.yaml
|
|
│ │ └── pvc.yaml
|
|
│ └── prepull-daemonset.yaml # Alternativa para cold start
|
|
│
|
|
└── benchmarks/
|
|
├── benchmark-postgresql.sh
|
|
└── benchmark-toolbox.sh
|
|
```
|
|
|
|
## Instalação
|
|
|
|
### 1. Executar Setup
|
|
|
|
```bash
|
|
cd aula-13
|
|
./setup.sh
|
|
```
|
|
|
|
### 2. Criar Projeto no Gitea
|
|
|
|
1. Criar org `factory` em `https://gitea.kube.quest/-/admin/orgs`
|
|
2. Criar repositório `postgresql` na org
|
|
3. Push dos arquivos:
|
|
|
|
```bash
|
|
git clone git@gitea.kube.quest:factory/postgresql.git
|
|
cd postgresql
|
|
cp /path/to/aula-13/images/postgresql/* .
|
|
mkdir -p .gitea/workflows
|
|
cp /path/to/aula-13/pipelines/postgresql/ci.yml .gitea/workflows/ci.yml
|
|
git add . && git commit -m "Initial commit" && git push
|
|
```
|
|
|
|
### 3. Deploy
|
|
|
|
```bash
|
|
kubectl apply -f k8s/postgresql/ -n factory
|
|
kubectl get pods -n factory -w
|
|
```
|
|
|
|
---
|
|
|
|
# Parte 2: eStargz e Lazy Pulling
|
|
|
|
## O que é eStargz?
|
|
|
|
**eStargz** (Externally Seekable Stargz) permite **lazy pulling** - o container inicia ANTES de baixar a imagem completa:
|
|
|
|
```
|
|
Imagem Tradicional:
|
|
Download 100% (30s) → Extract (10s) → Start (1s) = 41s total
|
|
|
|
Imagem eStargz:
|
|
Download 5% (1s) → Start (1s) → Download resto em background = 2s para iniciar
|
|
```
|
|
|
|
## Quando eStargz FUNCIONA (casos reais)
|
|
|
|
| Empresa | Melhoria | Cenário |
|
|
|---------|----------|---------|
|
|
| **CERN** | 13x mais rápido | Pipelines de análise nuclear |
|
|
| **BuildBuddy** | 10x redução latência | Executor images |
|
|
| **AWS (SOCI)** | 10-15x melhoria | ML containers |
|
|
|
|
**Condições para sucesso:**
|
|
- Imagem > 250MB (comprimida)
|
|
- < 10% dos dados necessários no boot
|
|
- Container que NÃO precisa de todos os arquivos para iniciar
|
|
|
|
## Quando eStargz NÃO ajuda
|
|
|
|
### Aplicações que NÃO se beneficiam:
|
|
|
|
| Aplicação | Tamanho | Por que NÃO funciona |
|
|
|-----------|---------|---------------------|
|
|
| PostgreSQL | ~100MB | Database precisa de 100% dos arquivos |
|
|
| MongoDB | ~500MB | Database precisa de 100% dos arquivos |
|
|
| CockroachDB | ~400MB | Database precisa de 100% dos arquivos |
|
|
| n8n | ~500MB | Carrega TODAS as 400+ integrações no boot |
|
|
| Laravel Octane | ~400MB | Precarrega TODA a aplicação na RAM |
|
|
|
|
### Por que não funcionou nos testes?
|
|
|
|
Benchmark com PostgreSQL mostrou **performance idêntica** entre eStargz e GZIP:
|
|
|
|
| Imagem | Tempo (cold start) |
|
|
|--------|-------------------|
|
|
| PostgreSQL eStargz | ~10s |
|
|
| PostgreSQL GZIP | ~10s |
|
|
|
|
**Razões:**
|
|
1. Imagem pequena (~100MB) - overhead do lazy pull > benefício
|
|
2. PostgreSQL precisa de TODOS os arquivos para iniciar
|
|
3. Não há arquivos "opcionais" para lazy load
|
|
|
|
## Matriz de Decisão
|
|
|
|
| Cenário | Usar eStargz? | Motivo |
|
|
|---------|---------------|--------|
|
|
| Imagem < 250MB | ❌ **Não** | Overhead > benefício |
|
|
| Database (PostgreSQL, MySQL, MongoDB) | ❌ **Não** | Precisa de ~100% dos arquivos |
|
|
| Apps que precarregam (Laravel Octane, n8n) | ❌ **Não** | Carrega tudo no boot |
|
|
| Imagem > 1GB com uso parcial | ✅ **Sim** | Lazy pulling efetivo |
|
|
| ML/AI com múltiplos modelos | ✅ **Sim** | Só baixa modelo usado |
|
|
| DevOps toolbox (terraform, kubectl) | ✅ **Sim** | Só baixa ferramenta usada |
|
|
| Serverless/FaaS | ✅ **Sim** | Cold start crítico |
|
|
|
|
## Demonstração: DevOps Toolbox (eStargz vantajoso)
|
|
|
|
Para demonstrar quando eStargz **realmente funciona**, criamos uma imagem grande com uso parcial:
|
|
|
|
```dockerfile
|
|
# images/devops-toolbox/Dockerfile
|
|
FROM alpine:3.21
|
|
|
|
# Camada 1: Base (~50MB)
|
|
RUN apk add --no-cache bash curl wget jq git
|
|
|
|
# Camada 2: Terraform (~100MB)
|
|
RUN wget -q https://releases.hashicorp.com/terraform/1.9.0/terraform_1.9.0_linux_arm64.zip && \
|
|
unzip terraform_*.zip && mv terraform /usr/local/bin/ && rm terraform_*.zip
|
|
|
|
# Camada 3: Kubectl + Helm (~150MB)
|
|
RUN curl -sLO "https://dl.k8s.io/release/v1.31.0/bin/linux/arm64/kubectl" && \
|
|
chmod +x kubectl && mv kubectl /usr/local/bin/
|
|
|
|
# Camada 4: AWS CLI (~200MB)
|
|
RUN apk add --no-cache aws-cli
|
|
|
|
# Camada 5: Ansible (~150MB)
|
|
RUN apk add --no-cache python3 py3-pip && \
|
|
pip3 install ansible --break-system-packages --quiet
|
|
|
|
# Total: ~650MB - mas você só usa UMA ferramenta por vez!
|
|
```
|
|
|
|
**Resultado esperado:**
|
|
- GZIP: Baixa 650MB inteiro → ~60s
|
|
- eStargz: Baixa só camada necessária (~100MB) → ~10s
|
|
- **Melhoria: 6x mais rápido**
|
|
|
|
---
|
|
|
|
# Parte 3: Alternativas para Cold Start
|
|
|
|
Se suas aplicações são databases ou apps que precarregam, use estas alternativas:
|
|
|
|
## Opção 1: Pre-pull DaemonSet (Recomendado)
|
|
|
|
Garante que imagens já estão em TODOS os nodes antes do KEDA escalar:
|
|
|
|
```yaml
|
|
# k8s/prepull-daemonset.yaml
|
|
apiVersion: apps/v1
|
|
kind: DaemonSet
|
|
metadata:
|
|
name: image-prepuller
|
|
namespace: kube-system
|
|
spec:
|
|
selector:
|
|
matchLabels:
|
|
app: image-prepuller
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: image-prepuller
|
|
spec:
|
|
initContainers:
|
|
- name: prepull-postgres
|
|
image: gitea.kube.quest/factory/postgresql:17
|
|
command: ["echo", "Image pulled"]
|
|
imagePullPolicy: Always
|
|
- name: prepull-n8n
|
|
image: docker.n8n.io/n8nio/n8n:latest
|
|
command: ["echo", "Image pulled"]
|
|
imagePullPolicy: Always
|
|
containers:
|
|
- name: pause
|
|
image: gcr.io/google_containers/pause:3.2
|
|
imagePullSecrets:
|
|
- name: gitea-registry
|
|
```
|
|
|
|
**Resultado:** Quando KEDA escalar, imagens já estão em cache em todos os nodes.
|
|
|
|
## Opção 2: Over-provisioning
|
|
|
|
Manter `minReplicas > 0` para evitar cold starts:
|
|
|
|
```yaml
|
|
# ScaledObject com mínimo de 1 réplica
|
|
apiVersion: keda.sh/v1alpha1
|
|
kind: ScaledObject
|
|
spec:
|
|
minReplicaCount: 1 # Sempre tem pelo menos 1 pod rodando
|
|
maxReplicaCount: 10
|
|
```
|
|
|
|
## Opção 3: Imagens menores
|
|
|
|
Reduzir tamanho da imagem para downloads mais rápidos:
|
|
|
|
| Base | Tamanho típico | Uso |
|
|
|------|----------------|-----|
|
|
| `alpine` | ~5MB | Apps Go, binários estáticos |
|
|
| `distroless` | ~20MB | Java, Node.js |
|
|
| `debian-slim` | ~80MB | Quando Alpine não funciona |
|
|
|
|
## Comparação de Soluções
|
|
|
|
| Solução | Cold Start | Complexidade | Custo |
|
|
|---------|------------|--------------|-------|
|
|
| Pre-pull DaemonSet | ⭐⭐⭐⭐⭐ | Baixa | Nenhum |
|
|
| Over-provisioning | ⭐⭐⭐⭐ | Baixa | $$ (pods sempre rodando) |
|
|
| Imagens menores | ⭐⭐⭐ | Média | Nenhum |
|
|
| eStargz | ⭐⭐ (só imagens grandes) | Alta | Nenhum |
|
|
|
|
---
|
|
|
|
# Apêndice: Configuração Técnica
|
|
|
|
## Pipeline CI com eStargz (Gitea Actions)
|
|
|
|
```yaml
|
|
# .gitea/workflows/ci.yml
|
|
name: Build PostgreSQL
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
env:
|
|
REGISTRY: gitea.kube.quest
|
|
IMAGE_NAME: factory/postgresql
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: docker/setup-buildx-action@v3
|
|
- run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login ${{ env.REGISTRY }} -u ${{ gitea.actor }} --password-stdin
|
|
- run: |
|
|
docker buildx build \
|
|
--output type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest,push=true,compression=estargz,force-compression=true,oci-mediatypes=true \
|
|
.
|
|
```
|
|
|
|
| Parâmetro | Descrição |
|
|
|-----------|-----------|
|
|
| `compression=estargz` | Formato para lazy pulling |
|
|
| `force-compression=true` | Recomprime camadas da base |
|
|
| `oci-mediatypes=true` | Media types OCI (requerido) |
|
|
|
|
## Verificar stargz-snapshotter no Talos
|
|
|
|
```bash
|
|
# Extensão instalada
|
|
talosctl get extensionstatuses | grep stargz
|
|
|
|
# Configuração
|
|
talosctl read /etc/cri/conf.d/10-stargz-snapshotter.part
|
|
# Output: snapshotter = "stargz"
|
|
```
|
|
|
|
## Testar PostgreSQL
|
|
|
|
```bash
|
|
# Obter senha
|
|
PGPASSWORD=$(kubectl get secret postgresql-secret -n factory -o jsonpath='{.data.password}' | base64 -d)
|
|
|
|
# Conectar
|
|
kubectl run pg-client --rm -it --restart=Never \
|
|
--image=postgres:17-alpine \
|
|
--env=PGPASSWORD=$PGPASSWORD \
|
|
-- psql -h postgresql.factory.svc.cluster.local -U postgres -d app
|
|
```
|
|
|
|
---
|
|
|
|
# Conclusão
|
|
|
|
## Resumo
|
|
|
|
| Tema | Aprendizado |
|
|
|------|-------------|
|
|
| **Container Factory** | Crie imagens próprias para independência de terceiros |
|
|
| **eStargz** | Só vale para imagens > 250MB com uso parcial |
|
|
| **Cold Start** | Para databases, use pre-pull DaemonSet |
|
|
|
|
## Regra de Ouro
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ eStargz funciona quando: │
|
|
│ - Imagem > 250MB │
|
|
│ - < 10% dos dados necessários no boot │
|
|
│ - Container NÃO precisa de todos os arquivos para iniciar │
|
|
│ │
|
|
│ Para databases e apps que precarregam: │
|
|
│ → Use Pre-pull DaemonSet │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Referências
|
|
|
|
- [CERN: Lazy Pulling](https://indico.cern.ch/event/1338689/) - 13x mais rápido
|
|
- [BuildBuddy: SOCI](https://www.buildbuddy.io/blog/image-streaming/) - 10x redução latência
|
|
- [AWS: SOCI Snapshotter](https://aws.amazon.com/blogs/containers/under-the-hood-lazy-loading-container-images-with-seekable-oci-and-aws-fargate/)
|
|
- [NTT Labs: eStargz](https://medium.com/nttlabs/startup-containers-in-lightning-speed-with-lazy-image-distribution-on-containerd-243d94522361)
|
|
- [stargz-snapshotter](https://github.com/containerd/stargz-snapshotter)
|
|
|
|
## Cleanup
|
|
|
|
```bash
|
|
./cleanup.sh
|
|
```
|