Workshop completo: aulas 08-10 com Talos, n8n e GitLab na Hetzner

Aula 08 - Cluster Kubernetes HA:
- Setup interativo com OpenTofu para Talos na Hetzner
- CCM, CSI Driver, Cluster Autoscaler, Metrics Server
- NGINX Ingress com LoadBalancer (HTTP/HTTPS/SSH)

Aula 09 - n8n na Hetzner:
- Deploy via Helm com PostgreSQL e Redis
- Suporte multi-tenant com add-client.sh
- Integração com Hetzner CSI para volumes persistentes

Aula 10 - GitLab na Hetzner:
- Setup agnóstico: CloudFlare (trusted proxies) ou Let's Encrypt
- Anti-affinity para distribuir webservice/sidekiq em nós diferentes
- Container Registry e SSH via TCP passthrough
- Documentação do erro 422 e solução com trustedCIDRsForXForwardedFor

Melhorias gerais:
- READMEs atualizados com arquitetura e troubleshooting
- Scripts cleanup.sh para todas as aulas
- CLAUDE.md atualizado com contexto do projeto
This commit is contained in:
Allyson de Paula
2025-12-31 17:57:02 -03:00
parent 50dc74c1d8
commit 07b7ee62d3
35 changed files with 4665 additions and 311 deletions

233
aula-09/README.md Normal file
View File

