17a2891329
- Cluster PostgreSQL 3 instâncias com anti-affinity por hostname (hard) e por zona (soft) para spread entre datacenters Hetzner - Backup automático via Barman Cloud para Hetzner Object Storage (S3) - ScheduledBackup diário às 03:00 - App demo que valida conexão com o banco - setup.sh: instala operator, cria cluster, deploya demo - cleanup.sh: remoção limpa (backups S3 preservados)
347 lines
13 KiB
Bash
Executable File
347 lines
13 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
|
|
echo -ne "Endpoint (${GREEN}${S3_ENDPOINT}${NC}): "
|
|
read -r input
|
|
[[ -n "$input" ]] && S3_ENDPOINT="$input"
|
|
|
|
if [[ -z "$S3_BUCKET" ]]; then
|
|
S3_BUCKET="cnpg-backups"
|
|
fi
|
|
echo -ne "Bucket name (${GREEN}${S3_BUCKET}${NC}): "
|
|
read -r input
|
|
[[ -n "$input" ]] && S3_BUCKET="$input"
|
|
|
|
echo -ne "Access Key ID: "
|
|
read -r S3_ACCESS_KEY
|
|
if [[ -z "$S3_ACCESS_KEY" ]]; then
|
|
log_error "Access Key é obrigatória"
|
|
exit 1
|
|
fi
|
|
|
|
echo -ne "Secret Access Key: "
|
|
read -rs S3_SECRET_KEY
|
|
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-controller-manager -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}"
|
|
|
|
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|https://${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 "$@"
|