refactor: migrar GitLab → Gitea (aulas 10, 11, 13)
- 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
This commit is contained in:
409
aula-13/README.md
Normal file
409
aula-13/README.md
Normal file
@@ -0,0 +1,409 @@
|
||||
# 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
|
||||
```
|
||||
188
aula-13/benchmarks/benchmark-clean.sh
Executable file
188
aula-13/benchmarks/benchmark-clean.sh
Executable file
@@ -0,0 +1,188 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# Benchmark LIMPO: eStargz vs Traditional Image Pull
|
||||
# =============================================================================
|
||||
#
|
||||
# Este benchmark força execução em um node SEM cache das imagens
|
||||
# para medir tempo REAL de pull.
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
NAMESPACE="benchmark-clean"
|
||||
ESTARGZ_IMAGE="registry.kube.quest/factory/postgresql:17"
|
||||
TRADITIONAL_IMAGE="postgres:17-alpine"
|
||||
TARGET_NODE="talos-msadg4-worker-0" # Node sem cache
|
||||
|
||||
echo "========================================================================"
|
||||
echo "Benchmark LIMPO: eStargz vs Traditional Image Pull"
|
||||
echo "========================================================================"
|
||||
echo ""
|
||||
echo "Target node: $TARGET_NODE (sem cache de imagens)"
|
||||
echo ""
|
||||
echo "Comparando:"
|
||||
echo " Tradicional: $TRADITIONAL_IMAGE"
|
||||
echo " eStargz: $ESTARGZ_IMAGE"
|
||||
echo ""
|
||||
|
||||
# Verificar cluster
|
||||
echo "[1/6] Verificando cluster..."
|
||||
kubectl cluster-info >/dev/null || { echo "ERRO: Cluster inacessível"; exit 1; }
|
||||
echo " Cluster OK"
|
||||
|
||||
# Limpar ambiente anterior
|
||||
echo "[2/6] Limpando ambiente anterior..."
|
||||
kubectl delete namespace $NAMESPACE --ignore-not-found=true --wait=true 2>/dev/null || true
|
||||
echo " Ambiente limpo"
|
||||
|
||||
# Criar namespace
|
||||
echo "[3/6] Criando namespace de teste..."
|
||||
kubectl create namespace $NAMESPACE
|
||||
kubectl create secret docker-registry gitlab-registry \
|
||||
--docker-server=registry.kube.quest \
|
||||
--docker-username=root \
|
||||
--docker-password="${GITLAB_TOKEN:-glpat-dummy}" \
|
||||
-n $NAMESPACE 2>/dev/null || true
|
||||
echo " Namespace criado"
|
||||
|
||||
# Teste 1: Imagem tradicional
|
||||
echo ""
|
||||
echo "========================================================================"
|
||||
echo "[4/6] TESTE 1: Imagem Tradicional (gzip) - PULL REAL"
|
||||
echo "========================================================================"
|
||||
echo "Iniciando em $(date)"
|
||||
T1_START=$(date +%s)
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pg-traditional
|
||||
namespace: $NAMESPACE
|
||||
spec:
|
||||
nodeName: $TARGET_NODE
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: postgres
|
||||
image: $TRADITIONAL_IMAGE
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: POSTGRES_PASSWORD
|
||||
value: benchmarktest
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
EOF
|
||||
|
||||
kubectl wait --for=condition=Ready pod/pg-traditional -n $NAMESPACE --timeout=300s
|
||||
|
||||
T1_END=$(date +%s)
|
||||
TIME1=$((T1_END - T1_START))
|
||||
echo "Finalizado em $(date)"
|
||||
echo ">>> Tempo total: ${TIME1}s <<<"
|
||||
|
||||
# Teste 2: Imagem eStargz
|
||||
echo ""
|
||||
echo "========================================================================"
|
||||
echo "[5/6] TESTE 2: Imagem eStargz (lazy pulling) - PULL REAL"
|
||||
echo "========================================================================"
|
||||
echo "Iniciando em $(date)"
|
||||
T2_START=$(date +%s)
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pg-estargz
|
||||
namespace: $NAMESPACE
|
||||
spec:
|
||||
nodeName: $TARGET_NODE
|
||||
restartPolicy: Never
|
||||
imagePullSecrets:
|
||||
- name: gitlab-registry
|
||||
containers:
|
||||
- name: postgres
|
||||
image: $ESTARGZ_IMAGE
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: POSTGRES_PASSWORD
|
||||
value: benchmarktest
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
EOF
|
||||
|
||||
kubectl wait --for=condition=Ready pod/pg-estargz -n $NAMESPACE --timeout=300s
|
||||
|
||||
T2_END=$(date +%s)
|
||||
TIME2=$((T2_END - T2_START))
|
||||
echo "Finalizado em $(date)"
|
||||
echo ">>> Tempo total: ${TIME2}s <<<"
|
||||
|
||||
# Resultados
|
||||
echo ""
|
||||
echo "========================================================================"
|
||||
echo "[6/6] RESULTADOS"
|
||||
echo "========================================================================"
|
||||
echo ""
|
||||
|
||||
# Status dos pods
|
||||
echo "Status dos Pods:"
|
||||
kubectl get pods -n $NAMESPACE -o wide
|
||||
echo ""
|
||||
|
||||
# Eventos completos
|
||||
echo "Todos os Eventos (ordenados por tempo):"
|
||||
kubectl get events -n $NAMESPACE --sort-by='.lastTimestamp' \
|
||||
-o custom-columns='TIMESTAMP:.lastTimestamp,REASON:.reason,POD:.involvedObject.name,MESSAGE:.message'
|
||||
echo ""
|
||||
|
||||
# Verificar se houve pull real
|
||||
echo "Análise de Pull:"
|
||||
TRAD_PULL=$(kubectl get events -n $NAMESPACE --field-selector involvedObject.name=pg-traditional,reason=Pulled -o jsonpath='{.items[0].message}' 2>/dev/null)
|
||||
ESTARGZ_PULL=$(kubectl get events -n $NAMESPACE --field-selector involvedObject.name=pg-estargz,reason=Pulled -o jsonpath='{.items[0].message}' 2>/dev/null)
|
||||
|
||||
echo " Tradicional: $TRAD_PULL"
|
||||
echo " eStargz: $ESTARGZ_PULL"
|
||||
echo ""
|
||||
|
||||
# Tabela de resultados
|
||||
echo "┌─────────────────────────────────────────────────────────────────┐"
|
||||
echo "│ RESULTADOS DO BENCHMARK (PULL REAL) │"
|
||||
echo "├───────────────────┬─────────────────┬─────────────────────────────┤"
|
||||
echo "│ Métrica │ Tradicional │ eStargz │"
|
||||
echo "├───────────────────┼─────────────────┼─────────────────────────────┤"
|
||||
printf "│ Tempo até Ready │ %12ss │ %12ss │\n" "$TIME1" "$TIME2"
|
||||
echo "├───────────────────┼─────────────────┼─────────────────────────────┤"
|
||||
|
||||
if [ "$TIME1" -gt "$TIME2" ]; then
|
||||
DIFF=$((TIME1 - TIME2))
|
||||
PERCENT=$(( (DIFF * 100) / TIME1 ))
|
||||
echo "│ Diferença │ baseline │ -${DIFF}s (${PERCENT}% mais rápido) │"
|
||||
elif [ "$TIME2" -gt "$TIME1" ]; then
|
||||
DIFF=$((TIME2 - TIME1))
|
||||
PERCENT=$(( (DIFF * 100) / TIME2 ))
|
||||
echo "│ Diferença │ +${DIFF}s mais rápido │ baseline │"
|
||||
else
|
||||
echo "│ Diferença │ igual │ igual │"
|
||||
fi
|
||||
|
||||
echo "└───────────────────┴─────────────────┴─────────────────────────────┘"
|
||||
|
||||
echo ""
|
||||
echo "Nota: Este benchmark usou imagePullPolicy: Always no node '$TARGET_NODE'"
|
||||
echo " que não tinha as imagens em cache, forçando pull real."
|
||||
echo ""
|
||||
echo "O benefício do eStargz (lazy pulling) é mais significativo em:"
|
||||
echo " - Imagens maiores (1GB+)"
|
||||
echo " - Scale-out events (novos nodes)"
|
||||
echo " - Cold starts"
|
||||
echo ""
|
||||
echo "Para limpar: kubectl delete namespace $NAMESPACE"
|
||||
170
aula-13/benchmarks/benchmark-postgresql.sh
Executable file
170
aula-13/benchmarks/benchmark-postgresql.sh
Executable file
@@ -0,0 +1,170 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# Benchmark: eStargz vs Traditional Image Pull
|
||||
# =============================================================================
|
||||
#
|
||||
# Compara tempo de startup entre:
|
||||
# - postgres:17-alpine (gzip tradicional)
|
||||
# - registry.kube.quest/factory/postgresql:17 (eStargz)
|
||||
#
|
||||
# Este script usa timestamps dos eventos do Kubernetes para medir:
|
||||
# - Tempo de pull (Pulling -> Pulled)
|
||||
# - Tempo total (Scheduled -> Started)
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
NAMESPACE="benchmark-test"
|
||||
ESTARGZ_IMAGE="registry.kube.quest/factory/postgresql:17"
|
||||
TRADITIONAL_IMAGE="postgres:17-alpine"
|
||||
|
||||
echo "========================================================================"
|
||||
echo "Benchmark: eStargz vs Traditional Image Pull"
|
||||
echo "========================================================================"
|
||||
echo ""
|
||||
echo "Comparando:"
|
||||
echo " Tradicional: $TRADITIONAL_IMAGE"
|
||||
echo " eStargz: $ESTARGZ_IMAGE"
|
||||
echo ""
|
||||
|
||||
# Verificar cluster
|
||||
echo "[1/6] Verificando cluster..."
|
||||
kubectl cluster-info >/dev/null || { echo "ERRO: Cluster inacessível"; exit 1; }
|
||||
echo " Cluster OK"
|
||||
|
||||
# Limpar ambiente anterior
|
||||
echo "[2/6] Limpando ambiente anterior..."
|
||||
kubectl delete namespace $NAMESPACE --ignore-not-found=true --wait=true 2>/dev/null || true
|
||||
echo " Ambiente limpo"
|
||||
|
||||
# Criar namespace
|
||||
echo "[3/6] Criando namespace de teste..."
|
||||
kubectl create namespace $NAMESPACE
|
||||
kubectl create secret docker-registry gitlab-registry \
|
||||
--docker-server=registry.kube.quest \
|
||||
--docker-username=root \
|
||||
--docker-password="${GITLAB_TOKEN:-glpat-dummy}" \
|
||||
-n $NAMESPACE 2>/dev/null || true
|
||||
echo " Namespace criado"
|
||||
|
||||
# Teste 1: Imagem tradicional
|
||||
echo ""
|
||||
echo "========================================================================"
|
||||
echo "[4/6] TESTE 1: Imagem Tradicional (gzip)"
|
||||
echo "========================================================================"
|
||||
T1_START=$(date +%s)
|
||||
|
||||
kubectl run pg-traditional --image=$TRADITIONAL_IMAGE --restart=Never \
|
||||
--env=POSTGRES_PASSWORD=benchmarktest \
|
||||
-n $NAMESPACE 2>&1 | grep -v "Warning:"
|
||||
|
||||
kubectl wait --for=condition=Ready pod/pg-traditional -n $NAMESPACE --timeout=180s
|
||||
|
||||
T1_END=$(date +%s)
|
||||
TIME1=$((T1_END - T1_START))
|
||||
echo "Tempo total: ${TIME1}s"
|
||||
|
||||
# Teste 2: Imagem eStargz
|
||||
echo ""
|
||||
echo "========================================================================"
|
||||
echo "[5/6] TESTE 2: Imagem eStargz (lazy pulling)"
|
||||
echo "========================================================================"
|
||||
T2_START=$(date +%s)
|
||||
|
||||
kubectl run pg-estargz --image=$ESTARGZ_IMAGE --restart=Never \
|
||||
--env=POSTGRES_PASSWORD=benchmarktest \
|
||||
--overrides='{"spec":{"imagePullSecrets":[{"name":"gitlab-registry"}]}}' \
|
||||
-n $NAMESPACE 2>&1 | grep -v "Warning:"
|
||||
|
||||
kubectl wait --for=condition=Ready pod/pg-estargz -n $NAMESPACE --timeout=180s
|
||||
|
||||
T2_END=$(date +%s)
|
||||
TIME2=$((T2_END - T2_START))
|
||||
echo "Tempo total: ${TIME2}s"
|
||||
|
||||
# Resultados
|
||||
echo ""
|
||||
echo "========================================================================"
|
||||
echo "[6/6] RESULTADOS"
|
||||
echo "========================================================================"
|
||||
echo ""
|
||||
|
||||
# Status dos pods
|
||||
echo "Status dos Pods:"
|
||||
kubectl get pods -n $NAMESPACE -o wide
|
||||
echo ""
|
||||
|
||||
# Eventos completos
|
||||
echo "Todos os Eventos (ordenados por tempo):"
|
||||
kubectl get events -n $NAMESPACE --sort-by='.lastTimestamp' \
|
||||
-o custom-columns='TIMESTAMP:.lastTimestamp,REASON:.reason,POD:.involvedObject.name,MESSAGE:.message'
|
||||
echo ""
|
||||
|
||||
# Verificar se houve pull real ou cache hit
|
||||
echo "Análise de Pull:"
|
||||
TRAD_PULL=$(kubectl get events -n $NAMESPACE --field-selector involvedObject.name=pg-traditional,reason=Pulled -o jsonpath='{.items[0].message}' 2>/dev/null)
|
||||
ESTARGZ_PULL=$(kubectl get events -n $NAMESPACE --field-selector involvedObject.name=pg-estargz,reason=Pulled -o jsonpath='{.items[0].message}' 2>/dev/null)
|
||||
|
||||
echo " Tradicional: $TRAD_PULL"
|
||||
echo " eStargz: $ESTARGZ_PULL"
|
||||
echo ""
|
||||
|
||||
# Tabela de resultados
|
||||
echo "┌─────────────────────────────────────────────────────────────────┐"
|
||||
echo "│ RESULTADOS DO BENCHMARK │"
|
||||
echo "├───────────────────┬─────────────────┬─────────────────────────────┤"
|
||||
echo "│ Métrica │ Tradicional │ eStargz │"
|
||||
echo "├───────────────────┼─────────────────┼─────────────────────────────┤"
|
||||
printf "│ Tempo até Ready │ %12ss │ %12ss │\n" "$TIME1" "$TIME2"
|
||||
echo "├───────────────────┼─────────────────┼─────────────────────────────┤"
|
||||
|
||||
if [ "$TIME1" -gt 0 ] && [ "$TIME2" -gt 0 ]; then
|
||||
if [ "$TIME1" -gt "$TIME2" ]; then
|
||||
DIFF=$((TIME1 - TIME2))
|
||||
echo "│ Diferença │ baseline │ -${DIFF}s mais rápido │"
|
||||
elif [ "$TIME2" -gt "$TIME1" ]; then
|
||||
DIFF=$((TIME2 - TIME1))
|
||||
echo "│ Diferença │ -${DIFF}s mais rápido │ baseline │"
|
||||
else
|
||||
echo "│ Diferença │ igual │ igual │"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "└───────────────────┴─────────────────┴─────────────────────────────┘"
|
||||
|
||||
# Verificar cache hit
|
||||
if echo "$TRAD_PULL" | grep -q "already present"; then
|
||||
TRAD_CACHED="SIM"
|
||||
else
|
||||
TRAD_CACHED="NAO"
|
||||
fi
|
||||
|
||||
if echo "$ESTARGZ_PULL" | grep -q "already present"; then
|
||||
ESTARGZ_CACHED="SIM"
|
||||
else
|
||||
ESTARGZ_CACHED="NAO"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Cache Status:"
|
||||
echo " Tradicional em cache: $TRAD_CACHED"
|
||||
echo " eStargz em cache: $ESTARGZ_CACHED"
|
||||
|
||||
if [ "$TRAD_CACHED" = "SIM" ] || [ "$ESTARGZ_CACHED" = "SIM" ]; then
|
||||
echo ""
|
||||
echo "AVISO: Imagens em cache - benchmark não reflete tempo real de pull!"
|
||||
echo ""
|
||||
echo "Para benchmark preciso, limpe o cache dos worker nodes com:"
|
||||
echo ""
|
||||
echo " # Via talosctl (para cada worker node):"
|
||||
echo " export TALOSCONFIG=/private/data/app/workshop/aula-08/talosconfig"
|
||||
echo " WORKER_IP=46.224.192.153 # IP do worker"
|
||||
echo " talosctl -n \$WORKER_IP service restart containerd"
|
||||
echo ""
|
||||
echo " # OU escale um novo worker sem cache"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Namespace de teste mantido. Para limpar:"
|
||||
echo " kubectl delete namespace $NAMESPACE"
|
||||
129
aula-13/benchmarks/benchmark-pull-only.sh
Executable file
129
aula-13/benchmarks/benchmark-pull-only.sh
Executable file
@@ -0,0 +1,129 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# Benchmark de PULL: eStargz vs Traditional
|
||||
# =============================================================================
|
||||
#
|
||||
# Mede apenas o tempo de PULL das imagens (não espera container ficar Ready)
|
||||
# Executa em node limpo sem cache.
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
NAMESPACE="benchmark-pull"
|
||||
ESTARGZ_IMAGE="registry.kube.quest/factory/postgresql:17"
|
||||
TRADITIONAL_IMAGE="postgres:17-alpine"
|
||||
TARGET_NODE="worker-pool-6bea48339a15ab6e" # Node 128.140.11.113 - sem cache
|
||||
|
||||
echo "========================================================================"
|
||||
echo "Benchmark de PULL: eStargz vs Traditional"
|
||||
echo "========================================================================"
|
||||
echo ""
|
||||
echo "Target node: $TARGET_NODE (sem cache)"
|
||||
echo ""
|
||||
|
||||
# Setup
|
||||
kubectl delete namespace $NAMESPACE --ignore-not-found=true --wait=true 2>/dev/null || true
|
||||
kubectl create namespace $NAMESPACE
|
||||
kubectl create secret docker-registry gitlab-registry \
|
||||
--docker-server=registry.kube.quest \
|
||||
--docker-username=root \
|
||||
--docker-password="${GITLAB_TOKEN:-glpat-dummy}" \
|
||||
-n $NAMESPACE 2>/dev/null || true
|
||||
|
||||
echo ""
|
||||
echo "========================================================================"
|
||||
echo "TESTE 1: Pull de Imagem Tradicional (gzip)"
|
||||
echo "========================================================================"
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test-traditional
|
||||
namespace: $NAMESPACE
|
||||
spec:
|
||||
nodeName: $TARGET_NODE
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: postgres
|
||||
image: $TRADITIONAL_IMAGE
|
||||
imagePullPolicy: Always
|
||||
command: ["sleep", "infinity"]
|
||||
env:
|
||||
- name: POSTGRES_PASSWORD
|
||||
value: test
|
||||
EOF
|
||||
|
||||
echo "Aguardando pull..."
|
||||
sleep 2
|
||||
while true; do
|
||||
PULLED=$(kubectl get events -n $NAMESPACE --field-selector involvedObject.name=test-traditional,reason=Pulled -o jsonpath='{.items[0].message}' 2>/dev/null)
|
||||
if [ -n "$PULLED" ]; then
|
||||
echo "RESULTADO: $PULLED"
|
||||
break
|
||||
fi
|
||||
PULLING=$(kubectl get events -n $NAMESPACE --field-selector involvedObject.name=test-traditional,reason=Pulling -o jsonpath='{.items[0].message}' 2>/dev/null)
|
||||
if [ -n "$PULLING" ]; then
|
||||
echo -n "."
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "========================================================================"
|
||||
echo "TESTE 2: Pull de Imagem eStargz (lazy pulling)"
|
||||
echo "========================================================================"
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test-estargz
|
||||
namespace: $NAMESPACE
|
||||
spec:
|
||||
nodeName: $TARGET_NODE
|
||||
restartPolicy: Never
|
||||
imagePullSecrets:
|
||||
- name: gitlab-registry
|
||||
containers:
|
||||
- name: postgres
|
||||
image: $ESTARGZ_IMAGE
|
||||
imagePullPolicy: Always
|
||||
command: ["sleep", "infinity"]
|
||||
env:
|
||||
- name: POSTGRES_PASSWORD
|
||||
value: test
|
||||
EOF
|
||||
|
||||
echo "Aguardando pull..."
|
||||
sleep 2
|
||||
while true; do
|
||||
PULLED=$(kubectl get events -n $NAMESPACE --field-selector involvedObject.name=test-estargz,reason=Pulled -o jsonpath='{.items[0].message}' 2>/dev/null)
|
||||
if [ -n "$PULLED" ]; then
|
||||
echo "RESULTADO: $PULLED"
|
||||
break
|
||||
fi
|
||||
PULLING=$(kubectl get events -n $NAMESPACE --field-selector involvedObject.name=test-estargz,reason=Pulling -o jsonpath='{.items[0].message}' 2>/dev/null)
|
||||
if [ -n "$PULLING" ]; then
|
||||
echo -n "."
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "========================================================================"
|
||||
echo "RESUMO"
|
||||
echo "========================================================================"
|
||||
echo ""
|
||||
echo "Todos os eventos de pull:"
|
||||
kubectl get events -n $NAMESPACE --sort-by='.lastTimestamp' \
|
||||
-o custom-columns='TIME:.lastTimestamp,REASON:.reason,POD:.involvedObject.name,MESSAGE:.message' \
|
||||
| grep -E "Pull|pull"
|
||||
|
||||
echo ""
|
||||
echo "Status dos pods:"
|
||||
kubectl get pods -n $NAMESPACE -o wide
|
||||
|
||||
echo ""
|
||||
echo "Para limpar: kubectl delete namespace $NAMESPACE"
|
||||
138
aula-13/benchmarks/benchmark-toolbox.sh
Executable file
138
aula-13/benchmarks/benchmark-toolbox.sh
Executable file
@@ -0,0 +1,138 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# Benchmark: DevOps Toolbox - eStargz vs GZIP
|
||||
# =============================================================================
|
||||
# Compara tempo de startup usando apenas UMA ferramenta (terraform version)
|
||||
# para demonstrar o benefício do lazy pulling em imagens grandes.
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
NAMESPACE="benchmark-toolbox"
|
||||
REGISTRY="registry.kube.quest"
|
||||
IMAGE_NAME="factory/devops-toolbox"
|
||||
|
||||
# Cores
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_ok() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
cleanup() {
|
||||
log_info "Limpando recursos..."
|
||||
kubectl delete namespace $NAMESPACE --ignore-not-found --wait=false 2>/dev/null || true
|
||||
}
|
||||
|
||||
measure_startup() {
|
||||
local name=$1
|
||||
local image=$2
|
||||
local tag=$3
|
||||
|
||||
log_info "Testando $name ($tag)..."
|
||||
|
||||
# Criar pod
|
||||
cat <<EOF | kubectl apply -f - -n $NAMESPACE
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: $name
|
||||
spec:
|
||||
containers:
|
||||
- name: toolbox
|
||||
image: ${REGISTRY}/${IMAGE_NAME}:${tag}
|
||||
command: ["terraform", "version"]
|
||||
imagePullPolicy: Always
|
||||
restartPolicy: Never
|
||||
imagePullSecrets:
|
||||
- name: gitlab-registry
|
||||
EOF
|
||||
|
||||
# Medir tempo até completar
|
||||
local start_time=$(date +%s.%N)
|
||||
|
||||
# Aguardar pod completar ou falhar
|
||||
kubectl wait --for=condition=Ready pod/$name -n $NAMESPACE --timeout=300s 2>/dev/null || true
|
||||
kubectl wait --for=jsonpath='{.status.phase}'=Succeeded pod/$name -n $NAMESPACE --timeout=300s 2>/dev/null || true
|
||||
|
||||
local end_time=$(date +%s.%N)
|
||||
local duration=$(echo "$end_time - $start_time" | bc)
|
||||
|
||||
echo "$duration"
|
||||
}
|
||||
|
||||
main() {
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Benchmark: DevOps Toolbox"
|
||||
echo " eStargz vs GZIP"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Verificar se imagens existem
|
||||
log_info "Verificando imagens no registry..."
|
||||
|
||||
# Limpar namespace anterior
|
||||
cleanup
|
||||
sleep 5
|
||||
|
||||
# Criar namespace
|
||||
kubectl create namespace $NAMESPACE 2>/dev/null || true
|
||||
|
||||
# Copiar secret do registry
|
||||
if kubectl get secret gitlab-registry -n gitlab &>/dev/null; then
|
||||
kubectl get secret gitlab-registry -n gitlab -o yaml | \
|
||||
sed "s/namespace: gitlab/namespace: $NAMESPACE/" | \
|
||||
kubectl apply -f - 2>/dev/null || true
|
||||
else
|
||||
log_warn "Secret gitlab-registry não encontrado. Usando imagens públicas."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "Iniciando benchmarks..."
|
||||
echo ""
|
||||
|
||||
# Teste 1: eStargz
|
||||
log_info "=== Teste 1: eStargz (lazy pulling) ==="
|
||||
time_estargz=$(measure_startup "toolbox-estargz" "$IMAGE_NAME" "latest")
|
||||
log_ok "eStargz: ${time_estargz}s"
|
||||
|
||||
# Limpar para teste justo
|
||||
kubectl delete pod toolbox-estargz -n $NAMESPACE --wait=true 2>/dev/null || true
|
||||
sleep 5
|
||||
|
||||
# Teste 2: GZIP
|
||||
log_info "=== Teste 2: GZIP (tradicional) ==="
|
||||
time_gzip=$(measure_startup "toolbox-gzip" "$IMAGE_NAME" "gzip")
|
||||
log_ok "GZIP: ${time_gzip}s"
|
||||
|
||||
# Resultados
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " RESULTADOS"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "| Formato | Tempo |"
|
||||
echo "|----------|----------|"
|
||||
printf "| eStargz | %6.1fs |\n" "$time_estargz"
|
||||
printf "| GZIP | %6.1fs |\n" "$time_gzip"
|
||||
echo ""
|
||||
|
||||
# Calcular diferença
|
||||
if command -v bc &>/dev/null; then
|
||||
speedup=$(echo "scale=1; $time_gzip / $time_estargz" | bc)
|
||||
echo "Speedup eStargz: ${speedup}x mais rápido"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "Para ver logs: kubectl logs -n $NAMESPACE toolbox-estargz"
|
||||
log_info "Para limpar: kubectl delete namespace $NAMESPACE"
|
||||
}
|
||||
|
||||
# Executar
|
||||
main "$@"
|
||||
71
aula-13/cleanup.sh
Executable file
71
aula-13/cleanup.sh
Executable file
@@ -0,0 +1,71 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# Aula 13 - Cleanup
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Carregar configuração
|
||||
if [[ -f "${SCRIPT_DIR}/.env" ]]; then
|
||||
source "${SCRIPT_DIR}/.env"
|
||||
fi
|
||||
|
||||
DEPLOY_NAMESPACE="${DEPLOY_NAMESPACE:-factory}"
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Removendo Container Factory"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Remover recursos do PostgreSQL
|
||||
log_info "Removendo PostgreSQL..."
|
||||
kubectl delete -f "${SCRIPT_DIR}/k8s/postgresql/deployment.yaml" -n ${DEPLOY_NAMESPACE} 2>/dev/null || true
|
||||
kubectl delete -f "${SCRIPT_DIR}/k8s/postgresql/service.yaml" -n ${DEPLOY_NAMESPACE} 2>/dev/null || true
|
||||
kubectl delete -f "${SCRIPT_DIR}/k8s/postgresql/configmap.yaml" -n ${DEPLOY_NAMESPACE} 2>/dev/null || true
|
||||
kubectl delete secret postgresql-secret -n ${DEPLOY_NAMESPACE} 2>/dev/null || true
|
||||
|
||||
# Perguntar sobre PVC (dados serão perdidos!)
|
||||
echo ""
|
||||
log_warn "O PVC contém os dados do PostgreSQL!"
|
||||
read -p "Remover PVC (DADOS SERÃO PERDIDOS)? [y/N]: " REMOVE_PVC
|
||||
if [[ "$REMOVE_PVC" =~ ^[Yy]$ ]]; then
|
||||
kubectl delete -f "${SCRIPT_DIR}/k8s/postgresql/pvc.yaml" -n ${DEPLOY_NAMESPACE} 2>/dev/null || true
|
||||
log_success "PVC removido"
|
||||
else
|
||||
log_info "PVC mantido"
|
||||
fi
|
||||
|
||||
# Remover namespace (opcional)
|
||||
echo ""
|
||||
read -p "Remover namespace ${DEPLOY_NAMESPACE}? [y/N]: " REMOVE_NS
|
||||
if [[ "$REMOVE_NS" =~ ^[Yy]$ ]]; then
|
||||
kubectl delete namespace ${DEPLOY_NAMESPACE} --timeout=60s 2>/dev/null || true
|
||||
log_success "Namespace removido"
|
||||
fi
|
||||
|
||||
# Remover .env
|
||||
if [[ -f "${SCRIPT_DIR}/.env" ]]; then
|
||||
rm "${SCRIPT_DIR}/.env"
|
||||
log_info ".env removido"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_success "Cleanup concluído!"
|
||||
echo ""
|
||||
echo "Nota: As imagens no registry não foram removidas."
|
||||
echo "Para remover, acesse o Gitea Packages manualmente:"
|
||||
echo " https://${GITEA_HOST:-gitea.kube.quest}/factory/postgresql/packages"
|
||||
echo ""
|
||||
77
aula-13/images/devops-toolbox/.gitlab-ci.yml
Normal file
77
aula-13/images/devops-toolbox/.gitlab-ci.yml
Normal file
@@ -0,0 +1,77 @@
|
||||
# =============================================================================
|
||||
# Pipeline CI: DevOps Toolbox (eStargz + GZIP)
|
||||
# =============================================================================
|
||||
# Constrói imagem em ambos os formatos para benchmark
|
||||
# =============================================================================
|
||||
|
||||
stages:
|
||||
- build
|
||||
- push
|
||||
|
||||
variables:
|
||||
REGISTRY: registry.kube.quest
|
||||
IMAGE_NAME: factory/devops-toolbox
|
||||
DOCKER_HOST: tcp://docker:2376
|
||||
DOCKER_TLS_CERTDIR: "/certs"
|
||||
DOCKER_TLS_VERIFY: 1
|
||||
DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Build eStargz (lazy pulling)
|
||||
# -----------------------------------------------------------------------------
|
||||
build-estargz:
|
||||
stage: build
|
||||
image: docker:27-dind
|
||||
services:
|
||||
- docker:27-dind
|
||||
before_script:
|
||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $REGISTRY
|
||||
- docker buildx create --use --name multiarch --driver docker-container
|
||||
script:
|
||||
- |
|
||||
docker buildx build \
|
||||
--platform linux/arm64,linux/amd64 \
|
||||
--output type=image,name=${REGISTRY}/${IMAGE_NAME}:latest,push=true,compression=estargz,force-compression=true,oci-mediatypes=true \
|
||||
--cache-from type=registry,ref=${REGISTRY}/${IMAGE_NAME}:cache \
|
||||
--cache-to type=registry,ref=${REGISTRY}/${IMAGE_NAME}:cache,mode=max \
|
||||
.
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "main"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Build GZIP (tradicional, para benchmark)
|
||||
# -----------------------------------------------------------------------------
|
||||
build-gzip:
|
||||
stage: build
|
||||
image: docker:27-dind
|
||||
services:
|
||||
- docker:27-dind
|
||||
before_script:
|
||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $REGISTRY
|
||||
- docker buildx create --use --name multiarch --driver docker-container
|
||||
script:
|
||||
- |
|
||||
docker buildx build \
|
||||
--platform linux/arm64,linux/amd64 \
|
||||
--output type=image,name=${REGISTRY}/${IMAGE_NAME}:gzip,push=true,compression=gzip,oci-mediatypes=true \
|
||||
--cache-from type=registry,ref=${REGISTRY}/${IMAGE_NAME}:cache \
|
||||
.
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "main"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Tag como versão
|
||||
# -----------------------------------------------------------------------------
|
||||
push-tags:
|
||||
stage: push
|
||||
image: docker:27-cli
|
||||
services:
|
||||
- docker:27-dind
|
||||
before_script:
|
||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $REGISTRY
|
||||
script:
|
||||
- docker buildx imagetools create -t ${REGISTRY}/${IMAGE_NAME}:v1 ${REGISTRY}/${IMAGE_NAME}:latest
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "main"
|
||||
needs:
|
||||
- build-estargz
|
||||
90
aula-13/images/devops-toolbox/Dockerfile
Normal file
90
aula-13/images/devops-toolbox/Dockerfile
Normal file
@@ -0,0 +1,90 @@
|
||||
# =============================================================================
|
||||
# DevOps Toolbox - Demonstração de eStargz
|
||||
# =============================================================================
|
||||
# Imagem grande (~650MB) com múltiplas ferramentas em camadas separadas.
|
||||
# Ideal para demonstrar lazy pulling: você só usa UMA ferramenta por vez!
|
||||
# =============================================================================
|
||||
|
||||
FROM alpine:3.21
|
||||
|
||||
LABEL maintainer="workshop"
|
||||
LABEL description="DevOps toolbox for eStargz lazy pulling demonstration"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Camada 1: Ferramentas básicas (~50MB)
|
||||
# -----------------------------------------------------------------------------
|
||||
RUN apk add --no-cache \
|
||||
bash \
|
||||
curl \
|
||||
wget \
|
||||
jq \
|
||||
git \
|
||||
openssh-client \
|
||||
ca-certificates \
|
||||
unzip
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Camada 2: Terraform (~100MB)
|
||||
# -----------------------------------------------------------------------------
|
||||
ARG TERRAFORM_VERSION=1.9.8
|
||||
ARG TARGETARCH
|
||||
RUN wget -q "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_${TARGETARCH}.zip" -O /tmp/terraform.zip && \
|
||||
unzip -q /tmp/terraform.zip -d /usr/local/bin/ && \
|
||||
rm /tmp/terraform.zip && \
|
||||
chmod +x /usr/local/bin/terraform
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Camada 3: OpenTofu (~100MB)
|
||||
# -----------------------------------------------------------------------------
|
||||
ARG TOFU_VERSION=1.8.8
|
||||
RUN wget -q "https://github.com/opentofu/opentofu/releases/download/v${TOFU_VERSION}/tofu_${TOFU_VERSION}_linux_${TARGETARCH}.zip" -O /tmp/tofu.zip && \
|
||||
unzip -q /tmp/tofu.zip -d /usr/local/bin/ && \
|
||||
rm /tmp/tofu.zip && \
|
||||
chmod +x /usr/local/bin/tofu
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Camada 4: Kubectl (~50MB)
|
||||
# -----------------------------------------------------------------------------
|
||||
ARG KUBECTL_VERSION=1.31.4
|
||||
RUN curl -sLO "https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/linux/${TARGETARCH}/kubectl" && \
|
||||
chmod +x kubectl && \
|
||||
mv kubectl /usr/local/bin/
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Camada 5: Helm (~50MB)
|
||||
# -----------------------------------------------------------------------------
|
||||
ARG HELM_VERSION=3.16.4
|
||||
RUN wget -q "https://get.helm.sh/helm-v${HELM_VERSION}-linux-${TARGETARCH}.tar.gz" -O /tmp/helm.tar.gz && \
|
||||
tar -xzf /tmp/helm.tar.gz -C /tmp && \
|
||||
mv /tmp/linux-${TARGETARCH}/helm /usr/local/bin/ && \
|
||||
rm -rf /tmp/helm.tar.gz /tmp/linux-${TARGETARCH}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Camada 6: AWS CLI (~200MB)
|
||||
# -----------------------------------------------------------------------------
|
||||
RUN apk add --no-cache aws-cli
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Camada 7: Python + Ansible (~150MB)
|
||||
# -----------------------------------------------------------------------------
|
||||
RUN apk add --no-cache python3 py3-pip && \
|
||||
pip3 install --no-cache-dir ansible --break-system-packages --quiet
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Camada 8: k9s (~50MB)
|
||||
# -----------------------------------------------------------------------------
|
||||
ARG K9S_VERSION=0.32.7
|
||||
RUN wget -q "https://github.com/derailed/k9s/releases/download/v${K9S_VERSION}/k9s_Linux_${TARGETARCH}.tar.gz" -O /tmp/k9s.tar.gz && \
|
||||
tar -xzf /tmp/k9s.tar.gz -C /usr/local/bin/ k9s && \
|
||||
rm /tmp/k9s.tar.gz
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Entrypoint
|
||||
# -----------------------------------------------------------------------------
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["--help"]
|
||||
64
aula-13/images/devops-toolbox/entrypoint.sh
Normal file
64
aula-13/images/devops-toolbox/entrypoint.sh
Normal file
@@ -0,0 +1,64 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# DevOps Toolbox Entrypoint
|
||||
# =============================================================================
|
||||
# Executa a ferramenta especificada ou mostra ajuda
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# Ferramentas disponíveis
|
||||
TOOLS="terraform tofu kubectl helm aws ansible k9s"
|
||||
|
||||
show_help() {
|
||||
echo "DevOps Toolbox - Demonstração de eStargz Lazy Pulling"
|
||||
echo ""
|
||||
echo "Uso: docker run toolbox <ferramenta> [argumentos]"
|
||||
echo ""
|
||||
echo "Ferramentas disponíveis:"
|
||||
echo " terraform - Infrastructure as Code"
|
||||
echo " tofu - OpenTofu (Terraform fork)"
|
||||
echo " kubectl - Kubernetes CLI"
|
||||
echo " helm - Kubernetes package manager"
|
||||
echo " aws - AWS CLI"
|
||||
echo " ansible - Configuration management"
|
||||
echo " k9s - Kubernetes TUI"
|
||||
echo ""
|
||||
echo "Exemplos:"
|
||||
echo " docker run toolbox terraform version"
|
||||
echo " docker run toolbox kubectl version --client"
|
||||
echo " docker run toolbox helm version"
|
||||
echo ""
|
||||
echo "Com eStargz, apenas a camada da ferramenta usada é baixada!"
|
||||
}
|
||||
|
||||
show_versions() {
|
||||
echo "Versões instaladas:"
|
||||
echo ""
|
||||
terraform version 2>/dev/null | head -1 || echo "terraform: não disponível"
|
||||
tofu version 2>/dev/null | head -1 || echo "tofu: não disponível"
|
||||
kubectl version --client 2>/dev/null | head -1 || echo "kubectl: não disponível"
|
||||
helm version --short 2>/dev/null || echo "helm: não disponível"
|
||||
aws --version 2>/dev/null || echo "aws: não disponível"
|
||||
ansible --version 2>/dev/null | head -1 || echo "ansible: não disponível"
|
||||
k9s version --short 2>/dev/null || echo "k9s: não disponível"
|
||||
}
|
||||
|
||||
# Processa argumentos
|
||||
case "$1" in
|
||||
--help|-h|"")
|
||||
show_help
|
||||
;;
|
||||
--versions|-v)
|
||||
show_versions
|
||||
;;
|
||||
terraform|tofu|kubectl|helm|aws|ansible|k9s)
|
||||
exec "$@"
|
||||
;;
|
||||
*)
|
||||
echo "Erro: Ferramenta '$1' não reconhecida"
|
||||
echo ""
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
38
aula-13/images/large-test/.gitlab-ci.yml
Normal file
38
aula-13/images/large-test/.gitlab-ci.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
stages:
|
||||
- build
|
||||
|
||||
variables:
|
||||
REGISTRY: registry.kube.quest
|
||||
IMAGE_NAME: factory/large-test
|
||||
|
||||
build:
|
||||
stage: build
|
||||
image: docker:27-dind
|
||||
services:
|
||||
- docker:27-dind
|
||||
variables:
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
DOCKER_HOST: tcp://docker:2375
|
||||
before_script:
|
||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||
- docker buildx create --use --name builder --driver docker-container
|
||||
script:
|
||||
# Build eStargz (lazy pulling)
|
||||
- echo "Building eStargz version..."
|
||||
- |
|
||||
docker buildx build \
|
||||
--output type=image,name=${REGISTRY}/${IMAGE_NAME}:latest,push=true,compression=estargz,force-compression=true,oci-mediatypes=true \
|
||||
.
|
||||
|
||||
# Build GZIP tradicional
|
||||
- echo "Building GZIP version..."
|
||||
- |
|
||||
docker buildx build \
|
||||
--output type=image,name=${REGISTRY}/${IMAGE_NAME}:gzip,push=true,compression=gzip,oci-mediatypes=true \
|
||||
.
|
||||
|
||||
- echo "Images pushed:"
|
||||
- echo " - ${REGISTRY}/${IMAGE_NAME}:latest (eStargz ~1.5GB)"
|
||||
- echo " - ${REGISTRY}/${IMAGE_NAME}:gzip (GZIP ~1.5GB)"
|
||||
tags:
|
||||
- kubernetes
|
||||
34
aula-13/images/large-test/Dockerfile
Normal file
34
aula-13/images/large-test/Dockerfile
Normal file
@@ -0,0 +1,34 @@
|
||||
# Imagem de teste grande (~1.5GB) para benchmark de lazy pulling
|
||||
FROM alpine:3.21
|
||||
|
||||
# Camada 1: Base tools (~50MB)
|
||||
RUN apk add --no-cache \
|
||||
bash \
|
||||
curl \
|
||||
wget \
|
||||
jq \
|
||||
python3 \
|
||||
py3-pip
|
||||
|
||||
# Camada 2: Dados dummy 1 (~300MB)
|
||||
RUN dd if=/dev/urandom of=/data1.bin bs=1M count=300
|
||||
|
||||
# Camada 3: Dados dummy 2 (~300MB)
|
||||
RUN dd if=/dev/urandom of=/data2.bin bs=1M count=300
|
||||
|
||||
# Camada 4: Dados dummy 3 (~300MB)
|
||||
RUN dd if=/dev/urandom of=/data3.bin bs=1M count=300
|
||||
|
||||
# Camada 5: Dados dummy 4 (~300MB)
|
||||
RUN dd if=/dev/urandom of=/data4.bin bs=1M count=300
|
||||
|
||||
# Camada 6: Dados dummy 5 (~300MB)
|
||||
RUN dd if=/dev/urandom of=/data5.bin bs=1M count=300
|
||||
|
||||
# Script de teste que acessa apenas arquivos pequenos
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
# Entrypoint simples que NÃO acessa os arquivos grandes
|
||||
# Isso permite testar o lazy pulling - container inicia sem precisar dos dados
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
12
aula-13/images/large-test/entrypoint.sh
Normal file
12
aula-13/images/large-test/entrypoint.sh
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
# Entrypoint simples que NÃO acessa os arquivos grandes
|
||||
# Permite testar lazy pulling - container inicia sem baixar dados
|
||||
|
||||
echo "Container iniciado em $(date)"
|
||||
echo "Hostname: $(hostname)"
|
||||
echo "Este container tem ~1.5GB de dados que NÃO são acessados no startup"
|
||||
|
||||
# Loop infinito para manter container running
|
||||
while true; do
|
||||
sleep 3600
|
||||
done
|
||||
51
aula-13/images/postgresql/Dockerfile
Normal file
51
aula-13/images/postgresql/Dockerfile
Normal file
@@ -0,0 +1,51 @@
|
||||
# =============================================================================
|
||||
# Dockerfile - PostgreSQL Production (Alpine)
|
||||
# =============================================================================
|
||||
#
|
||||
# Imagem customizada PostgreSQL para substituir Bitnami.
|
||||
# Otimizada para produção e formato eStargz (lazy pulling).
|
||||
#
|
||||
# Build (formato eStargz):
|
||||
# docker buildx build \
|
||||
# --output type=image,name=registry.kube.quest/factory/postgresql:17,push=true,compression=estargz,force-compression=true,oci-mediatypes=true \
|
||||
# .
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
FROM postgres:17-alpine
|
||||
|
||||
LABEL maintainer="workshop"
|
||||
LABEL description="PostgreSQL 17 Alpine - Production Ready"
|
||||
LABEL org.opencontainers.image.title="postgresql"
|
||||
LABEL org.opencontainers.image.version="17"
|
||||
|
||||
# Variáveis de ambiente padrão
|
||||
ENV POSTGRES_USER=postgres \
|
||||
POSTGRES_DB=postgres \
|
||||
PGDATA=/var/lib/postgresql/data/pgdata \
|
||||
LANG=en_US.UTF-8
|
||||
|
||||
# Instalar dependências adicionais úteis
|
||||
RUN apk add --no-cache \
|
||||
tzdata \
|
||||
curl \
|
||||
jq
|
||||
|
||||
# Criar diretório para configurações customizadas
|
||||
RUN mkdir -p /etc/postgresql/postgresql.conf.d
|
||||
|
||||
# Copiar configuração de produção
|
||||
COPY postgresql.conf /etc/postgresql/postgresql.conf.d/00-production.conf
|
||||
|
||||
# Healthcheck nativo
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
|
||||
CMD pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB} || exit 1
|
||||
|
||||
# Expor porta padrão
|
||||
EXPOSE 5432
|
||||
|
||||
# Usuário não-root (já vem configurado na imagem oficial)
|
||||
USER postgres
|
||||
|
||||
# Comando padrão com configuração customizada
|
||||
CMD ["postgres", "-c", "config_file=/etc/postgresql/postgresql.conf.d/00-production.conf"]
|
||||
77
aula-13/images/postgresql/postgresql.conf
Normal file
77
aula-13/images/postgresql/postgresql.conf
Normal file
@@ -0,0 +1,77 @@
|
||||
# =============================================================================
|
||||
# PostgreSQL Production Configuration
|
||||
# =============================================================================
|
||||
# Otimizado para containers Kubernetes com ~1GB de memória
|
||||
# Documentação: https://www.postgresql.org/docs/17/runtime-config.html
|
||||
# =============================================================================
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Conexões
|
||||
# -----------------------------------------------------------------------------
|
||||
listen_addresses = '*'
|
||||
max_connections = 100
|
||||
superuser_reserved_connections = 3
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Memória (para container com 1GB)
|
||||
# -----------------------------------------------------------------------------
|
||||
# shared_buffers: ~25% da RAM disponível
|
||||
shared_buffers = 256MB
|
||||
|
||||
# effective_cache_size: ~50% da RAM (estimativa para o planner)
|
||||
effective_cache_size = 512MB
|
||||
|
||||
# work_mem: memória por operação de sort/hash (cuidado com max_connections)
|
||||
work_mem = 8MB
|
||||
|
||||
# maintenance_work_mem: para VACUUM, CREATE INDEX, etc.
|
||||
maintenance_work_mem = 64MB
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# WAL (Write-Ahead Logging)
|
||||
# -----------------------------------------------------------------------------
|
||||
wal_level = replica
|
||||
max_wal_size = 1GB
|
||||
min_wal_size = 80MB
|
||||
checkpoint_completion_target = 0.9
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Query Planner
|
||||
# -----------------------------------------------------------------------------
|
||||
# random_page_cost: baixo para SSD (1.1) vs HDD (4.0)
|
||||
random_page_cost = 1.1
|
||||
|
||||
# effective_io_concurrency: alto para SSD
|
||||
effective_io_concurrency = 200
|
||||
|
||||
# default_statistics_target: mais estatísticas = melhores planos
|
||||
default_statistics_target = 100
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Logging
|
||||
# -----------------------------------------------------------------------------
|
||||
# Log para stderr (compatível com kubectl logs)
|
||||
log_destination = 'stderr'
|
||||
logging_collector = off
|
||||
|
||||
# O que logar
|
||||
log_statement = 'ddl'
|
||||
log_min_duration_statement = 1000
|
||||
|
||||
# Formato do log
|
||||
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Locale/Encoding
|
||||
# -----------------------------------------------------------------------------
|
||||
timezone = 'America/Sao_Paulo'
|
||||
lc_messages = 'en_US.UTF-8'
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Performance
|
||||
# -----------------------------------------------------------------------------
|
||||
# JIT: desabilitar em containers pequenos (overhead de compilação)
|
||||
jit = off
|
||||
|
||||
# Huge pages: requer configuração do host
|
||||
huge_pages = off
|
||||
12
aula-13/k8s/postgresql/configmap.yaml
Normal file
12
aula-13/k8s/postgresql/configmap.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
# =============================================================================
|
||||
# ConfigMap - PostgreSQL Configuration
|
||||
# =============================================================================
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: postgresql-config
|
||||
labels:
|
||||
app: postgresql
|
||||
data:
|
||||
# Nome do banco de dados padrão
|
||||
database: "app"
|
||||
111
aula-13/k8s/postgresql/deployment.yaml
Normal file
111
aula-13/k8s/postgresql/deployment.yaml
Normal file
@@ -0,0 +1,111 @@
|
||||
# =============================================================================
|
||||
# Deployment - PostgreSQL (Container Factory)
|
||||
# =============================================================================
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: postgresql
|
||||
labels:
|
||||
app: postgresql
|
||||
app.kubernetes.io/name: postgresql
|
||||
app.kubernetes.io/component: database
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate # PostgreSQL não suporta rolling update
|
||||
selector:
|
||||
matchLabels:
|
||||
app: postgresql
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: postgresql
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 30
|
||||
imagePullSecrets:
|
||||
- name: gitlab-registry
|
||||
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 70 # postgres user no Alpine
|
||||
fsGroup: 70
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
|
||||
containers:
|
||||
- name: postgresql
|
||||
# Imagem da Container Factory (eStargz)
|
||||
image: registry.kube.quest/factory/postgresql:17
|
||||
imagePullPolicy: IfNotPresent
|
||||
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
name: postgresql
|
||||
protocol: TCP
|
||||
|
||||
env:
|
||||
- name: POSTGRES_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgresql-secret
|
||||
key: username
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgresql-secret
|
||||
key: password
|
||||
- name: POSTGRES_DB
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: postgresql-config
|
||||
key: database
|
||||
- name: PGDATA
|
||||
value: /var/lib/postgresql/data/pgdata
|
||||
|
||||
resources:
|
||||
requests:
|
||||
memory: "512Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "1Gi"
|
||||
cpu: "500m"
|
||||
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /var/lib/postgresql/data
|
||||
|
||||
# Liveness: reinicia se PostgreSQL travar
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- pg_isready -U $POSTGRES_USER -d $POSTGRES_DB
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 6
|
||||
|
||||
# Readiness: remove do service se não estiver pronto
|
||||
readinessProbe:
|
||||
exec:
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- pg_isready -U $POSTGRES_USER -d $POSTGRES_DB
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: postgresql-data
|
||||
17
aula-13/k8s/postgresql/pvc.yaml
Normal file
17
aula-13/k8s/postgresql/pvc.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
# =============================================================================
|
||||
# PersistentVolumeClaim - PostgreSQL Data
|
||||
# =============================================================================
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: postgresql-data
|
||||
labels:
|
||||
app: postgresql
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
# Hetzner Cloud Volumes (aula-08 CSI)
|
||||
storageClassName: hcloud-volumes
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
24
aula-13/k8s/postgresql/secret.yaml
Normal file
24
aula-13/k8s/postgresql/secret.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
# =============================================================================
|
||||
# Secret - PostgreSQL Credentials (Template)
|
||||
# =============================================================================
|
||||
#
|
||||
# IMPORTANTE: Este arquivo é um template.
|
||||
# O setup.sh gera o secret automaticamente com senha aleatória.
|
||||
#
|
||||
# Para criar manualmente:
|
||||
# kubectl create secret generic postgresql-secret \
|
||||
# --from-literal=username=postgres \
|
||||
# --from-literal=password=SUA_SENHA_AQUI \
|
||||
# -n factory
|
||||
#
|
||||
# =============================================================================
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: postgresql-secret
|
||||
labels:
|
||||
app: postgresql
|
||||
type: Opaque
|
||||
stringData:
|
||||
username: postgres
|
||||
password: CHANGE_ME_USE_SETUP_SH
|
||||
18
aula-13/k8s/postgresql/service.yaml
Normal file
18
aula-13/k8s/postgresql/service.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
# =============================================================================
|
||||
# Service - PostgreSQL
|
||||
# =============================================================================
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: postgresql
|
||||
labels:
|
||||
app: postgresql
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 5432
|
||||
targetPort: 5432
|
||||
protocol: TCP
|
||||
name: postgresql
|
||||
selector:
|
||||
app: postgresql
|
||||
131
aula-13/k8s/prepull-daemonset.yaml
Normal file
131
aula-13/k8s/prepull-daemonset.yaml
Normal file
@@ -0,0 +1,131 @@
|
||||
# =============================================================================
|
||||
# Pre-pull DaemonSet - Alternativa para Cold Start
|
||||
# =============================================================================
|
||||
# Garante que imagens críticas estejam em cache em TODOS os nodes.
|
||||
# Quando KEDA/Cluster Autoscaler criar pods, imagens já estarão disponíveis.
|
||||
# =============================================================================
|
||||
#
|
||||
# COMO USAR:
|
||||
# 1. Edite a lista de initContainers com suas imagens
|
||||
# 2. kubectl apply -f prepull-daemonset.yaml
|
||||
# 3. Aguarde todos os pods ficarem Ready
|
||||
# 4. Imagens estarão em cache em todos os nodes
|
||||
#
|
||||
# QUANDO USAR:
|
||||
# - Databases (PostgreSQL, MongoDB) que precisam de 100% dos arquivos
|
||||
# - Apps que precarregam (n8n, Laravel Octane)
|
||||
# - Qualquer imagem onde eStargz NÃO ajuda
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: image-prepuller
|
||||
namespace: kube-system
|
||||
labels:
|
||||
app: image-prepuller
|
||||
purpose: cold-start-optimization
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: image-prepuller
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: image-prepuller
|
||||
spec:
|
||||
# Tolera todos os taints para rodar em TODOS os nodes
|
||||
tolerations:
|
||||
- operator: Exists
|
||||
|
||||
# InitContainers baixam as imagens e terminam
|
||||
initContainers:
|
||||
# ---------------------------------------------------------------------
|
||||
# PostgreSQL (Container Factory)
|
||||
# ---------------------------------------------------------------------
|
||||
- name: prepull-postgresql
|
||||
image: registry.kube.quest/factory/postgresql:17
|
||||
command: ["echo", "PostgreSQL image cached"]
|
||||
imagePullPolicy: Always
|
||||
resources:
|
||||
requests:
|
||||
cpu: 1m
|
||||
memory: 1Mi
|
||||
limits:
|
||||
cpu: 10m
|
||||
memory: 10Mi
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# n8n
|
||||
# ---------------------------------------------------------------------
|
||||
- name: prepull-n8n
|
||||
image: docker.n8n.io/n8nio/n8n:latest
|
||||
command: ["echo", "n8n image cached"]
|
||||
imagePullPolicy: Always
|
||||
resources:
|
||||
requests:
|
||||
cpu: 1m
|
||||
memory: 1Mi
|
||||
limits:
|
||||
cpu: 10m
|
||||
memory: 10Mi
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# PostgreSQL Oficial (para clientes)
|
||||
# ---------------------------------------------------------------------
|
||||
- name: prepull-postgres-alpine
|
||||
image: postgres:17-alpine
|
||||
command: ["echo", "PostgreSQL Alpine image cached"]
|
||||
imagePullPolicy: Always
|
||||
resources:
|
||||
requests:
|
||||
cpu: 1m
|
||||
memory: 1Mi
|
||||
limits:
|
||||
cpu: 10m
|
||||
memory: 10Mi
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Redis
|
||||
# ---------------------------------------------------------------------
|
||||
- name: prepull-redis
|
||||
image: redis:7-alpine
|
||||
command: ["echo", "Redis image cached"]
|
||||
imagePullPolicy: Always
|
||||
resources:
|
||||
requests:
|
||||
cpu: 1m
|
||||
memory: 1Mi
|
||||
limits:
|
||||
cpu: 10m
|
||||
memory: 10Mi
|
||||
|
||||
# Container principal apenas mantém o DaemonSet vivo
|
||||
containers:
|
||||
- name: pause
|
||||
image: gcr.io/google_containers/pause:3.9
|
||||
resources:
|
||||
requests:
|
||||
cpu: 1m
|
||||
memory: 1Mi
|
||||
limits:
|
||||
cpu: 10m
|
||||
memory: 10Mi
|
||||
|
||||
# Secrets para registries privados
|
||||
imagePullSecrets:
|
||||
- name: gitlab-registry
|
||||
|
||||
---
|
||||
# =============================================================================
|
||||
# Secret para Registry Privado (template)
|
||||
# =============================================================================
|
||||
# Crie este secret em kube-system se usar registry privado:
|
||||
#
|
||||
# kubectl create secret docker-registry gitlab-registry \
|
||||
# --namespace=kube-system \
|
||||
# --docker-server=registry.kube.quest \
|
||||
# --docker-username=<usuario> \
|
||||
# --docker-password=<token>
|
||||
# =============================================================================
|
||||
183
aula-13/pipelines/postgresql/.gitlab-ci.yml
Normal file
183
aula-13/pipelines/postgresql/.gitlab-ci.yml
Normal file
@@ -0,0 +1,183 @@
|
||||
# =============================================================================
|
||||
# GitLab CI/CD Pipeline - PostgreSQL Container Factory
|
||||
# =============================================================================
|
||||
#
|
||||
# Build de imagem PostgreSQL customizada em formato eStargz.
|
||||
# Push para registry.kube.quest com lazy pulling habilitado.
|
||||
#
|
||||
# Requisitos:
|
||||
# - GitLab Runner com Docker-in-Docker (aula-11)
|
||||
# - BuildKit habilitado
|
||||
#
|
||||
# Uso:
|
||||
# 1. Criar grupo 'factory' no GitLab
|
||||
# 2. Criar projeto 'postgresql' dentro do grupo
|
||||
# 3. Copiar Dockerfile, postgresql.conf e este .gitlab-ci.yml
|
||||
# 4. Push para main - pipeline roda automaticamente
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- push
|
||||
|
||||
variables:
|
||||
# Registry do GitLab (aula-10)
|
||||
REGISTRY: ${CI_REGISTRY}
|
||||
IMAGE_NAME: ${CI_PROJECT_PATH}
|
||||
POSTGRES_VERSION: "17"
|
||||
|
||||
# BuildKit para suporte a eStargz
|
||||
DOCKER_BUILDKIT: "1"
|
||||
BUILDKIT_PROGRESS: plain
|
||||
|
||||
# Docker-in-Docker TLS connection
|
||||
DOCKER_HOST: tcp://docker:2376
|
||||
DOCKER_TLS_CERTDIR: "/certs"
|
||||
DOCKER_CERT_PATH: "/certs/client"
|
||||
DOCKER_TLS_VERIFY: "1"
|
||||
|
||||
# =============================================================================
|
||||
# BUILD - Construir imagem em formato eStargz
|
||||
# =============================================================================
|
||||
build:
|
||||
stage: build
|
||||
image: docker:24
|
||||
services:
|
||||
- docker:24-dind
|
||||
before_script:
|
||||
# Aguardar Docker daemon estar pronto
|
||||
- until docker info; do sleep 1; done
|
||||
# Login no registry
|
||||
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
|
||||
# Configurar buildx com driver docker (usa daemon existente)
|
||||
- docker buildx create --name estargz-builder --driver docker --use || true
|
||||
- docker buildx inspect --bootstrap
|
||||
script:
|
||||
- echo "Building ${IMAGE_NAME}:${POSTGRES_VERSION}-${CI_COMMIT_SHA:0:8} with eStargz compression"
|
||||
# Build com formato eStargz
|
||||
- |
|
||||
docker buildx build \
|
||||
--output type=image,name=${REGISTRY}/${IMAGE_NAME}:${POSTGRES_VERSION}-${CI_COMMIT_SHA:0:8},push=true,compression=estargz,force-compression=true,oci-mediatypes=true \
|
||||
--label "org.opencontainers.image.revision=${CI_COMMIT_SHA}" \
|
||||
--label "org.opencontainers.image.created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||
--label "org.opencontainers.image.source=${CI_PROJECT_URL}" \
|
||||
--build-arg POSTGRES_VERSION=${POSTGRES_VERSION} \
|
||||
.
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "main"
|
||||
- if: $CI_COMMIT_TAG
|
||||
tags:
|
||||
- kubernetes
|
||||
- docker
|
||||
|
||||
# =============================================================================
|
||||
# TEST - Testar imagem construída
|
||||
# =============================================================================
|
||||
test:
|
||||
stage: test
|
||||
image: docker:24
|
||||
services:
|
||||
- docker:24-dind
|
||||
before_script:
|
||||
- until docker info; do sleep 1; done
|
||||
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
|
||||
script:
|
||||
- echo "Testing PostgreSQL image..."
|
||||
|
||||
# Iniciar container de teste
|
||||
- |
|
||||
docker run -d --name pg-test \
|
||||
-e POSTGRES_PASSWORD=testpassword \
|
||||
-e POSTGRES_DB=testdb \
|
||||
${REGISTRY}/${IMAGE_NAME}:${POSTGRES_VERSION}-${CI_COMMIT_SHA:0:8}
|
||||
|
||||
# Aguardar inicialização (30s max)
|
||||
- |
|
||||
for i in $(seq 1 30); do
|
||||
if docker exec pg-test pg_isready -U postgres -d testdb 2>/dev/null; then
|
||||
echo "PostgreSQL ready!"
|
||||
break
|
||||
fi
|
||||
echo "Waiting for PostgreSQL... ($i/30)"
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Verificar healthcheck
|
||||
- docker exec pg-test pg_isready -U postgres -d testdb
|
||||
|
||||
# Testar conexão e queries básicas
|
||||
- docker exec pg-test psql -U postgres -d testdb -c "SELECT version();"
|
||||
- docker exec pg-test psql -U postgres -d testdb -c "SHOW shared_buffers;"
|
||||
- docker exec pg-test psql -U postgres -d testdb -c "SHOW max_connections;"
|
||||
|
||||
# Testar criação de tabela
|
||||
- docker exec pg-test psql -U postgres -d testdb -c "CREATE TABLE test (id serial PRIMARY KEY, name text);"
|
||||
- docker exec pg-test psql -U postgres -d testdb -c "INSERT INTO test (name) VALUES ('test');"
|
||||
- docker exec pg-test psql -U postgres -d testdb -c "SELECT * FROM test;"
|
||||
- docker exec pg-test psql -U postgres -d testdb -c "DROP TABLE test;"
|
||||
|
||||
# Cleanup
|
||||
- docker stop pg-test && docker rm pg-test
|
||||
- echo "All tests passed!"
|
||||
needs:
|
||||
- build
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "main"
|
||||
- if: $CI_COMMIT_TAG
|
||||
tags:
|
||||
- kubernetes
|
||||
- docker
|
||||
|
||||
# =============================================================================
|
||||
# PUSH - Tag como versão e latest
|
||||
# =============================================================================
|
||||
push:
|
||||
stage: push
|
||||
image: docker:24
|
||||
services:
|
||||
- docker:24-dind
|
||||
before_script:
|
||||
- until docker info; do sleep 1; done
|
||||
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
|
||||
- docker buildx create --name estargz-builder --driver docker --use || true
|
||||
- docker buildx inspect --bootstrap
|
||||
script:
|
||||
- echo "Tagging and pushing final images..."
|
||||
|
||||
# Re-tag como versão (17)
|
||||
- |
|
||||
docker buildx build \
|
||||
--output type=image,name=${REGISTRY}/${IMAGE_NAME}:${POSTGRES_VERSION},push=true,compression=estargz,force-compression=true,oci-mediatypes=true \
|
||||
--label "org.opencontainers.image.revision=${CI_COMMIT_SHA}" \
|
||||
--label "org.opencontainers.image.created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||
.
|
||||
|
||||
# Re-tag como latest
|
||||
- |
|
||||
docker buildx build \
|
||||
--output type=image,name=${REGISTRY}/${IMAGE_NAME}:latest,push=true,compression=estargz,force-compression=true,oci-mediatypes=true \
|
||||
--label "org.opencontainers.image.revision=${CI_COMMIT_SHA}" \
|
||||
--label "org.opencontainers.image.created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||
.
|
||||
|
||||
# Build versão GZIP (para benchmark)
|
||||
- |
|
||||
docker buildx build \
|
||||
--output type=image,name=${REGISTRY}/${IMAGE_NAME}:${POSTGRES_VERSION}-gzip,push=true,compression=gzip,oci-mediatypes=true \
|
||||
--label "org.opencontainers.image.revision=${CI_COMMIT_SHA}" \
|
||||
--label "org.opencontainers.image.created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||
.
|
||||
|
||||
- echo "Images pushed:"
|
||||
- echo " - ${REGISTRY}/${IMAGE_NAME}:${POSTGRES_VERSION} (eStargz)"
|
||||
- echo " - ${REGISTRY}/${IMAGE_NAME}:${POSTGRES_VERSION}-gzip (tradicional)"
|
||||
- echo " - ${REGISTRY}/${IMAGE_NAME}:latest (eStargz)"
|
||||
needs:
|
||||
- test
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "main"
|
||||
tags:
|
||||
- kubernetes
|
||||
- docker
|
||||
157
aula-13/pipelines/postgresql/ci.yml
Normal file
157
aula-13/pipelines/postgresql/ci.yml
Normal file
@@ -0,0 +1,157 @@
|
||||
# =============================================================================
|
||||
# Gitea Actions Workflow - PostgreSQL Container Factory
|
||||
# =============================================================================
|
||||
#
|
||||
# Build de imagem PostgreSQL customizada em formato eStargz.
|
||||
# Push para Gitea Container Registry com lazy pulling habilitado.
|
||||
#
|
||||
# Requisitos:
|
||||
# - Gitea Actions Runner com Docker (aula-10)
|
||||
# - Secret REGISTRY_TOKEN configurado no repo
|
||||
#
|
||||
# Uso:
|
||||
# 1. Criar org 'factory' no Gitea
|
||||
# 2. Criar repositório 'postgresql' na org
|
||||
# 3. Copiar Dockerfile, postgresql.conf e este ci.yml para .gitea/workflows/
|
||||
# 4. Push para main - pipeline roda automaticamente
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
name: Build PostgreSQL
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags: ['*']
|
||||
|
||||
env:
|
||||
REGISTRY: gitea.kube.quest
|
||||
IMAGE_NAME: factory/postgresql
|
||||
POSTGRES_VERSION: "17"
|
||||
|
||||
jobs:
|
||||
# ===========================================================================
|
||||
# BUILD - Construir imagem em formato eStargz
|
||||
# ===========================================================================
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Gitea Registry
|
||||
run: |
|
||||
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login ${{ env.REGISTRY }} \
|
||||
-u ${{ gitea.actor }} --password-stdin
|
||||
|
||||
- name: Build eStargz image
|
||||
run: |
|
||||
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-8)
|
||||
echo "Building ${{ env.IMAGE_NAME }}:${{ env.POSTGRES_VERSION }}-${SHORT_SHA} with eStargz compression"
|
||||
docker buildx build \
|
||||
--output type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.POSTGRES_VERSION }}-${SHORT_SHA},push=true,compression=estargz,force-compression=true,oci-mediatypes=true \
|
||||
--label "org.opencontainers.image.revision=${{ github.sha }}" \
|
||||
--label "org.opencontainers.image.created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||
--build-arg POSTGRES_VERSION=${{ env.POSTGRES_VERSION }} \
|
||||
.
|
||||
|
||||
# ===========================================================================
|
||||
# TEST - Testar imagem construída
|
||||
# ===========================================================================
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Login to Gitea Registry
|
||||
run: |
|
||||
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login ${{ env.REGISTRY }} \
|
||||
-u ${{ gitea.actor }} --password-stdin
|
||||
|
||||
- name: Test PostgreSQL image
|
||||
run: |
|
||||
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-8)
|
||||
echo "Testing PostgreSQL image..."
|
||||
|
||||
docker run -d --name pg-test \
|
||||
-e POSTGRES_PASSWORD=testpassword \
|
||||
-e POSTGRES_DB=testdb \
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.POSTGRES_VERSION }}-${SHORT_SHA}
|
||||
|
||||
# Aguardar inicialização (30s max)
|
||||
for i in $(seq 1 30); do
|
||||
if docker exec pg-test pg_isready -U postgres -d testdb 2>/dev/null; then
|
||||
echo "PostgreSQL ready!"
|
||||
break
|
||||
fi
|
||||
echo "Waiting for PostgreSQL... ($i/30)"
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Verificar healthcheck
|
||||
docker exec pg-test pg_isready -U postgres -d testdb
|
||||
|
||||
# Testar conexão e queries básicas
|
||||
docker exec pg-test psql -U postgres -d testdb -c "SELECT version();"
|
||||
docker exec pg-test psql -U postgres -d testdb -c "SHOW shared_buffers;"
|
||||
docker exec pg-test psql -U postgres -d testdb -c "SHOW max_connections;"
|
||||
|
||||
# Testar criação de tabela
|
||||
docker exec pg-test psql -U postgres -d testdb -c "CREATE TABLE test (id serial PRIMARY KEY, name text);"
|
||||
docker exec pg-test psql -U postgres -d testdb -c "INSERT INTO test (name) VALUES ('test');"
|
||||
docker exec pg-test psql -U postgres -d testdb -c "SELECT * FROM test;"
|
||||
docker exec pg-test psql -U postgres -d testdb -c "DROP TABLE test;"
|
||||
|
||||
# Cleanup
|
||||
docker stop pg-test && docker rm pg-test
|
||||
echo "All tests passed!"
|
||||
|
||||
# ===========================================================================
|
||||
# PUSH - Tag como versão e latest
|
||||
# ===========================================================================
|
||||
push:
|
||||
runs-on: ubuntu-latest
|
||||
needs: test
|
||||
if: github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Gitea Registry
|
||||
run: |
|
||||
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login ${{ env.REGISTRY }} \
|
||||
-u ${{ gitea.actor }} --password-stdin
|
||||
|
||||
- name: Push version and latest tags (eStargz)
|
||||
run: |
|
||||
echo "Tagging and pushing final images..."
|
||||
|
||||
# Tag como versão (17)
|
||||
docker buildx build \
|
||||
--output type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.POSTGRES_VERSION }},push=true,compression=estargz,force-compression=true,oci-mediatypes=true \
|
||||
--label "org.opencontainers.image.revision=${{ github.sha }}" \
|
||||
--label "org.opencontainers.image.created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||
.
|
||||
|
||||
# Tag como latest
|
||||
docker buildx build \
|
||||
--output type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest,push=true,compression=estargz,force-compression=true,oci-mediatypes=true \
|
||||
--label "org.opencontainers.image.revision=${{ github.sha }}" \
|
||||
--label "org.opencontainers.image.created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||
.
|
||||
|
||||
- name: Push GZIP version (for benchmark)
|
||||
run: |
|
||||
docker buildx build \
|
||||
--output type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.POSTGRES_VERSION }}-gzip,push=true,compression=gzip,oci-mediatypes=true \
|
||||
--label "org.opencontainers.image.revision=${{ github.sha }}" \
|
||||
--label "org.opencontainers.image.created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||
.
|
||||
|
||||
echo "Images pushed:"
|
||||
echo " - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.POSTGRES_VERSION }} (eStargz)"
|
||||
echo " - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.POSTGRES_VERSION }}-gzip (GZIP)"
|
||||
echo " - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest (eStargz)"
|
||||
234
aula-13/setup.sh
Executable file
234
aula-13/setup.sh
Executable file
@@ -0,0 +1,234 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# Aula 13 - Container Factory (eStargz Images)
|
||||
# =============================================================================
|
||||
#
|
||||
# Este script configura:
|
||||
# 1. Namespace para deploy de imagens customizadas
|
||||
# 2. Secrets e ConfigMaps
|
||||
# 3. Instruções para criar repositório no Gitea
|
||||
#
|
||||
# Pré-requisitos:
|
||||
# - Cluster Kubernetes com Talos + stargz-snapshotter (aula-07/08)
|
||||
# - Gitea instalado (aula-10)
|
||||
# - Gitea Actions Runner (aula-10)
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# Cores para output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ENV_FILE="${SCRIPT_DIR}/.env"
|
||||
|
||||
# =============================================================================
|
||||
# VERIFICAR PRÉ-REQUISITOS
|
||||
# =============================================================================
|
||||
|
||||
log_info "Verificando pré-requisitos..."
|
||||
|
||||
# Verificar kubectl
|
||||
if ! command -v kubectl &> /dev/null; then
|
||||
log_error "kubectl não encontrado. Instale com: brew install kubectl"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verificar conexão com cluster
|
||||
if ! kubectl cluster-info &> /dev/null; then
|
||||
log_error "Não foi possível conectar ao cluster Kubernetes"
|
||||
log_info "Verifique se KUBECONFIG está configurado corretamente"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verificar se Gitea está instalado
|
||||
if ! kubectl get namespace gitea &> /dev/null; then
|
||||
log_error "Namespace 'gitea' não encontrado"
|
||||
log_info "Execute primeiro a aula-10 para instalar o Gitea"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "Pré-requisitos verificados"
|
||||
|
||||
# =============================================================================
|
||||
# CARREGAR CONFIGURAÇÃO EXISTENTE
|
||||
# =============================================================================
|
||||
|
||||
# Carregar configuração local PRIMEIRO (se existir)
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
log_info "Carregando configuração local..."
|
||||
source "$ENV_FILE"
|
||||
fi
|
||||
|
||||
# Se não tiver configuração local, tentar herdar da aula-10
|
||||
if [[ -z "$DOMAIN" ]]; then
|
||||
AULA10_ENV="${SCRIPT_DIR}/../aula-10/.env"
|
||||
if [[ -f "$AULA10_ENV" ]]; then
|
||||
log_info "Herdando configuração da aula-10..."
|
||||
source "$AULA10_ENV"
|
||||
fi
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# COLETAR CONFIGURAÇÃO
|
||||
# =============================================================================
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Container Factory - eStargz Images"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Domínio
|
||||
if [[ -z "$DOMAIN" ]]; then
|
||||
read -p "Domínio base (ex: kube.quest): " DOMAIN
|
||||
fi
|
||||
log_info "Domínio: ${DOMAIN}"
|
||||
|
||||
GITEA_HOST="gitea.${DOMAIN}"
|
||||
|
||||
# Namespace para deploy
|
||||
if [[ -z "$DEPLOY_NAMESPACE" ]]; then
|
||||
DEFAULT_NS="factory"
|
||||
read -p "Namespace para deploy [${DEFAULT_NS}]: " DEPLOY_NAMESPACE
|
||||
DEPLOY_NAMESPACE="${DEPLOY_NAMESPACE:-$DEFAULT_NS}"
|
||||
fi
|
||||
log_info "Namespace: ${DEPLOY_NAMESPACE}"
|
||||
|
||||
# Gerar senha PostgreSQL se não existir
|
||||
if [[ -z "$POSTGRES_PASSWORD" ]]; then
|
||||
POSTGRES_PASSWORD=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)
|
||||
log_info "Senha PostgreSQL gerada automaticamente"
|
||||
fi
|
||||
|
||||
# Salvar configuração
|
||||
cat > "$ENV_FILE" << EOF
|
||||
# Configuração gerada pelo setup.sh - $(date)
|
||||
DOMAIN=${DOMAIN}
|
||||
GITEA_HOST=${GITEA_HOST}
|
||||
DEPLOY_NAMESPACE=${DEPLOY_NAMESPACE}
|
||||
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||
EOF
|
||||
|
||||
log_success "Configuração salva em ${ENV_FILE}"
|
||||
|
||||
# =============================================================================
|
||||
# CRIAR NAMESPACE
|
||||
# =============================================================================
|
||||
|
||||
echo ""
|
||||
log_info "=== Criando Namespace ==="
|
||||
|
||||
kubectl create namespace ${DEPLOY_NAMESPACE} --dry-run=client -o yaml | kubectl apply -f -
|
||||
log_success "Namespace ${DEPLOY_NAMESPACE} criado"
|
||||
|
||||
# =============================================================================
|
||||
# CRIAR SECRET DO POSTGRESQL
|
||||
# =============================================================================
|
||||
|
||||
log_info "Criando secret do PostgreSQL..."
|
||||
kubectl create secret generic postgresql-secret \
|
||||
--namespace ${DEPLOY_NAMESPACE} \
|
||||
--from-literal=username=postgres \
|
||||
--from-literal=password="${POSTGRES_PASSWORD}" \
|
||||
--dry-run=client -o yaml | kubectl apply -f -
|
||||
log_success "Secret postgresql-secret criado"
|
||||
|
||||
# =============================================================================
|
||||
# CRIAR CONFIGMAP
|
||||
# =============================================================================
|
||||
|
||||
log_info "Criando ConfigMap do PostgreSQL..."
|
||||
kubectl apply -f "${SCRIPT_DIR}/k8s/postgresql/configmap.yaml" -n ${DEPLOY_NAMESPACE}
|
||||
log_success "ConfigMap postgresql-config criado"
|
||||
|
||||
# =============================================================================
|
||||
# INSTRUÇÕES PARA CRIAR REPOSITÓRIO
|
||||
# =============================================================================
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Próximos Passos"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo -e "${CYAN}1. Criar organização 'factory' no Gitea:${NC}"
|
||||
echo " URL: https://${GITEA_HOST}/-/admin/orgs"
|
||||
echo " Nome: factory"
|
||||
echo " Visibilidade: Private"
|
||||
echo ""
|
||||
echo -e "${CYAN}2. Criar repositório 'postgresql' na org:${NC}"
|
||||
echo " URL: https://${GITEA_HOST}/repo/create"
|
||||
echo " Owner: factory"
|
||||
echo " Nome: postgresql"
|
||||
echo ""
|
||||
echo -e "${CYAN}3. Clonar e copiar os arquivos:${NC}"
|
||||
echo ""
|
||||
echo " git clone git@${GITEA_HOST}:factory/postgresql.git"
|
||||
echo " cd postgresql"
|
||||
echo " cp ${SCRIPT_DIR}/images/postgresql/* ."
|
||||
echo " mkdir -p .gitea/workflows"
|
||||
echo " cp ${SCRIPT_DIR}/pipelines/postgresql/ci.yml .gitea/workflows/ci.yml"
|
||||
echo ""
|
||||
echo -e "${CYAN}4. Push inicial:${NC}"
|
||||
echo ""
|
||||
echo " git add ."
|
||||
echo " git commit -m 'Initial commit: PostgreSQL factory image'"
|
||||
echo " git push -u origin main"
|
||||
echo ""
|
||||
echo -e "${CYAN}5. Aguardar pipeline (Gitea Actions):${NC}"
|
||||
echo " https://${GITEA_HOST}/factory/postgresql/actions"
|
||||
echo ""
|
||||
echo -e "${CYAN}6. Após pipeline completo, deploy no cluster:${NC}"
|
||||
echo ""
|
||||
echo " kubectl apply -f ${SCRIPT_DIR}/k8s/postgresql/pvc.yaml -n ${DEPLOY_NAMESPACE}"
|
||||
echo " kubectl apply -f ${SCRIPT_DIR}/k8s/postgresql/deployment.yaml -n ${DEPLOY_NAMESPACE}"
|
||||
echo " kubectl apply -f ${SCRIPT_DIR}/k8s/postgresql/service.yaml -n ${DEPLOY_NAMESPACE}"
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Credenciais PostgreSQL"
|
||||
echo "=========================================="
|
||||
echo " Host: postgresql.${DEPLOY_NAMESPACE}.svc.cluster.local"
|
||||
echo " Port: 5432"
|
||||
echo " User: postgres"
|
||||
echo " Pass: ${POSTGRES_PASSWORD}"
|
||||
echo " DB: app"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo -e "${CYAN}7. Testar conexão:${NC}"
|
||||
echo ""
|
||||
echo " kubectl run pg-client --rm -it --restart=Never \\"
|
||||
echo " --image=postgres:17-alpine \\"
|
||||
echo " --env=PGPASSWORD=${POSTGRES_PASSWORD} \\"
|
||||
echo " -- psql -h postgresql.${DEPLOY_NAMESPACE}.svc.cluster.local -U postgres -d app"
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Container Registry (Gitea Packages)"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo " # Login"
|
||||
echo " docker login ${GITEA_HOST}"
|
||||
echo ""
|
||||
echo " # Imagens são publicadas automaticamente pelo Gitea Actions"
|
||||
echo " # Após pipeline: ${GITEA_HOST}/factory/postgresql/packages"
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Verificar Lazy Pulling (eStargz)"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo " # Ver tempo de startup do pod"
|
||||
echo " kubectl get pods -n ${DEPLOY_NAMESPACE} -w"
|
||||
echo ""
|
||||
echo " # Ver logs do stargz-snapshotter (se tiver acesso ao node)"
|
||||
echo " talosctl -n <node-ip> logs stargz-snapshotter"
|
||||
echo ""
|
||||
Reference in New Issue
Block a user