Files
workshop/aula-17/setup.sh
T
ArgoCD Setup a0cf1e6cef fix: corrigir tipos de servidor AMD (CPX→CX), autoscaler, stdin e cleanup
- Corrigir tipos Hetzner: CPX não existe, usar CX (Intel shared vCPU)
  - CX23 (2 vCPU, 4GB) equivalente ao CAX11
  - CX33 (4 vCPU, 8GB) equivalente ao CAX21
  - CX43 (8 vCPU, 16GB) equivalente ao CAX31
- Corrigir location no autoscaler para amd64 (nbg1→fsn1)
- Remover gitlab-pool do autoscaler (substituído pelo Gitea)
- Aumentar worker-pool max para 30
- Corrigir Floating IP e Load Balancer hardcoded em nbg1
- Corrigir stdin em read interativo: usar < /dev/tty e read -p
- Corrigir cleanup de namespace: lidar com Terminating e finalizers
- Corrigir CNPG: nome do deployment, secret e endpointURL duplo
- Aulas afetadas: 08, 09, 10, 11, 12, 14, 17
2026-05-09 07:24:26 -03:00

353 lines
14 KiB
Bash
Executable File

#!/bin/bash
# ============================================================================
# Aula 17 - Cloud Native PostgreSQL (CNPG)
# ============================================================================
# Instala o CloudNativePG operator e cria um cluster PostgreSQL HA
# com backup automático para Hetzner Object Storage (S3).
#
# Componentes:
# - CNPG Operator (gerenciamento do ciclo de vida do PostgreSQL)
# - Cluster PostgreSQL (3 instâncias: 1 primary + 1 sync + 1 async)
# - Backup S3 (WAL archiving + base backup diário)
# - App demo (valida conexão com o banco)
#
# Pré-requisitos:
# - Cluster Kubernetes na Hetzner (aula-08)
# - Victoria Metrics (aula-12) para métricas
# - Hetzner Object Storage com bucket criado
# - kubectl e helm instalados
# ============================================================================
set -e
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}[ERRO]${NC} $1"; }
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${SCRIPT_DIR}/.env"
# ============================================================================
# Gerenciamento de Configuração
# ============================================================================
load_config() {
if [[ -f "$ENV_FILE" ]]; then
source "$ENV_FILE"
return 0
fi
return 1
}
save_config() {
cat > "$ENV_FILE" << EOF
# Configuração da Aula 17 - CNPG
# Gerado em: $(date)
S3_ACCESS_KEY=${S3_ACCESS_KEY}
S3_SECRET_KEY=${S3_SECRET_KEY}
S3_ENDPOINT=${S3_ENDPOINT}
S3_BUCKET=${S3_BUCKET}
EOF
log_success "Configuração salva em .env"
}
# ============================================================================
# Verificação de Pré-requisitos
# ============================================================================
check_prerequisites() {
echo ""
log_info "Verificando pré-requisitos..."
local failed=false
for cmd in kubectl helm; do
if command -v "$cmd" &> /dev/null; then
log_success "$cmd encontrado"
else
log_error "$cmd não encontrado"
failed=true
fi
done
if ! kubectl cluster-info &> /dev/null; then
log_error "Cluster Kubernetes não acessível. Execute a aula-08 primeiro."
exit 1
fi
log_success "Cluster Kubernetes acessível"
# Verificar Victoria Metrics
if kubectl get svc -n monitoring vmsingle-monitoring-victoria-metrics-k8s-stack &> /dev/null; then
log_success "Victoria Metrics encontrado"
else
log_warn "Victoria Metrics não encontrado (aula-12). PodMonitor não terá scrape target."
log_warn "O CNPG funcionará sem métricas, mas recomenda-se instalar a aula-12."
fi
if [[ "$failed" == "true" ]]; then
exit 1
fi
}
# ============================================================================
# Coleta de Configuração
# ============================================================================
collect_config() {
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Configuração do CNPG - Hetzner Object Storage${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo ""
echo -e "Você precisa de um bucket no Hetzner Object Storage."
echo -e "Crie em: ${GREEN}https://console.hetzner.cloud/storage/object${NC}"
echo ""
if load_config; then
echo -e "Configuração existente encontrada:"
echo -e " Endpoint: ${GREEN}${S3_ENDPOINT}${NC}"
echo -e " Bucket: ${GREEN}${S3_BUCKET}${NC}"
echo ""
echo -e "[1] Usar configuração existente"
echo -e "[2] Inserir nova configuração"
read -p "Escolha [1/2]: " choice
if [[ "$choice" == "1" ]]; then
return 0
fi
fi
echo ""
echo -e "${YELLOW}Endpoints disponíveis:${NC}"
echo " fsn1.your-objectstorage.com (Frankfurt)"
echo " nbg1.your-objectstorage.com (Nuremberg)"
echo " hel1.your-objectstorage.com (Helsinki)"
echo ""
if [[ -z "$S3_ENDPOINT" ]]; then
S3_ENDPOINT="nbg1.your-objectstorage.com"
fi
read -r -p "Endpoint (${GREEN}${S3_ENDPOINT}${NC}): " input < /dev/tty
[[ -n "$input" ]] && S3_ENDPOINT="$input"
if [[ -z "$S3_BUCKET" ]]; then
S3_BUCKET="cnpg-backups"
fi
read -r -p "Bucket name (${GREEN}${S3_BUCKET}${NC}): " input < /dev/tty
[[ -n "$input" ]] && S3_BUCKET="$input"
read -r -p "Access Key ID: " S3_ACCESS_KEY < /dev/tty
if [[ -z "$S3_ACCESS_KEY" ]]; then
log_error "Access Key é obrigatória"
exit 1
fi
read -rs -p "Secret Access Key: " S3_SECRET_KEY < /dev/tty
echo ""
if [[ -z "$S3_SECRET_KEY" ]]; then
log_error "Secret Key é obrigatória"
exit 1
fi
save_config
}
# ============================================================================
# Instalação do CNPG Operator
# ============================================================================
install_cnpg_operator() {
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Instalando CNPG Operator${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
helm repo add cnpg https://cloudnative-pg.github.io/charts 2>/dev/null || true
helm repo update cnpg
log_info "Instalando CNPG operator no namespace cnpg-system..."
if helm status cnpg -n cnpg-system &> /dev/null; then
helm upgrade cnpg cnpg/cloudnative-pg \
-n cnpg-system \
--wait
else
helm install cnpg cnpg/cloudnative-pg \
-n cnpg-system \
--create-namespace \
--wait
fi
log_success "CNPG operator instalado"
log_info "Aguardando operator ficar ready..."
kubectl wait --for=condition=available deployment/cnpg-cloudnative-pg -n cnpg-system --timeout=120s
log_success "CNPG operator pronto"
}
# ============================================================================
# Criar Namespace e Secrets
# ============================================================================
setup_namespace_and_secrets() {
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Criando namespace e secrets${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
if kubectl get namespace cnpg &>/dev/null; then
NS_PHASE=$(kubectl get namespace cnpg -o jsonpath='{.status.phase}' 2>/dev/null)
if [[ "$NS_PHASE" == "Terminating" ]]; then
log_info "Namespace cnpg em Terminating, forçando remoção..."
kubectl get namespace cnpg -o json | \
python3 -c "import sys,json; d=json.load(sys.stdin); d['metadata'].pop('finalizers',None); json.dump(d,sys.stdout)" | \
kubectl replace --raw /api/v1/namespaces/cnpg/finalize -f - 2>/dev/null || true
kubectl wait --for=delete namespace cnpg --timeout=60s 2>/dev/null || sleep 5
fi
fi
kubectl create namespace cnpg --dry-run=client -o yaml | kubectl apply -f -
log_info "Criando secret de credenciais S3..."
kubectl create secret generic cnpg-backup-credentials \
-n cnpg \
--from-literal=ACCESS_KEY_ID="$S3_ACCESS_KEY" \
--from-literal=ACCESS_SECRET_KEY="$S3_SECRET_KEY" \
--dry-run=client -o yaml | kubectl apply -f -
log_success "Secret criado"
}
# ============================================================================
# Criar Cluster PostgreSQL
# ============================================================================
create_cluster() {
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Criando Cluster PostgreSQL${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
log_info "Gerando cluster.yaml com suas configurações..."
sed -e "s|CHANGE_BUCKET_NAME|${S3_BUCKET}|g" \
-e "s|CHANGE_ENDPOINT|${S3_ENDPOINT}|g" \
"${SCRIPT_DIR}/cnpg/cluster.yaml" | kubectl apply -f -
log_success "Cluster aplicado"
log_info "Aplicando backup agendado..."
kubectl apply -f "${SCRIPT_DIR}/cnpg/scheduled-backup.yaml"
log_success "Backup agendado aplicado"
log_info "Aguardando cluster ficar ready (pode levar 2-5 minutos)..."
kubectl wait --for=condition=Ready cluster/shared-postgres -n cnpg --timeout=600s
log_success "Cluster PostgreSQL pronto!"
}
# ============================================================================
# Deploy App Demo
# ============================================================================
deploy_demo() {
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Deploy App Demo${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
log_info "Deployando app de demonstração..."
kubectl apply -f "${SCRIPT_DIR}/app-demo/deployment.yaml"
kubectl apply -f "${SCRIPT_DIR}/app-demo/service.yaml"
log_info "Aguardando app demo..."
kubectl wait --for=condition=available deployment/pg-demo -n cnpg --timeout=120s 2>/dev/null || true
# Mostrar resultado
sleep 3
echo ""
log_info "Resultado da conexão do app demo:"
kubectl logs deployment/pg-demo -n cnpg --tail=20 2>/dev/null || log_warn "Aguardando logs..."
log_success "App demo deployado"
}
# ============================================================================
# Resumo
# ============================================================================
show_summary() {
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Instalação Concluída${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo ""
echo -e "${GREEN}Componentes instalados:${NC}"
echo " - CNPG Operator (namespace: cnpg-system)"
echo " - Cluster PostgreSQL 3 instâncias (namespace: cnpg)"
echo " - Backup diário às 03:00 → Hetzner Object Storage"
echo " - App demo (valida conexão)"
echo ""
# Status do cluster
echo -e "${GREEN}Status do cluster:${NC}"
kubectl get cluster -n cnpg
echo ""
echo -e "${GREEN}Instâncias:${NC}"
kubectl get pods -n cnpg -l cnpg.io/cluster=shared-postgres
echo ""
echo -e "${GREEN}Serviços criados pelo CNPG:${NC}"
kubectl get svc -n cnpg -l cnpg.io/cluster=shared-postgres
echo ""
echo -e "${GREEN}Comandos essenciais:${NC}"
echo ""
echo " # Status do cluster"
echo " kubectl get cluster -n cnpg"
echo ""
echo " # Ver instâncias (primary vs replica)"
echo " kubectl get pods -n cnpg -l cnpg.io/cluster=shared-postgres -o wide"
echo ""
echo " # Failover manual (promover replica)"
echo " kubectl cnpg promote shared-postgres <instance-name> -n cnpg"
echo ""
echo " # Trigger backup manual"
echo " kubectl cnpg backup shared-postgres -n cnpg"
echo ""
echo " # Listar backups"
echo " kubectl get backups -n cnpg"
echo ""
echo " # Logs do app demo"
echo " kubectl logs -f deployment/pg-demo -n cnpg"
echo ""
echo -e "${YELLOW}Próximos passos:${NC}"
echo " - Conecte sua app ao service: shared-postgres-rw.cnpg.svc:5432"
echo " - O backup automático roda às 03:00 diariamente"
echo " - Para recovery/PITR, consulte o README"
echo ""
}
# ============================================================================
# Execução
# ============================================================================
main() {
echo ""
echo -e "${CYAN}╔═══════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ Aula 17 - Cloud Native PostgreSQL (CNPG) ║${NC}"
echo -e "${CYAN}╚═══════════════════════════════════════════════════════════╝${NC}"
check_prerequisites
collect_config
install_cnpg_operator
setup_namespace_and_secrets
create_cluster
deploy_demo
show_summary
}
main "$@"