Bugs corrigidos: - aula-15: tracing.js fallback OTel endpoint usava service name errado (otel-collector vs otel-collector-opentelemetry-collector) - aula-11/13: manifests k8s com gitea.kube.quest hardcoded → placeholder Arquivos legado removidos (9): - aula-10: gitlab-values.yaml, gitlab-registry-storage-secret.yaml, object-storage-secret.yaml, registry-storage-secret.yaml - aula-11: gitlab-runner-values.yaml, node-bugado/.gitlab-ci.yml - aula-13: 3x .gitlab-ci.yml (substituídos por .gitea/workflows/ci.yml) CLAUDE.md: comandos rápidos agora incluem aula-14 e aula-15
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
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
cd aula-13
./setup.sh
2. Criar Projeto no Gitea
- Criar org
factoryemhttps://gitea.kube.quest/-/admin/orgs - Criar repositório
postgresqlna org - Push dos arquivos:
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
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:
- Imagem pequena (~100MB) - overhead do lazy pull > benefício
- PostgreSQL precisa de TODOS os arquivos para iniciar
- 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:
# 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:
# 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:
# 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)
# .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
# 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
# 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 - 13x mais rápido
- BuildBuddy: SOCI - 10x redução latência
- AWS: SOCI Snapshotter
- NTT Labs: eStargz
- stargz-snapshotter
Cleanup
./cleanup.sh