@@ -0,0 +1,233 @@
# Aula 09 - n8n via Helm (Cluster Hetzner Cloud)
Deploy do n8n workflow automation em cluster Kubernetes na Hetzner Cloud com volumes persistentes, LoadBalancer e TLS configurável.
## Arquitetura
```
Internet
┌─────────────────┐
│ LoadBalancer │
│ (Hetzner LB) │
└────────┬────────┘
┌────────────────────────┐
│ NGINX Ingress │
│ n8n.{domain}:443 │
└───────────┬────────────┘
┌─────────────────────────┼─────────────────────────┐
│ Namespace: n8n │
│ │ │
│ ┌────────────────────┼────────────────────┐ │
│ │ ▼ │ │
│ │ ┌──────────┐ │ │
│ │ │ Main │ │ │
│ │ │ (n8n) │ │ │
│ │ └────┬─────┘ │ │
│ │ │ │ │
│ │ ┌─────────────┼─────────────┐ │ │
│ │ ▼ ▼ ▼ │ │
│ │ ┌───────┐ ┌──────────┐ ┌────────┐ │ │
│ │ │Workers│ │ Webhooks │ │ MCP │ │ │
│ │ │ (2-5) │ │ (1-3) │ │Webhook │ │ │
│ │ └───────┘ └──────────┘ └────────┘ │ │
│ │ │ │
│ │ Queue Mode │ │
│ └────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ │
│ │PostgreSQL│ │ Redis │ │
│ │ (10Gi) │ │ (10Gi) │ │
│ │ hcloud │ │ hcloud │ │
│ └──────────┘ └──────────┘ │
└───────────────────────────────────────────────────┘
Hetzner Cloud
```
## Pré-requisitos
1. **Cluster Talos na Hetzner** (aula-08) com:
- Hetzner CSI Driver (StorageClass: hcloud-volumes)
- NGINX Ingress Controller com LoadBalancer
2. **kubectl** instalado
3. **Helm** 3.x instalado
## Contexto do Cluster
Esta aula usa o cluster Kubernetes provisionado na **aula-08**. O `kubeconfig` foi gerado automaticamente pelo OpenTofu e está em `aula-08/kubeconfig`.
```bash
# Verificar se o cluster está acessível
export KUBECONFIG=$(pwd)/../aula-08/kubeconfig
kubectl cluster-info
# Esperado:
# Kubernetes control plane is running at https://<floating-ip>:6443
```
## Instalação
```bash
cd aula-09
# Configurar acesso ao cluster (kubeconfig da aula-08)
export KUBECONFIG=$(pwd)/../aula-08/kubeconfig
# Executar setup interativo
chmod +x setup.sh
./setup.sh
```
O script é interativo e pergunta:
1. **Hostname do n8n** (ex: `n8n.kube.quest`) - FQDN completo
2. **Usa CloudFlare?** (com proxy/CDN)
3. **Ativar Let's Encrypt?** (se não usar CloudFlare)
### Opções de TLS
| Cenário | TLS Provider | Configuração |
|---------|--------------|--------------|
| **CloudFlare (proxy)** | CloudFlare Edge | Sem cert-manager, TLS na borda |
| **Outro DNS + Let's Encrypt** | cert-manager | HTTP-01 challenge automático |
| **Outro DNS + HTTP** | Nenhum | Apenas HTTP (dev/workshop) |
### Exemplo de Instalação
```
═══════════════════════════════════════════════════
n8n no Kubernetes (Hetzner Cloud)
═══════════════════════════════════════════════════
Digite o hostname do n8n (ex: n8n.kube.quest): n8n.kube.quest
Você usa CloudFlare para DNS?
1) Sim (com proxy/CDN ativado - ícone laranja)
2) Não
Escolha [1/2]: 1
[INFO] CloudFlare irá gerenciar TLS automaticamente na edge
[OK] Configuração salva em .env
...instalação...
═══════════════════════════════════════════════════
Configure o DNS
═══════════════════════════════════════════════════
No painel do CloudFlare (https://dash.cloudflare.com):
Tipo: A
Nome: n8n
Conteúdo: 123.45.67.89
Proxy: ✓ (ícone laranja)
O CloudFlare cuida do TLS automaticamente!
Acesse: https://n8n.kube.quest
```
## Componentes Instalados
**Da aula-08 (infraestrutura):**
- Hetzner CSI Driver (StorageClass: hcloud-volumes)
- NGINX Ingress Controller com LoadBalancer
**Instalados por este script:**
- **cert-manager** (se Let's Encrypt ativado)
- **n8n** com todos os componentes
| Componente | Réplicas | Recursos | Volume |
|------------|----------|----------|--------|
| Main (n8n) | 1 | 256Mi-1Gi RAM | 10Gi |
| Workers | 2-5 (HPA) | 256Mi-512Mi RAM | - |
| Webhooks | 1-3 (HPA) | 128Mi-256Mi RAM | - |
| PostgreSQL | 1 | - | 10Gi |
| Redis | 1 | - | 10Gi |
**Total volumes:** 30Gi (~$1.45/mês)
## Multi-Tenant: Adicionar Clientes
Para provisionar n8n para múltiplos clientes em namespaces separados:
```bash
# Adicionar novo cliente
./add-client.sh acme
# Isso cria:
# - Namespace: acme-n8n
# - n8n em: https://acme-n8n.{domain}
# - Grupo GitLab: /acme/ (se GitLab instalado - aula-10)
```
O script `add-client.sh` herda a configuração do `.env` gerado pelo `setup.sh`.
### Padrão de Domínio
Os clientes usam o padrão `{cliente}-n8n.{domain}`:
- `acme-n8n.kube.quest`
- `empresa-n8n.kube.quest`
Se você configurou um wildcard DNS (`*.kube.quest`), os novos clientes funcionam automaticamente.
## Arquivo de Configuração
O `setup.sh` gera um arquivo `.env` com as configurações:
```bash
# aula-09/.env (gerado automaticamente)
N8N_HOST=n8n.kube.quest
DOMAIN=kube.quest
USE_CLOUDFLARE=true
USE_LETSENCRYPT=false
LETSENCRYPT_EMAIL=
```
Este arquivo é usado pelo `add-client.sh` e em re-execuções do `setup.sh`.
## Comandos Úteis
```bash
# Ver pods
kubectl get pods -n n8n
# Ver logs do n8n
kubectl logs -f -n n8n deployment/n8n
# Ver autoscaling
kubectl get hpa -n n8n
# Ver volumes
kubectl get pvc -n n8n
# Ver certificado (se Let's Encrypt)
kubectl get certificate -n n8n
# Desinstalar (apenas n8n)
./cleanup.sh
# Ou manualmente:
helm uninstall n8n -n n8n
kubectl delete pvc --all -n n8n
kubectl delete ns n8n
```
**Nota:** O `cleanup.sh` remove apenas n8n, clientes e cert-manager. A infraestrutura (CSI Driver, NGINX Ingress, LoadBalancer) é mantida pois pertence à aula-08.
## Custos Estimados
| Recurso | Custo/mês |
|---------|-----------|
| Volumes (3x 10Gi) | ~$1.45 |
| Worker (compartilhado) | ~$0.80 |
| **Total por cliente** | ~$2.25 |
## Para Ambiente Local
Se você quer testar em um cluster local (Docker Desktop, minikube), veja a **aula-06**.

299
aula-09/add-client.sh Executable file
View File

@@ -0,0 +1,299 @@
#!/bin/bash
# =============================================================================
# Script para Adicionar Novo Cliente
# =============================================================================
#
# Este script provisiona um novo cliente com:
# - n8n em namespace separado (cliente-n8n)
# - Grupo no GitLab compartilhado (opcional)
#
# Uso:
# ./add-client.sh <nome-cliente>
#
# Exemplo:
# ./add-client.sh acme
# -> Cria namespace acme-n8n com n8n
# -> Cria grupo /acme no GitLab (se disponível)
#
# Pré-requisitos:
# - ./setup.sh já executado (gera .env com configurações)
# - kubectl configurado
# - Helm instalado
#
# =============================================================================
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}[ERRO]${NC} $1"; }
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CLIENT=$1
# =============================================================================
# CARREGAR CONFIGURAÇÃO
# =============================================================================
if [[ ! -f "$SCRIPT_DIR/.env" ]]; then
log_error "Arquivo .env não encontrado!"
log_error "Execute ./setup.sh primeiro para configurar o ambiente."
exit 1
fi
source "$SCRIPT_DIR/.env"
# Suporte ao novo formato (N8N_HOST) e legado (DOMAIN)
if [[ -n "$N8N_HOST" && -z "$DOMAIN" ]]; then
# Extrair DOMAIN do N8N_HOST (ex: n8n.kube.quest → kube.quest)
DOMAIN=$(echo "$N8N_HOST" | sed 's/^[^.]*\.//')
fi
if [[ -z "$DOMAIN" ]]; then
log_error "Variável DOMAIN ou N8N_HOST não definida no .env"
log_error "Execute ./setup.sh novamente."
exit 1
fi
# Carregar configuração do GitLab se disponível
GITLAB_HOST=""
if [[ -f "$SCRIPT_DIR/../aula-10/.env" ]]; then
source "$SCRIPT_DIR/../aula-10/.env"
fi
# =============================================================================
# VALIDAÇÕES
# =============================================================================
if [ -z "$CLIENT" ]; then
echo "Uso: $0 <nome-cliente>"
echo ""
echo "Exemplo:"
echo " $0 acme"
echo ""
echo "Configuração atual (de .env):"
echo " Domínio: ${DOMAIN}"
echo " CloudFlare: ${USE_CLOUDFLARE}"
echo " Let's Encrypt: ${USE_LETSENCRYPT}"
echo ""
echo "Isso vai criar:"
echo " - Namespace: acme-n8n"
PROTOCOL="https"
[[ "$USE_CLOUDFLARE" == "false" && "$USE_LETSENCRYPT" == "false" ]] && PROTOCOL="http"
echo " - n8n em: ${PROTOCOL}://acme-n8n.${DOMAIN}"
if [[ -n "$GITLAB_HOST" ]]; then
echo " - GitLab grupo: https://${GITLAB_HOST}/acme/ (se configurado)"
fi
exit 1
fi
# Validar nome do cliente (só letras minúsculas, números e hífens)
if ! [[ "$CLIENT" =~ ^[a-z0-9-]+$ ]]; then
log_error "Nome do cliente inválido: $CLIENT"
log_error "Use apenas letras minúsculas, números e hífens"
exit 1
fi
log_info "Verificando pré-requisitos..."
if ! command -v kubectl &> /dev/null; then
log_error "kubectl não encontrado"
exit 1
fi
if ! command -v helm &> /dev/null; then
log_error "Helm não encontrado"
exit 1
fi
if ! kubectl cluster-info &> /dev/null; then
log_error "Não foi possível conectar ao cluster"
exit 1
fi
log_success "Pré-requisitos OK"
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Adicionando Cliente: $CLIENT${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
echo ""
# =============================================================================
# 1. CRIAR NAMESPACE PARA N8N
# =============================================================================
log_info "Criando namespace ${CLIENT}-n8n..."
kubectl create namespace ${CLIENT}-n8n --dry-run=client -o yaml | kubectl apply -f -
log_success "Namespace criado"
# =============================================================================
# 2. INSTALAR N8N
# =============================================================================
log_info "Instalando n8n para ${CLIENT}..."
# Verificar se base-n8n-values.yaml existe
if [ ! -f "$SCRIPT_DIR/base-n8n-values.yaml" ]; then
log_error "Arquivo base-n8n-values.yaml não encontrado em $SCRIPT_DIR"
exit 1
fi
# Adicionar repo se necessário
helm repo add community-charts https://community-charts.github.io/helm-charts 2>/dev/null || true
helm repo update
# Construir argumentos do Helm dinamicamente
HELM_ARGS=""
# Configurar host do ingress
HELM_ARGS="$HELM_ARGS --set ingress.enabled=true"
HELM_ARGS="$HELM_ARGS --set ingress.className=nginx"
HELM_ARGS="$HELM_ARGS --set ingress.hosts[0].host=${CLIENT}-n8n.${DOMAIN}"
HELM_ARGS="$HELM_ARGS --set ingress.hosts[0].paths[0].path=/"
HELM_ARGS="$HELM_ARGS --set ingress.hosts[0].paths[0].pathType=Prefix"
# Configurar webhook URL
if [[ "$USE_CLOUDFLARE" == "true" || "$USE_LETSENCRYPT" == "true" ]]; then
HELM_ARGS="$HELM_ARGS --set webhook.url=https://${CLIENT}-n8n.${DOMAIN}"
else
HELM_ARGS="$HELM_ARGS --set webhook.url=http://${CLIENT}-n8n.${DOMAIN}"
fi
# Configurar TLS (se Let's Encrypt)
if [[ "$USE_LETSENCRYPT" == "true" ]]; then
HELM_ARGS="$HELM_ARGS --set ingress.annotations.cert-manager\\.io/cluster-issuer=letsencrypt"
HELM_ARGS="$HELM_ARGS --set ingress.tls[0].hosts[0]=${CLIENT}-n8n.${DOMAIN}"
HELM_ARGS="$HELM_ARGS --set ingress.tls[0].secretName=${CLIENT}-n8n-tls"
fi
# Configurar N8N_SECURE_COOKIE (se HTTPS)
if [[ "$USE_CLOUDFLARE" == "true" || "$USE_LETSENCRYPT" == "true" ]]; then
HELM_ARGS="$HELM_ARGS --set main.extraEnvVars.N8N_SECURE_COOKIE=true"
HELM_ARGS="$HELM_ARGS --set worker.extraEnvVars.N8N_SECURE_COOKIE=true"
HELM_ARGS="$HELM_ARGS --set webhook.extraEnvVars.N8N_SECURE_COOKIE=true"
fi
# Instalar n8n
eval helm upgrade --install ${CLIENT}-n8n community-charts/n8n \
--namespace ${CLIENT}-n8n \
-f "$SCRIPT_DIR/base-n8n-values.yaml" \
$HELM_ARGS \
--wait \
--timeout 5m
log_success "n8n instalado"
# =============================================================================
# 3. CRIAR GRUPO NO GITLAB (opcional)
# =============================================================================
log_info "Verificando GitLab..."
# Obter token do GitLab
if [ -z "$GITLAB_TOKEN" ]; then
GITLAB_TOKEN=$(kubectl get secret gitlab-gitlab-initial-root-password \
-n gitlab \
-o jsonpath='{.data.password}' 2>/dev/null | base64 -d || echo "")
fi
if [ -z "$GITLAB_TOKEN" ]; then
log_warn "GitLab não encontrado ou não configurado"
log_warn "Pule esta etapa ou crie o grupo manualmente"
else
log_info "Criando grupo no GitLab..."
# Usar GITLAB_HOST se disponível (da aula-10/.env)
if [[ -n "$GITLAB_HOST" ]]; then
GITLAB_URL="https://${GITLAB_HOST}"
else
GITLAB_URL="https://git.${DOMAIN}"
fi
# Criar grupo via API
RESULT=$(curl -s --request POST "${GITLAB_URL}/api/v4/groups" \
--header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \
--form "name=${CLIENT}" \
--form "path=${CLIENT}" \
--form "visibility=private" \
--write-out "%{http_code}" \
--output /tmp/gitlab-group-result.json 2>/dev/null || echo "000")
if [ "$RESULT" == "201" ]; then
log_success "Grupo criado no GitLab"
elif [ "$RESULT" == "400" ]; then
log_warn "Grupo já existe ou erro de validação"
else
log_warn "Não foi possível criar grupo (HTTP $RESULT)"
log_warn "Crie manualmente em ${GITLAB_URL}/admin/groups/new"
fi
fi
# =============================================================================
# RESUMO
# =============================================================================
# Obter IP do LoadBalancer
LB_IP=$(kubectl get svc ingress-nginx-controller \
-n ingress-nginx \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || echo "<pendente>")
# Determinar protocolo
PROTOCOL="https"
[[ "$USE_CLOUDFLARE" == "false" && "$USE_LETSENCRYPT" == "false" ]] && PROTOCOL="http"
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
echo -e "${GREEN} Cliente $CLIENT Provisionado!${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
echo ""
echo "Serviços:"
echo -e " n8n: ${GREEN}${PROTOCOL}://${CLIENT}-n8n.${DOMAIN}${NC}"
if [ -n "$GITLAB_TOKEN" ]; then
if [[ -n "$GITLAB_HOST" ]]; then
echo -e " GitLab: ${GREEN}https://${GITLAB_HOST}/${CLIENT}/${NC}"
else
echo -e " GitLab: ${GREEN}https://git.${DOMAIN}/${CLIENT}/${NC}"
fi
fi
echo ""
if [[ "$USE_CLOUDFLARE" == "true" ]]; then
echo "Configure no CloudFlare:"
echo -e " ${YELLOW}Tipo:${NC} A"
echo -e " ${YELLOW}Nome:${NC} ${CLIENT}-n8n"
echo -e " ${YELLOW}Conteúdo:${NC} ${GREEN}${LB_IP}${NC}"
echo -e " ${YELLOW}Proxy:${NC} ✓ (ícone laranja)"
echo ""
echo "(Ou use o wildcard *.${DOMAIN} se já configurado)"
else
echo "Configure o DNS:"
echo -e " ${YELLOW}Tipo:${NC} A"
echo -e " ${YELLOW}Nome:${NC} ${CLIENT}-n8n"
echo -e " ${YELLOW}Valor:${NC} ${GREEN}${LB_IP}${NC}"
echo ""
echo "(Ou use o wildcard *.${DOMAIN} se já configurado)"
fi
if [ -n "$GITLAB_TOKEN" ]; then
echo ""
echo "Git clone:"
if [[ -n "$GITLAB_HOST" ]]; then
echo " git clone git@${GITLAB_HOST}:${CLIENT}/meu-projeto.git"
else
echo " git clone git@git.${DOMAIN}:${CLIENT}/meu-projeto.git"
fi
fi
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"

View File

@@ -0,0 +1,140 @@
# =============================================================================
# Base Values para n8n de Clientes
# =============================================================================
#
# Este arquivo contém configurações base para instalações de n8n por cliente.
# O script add-client.sh usa este arquivo como template.
#
# Personalizações por cliente são feitas via --set no helm install.
#
# =============================================================================
# Imagem
image:
repository: n8nio/n8n
tag: "2.0.3"
pullPolicy: IfNotPresent
# Chave de criptografia (será sobrescrita por cliente se necessário)
# IMPORTANTE: Em produção, use uma chave única por cliente
encryptionKey: "workshop-n8n-encryption-key-32ch"
# =============================================================================
# BANCO DE DADOS
# =============================================================================
db:
type: postgresdb
postgresql:
enabled: true
auth:
database: n8n
username: n8n
# Em produção, use secrets únicos por cliente
password: "n8n-postgres-workshop-2025"
primary:
persistence:
enabled: true
size: 10Gi # Mínimo Hetzner ($0.0484/GB)
storageClass: hcloud-volumes
# =============================================================================
# REDIS
# =============================================================================
redis:
enabled: true
architecture: standalone
auth:
enabled: true
password: "n8n-redis-workshop-2025"
master:
persistence:
enabled: true
size: 10Gi # Mínimo Hetzner ($0.0484/GB)
storageClass: hcloud-volumes
# =============================================================================
# INGRESS
# =============================================================================
# Host é configurado dinamicamente pelo add-client.sh
ingress:
enabled: true
className: nginx
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
# =============================================================================
# MAIN NODE
# =============================================================================
# N8N_SECURE_COOKIE é configurado dinamicamente pelo add-client.sh via --set
main:
persistence:
enabled: true
size: 10Gi # Mínimo Hetzner ($0.0484/GB)
storageClass: hcloud-volumes
mountPath: "/home/node/.n8n"
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
# =============================================================================
# WORKERS (Queue Mode)
# =============================================================================
worker:
mode: queue
count: 1
concurrency: 10
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 1
maxReplicas: 3
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
# =============================================================================
# WEBHOOKS
# =============================================================================
webhook:
mode: queue
count: 1
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
autoscaling:
enabled: false # Manter simples para clientes menores
# =============================================================================
# SEGURANÇA
# =============================================================================
securityContext:
runAsNonRoot: true
runAsUser: 1000

185
aula-09/cleanup.sh Executable file
View File

@@ -0,0 +1,185 @@
#!/bin/bash
# =============================================================================
# Cleanup da Aula 09 - Remove n8n e clientes
# =============================================================================
#
# Este script remove:
# - n8n principal (namespace n8n)
# - Todos os clientes (namespaces *-n8n)
# - cert-manager (se instalado por esta aula)
# - Arquivo .env
#
# NÃO remove (gerenciados pela aula-08):
# - NGINX Ingress Controller
# - Hetzner CSI Driver
# - LoadBalancer
#
# =============================================================================
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}[ERRO]${NC} $1"; }
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo ""
echo -e "${CYAN}============================================${NC}"
echo -e "${CYAN} Cleanup - Aula 09 (n8n)${NC}"
echo -e "${CYAN}============================================${NC}"
echo ""
log_warn "ATENÇÃO: Isso vai remover os recursos da aula 09:"
echo " - n8n principal e todos os clientes"
echo " - Volumes persistentes (PostgreSQL, Redis, n8n)"
echo " - cert-manager (se instalado)"
echo " - Arquivo .env"
echo ""
echo "Mantido (aula-08):"
echo " - NGINX Ingress Controller"
echo " - Hetzner CSI Driver"
echo " - LoadBalancer"
echo ""
read -p "Continuar? (digite 'sim' para confirmar): " confirm
if [ "$confirm" != "sim" ]; then
log_info "Operação cancelada"
exit 0
fi
echo ""
# =============================================================================
# 1. REMOVER n8n PRINCIPAL
# =============================================================================
log_info "=== Removendo n8n principal ==="
if helm status n8n -n n8n &> /dev/null; then
log_info "Removendo Helm release n8n..."
helm uninstall n8n -n n8n --wait 2>/dev/null || true
log_success "n8n removido"
else
log_info "n8n não está instalado"
fi
# Remover PVCs
if kubectl get pvc -n n8n &> /dev/null 2>&1; then
log_info "Removendo PVCs do namespace n8n..."
kubectl delete pvc --all -n n8n --wait=false 2>/dev/null || true
fi
# Remover namespace
if kubectl get namespace n8n &> /dev/null; then
log_info "Removendo namespace n8n..."
kubectl delete namespace n8n --wait=false 2>/dev/null || true
log_success "Namespace n8n removido"
fi
# =============================================================================
# 2. REMOVER CLIENTES (namespaces *-n8n)
# =============================================================================
log_info "=== Removendo clientes ==="
CLIENT_NAMESPACES=$(kubectl get namespaces -o name 2>/dev/null | grep -E ".*-n8n$" | sed 's/namespace\///' || true)
if [ -n "$CLIENT_NAMESPACES" ]; then
for ns in $CLIENT_NAMESPACES; do
log_info "Removendo cliente: $ns"
# Remover helm release
RELEASE_NAME=$(echo "$ns" | sed 's/-n8n$//')
helm uninstall "${RELEASE_NAME}-n8n" -n "$ns" --wait 2>/dev/null || true
# Remover PVCs
kubectl delete pvc --all -n "$ns" --wait=false 2>/dev/null || true
# Remover namespace
kubectl delete namespace "$ns" --wait=false 2>/dev/null || true
log_success "Cliente $ns removido"
done
else
log_info "Nenhum cliente encontrado"
fi
# =============================================================================
# 3. REMOVER CERT-MANAGER (se instalado)
# =============================================================================
log_info "=== Verificando cert-manager ==="
if helm status cert-manager -n cert-manager &> /dev/null; then
log_info "Removendo cert-manager..."
# Remover ClusterIssuer primeiro
kubectl delete clusterissuer --all 2>/dev/null || true
# Remover helm release
helm uninstall cert-manager -n cert-manager --wait 2>/dev/null || true
# Remover CRDs
kubectl delete crd \
certificates.cert-manager.io \
certificaterequests.cert-manager.io \
challenges.acme.cert-manager.io \
clusterissuers.cert-manager.io \
issuers.cert-manager.io \
orders.acme.cert-manager.io \
2>/dev/null || true
# Remover namespace
kubectl delete namespace cert-manager --wait=false 2>/dev/null || true
log_success "cert-manager removido"
else
log_info "cert-manager não está instalado"
fi
# =============================================================================
# 4. REMOVER ARQUIVO .env
# =============================================================================
log_info "=== Removendo arquivo de configuração ==="
if [ -f "$SCRIPT_DIR/.env" ]; then
rm -f "$SCRIPT_DIR/.env"
log_success "Arquivo .env removido"
else
log_info "Arquivo .env não existe"
fi
# =============================================================================
# RESUMO
# =============================================================================
echo ""
echo -e "${CYAN}============================================${NC}"
echo -e "${GREEN} Cleanup Concluído!${NC}"
echo -e "${CYAN}============================================${NC}"
echo ""
echo "Removido:"
echo " - n8n principal e clientes"
echo " - cert-manager (se existia)"
echo " - Arquivo .env"
echo ""
echo "Mantido (aula-08):"
echo " - NGINX Ingress Controller"
echo " - Hetzner CSI Driver"
echo " - LoadBalancer"
echo " - Cluster Kubernetes"
echo ""
log_warn "Volumes Hetzner podem demorar alguns minutos para serem"
log_warn "completamente removidos. Verifique no painel da Hetzner."
echo ""

