aula-07 e aula-08: Cluster Talos HA na Hetzner com Autoscaler
aula-07: Criação de imagem Talos customizada na Hetzner Cloud - Usa Talos Factory para gerar imagem ARM64/AMD64 - Inclui extensões: qemu-guest-agent, hcloud aula-08: Provisionamento de cluster Kubernetes Talos via OpenTofu - 3 Control Planes em HA (CAX11 ARM64) - 1 Worker Node (CAX11 ARM64) - Rede privada, Floating IP, Firewall - Cluster Autoscaler para Hetzner (0-5 workers extras) - Setup interativo com validação de pré-requisitos - Custo estimado: ~€18/mês (base) Também inclui: - .gitignore para ignorar arquivos sensíveis - CLAUDE.md com instruções do projeto
This commit is contained in:
361
aula-08/setup.sh
Executable file
361
aula-08/setup.sh
Executable file
@@ -0,0 +1,361 @@
|
||||
#!/bin/bash
|
||||
|
||||
############################################################
|
||||
# Aula 08 - OpenTofu + Talos + Hetzner Cloud
|
||||
# Provisiona cluster Kubernetes Talos em HA
|
||||
############################################################
|
||||
|
||||
set -e
|
||||
|
||||
# Cores para output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Diretório do script
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
# Funções de log
|
||||
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"
|
||||
}
|
||||
|
||||
############################################################
|
||||
# VERIFICAÇÃO DE PRÉ-REQUISITOS
|
||||
############################################################
|
||||
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo " Aula 08 - Cluster Talos via OpenTofu"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
log_info "Verificando pré-requisitos..."
|
||||
|
||||
# Verificar OpenTofu
|
||||
if ! command -v tofu &> /dev/null; then
|
||||
log_error "OpenTofu não encontrado!"
|
||||
echo ""
|
||||
echo "Instale o OpenTofu:"
|
||||
echo " brew install opentofu # macOS"
|
||||
echo " snap install opentofu # Linux"
|
||||
echo ""
|
||||
echo "Mais info: https://opentofu.org/docs/intro/install/"
|
||||
exit 1
|
||||
fi
|
||||
log_success "OpenTofu $(tofu version | head -1)"
|
||||
|
||||
# Verificar talosctl
|
||||
if ! command -v talosctl &> /dev/null; then
|
||||
log_error "talosctl não encontrado!"
|
||||
echo ""
|
||||
echo "Instale o talosctl:"
|
||||
echo " brew install siderolabs/tap/talosctl # macOS"
|
||||
echo " curl -sL https://talos.dev/install | sh # Linux"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
log_success "talosctl $(talosctl version --client 2>/dev/null | grep 'Client' | awk '{print $2}' || echo 'instalado')"
|
||||
|
||||
# Verificar kubectl
|
||||
if ! command -v kubectl &> /dev/null; then
|
||||
log_error "kubectl não encontrado!"
|
||||
echo ""
|
||||
echo "Instale o kubectl:"
|
||||
echo " brew install kubectl # macOS"
|
||||
echo " snap install kubectl # Linux"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
log_success "kubectl $(kubectl version --client -o yaml 2>/dev/null | grep gitVersion | awk '{print $2}' || echo 'instalado')"
|
||||
|
||||
# Verificar hcloud CLI (opcional, mas útil)
|
||||
if command -v hcloud &> /dev/null; then
|
||||
log_success "hcloud CLI instalado"
|
||||
else
|
||||
log_warn "hcloud CLI não instalado (opcional)"
|
||||
echo " Para listar imagens: brew install hcloud"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
############################################################
|
||||
# COLETA DE CREDENCIAIS
|
||||
############################################################
|
||||
|
||||
# Verificar se terraform.tfvars já existe
|
||||
if [ -f "terraform.tfvars" ]; then
|
||||
log_warn "terraform.tfvars já existe!"
|
||||
read -p "Deseja sobrescrever? (s/N): " overwrite
|
||||
if [[ ! "$overwrite" =~ ^[Ss]$ ]]; then
|
||||
log_info "Usando terraform.tfvars existente"
|
||||
SKIP_CREDENTIALS=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$SKIP_CREDENTIALS" != "true" ]; then
|
||||
echo "============================================"
|
||||
echo " Configuração de Credenciais"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
# Token Hetzner
|
||||
echo "1. Token da API Hetzner Cloud"
|
||||
echo " Obtenha em: https://console.hetzner.cloud/projects/*/security/tokens"
|
||||
echo ""
|
||||
read -sp " Digite o token: " HCLOUD_TOKEN
|
||||
echo ""
|
||||
|
||||
if [ -z "$HCLOUD_TOKEN" ]; then
|
||||
log_error "Token não pode ser vazio!"
|
||||
exit 1
|
||||
fi
|
||||
log_success "Token configurado"
|
||||
echo ""
|
||||
|
||||
# SSH Key
|
||||
echo "2. Chave SSH pública"
|
||||
DEFAULT_SSH_KEY="$HOME/.ssh/id_rsa.pub"
|
||||
if [ -f "$DEFAULT_SSH_KEY" ]; then
|
||||
echo " Encontrada: $DEFAULT_SSH_KEY"
|
||||
read -p " Usar esta chave? (S/n): " use_default
|
||||
if [[ ! "$use_default" =~ ^[Nn]$ ]]; then
|
||||
SSH_PUBLIC_KEY=$(cat "$DEFAULT_SSH_KEY")
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$SSH_PUBLIC_KEY" ]; then
|
||||
read -p " Caminho da chave pública: " ssh_path
|
||||
if [ -f "$ssh_path" ]; then
|
||||
SSH_PUBLIC_KEY=$(cat "$ssh_path")
|
||||
else
|
||||
log_error "Arquivo não encontrado: $ssh_path"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
log_success "Chave SSH configurada"
|
||||
echo ""
|
||||
|
||||
# ID da imagem Talos
|
||||
echo "3. ID da imagem Talos (snapshot da aula-07)"
|
||||
echo " Para listar: hcloud image list --type snapshot"
|
||||
echo ""
|
||||
read -p " Digite o ID da imagem: " TALOS_IMAGE_ID
|
||||
|
||||
if [ -z "$TALOS_IMAGE_ID" ]; then
|
||||
log_error "ID da imagem não pode ser vazio!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validar que é número
|
||||
if ! [[ "$TALOS_IMAGE_ID" =~ ^[0-9]+$ ]]; then
|
||||
log_error "ID deve ser um número!"
|
||||
exit 1
|
||||
fi
|
||||
log_success "Image ID: $TALOS_IMAGE_ID"
|
||||
echo ""
|
||||
|
||||
# Criar terraform.tfvars
|
||||
log_info "Criando terraform.tfvars..."
|
||||
cat > terraform.tfvars << EOF
|
||||
# Gerado automaticamente por setup.sh
|
||||
# $(date)
|
||||
|
||||
hcloud_token = "$HCLOUD_TOKEN"
|
||||
ssh_public_key = "$SSH_PUBLIC_KEY"
|
||||
talos_image_id = $TALOS_IMAGE_ID
|
||||
|
||||
environment = "workshop"
|
||||
enable_monitoring = true
|
||||
EOF
|
||||
log_success "terraform.tfvars criado"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
############################################################
|
||||
# INICIALIZAÇÃO DO OPENTOFU
|
||||
############################################################
|
||||
|
||||
echo "============================================"
|
||||
echo " Inicializando OpenTofu"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
log_info "Executando tofu init..."
|
||||
tofu init
|
||||
|
||||
log_success "OpenTofu inicializado"
|
||||
echo ""
|
||||
|
||||
############################################################
|
||||
# PLANEJAMENTO
|
||||
############################################################
|
||||
|
||||
echo "============================================"
|
||||
echo " Planejando Infraestrutura"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
log_info "Executando tofu plan..."
|
||||
tofu plan -out=tfplan
|
||||
|
||||
echo ""
|
||||
log_success "Plano criado!"
|
||||
echo ""
|
||||
|
||||
# Mostrar resumo
|
||||
echo "============================================"
|
||||
echo " Recursos a serem criados:"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
echo " - 4x CAX11 (3 CP + 1 Worker) = 4 x €3.79 = €15.16"
|
||||
echo " - 1x Floating IPv4 = €3.00"
|
||||
echo " - Rede/Firewall/Placement = Grátis"
|
||||
echo ""
|
||||
echo " Custo estimado: ~€18.16/mês (sem VAT)"
|
||||
echo ""
|
||||
|
||||
############################################################
|
||||
# APLICAÇÃO
|
||||
############################################################
|
||||
|
||||
read -p "Deseja aplicar o plano? (s/N): " apply
|
||||
if [[ ! "$apply" =~ ^[Ss]$ ]]; then
|
||||
log_warn "Operação cancelada pelo usuário"
|
||||
echo ""
|
||||
echo "Para aplicar manualmente:"
|
||||
echo " tofu apply tfplan"
|
||||
echo ""
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "Aplicando infraestrutura..."
|
||||
echo ""
|
||||
|
||||
tofu apply tfplan
|
||||
|
||||
echo ""
|
||||
log_success "Infraestrutura provisionada!"
|
||||
echo ""
|
||||
|
||||
############################################################
|
||||
# CONFIGURAÇÃO PÓS-DEPLOY
|
||||
############################################################
|
||||
|
||||
echo "============================================"
|
||||
echo " Configuração Pós-Deploy"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
# Aguardar cluster ficar pronto
|
||||
log_info "Aguardando cluster Talos ficar pronto..."
|
||||
sleep 10
|
||||
|
||||
# Configurar talosctl
|
||||
if [ -f "talosconfig" ]; then
|
||||
log_info "Configurando talosctl..."
|
||||
export TALOSCONFIG="$SCRIPT_DIR/talosconfig"
|
||||
|
||||
# Obter IP do control plane
|
||||
CP_IP=$(tofu output -raw control_plane_ip 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$CP_IP" ]; then
|
||||
log_info "Aguardando API do Talos em $CP_IP..."
|
||||
|
||||
# Tentar health check (pode demorar alguns minutos)
|
||||
for i in {1..30}; do
|
||||
if talosctl --talosconfig talosconfig -n "$CP_IP" health --wait-timeout 10s 2>/dev/null; then
|
||||
log_success "Cluster Talos saudável!"
|
||||
break
|
||||
fi
|
||||
echo -n "."
|
||||
sleep 10
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
|
||||
# Configurar kubectl
|
||||
if [ -f "kubeconfig" ]; then
|
||||
log_info "Configurando kubectl..."
|
||||
export KUBECONFIG="$SCRIPT_DIR/kubeconfig"
|
||||
|
||||
log_info "Aguardando nodes ficarem Ready..."
|
||||
for i in {1..30}; do
|
||||
if kubectl get nodes 2>/dev/null | grep -q "Ready"; then
|
||||
log_success "Nodes prontos!"
|
||||
kubectl get nodes
|
||||
break
|
||||
fi
|
||||
echo -n "."
|
||||
sleep 10
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
############################################################
|
||||
# RESUMO FINAL
|
||||
############################################################
|
||||
|
||||
echo "============================================"
|
||||
echo " Cluster Provisionado com Sucesso!"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
# Mostrar outputs
|
||||
echo "Endpoints:"
|
||||
tofu output -raw kubernetes_api_endpoint 2>/dev/null && echo "" || true
|
||||
tofu output -raw talos_api_endpoint 2>/dev/null && echo "" || true
|
||||
echo ""
|
||||
|
||||
echo "Arquivos gerados:"
|
||||
echo " - kubeconfig : Configuração do kubectl"
|
||||
echo " - talosconfig : Configuração do talosctl"
|
||||
echo ""
|
||||
|
||||
echo "Comandos úteis:"
|
||||
echo ""
|
||||
echo " # Usar kubectl com este cluster"
|
||||
echo " export KUBECONFIG=$SCRIPT_DIR/kubeconfig"
|
||||
echo " kubectl get nodes"
|
||||
echo ""
|
||||
echo " # Usar talosctl com este cluster"
|
||||
echo " export TALOSCONFIG=$SCRIPT_DIR/talosconfig"
|
||||
echo " talosctl -n <IP> health"
|
||||
echo ""
|
||||
echo " # Ver outputs do OpenTofu"
|
||||
echo " tofu output"
|
||||
echo ""
|
||||
echo " # Destruir infraestrutura (CUIDADO!)"
|
||||
echo " ./cleanup.sh"
|
||||
echo ""
|
||||
|
||||
log_success "Setup concluído!"
|
||||
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo " Próximo passo (opcional)"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
echo " Para habilitar autoscaling de 1-5 workers:"
|
||||
echo " ./install-autoscaler.sh"
|
||||
echo ""
|
||||
Reference in New Issue
Block a user