Files
workshop/aula-13
ArgoCD Setup 2904628bef fix: auditoria de coerência entre aulas
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
2026-03-14 02:41:35 -03:00
..

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

  1. Criar org factory em https://gitea.kube.quest/-/admin/orgs
  2. Criar repositório postgresql na org
  3. 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:

  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:

# 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

Cleanup

./cleanup.sh