a0cf1e6cef
- 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
353 lines
14 KiB
Bash
Executable File
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 "$@"
|