148
aula-09/custom-values.yaml Normal file
View File

@@ -0,0 +1,148 @@
# =============================================================================
# n8n Helm Chart - Custom Values
# =============================================================================
# Aula 06 - Deploy n8n via Helm
#
# Chart: community-charts/n8n
# Docs: https://community-charts.github.io/docs/charts/n8n/configuration
# =============================================================================
# -----------------------------------------------------------------------------
# Imagem
# -----------------------------------------------------------------------------
image:
repository: n8nio/n8n
tag: "2.0.3"
pullPolicy: IfNotPresent
# -----------------------------------------------------------------------------
# Chave de Criptografia
# -----------------------------------------------------------------------------
encryptionKey: "workshop-n8n-encryption-key-32ch"
# -----------------------------------------------------------------------------
# Banco de Dados PostgreSQL
# -----------------------------------------------------------------------------
db:
type: postgresdb
postgresql:
enabled: true
auth:
database: n8n
username: n8n
password: "n8n-postgres-workshop-2025"
primary:
persistence:
enabled: true
size: 10Gi # Mínimo Hetzner ($0.0484/GB)
# -----------------------------------------------------------------------------
# Redis (necessário para Queue Mode)
# -----------------------------------------------------------------------------
redis:
enabled: true
architecture: standalone
auth:
enabled: true
password: "n8n-redis-workshop-2025"
master:
persistence:
enabled: true
size: 10Gi # Mínimo Hetzner ($0.0484/GB)
# -----------------------------------------------------------------------------
# Ingress NGINX
# -----------------------------------------------------------------------------
# Host e TLS são configurados dinamicamente pelo setup.sh via --set
ingress:
enabled: true
className: nginx
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
# -----------------------------------------------------------------------------
# Main Node
# -----------------------------------------------------------------------------
# N8N_SECURE_COOKIE é configurado dinamicamente pelo setup.sh via --set
main:
persistence:
enabled: true
size: 10Gi # Mínimo Hetzner ($0.0484/GB)
mountPath: "/home/node/.n8n"
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi
# -----------------------------------------------------------------------------
# Workers (Queue Mode)
# -----------------------------------------------------------------------------
worker:
mode: queue
count: 2
concurrency: 10
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 5
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
# -----------------------------------------------------------------------------
# Webhooks (Queue Mode)
# -----------------------------------------------------------------------------
# URL é configurada dinamicamente pelo setup.sh via --set
webhook:
mode: queue
count: 1
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
autoscaling:
enabled: true
minReplicas: 1
maxReplicas: 3
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
# -----------------------------------------------------------------------------
# Configurações de Segurança
# -----------------------------------------------------------------------------
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault

526
aula-09/setup.sh Executable file
View File

@@ -0,0 +1,526 @@
#!/bin/bash
# =============================================================================
# Setup da Aula 09 - n8n via Helm (Hetzner Cloud)
# =============================================================================
#
# Este script instala e configura:
# 1. cert-manager (opcional, para Let's Encrypt)
# 2. n8n com PostgreSQL, Redis, Workers e Webhooks
#
# Pré-requisitos:
# - Kubernetes cluster Talos na Hetzner (aula-08) com:
# - Hetzner CSI Driver (StorageClass: hcloud-volumes)
# - NGINX Ingress Controller com LoadBalancer
# - kubectl configurado (KUBECONFIG=../aula-08/kubeconfig)
# - Helm 3.x instalado
#
# Uso:
# export KUBECONFIG=$(pwd)/../aula-08/kubeconfig
# chmod +x setup.sh
# ./setup.sh
#
# =============================================================================
set -e # Para na primeira falha
# 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' # No Color
# 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}[ERRO]${NC} $1"; }
# Diretório do script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Variáveis de configuração (serão preenchidas pelo usuário ou .env)
N8N_HOST=""
DOMAIN="" # Extraído automaticamente do N8N_HOST
USE_CLOUDFLARE=""
USE_LETSENCRYPT=""
LETSENCRYPT_EMAIL=""
# =============================================================================
# FUNÇÕES DE CONFIGURAÇÃO
# =============================================================================
save_config() {
cat > "$SCRIPT_DIR/.env" <<EOF
# Configuração gerada pelo setup.sh
# $(date)
N8N_HOST=${N8N_HOST}
DOMAIN=${DOMAIN}
USE_CLOUDFLARE=${USE_CLOUDFLARE}
USE_LETSENCRYPT=${USE_LETSENCRYPT}
LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL}
EOF
log_success "Configuração salva em .env"
}
load_config() {
if [[ -f "$SCRIPT_DIR/.env" ]]; then
source "$SCRIPT_DIR/.env"
# Compatibilidade: se não tem N8N_HOST, deriva do DOMAIN
if [[ -z "$N8N_HOST" && -n "$DOMAIN" ]]; then
N8N_HOST="n8n.${DOMAIN}"
fi
return 0
fi
return 1
}
# Extrai domínio base de um FQDN (ex: n8n.kube.quest → kube.quest)
extract_domain() {
local fqdn="$1"
echo "$fqdn" | sed 's/^[^.]*\.//'
}
collect_user_input() {
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
echo -e "${CYAN} n8n no Kubernetes (Hetzner Cloud)${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
echo ""
# Verificar se já existe configuração
if load_config; then
echo -e "Configuração existente encontrada:"
echo -e " Hostname: ${GREEN}${N8N_HOST}${NC}"
echo -e " CloudFlare: ${GREEN}${USE_CLOUDFLARE}${NC}"
echo -e " Let's Encrypt: ${GREEN}${USE_LETSENCRYPT}${NC}"
echo ""
echo -e "Deseja usar esta configuração?"
echo -e " 1) Sim, continuar com a configuração existente"
echo -e " 2) Não, reconfigurar"
echo -n "Escolha [1/2]: "
read -r choice
if [[ "$choice" == "1" ]]; then
return 0
fi
fi
# Coletar hostname completo
echo ""
echo -n "Digite o hostname do n8n (ex: n8n.kube.quest): "
read -r N8N_HOST
if [[ -z "$N8N_HOST" ]]; then
log_error "Hostname não pode ser vazio"
exit 1
fi
# Extrair domínio base automaticamente
DOMAIN=$(extract_domain "$N8N_HOST")
# Perguntar sobre CloudFlare
echo ""
echo "Você usa CloudFlare para DNS?"
echo " 1) Sim (com proxy/CDN ativado - ícone laranja)"
echo " 2) Não"
echo -n "Escolha [1/2]: "
read -r choice
if [[ "$choice" == "1" ]]; then
USE_CLOUDFLARE=true
USE_LETSENCRYPT=false
log_info "CloudFlare irá gerenciar TLS automaticamente na edge"
else
USE_CLOUDFLARE=false
# Perguntar sobre Let's Encrypt
echo ""
echo "Deseja ativar HTTPS com Let's Encrypt?"
echo " 1) Sim (recomendado para produção)"
echo " 2) Não (apenas HTTP)"
echo -n "Escolha [1/2]: "
read -r choice
if [[ "$choice" == "1" ]]; then
USE_LETSENCRYPT=true
echo ""
echo -n "Digite seu email para Let's Encrypt: "
read -r LETSENCRYPT_EMAIL
if [[ -z "$LETSENCRYPT_EMAIL" ]]; then
log_error "Email é obrigatório para Let's Encrypt"
exit 1
fi
else
USE_LETSENCRYPT=false
fi
fi
# Salvar configuração
save_config
}
# =============================================================================
# FUNÇÕES DE INSTALAÇÃO
# =============================================================================
install_cert_manager() {
log_info "Instalando cert-manager..."
helm repo add jetstack https://charts.jetstack.io 2>/dev/null || true
helm repo update jetstack
if helm status cert-manager -n cert-manager &> /dev/null; then
log_success "cert-manager já está instalado"
else
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.16.2 \
--set crds.enabled=true \
--wait \
--timeout 5m
log_success "cert-manager instalado"
fi
}
create_cluster_issuer() {
log_info "Criando ClusterIssuer para Let's Encrypt..."
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: ${LETSENCRYPT_EMAIL}
privateKeySecretRef:
name: letsencrypt-key
solvers:
- http01:
ingress:
class: nginx
EOF
log_success "ClusterIssuer 'letsencrypt' criado"
}
show_dns_instructions() {
# Obter IP do LoadBalancer
LB_IP=$(kubectl get svc -n ingress-nginx ingress-nginx-controller \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || echo "<pendente>")
# Extrair nome do host (parte antes do primeiro ponto)
HOST_NAME=$(echo "$N8N_HOST" | cut -d. -f1)
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Configure o DNS${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
if [[ "$USE_CLOUDFLARE" == "true" ]]; then
echo ""
echo "No painel do CloudFlare (https://dash.cloudflare.com):"
echo ""
echo -e " ${YELLOW}Tipo:${NC} A"
echo -e " ${YELLOW}Nome:${NC} ${HOST_NAME}"
echo -e " ${YELLOW}Conteúdo:${NC} ${GREEN}${LB_IP}${NC}"
echo -e " ${YELLOW}Proxy:${NC} ✓ (ícone laranja)"
echo ""
echo -e " ${YELLOW}Tipo:${NC} A"
echo -e " ${YELLOW}Nome:${NC} * (wildcard, para multi-tenant)"
echo -e " ${YELLOW}Conteúdo:${NC} ${GREEN}${LB_IP}${NC}"
echo -e " ${YELLOW}Proxy:${NC} ✓ (ícone laranja)"
echo ""
echo -e "${GREEN}O CloudFlare cuida do TLS automaticamente!${NC}"
else
echo ""
echo "No seu provedor DNS:"
echo ""
echo -e " ${YELLOW}Tipo:${NC} A"
echo -e " ${YELLOW}Nome:${NC} ${HOST_NAME}"
echo -e " ${YELLOW}Valor:${NC} ${GREEN}${LB_IP}${NC}"
echo ""
echo -e " ${YELLOW}Tipo:${NC} A"
echo -e " ${YELLOW}Nome:${NC} * (wildcard, para multi-tenant)"
echo -e " ${YELLOW}Valor:${NC} ${GREEN}${LB_IP}${NC}"
if [[ "$USE_LETSENCRYPT" == "true" ]]; then
echo ""
echo -e "${GREEN}Let's Encrypt irá emitir certificados automaticamente!${NC}"
echo -e "Aguarde ~2 minutos após configurar o DNS."
fi
fi
# Determinar protocolo
PROTOCOL="https"
if [[ "$USE_CLOUDFLARE" == "false" && "$USE_LETSENCRYPT" == "false" ]]; then
PROTOCOL="http"
fi
echo ""
echo -e "Acesse: ${GREEN}${PROTOCOL}://${N8N_HOST}${NC}"
}
# =============================================================================
# VERIFICAÇÕES INICIAIS
# =============================================================================
log_info "Verificando pré-requisitos..."
# Verificar kubectl
if ! command -v kubectl &> /dev/null; then
log_error "kubectl não encontrado. Instale o kubectl primeiro."
exit 1
fi
log_success "kubectl encontrado"
# Verificar conexão com cluster
if ! kubectl cluster-info &> /dev/null; then
log_error "Não foi possível conectar ao cluster Kubernetes."
exit 1
fi
log_success "Conectado ao cluster Kubernetes"
# Verificar helm
if ! command -v helm &> /dev/null; then
log_error "Helm não encontrado. Instale o Helm >= 3.14 primeiro."
log_info "Instalação: https://helm.sh/docs/intro/install/"
exit 1
fi
log_success "Helm $(helm version --short) encontrado"
# =============================================================================
# COLETAR INPUT DO USUÁRIO
# =============================================================================
collect_user_input
echo ""
# =============================================================================
# 1. VERIFICAR PRÉ-REQUISITOS DA AULA-08
# =============================================================================
log_info "=== Verificando infraestrutura (aula-08) ==="
# Verificar Hetzner CSI Driver
if ! kubectl get storageclass hcloud-volumes &> /dev/null; then
log_error "StorageClass hcloud-volumes não encontrado!"
log_error "Execute primeiro o setup.sh da aula-08 para instalar o CSI Driver."
exit 1
fi
log_success "Hetzner CSI Driver instalado (hcloud-volumes)"
# Verificar NGINX Ingress
if ! kubectl get ingressclass nginx &> /dev/null; then
log_error "NGINX Ingress não encontrado!"
log_error "Execute primeiro o setup.sh da aula-08 para instalar o Ingress Controller."
exit 1
fi
log_success "NGINX Ingress Controller instalado"
echo ""
# =============================================================================
# 2. INSTALAR CERT-MANAGER (se Let's Encrypt)
# =============================================================================
if [[ "$USE_LETSENCRYPT" == "true" ]]; then
log_info "=== Configurando cert-manager ==="
install_cert_manager
create_cluster_issuer
echo ""
fi
# =============================================================================
# 3. CRIAR NAMESPACE E APLICAR SECRETS
# =============================================================================
log_info "=== Configurando namespace n8n ==="
# Criar namespace se não existir
if kubectl get namespace n8n &> /dev/null; then
log_success "Namespace n8n já existe"
else
log_info "Criando namespace n8n..."
kubectl create namespace n8n
log_success "Namespace n8n criado"
fi
echo ""
# =============================================================================
# 4. INSTALAR n8n VIA HELM
# =============================================================================
log_info "=== Instalando n8n ==="
# Adicionar repo do community-charts
log_info "Adicionando repositório Helm do community-charts..."
helm repo add community-charts https://community-charts.github.io/helm-charts 2>/dev/null || true
helm repo update
# Construir argumentos do Helm dinamicamente
HELM_ARGS=""
# Configurar host do ingress
HELM_ARGS="$HELM_ARGS --set ingress.hosts[0].host=${N8N_HOST}"
HELM_ARGS="$HELM_ARGS --set ingress.hosts[0].paths[0].path=/"
HELM_ARGS="$HELM_ARGS --set ingress.hosts[0].paths[0].pathType=Prefix"
# Configurar webhook URL
if [[ "$USE_CLOUDFLARE" == "true" || "$USE_LETSENCRYPT" == "true" ]]; then
HELM_ARGS="$HELM_ARGS --set webhook.url=https://${N8N_HOST}"
else
HELM_ARGS="$HELM_ARGS --set webhook.url=http://${N8N_HOST}"
fi
# Configurar TLS
if [[ "$USE_LETSENCRYPT" == "true" ]]; then
HELM_ARGS="$HELM_ARGS --set ingress.annotations.cert-manager\\.io/cluster-issuer=letsencrypt"
HELM_ARGS="$HELM_ARGS --set ingress.tls[0].hosts[0]=${N8N_HOST}"
HELM_ARGS="$HELM_ARGS --set ingress.tls[0].secretName=n8n-tls"
fi
# Configurar N8N_SECURE_COOKIE
if [[ "$USE_CLOUDFLARE" == "true" || "$USE_LETSENCRYPT" == "true" ]]; then
HELM_ARGS="$HELM_ARGS --set main.extraEnvVars.N8N_SECURE_COOKIE=true"
HELM_ARGS="$HELM_ARGS --set worker.extraEnvVars.N8N_SECURE_COOKIE=true"
HELM_ARGS="$HELM_ARGS --set webhook.extraEnvVars.N8N_SECURE_COOKIE=true"
fi
# Verificar se já está instalado
if helm status n8n -n n8n &> /dev/null; then
log_warn "n8n já está instalado. Atualizando..."
eval helm upgrade n8n community-charts/n8n \
--namespace n8n \
--values "$SCRIPT_DIR/custom-values.yaml" \
$HELM_ARGS \
--wait \
--timeout 10m
log_success "n8n atualizado com sucesso!"
else
log_info "Instalando n8n..."
eval helm install n8n community-charts/n8n \
--namespace n8n \
--values "$SCRIPT_DIR/custom-values.yaml" \
$HELM_ARGS \
--wait \
--timeout 10m
log_success "n8n instalado com sucesso!"
fi
echo ""
# =============================================================================
# 5. AGUARDAR PODS FICAREM PRONTOS
# =============================================================================
log_info "=== Aguardando pods ficarem prontos ==="
log_info "Aguardando PostgreSQL..."
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/name=postgresql \
-n n8n \
--timeout=180s 2>/dev/null || log_warn "Timeout aguardando PostgreSQL"
log_info "Aguardando Redis..."
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/name=redis \
-n n8n \
--timeout=120s 2>/dev/null || log_warn "Timeout aguardando Redis"
log_info "Aguardando n8n main..."
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/component=main \
-n n8n \
--timeout=180s 2>/dev/null || log_warn "Timeout aguardando n8n main"
log_info "Aguardando n8n workers..."
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/component=worker \
-n n8n \
--timeout=120s 2>/dev/null || log_warn "Timeout aguardando workers"
log_info "Aguardando n8n webhooks..."
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/component=webhook \
-n n8n \
--timeout=120s 2>/dev/null || log_warn "Timeout aguardando webhooks"
log_success "Todos os componentes estão rodando!"
echo ""
# =============================================================================
# RESUMO FINAL
# =============================================================================
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
echo -e "${GREEN} Setup Completo!${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
echo ""
echo "Componentes instalados:"
if [[ "$USE_LETSENCRYPT" == "true" ]]; then
echo " - cert-manager (ClusterIssuer: letsencrypt)"
fi
echo " - n8n (namespace: n8n)"
echo " - Main node"
echo " - Workers (2-5 réplicas, autoscaling)"
echo " - Webhooks (1-3 réplicas, autoscaling)"
echo " - PostgreSQL"
echo " - Redis"
echo ""
echo "Configuração:"
echo " Hostname: ${N8N_HOST}"
echo " CloudFlare: ${USE_CLOUDFLARE}"
echo " Let's Encrypt: ${USE_LETSENCRYPT}"
# Mostrar instruções de DNS
show_dns_instructions
echo ""
echo "Comandos úteis:"
echo " # Ver todos os pods"
echo " kubectl get pods -n n8n"
echo ""
echo " # Ver logs do n8n"
echo " kubectl logs -f -n n8n deployment/n8n"
echo ""
echo " # Ver HPA (autoscaler)"
echo " kubectl get hpa -n n8n"
echo ""
echo " # Ver ingress"
echo " kubectl get ingress -n n8n"
echo ""
if [[ "$USE_LETSENCRYPT" == "true" ]]; then
echo " # Ver status do certificado"
echo " kubectl get certificate -n n8n"
echo ""
fi
echo " # Adicionar novo cliente"
echo " ./add-client.sh <nome-cliente>"
echo ""
echo " # Desinstalar"
echo " helm uninstall n8n -n n8n"
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
echo ""
# Mostrar status atual
log_info "Status atual dos pods:"
echo ""
kubectl get pods -n n8n
echo ""
log_info "Status do HPA:"
echo ""
kubectl get hpa -n n8n 2>/dev/null || echo "HPA ainda não criado"
echo ""
log_info "Status do Ingress:"
echo ""
kubectl get ingress -n n8n
echo ""