Saltar al contenido principal
Esta guía describe el despliegue completo de la infraestructura EKB EKS en AWS usando Terragrunt. Cubre la instalación de herramientas, configuración del entorno y una secuencia de despliegue por fases diseñada para garantizar el orden correcto de dependencias entre todos los componentes de infraestructura. Los despliegues se organizan en nueve fases:
  1. Gestión de Estado — Inicializa el bucket S3 usado para almacenar el estado de Terraform del entorno.
  2. Infraestructura EKS — Provisiona la VPC, subredes, NAT gateways, roles IAM y el cluster EKS con grupos de nodos administrados.
  3. Almacenamiento y Balanceo de Carga — Despliega el controlador EBS CSI para volúmenes persistentes y el AWS Load Balancer Controller para el ingreso ALB.
  4. Autoescalado con Karpenter — Configura el provisionamiento dinámico de nodos con soporte para instancias Spot y manejo de interrupciones via SQS y EventBridge.
  5. Autoescalado con KEDA — Despliega KEDA para el autoescalado a nivel de pod basado en umbrales de CPU y memoria.
  6. Servicios de Datos — Provisiona Supabase (autoalojado o Cloud), ElastiCache Redis y Amazon MQ RabbitMQ.
  7. Servicios Odin — Despliega el stack de aplicación EKB (Web, FastAPI, Celery, Automator) vía Helm.
  8. Observabilidad con SigNoz — Despliega la traza distribuida, métricas y agregación de logs a través de SigNoz y el agente k8s-infra.
  9. Despliegue Final — Ejecuta un terragrunt apply completo para reconciliar los recursos restantes.
Antes de comenzar, complete la lista de prerrequisitos con el cliente y asegúrese de que todos los placeholders <YOUR_*> en la plantilla del entorno estén completados. Algunos valores, incluyendo el ID de VPC, el endpoint del cluster EKS y los endpoints de Redis y RabbitMQ, solo están disponibles después de fases específicas, por lo que la guía indica exactamente cuándo capturarlos y aplicarlos.

Prerrequisitos

  • AWS CLI configurada con los permisos apropiados
  • Terraform (>= 1.0)
  • Terragrunt (última versión)
  • kubectl para la gestión de Kubernetes
  • helm para la gestión de charts Helm

Guía de Instalación

Instalando Terragrunt

macOS (Homebrew)
brew install terragrunt
Linux (apt)
# Agregar clave GPG de HashiCorp
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
 
# Agregar repositorio de HashiCorp
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
 
# Actualizar e instalar
sudo apt update
sudo apt install terragrunt
Windows (Chocolatey)
choco install terragrunt

Instalando kubectl

macOS (Homebrew)
brew install kubectl
Linux
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
Windows (Chocolatey)
choco install kubernetes-cli

Instalando Helm

macOS (Homebrew)
brew install helm
Linux
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
Windows (Chocolatey)
choco install kubernetes-helm

Verificando la Instalación

terragrunt --version
terraform --version
kubectl version --client
helm version

Configuración de AWS CLI

# Instalar AWS CLI
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
 
# Configurar credenciales de AWS
aws configure
 
# Verificar configuración
aws sts get-caller-identity

Creando un Nuevo Entorno

Paso 1: Copie la Plantilla del Entorno

La carpeta env-template-folder contiene archivos pre-estructurados con placeholders <YOUR_*> listos para ser completados. Cópiala completamente para crear su nueva carpeta de entorno.
# Navegar al directorio de entornos de terragrunt
cd terragrunt/environments
 
# Copiar la carpeta completa de la plantilla a un nuevo entorno (reemplazar 'your-env-name')
cp -r env-template-folder your-env-name
 
# La estructura de carpetas está lista:
# your-env-name/
# ├── terragrunt.hcl               # Configuración central del cluster
# ├── state/
# │   └── terragrunt.hcl           # Configuración del bucket de estado S3
# └── values/
#     ├── infrastructure.yaml      # AWS Load Balancer Controller
#     ├── karpenter-values.yaml    # Configuraciones del controlador Karpenter
#     ├── karpenter-nodeclasses.yaml  # Definiciones de EC2NodeClass
#     ├── karpenter.yaml           # Definiciones de Karpenter NodePool
#     ├── keda.yaml                # KEDA autoscaler
#     ├── aws-ebs-csi-driver.yaml  # Controlador EBS CSI
#     ├── odin-services.yaml       # Servicios de aplicación Odin
#     ├── supabase.yaml            # Supabase (si se autoaloja)
#     ├── ha-supabase-db.yaml      # DB HA de Supabase (si se autoaloja)
#     ├── cloudnative-pg.yaml      # Operador CloudNativePG (si se autoaloja)
#     ├── signoz.yaml              # Observabilidad SigNoz (opcional)
#     └── signoz-k8s-infra.yaml    # Métricas k8s de SigNoz (opcional)

Paso 2: Verifique que Todos los Placeholders Estén Presentes

cd your-env-name
 
# Listar todos los placeholders que necesitan ser completados
grep -r "<YOUR_" . --include="*.hcl" --include="*.yaml" | sort
Todos los placeholders siguen la convención <YOUR_*>. Los pasos a continuación describen cómo completarlos archivo por archivo.

Paso 3: Provisione Certificados SSL (AWS ACM)

Antes de establecer las variables de entorno necesita los ARNs de los certificados. Use la Consola de AWS para solicitar certificados SSL en AWS Certificate Manager (ACM) para todos los dominios que servirá su entorno. Opción A: Un solo certificado wildcard (recomendado) Un solo certificado wildcard cubre todos los subdominios con un solo ARN. Por ejemplo, si su dominio base es app.example.com, un solo certificado *.app.example.com cubre:
ServicioDominio
Webapp.example.com
FastAPIapi-app.example.com
Automatorautomations-app.example.com
Supabasesupabase-app.example.com
SigNozsignoz-app.example.com
Opción B: Certificados por servicio Solicite un certificado por dominio si no puede usar un wildcard. Repita los pasos a continuación para cada dominio: <YOUR_WEB_DOMAIN>, <YOUR_API_DOMAIN>, <YOUR_AUTOMATOR_DOMAIN>, <YOUR_SUPABASE_DOMAIN> (solo si ENABLE_SUPABASE=true), <YOUR_SIGNOZ_DOMAIN> (solo si ENABLE_SIGNOZ=true). Solicitando un certificado en la Consola de AWS
  1. Abra la consola de AWS Certificate Manager
  2. Cambie a la región correcta (arriba a la derecha) — debe coincidir con <YOUR_AWS_REGION>
  3. Haga clic en Solicitar un certificadoSolicitar un certificado públicoSiguiente
  4. Debajo de Nombre de dominio completo, ingrese el wildcard (ej., *.app.example.com) o un dominio específico
  5. Establezca el Método de validación en Validación por DNS
  6. Haga clic en Solicitar — el certificado se crea en estado Pending validation
Agregando el registro CNAME de validación DNS ACM genera un registro CNAME que debe agregar a su proveedor de DNS para demostrar la propiedad del dominio. Obtenga los valores de la Consola de ACM abriendo el certificado y expandiendo el dominio bajo Dominios.
Campo DNSValor
Tipo de registroCNAME
Nombre / Hostej., _fa187f22ac17bce6f508bf3c56439c61.signoz-app.example.com.
Valor / Apunta aej., _c7c97325fe38061e168e232d122c7ff3.jkddzztszm.acm-validations.aws.
Incluya el punto final (.) al final de los valores CNAME si su proveedor de DNS lo requiere.
Cloudflare
  1. Inicie sesión en Cloudflare → seleccione su dominio → vaya a DNSRecordsAdd record
  2. Establezca el Tipo en CNAME
  3. Pegue el nombre CNAME de ACM en Name y el valor CNAME de ACM en Target
  4. Establezca el Estado del proxy en DNS only (icono de nube gris) — el certificado no se validará a través del proxy de Cloudflare
  5. Haga clic en Save
Route 53
  1. Abra la consola de Route 53 → Hosted zones → seleccione su zona → Create record
  2. Establezca el Tipo de registro en CNAME
  3. Pegue el nombre CNAME de ACM en Record name (solo la parte del subdominio) y el valor en Value
  4. Establezca TTL en 300 y haga clic en Create records
En ACM también puede hacer clic en Create records in Route 53 para que ACM agregue el registro automáticamente si la hosted zone está en la misma cuenta.
Una vez que el DNS se propague (típicamente 1–5 minutos), el estado del certificado cambia a Issued. Copie el ARN desde la parte superior del certificado: se ve como arn:aws:acm:<region>:<account-id>:certificate/<uuid>.

Paso 4: Establezca las Variables de Entorno

Establezca estas variables de entorno del shell antes de ejecutar cualquier comando de Terragrunt. Son leídas directamente por terragrunt.hcl a través de get_env().
export AWS_REGION="<YOUR_AWS_REGION>"          # ej., "eu-west-2", "us-east-2"
export CLUSTER_NAME="<env_folder_name>"          # ej., "env-template-folder"
 
# Configuración de dominios
export WEB_DOMAIN="<YOUR_WEB_DOMAIN>"                    # ej., "app.example.com"
export FASTAPI_DOMAIN="<YOUR_API_DOMAIN>"                # ej., "api-app.example.com"
export AUTOMATOR_DOMAIN="<YOUR_AUTOMATOR_DOMAIN>"        # ej., "automations-app.example.com"
export SUPABASE_DOMAIN="<YOUR_SUPABASE_DOMAIN>"          # ej., "supabase-app.example.com"
export SIGNOZ_DOMAIN="<YOUR_SIGNOZ_DOMAIN>"              # ej., "signoz-app.example.com"
 
# ARNs de certificados SSL — Opción A: Un solo certificado wildcard (recomendado)
export WILDCARD_CERTIFICATE_ARN="arn:aws:acm:<YOUR_AWS_REGION>:<YOUR_AWS_ACCOUNT_ID>:certificate/<YOUR_WILDCARD_CERT_ID>"
 
# ARNs de certificados SSL — Opción B: Certificados por servicio
export WEB_CERTIFICATE_ARN="arn:aws:acm:<YOUR_AWS_REGION>:<YOUR_AWS_ACCOUNT_ID>:certificate/<YOUR_WEB_CERT_ID>"
export FASTAPI_CERTIFICATE_ARN="arn:aws:acm:<YOUR_AWS_REGION>:<YOUR_AWS_ACCOUNT_ID>:certificate/<YOUR_API_CERT_ID>"
export AUTOMATOR_CERTIFICATE_ARN="arn:aws:acm:<YOUR_AWS_REGION>:<YOUR_AWS_ACCOUNT_ID>:certificate/<YOUR_AUTOMATOR_CERT_ID>"
export SUPABASE_CERTIFICATE_ARN="arn:aws:acm:<YOUR_AWS_REGION>:<YOUR_AWS_ACCOUNT_ID>:certificate/<YOUR_SUPABASE_CERT_ID>"
export SIGNOZ_CERTIFICATE_ARN="arn:aws:acm:<YOUR_AWS_REGION>:<YOUR_AWS_ACCOUNT_ID>:certificate/<YOUR_SIGNOZ_CERT_ID>"
 
# Banderas de habilitación de servicios
export ENABLE_ALB_CONTROLLER="true"
export ENABLE_AWS_SERVICES="true"      # Establecer en "true" para habilitar ElastiCache y AmazonMQ
 
# Stack de Supabase (autoalojado) — habilitar los tres juntos si se autoaloja Supabase
export ENABLE_CNPG="true"             # Operador CloudNativePG  (namespace: cnpg-system)
export ENABLE_HA_SUPABASE_DB="true"   # DB HA de Supabase      (namespace: ha-supabase-db)
export ENABLE_SUPABASE="true"         # Aplicación Supabase     (namespace: supabase)
 
export ENABLE_SIGNOZ="true"           # Establecer en "true" para habilitar observabilidad SigNoz
export SSL_TERMINATION="alb"

Instancias Spot y Cargas de Trabajo con Estado

Las instancias Spot se configuran por NodePool en values/karpenter.yaml, no a través de variables de entorno. Cada NodePool declara su propia estrategia de capacidad:
NodePoolEtiqueta workload-typeTipo de CapacidadJustificación
generalgeneralSpot → respaldo bajo demandaOptimización de costos para cargas de trabajo por lotes/segundo plano sin estado
compute-intensivecompute-intensiveSpot → respaldo bajo demandaOptimización de costos para cargas de trabajo limitadas por CPU
memory-intensivememory-intensiveBajo demanda → respaldo SpotEstabilidad priorizada para pods de alta memoria
gpugpuSpot → respaldo bajo demandaOptimización de costos para cargas de trabajo de IA/ML por lotes
applicationapplicationSolo bajo demandaServicios de usuario estables (Supabase, Kong, etc.) — sin interrupciones Spot
databasedatabase / node-type: database-dedicatedSolo bajo demandaCon estado — la interrupción Spot es insegura para bases de datos
El NodePool application usa familias de instancias m/c (generación 5+) con solo bajo demanda. Los pods del servicio Supabase se fijan aquí mediante nodeSelector: workload-type: "application" para garantizar que nunca sean interrumpidos por un evento de recuperación Spot. El NodePool database-dedicated nunca usa Spot. Usa consolidationPolicy: WhenEmpty para que Karpenter no evict un nodo que aún tiene un pod ejecutándose, lo que lo hace seguro para cargas de trabajo con estado como PostgreSQL y réplicas de CloudNativePG. Pautas para aplicaciones con estado en Spot:
  • No programe bases de datos, colas persistentes o cualquier pod con un PersistentVolumeClaim en NodePools Spot.
  • Use un nodeSelector dirigido a node-type: database-dedicated con la tolerancia database-workload: "true" correspondiente para pods de bases de datos.
  • Use nodeSelector: workload-type: "application" para servicios de usuario sin estado que deben permanecer disponibles sin interrupciones.
  • Para cargas de trabajo de segundo plano (Web, API, Celery, Automator), el NodePool Spot general es apropiado — el manejador de interrupciones SQS de Karpenter drena los nodos Spot graciosamente antes de que AWS los recupere, y el conteo mínimo de réplicas de KEDA (≥ 2) garantiza disponibilidad durante la sustitución de nodos.
  • Para deshabilitar Spot globalmente, elimine "spot" de la lista de valores en cada NodePool dentro de values/karpenter.yaml.
Cómo Karpenter maneja las alertas de interrupción Spot: AWS proporciona un aviso de interrupción de 2 minutos antes de terminar una instancia Spot. Karpenter usa EventBridge y SQS para actuar automáticamente:
Evento de Interrupción Spot de AWS


Amazon EventBridge (CloudWatch Events)
  Regla: EC2 Spot Instance Interruption Warning


   Cola SQS (cola de interrupción de Karpenter)


  Controlador Karpenter (encuesta SQS continuamente)

        ├── Cordon el nodo (no se programan nuevos pods)
        ├── Drena pods existentes (respeta PodDisruptionBudgets)
        ├── Provisiona un nodo de reemplazo en paralelo
        └── Los pods se reprograman en el nuevo nodo antes de que se cierre la ventana de 2 min
Esto se configura en el bloque karpenter en terragrunt.hcl:
karpenter = {
  spot_interruption_handling = true   # crea la cola SQS y la regla de EventBridge
  enable_spot_instances       = true   # permite Spot en los requisitos de capacidad de NodePool
}

Paso 5: Actualice los Valores Específicos del Entorno

Realice una búsqueda y reemplazo en todos los archivos de su nueva carpeta de entorno para los siguientes placeholders:
PlaceholderDescripciónEjemplo
<YOUR_ENV_NAME>Identificador único del entornoapp-eks-prod
<YOUR_AWS_REGION>Región de AWS del clustereu-west-2, us-east-2
<YOUR_AWS_ACCOUNT_ID>ID de cuenta de AWS de 12 dígitos123456789012
<YOUR_ENVIRONMENT>Valor de etiqueta de entornoprod, staging, dev
<YOUR_PROJECT>Valor de etiqueta de proyectoodin, ekb
# Ejecutar desde su nueva carpeta de entorno para encontrar todos los placeholders restantes
grep -r "<YOUR_" .

5.1 terragrunt.hcl — Configuración central del cluster

nano terragrunt.hcl
CampoPlaceholderNotas
cluster_name<YOUR_ENV_NAME>Debe coincidir con el nombre del cluster EKS
cluster_region<YOUR_AWS_REGION>Región de AWS
aws_account_id<YOUR_AWS_ACCOUNT_ID>ID de cuenta de 12 dígitos
vpc_cidr<YOUR_VPC_CIDR>ej., 192.168.0.0/16
availability_zones<YOUR_REGION>a/b/c3 AZs en su región
tags.Environment<YOUR_ENVIRONMENT>ej., prod
tags.Project<YOUR_PROJECT>ej., odin
aws_services.amazon_mq.rabbitmq.username<YOUR_RABBITMQ_USERNAME>Nombre de usuario admin de RabbitMQ (solo cuando ENABLE_AWS_SERVICES=true)
aws_services.amazon_mq.rabbitmq.password<YOUR_RABBITMQ_PASSWORD>Mínimo 12 caracteres; debe incluir mayúsculas, minúsculas, dígitos y caracteres especiales

5.2 state/terragrunt.hcl — Bucket de estado S3

nano state/terragrunt.hcl
CampoPlaceholderNotas
bucket_nameodin-terraform-state-<YOUR_ENV_NAME>Debe ser único globalmente
region<YOUR_AWS_REGION>Misma región que el cluster

5.3 values/infrastructure.yaml — AWS Load Balancer Controller

Obtenga el ID de la VPC después de que se cree el cluster EKS antes de desplegar el AWS Load Balancer Controller.
# Obtener ID de VPC después de crear el cluster EKS
aws eks describe-cluster --name <YOUR_ENV_NAME> \
  --query "cluster.resourcesVpcConfig.vpcId" --output text
nano values/infrastructure.yaml
CampoPlaceholderNotas
clusterName<YOUR_ENV_NAME>Nombre del cluster EKS
region<YOUR_AWS_REGION>Región de AWS
vpcId<YOUR_VPC_ID>Requerido antes del despliegue ALB
serviceAccount.annotations.eks.amazonaws.com/role-arn<YOUR_AWS_ACCOUNT_ID>, <YOUR_ENV_NAME>Rol IAM para el controlador ALB

5.4 values/karpenter-values.yaml — Controlador Karpenter

Obtenga el endpoint del cluster EKS después de que se cree el cluster EKS y antes de desplegar Karpenter.
# Obtener endpoint del cluster después de crear el cluster EKS
aws eks describe-cluster --name <YOUR_ENV_NAME> \
  --query "cluster.endpoint" --output text
nano values/karpenter-values.yaml
CampoPlaceholderNotas
serviceAccount.annotations.eks.amazonaws.com/role-arn<YOUR_AWS_ACCOUNT_ID>, <YOUR_ENV_NAME>Rol IAM para Karpenter
env.CLUSTER_NAME<YOUR_ENV_NAME>Nombre del cluster EKS
env.CLUSTER_ENDPOINT<YOUR_EKS_CLUSTER_ENDPOINT>Requerido antes del despliegue Karpenter
settings.aws.defaultInstanceProfile<YOUR_ENV_NAME>Perfil de instancia de nodos Karpenter

5.5 values/karpenter-nodeclasses.yaml — Clases de nodos Karpenter

nano values/karpenter-nodeclasses.yaml
CampoPlaceholderNotas
Todas las etiquetas kubernetes.io/cluster/<YOUR_ENV_NAME><YOUR_ENV_NAME>Etiqueta de cluster para selectores de subred/SG
Nombre de cluster de bootstrap en user_data<YOUR_ENV_NAME>Script de bootstrap del nodo
tags.Environment<YOUR_ENVIRONMENT>ej., prod
tags.Project<YOUR_PROJECT>ej., odin

5.6 values/aws-ebs-csi-driver.yaml — Controlador EBS CSI

nano values/aws-ebs-csi-driver.yaml
CampoPlaceholderNotas
controller.serviceAccount.annotations.eks.amazonaws.com/role-arn<YOUR_AWS_ACCOUNT_ID>, <YOUR_ENV_NAME>Rol IAM para el controlador EBS CSI
node.serviceAccount.annotations.eks.amazonaws.com/role-arn<YOUR_AWS_ACCOUNT_ID>, <YOUR_ENV_NAME>Rol IAM para el nodo EBS CSI
controller.env.AWS_DEFAULT_REGION<YOUR_AWS_REGION>Región de AWS
controller.env.AWS_REGION<YOUR_AWS_REGION>Región de AWS
node.env.AWS_DEFAULT_REGION<YOUR_AWS_REGION>Región de AWS
node.env.AWS_REGION<YOUR_AWS_REGION>Región de AWS

5.7 values/karpenter.yaml — Karpenter NodePools

nano values/karpenter.yaml
CampoPlaceholderNotas
*.labels.Environment<YOUR_ENVIRONMENT>Aplicado a todas las etiquetas de NodePool
*.requirements topology.kubernetes.io/zone["<YOUR_REGION>a", "<YOUR_REGION>b", "<YOUR_REGION>c"]AZs para todos los NodePools
Los nombres de clases de nodo (general, compute-intensive, memory-intensive, gpu, database) deben coincidir con las entradas en karpenter-nodeclasses.yaml.

5.8 values/keda.yaml — KEDA Autoscaler

No se requieren placeholders específicos del entorno. Los límites de recursos y los conteos de réplicas están preconfigurados con valores predeterminados razonables. Revise y ajuste si es necesario.

5.9 values/supabase.yaml — Aplicación Supabase (solo si ENABLE_SUPABASE=true)

Todas las claves a continuación deben generarse de forma consistente y compartirse con ha-supabase-db.yaml. Generarlas una vez y usar los mismos valores en ambos archivos.
# Generar secreto JWT
openssl rand -hex 32
 
# Generar JWTs de anon/service role (requiere Supabase CLI)
brew install supabase/tap/supabase
supabase gen-keys
 
# Generar contraseñas y tokens
openssl rand -hex 24       # para contraseñas
openssl rand -base64 64    # para secretKeyBase
nano values/supabase.yaml
CampoPlaceholderNotas
secret.jwt.anonKey<YOUR_SUPABASE_ANON_KEY>Debe coincidir con anonKey en ha-supabase-db.yaml
secret.jwt.serviceKey<YOUR_SUPABASE_SERVICE_ROLE_KEY>Debe coincidir con serviceRoleKey en ha-supabase-db.yaml
secret.jwt.secret<YOUR_SUPABASE_JWT_SECRET>Debe coincidir con jwtSecret en ha-supabase-db.yaml
secret.db.password<YOUR_SUPABASE_DB_PASSWORD>Debe coincidir con postgresPassword en ha-supabase-db.yaml
secret.analytics.publicAccessToken<YOUR_SUPABASE_ANALYTICS_PUBLIC_TOKEN>Token interno de Logflare
secret.analytics.privateAccessToken<YOUR_SUPABASE_ANALYTICS_PRIVATE_TOKEN>Token interno de Logflare
secret.dashboard.username<YOUR_SUPABASE_DASHBOARD_USERNAME>Acceso al UI de Studio
secret.dashboard.password<YOUR_SUPABASE_DASHBOARD_PASSWORD>Acceso al UI de Studio
secret.realtime.secretKeyBase<YOUR_SUPABASE_REALTIME_SECRET_KEY_BASE>Clave secreta Phoenix
secret.meta.cryptoKey<YOUR_SUPABASE_META_CRYPTO_KEY>openssl rand -hex 32
secret.s3.keyId<YOUR_MINIO_KEY_ID>Debe coincidir con secret.minio.user (openssl rand -hex 16)
secret.s3.accessKey<YOUR_MINIO_ACCESS_KEY>Debe coincidir con secret.minio.password (openssl rand -hex 32)
secret.minio.user<YOUR_MINIO_KEY_ID>Mismo valor que secret.s3.keyId
secret.minio.password<YOUR_MINIO_ACCESS_KEY>Mismo valor que secret.s3.accessKey

5.10 values/ha-supabase-db.yaml — DB HA de Supabase (solo si ENABLE_HA_SUPABASE_DB=true)

Los secretos aquí deben coincidir con supabase.yaml. Use los mismos valores generados para postgresPassword, jwtSecret, anonKey y serviceRoleKey.
nano values/ha-supabase-db.yaml
CampoPlaceholderNotas
secrets.inline.postgresPassword<YOUR_SUPABASE_DB_PASSWORD>Debe coincidir con secret.db.password en supabase.yaml
secrets.inline.authenticatorPassword<YOUR_SUPABASE_DB_PASSWORD>Debe ser idéntico a postgresPassword
secrets.inline.pgbouncerPassword<YOUR_SUPABASE_DB_PASSWORD>Debe ser idéntico a postgresPassword
secrets.inline.jwtSecret<YOUR_SUPABASE_JWT_SECRET>Debe coincidir con secret.jwt.secret en supabase.yaml
secrets.inline.anonKey<YOUR_SUPABASE_ANON_KEY>Debe coincidir con secret.jwt.anonKey en supabase.yaml
secrets.inline.serviceRoleKey<YOUR_SUPABASE_SERVICE_ROLE_KEY>Debe coincidir con secret.jwt.serviceKey en supabase.yaml
La clase de almacenamiento (ebs-csi-gp2), el conteo de instancias y los límites de recursos están preconfigurados. Ajuste postgres.storage.size y postgres.walStorage.size para su volumen de datos esperado.

5.11 values/cloudnative-pg.yaml — Operador CloudNativePG (solo si ENABLE_CNPG=true)

No se requieren placeholders específicos del entorno. Esto despliega solo el controlador del operador CNPG. Los valores predeterminados (3 réplicas, límites de recursos) son adecuados para la mayoría de los entornos.

5.12 values/odin-services.yaml — Servicios de aplicación Odin

Los endpoints de Redis y RabbitMQ solo están disponibles después de que Terraform cree esos recursos de AWS. Los ARNs de certificados deben provisionarse en ACM antes del despliegue.
nano values/odin-services.yaml
Configuraciones generales:
CampoPlaceholderNotas
server<YOUR_WEB_DOMAIN>Dominio web principal
toolkitEncryptionKey<YOUR_TOOLKIT_ENCRYPTION_KEY>Generar: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
Supabase (dataServiceConfig) — autoalojado (ENABLE_SUPABASE=true):
CampoPlaceholderFuente
supabase.projectUrlhttp://supabase-kong:8000Fijo — Supabase Kong interno
supabase.key<YOUR_SUPABASE_SERVICE_ROLE_KEY>Igual que secret.jwt.serviceKey en supabase.yaml
supabase.postgres.userpostgresFijo para autoalojado
supabase.postgres.hostha-supabase-db-postgres-pooler-rw.ha-supabase-db.svc.cluster.localFijo — Servicio de Pool de DB dentro del clúster
supabase.postgres.password<YOUR_SUPABASE_DB_PASSWORD>Igual que secret.db.password en supabase.yaml
supabase.projectId(dejar vacío)No se usa en modo autoalojado
Supabase (dataServiceConfig) — Supabase Cloud (ENABLE_SUPABASE=false):
CampoPlaceholderFuente
supabase.projectUrl<YOUR_SUPABASE_PROJECT_URL>Panel de Supabase → Configuración del Proyecto → API
supabase.key<YOUR_SUPABASE_SERVICE_ROLE_KEY>Panel de Supabase → API → clave service_role
supabase.postgres.user<YOUR_SUPABASE_DB_USER>Panel de Supabase → Configuración del Proyecto → Base de datos
supabase.postgres.host<YOUR_SUPABASE_DB_HOST>Panel de Supabase → Base de datos (ej., aws-0-eu-west-2.pooler.supabase.com)
supabase.postgres.password<YOUR_SUPABASE_DB_PASSWORD>Panel de Supabase → Configuración del Proyecto → Base de datos
supabase.projectId<YOUR_SUPABASE_PROJECT_ID>De la URL de su proyecto Supabase
Redis:
CampoPlaceholderNotas
redis.urlrediss://<YOUR_REDIS_HOST>:6379?ssl_cert_reqs=noneDespués de que Terraform cree ElastiCache
redis.host<YOUR_REDIS_HOST>Endpoint principal de ElastiCache
# Obtener endpoint de Redis después de terraform apply
aws elasticache describe-cache-clusters \
  --show-cache-node-info \
  --query "CacheClusters[?starts_with(CacheClusterId,'<YOUR_ENV_NAME>')].CacheNodes[0].Endpoint.Address" \
  --output text
RabbitMQ:
CampoPlaceholderNotas
rabbitmq.urlamqps://<YOUR_RABBITMQ_USERNAME>:<YOUR_RABBITMQ_PASSWORD>@<YOUR_RABBITMQ_HOST>:5671Después de que Terraform cree AmazonMQ
rabbitmq.host<YOUR_RABBITMQ_HOST>Endpoint del broker AmazonMQ
rabbitmq.username<YOUR_RABBITMQ_USERNAME>Establecido en terragrunt.hcl
rabbitmq.password<YOUR_RABBITMQ_PASSWORD>Establecido en terragrunt.hcl
# Obtener endpoint de RabbitMQ después de terraform apply
aws mq list-brokers \
  --query "BrokerSummaries[?BrokerName=='odin-rabbitmq'].BrokerId" --output text | \
  xargs -I{} aws mq describe-broker --broker-id {} \
  --query "BrokerInstances[0].Endpoints[0]" --output text
SSL / ARNs de Certificados:
CampoPlaceholderNotas
ssl.services.web.domain<YOUR_WEB_DOMAIN>ej., app.example.com
ssl.services.web.certificateArn<YOUR_WEB_CERTIFICATE_ARN>ARN del certificado ACM
ssl.services.fastapiBackend.domain<YOUR_API_DOMAIN>ej., api-app.example.com
ssl.services.fastapiBackend.certificateArn<YOUR_API_CERTIFICATE_ARN>ARN del certificado ACM
ssl.services.automator.domain<YOUR_AUTOMATOR_DOMAIN>ej., automations-app.example.com
ssl.services.automator.certificateArn<YOUR_AUTOMATOR_CERTIFICATE_ARN>ARN del certificado ACM
ssl.services.supabase.domain<YOUR_SUPABASE_DOMAIN>ej., supabase-app.example.com
ssl.services.supabase.certificateArn<YOUR_SUPABASE_CERTIFICATE_ARN>ARN del certificado ACM
# Listar certificados ACM en su región
aws acm list-certificates --region <YOUR_AWS_REGION> \
  --query "CertificateSummaryList[*].[DomainName,CertificateArn]" --output table
Claves Supabase del frontend web — autoalojado (ENABLE_SUPABASE=true):
CampoPlaceholderFuente
web.supabase.urlhttps://<YOUR_SUPABASE_DOMAIN>URL externa enrutada vía ingreso ALB
web.supabase.anonKey<YOUR_SUPABASE_ANON_KEY>Igual que secret.jwt.anonKey en supabase.yaml
web.supabase.serviceRoleKey<YOUR_SUPABASE_SERVICE_ROLE_KEY>Igual que secret.jwt.serviceKey en supabase.yaml
web.supabase.clientanonKey<YOUR_SUPABASE_SERVICE_ROLE_KEY>Igual que secret.jwt.serviceKey en supabase.yaml
Claves Supabase del frontend web — Supabase Cloud (ENABLE_SUPABASE=false):
CampoPlaceholderFuente
web.supabase.url<YOUR_SUPABASE_PROJECT_URL>Panel de Supabase → Configuración del Proyecto → API
web.supabase.anonKey<YOUR_SUPABASE_ANON_KEY>Panel de Supabase → API → clave anon
web.supabase.serviceRoleKey<YOUR_SUPABASE_SERVICE_ROLE_KEY>Panel de Supabase → API → clave service_role
web.supabase.clientanonKey<YOUR_SUPABASE_CLIENT_ANON_KEY>Igual que la clave service_role

5.13 values/signoz.yaml — Observabilidad SigNoz (solo si ENABLE_SIGNOZ=true)

nano values/signoz.yaml
CampoPlaceholderNotas
global.clusterName<YOUR_ENV_NAME>Nombre del cluster EKS
signoz.ingress.annotations.alb.ingress.kubernetes.io/certificate-arn<YOUR_AWS_REGION>, <YOUR_AWS_ACCOUNT_ID>, <YOUR_SIGNOZ_CERTIFICATE_ID>Certificado ACM para SigNoz
signoz.ingress.hosts[0].host<YOUR_SIGNOZ_DOMAIN>ej., signoz-app.example.com

5.14 values/signoz-k8s-infra.yaml — Métricas K8s de SigNoz (solo si ENABLE_SIGNOZ=true)

nano values/signoz-k8s-infra.yaml
CampoPlaceholderNotas
global.clusterName<YOUR_ENV_NAME>Nombre del cluster EKS para etiquetado de métricas
El endpoint del colector OTel (signoz-otel-collector.monitoring.svc.cluster.local:4317) está preconfigurado asumiendo que tanto SigNoz como k8s-infra se despliegan en el namespace monitoring. No se necesita cambio a menos que use un nombre de release personalizado.

Recordatorio del Orden de Despliegue

Algunos valores solo están disponibles después de que cierta infraestructura se haya desplegado. Siga este orden:
  1. Antes de cualquier despliegue — Establecer: <YOUR_ENV_NAME>, <YOUR_AWS_REGION>, <YOUR_AWS_ACCOUNT_ID>, <YOUR_ENVIRONMENT>, <YOUR_PROJECT>, <YOUR_VPC_CIDR>, todos los nombres de dominio, todos los ARNs de certificados, todos los valores de Supabase, <YOUR_TOOLKIT_ENCRYPTION_KEY>, usuario/contraseña de RabbitMQ
  2. Después de crear el cluster EKS — Establecer: <YOUR_VPC_ID> (infrastructure.yaml), <YOUR_EKS_CLUSTER_ENDPOINT> (karpenter-values.yaml)
  3. Después de terraform apply para servicios AWS — Establecer: <YOUR_REDIS_HOST>, <YOUR_RABBITMQ_HOST> (odin-services.yaml)

Paso 6: Verifique que No Queden Placeholders

grep -r "<YOUR_" . --include="*.hcl" --include="*.yaml"
La salida esperada debería estar vacía, o contener solo referencias a recursos a punto de ser creados (VPC, Redis, MQ, EKS). Si quedan placeholders, consulte las subsecciones del Paso 5 anteriormente. Lista de verificación de archivos:
ArchivoPasoRequerido
terragrunt.hcl5.1Siempre
state/terragrunt.hcl5.2Siempre
values/infrastructure.yaml5.3Siempre
values/karpenter-values.yaml5.4Siempre
values/karpenter-nodeclasses.yaml5.5Siempre
values/karpenter.yaml5.7Siempre
values/keda.yaml5.8Siempre
values/aws-ebs-csi-driver.yaml5.6Siempre
values/odin-services.yaml5.12Siempre
values/cloudnative-pg.yaml5.11Solo si ENABLE_CNPG=true
values/ha-supabase-db.yaml5.10Solo si ENABLE_HA_SUPABASE_DB=true
values/supabase.yaml5.9Solo si ENABLE_SUPABASE=true
values/signoz.yaml5.13Solo si ENABLE_SIGNOZ=true
values/signoz-k8s-infra.yaml5.14Solo si ENABLE_SIGNOZ=true

Fase 1: Configuración de Gestión de Estado

Propósito: Creación del bucket S3 para el estado de Terraform. Cada módulo de gestión de estado del entorno crea un bucket S3 con el patrón odin-terraform-state-{environment-name}, configura cifrado, versionado y bloqueo de acceso público, y usa estado local para el módulo de estado en sí (patrón de bootstrap).
cd terragrunt/environments/{your-env-name}/state
terragrunt init
terragrunt plan
terragrunt apply

Fase 2: Despliegue de Infraestructura EKS

Propósito: Red central (VPC, subredes, NAT gateway), roles y políticas IAM, cluster EKS y grupos de nodos administrados.

2.1 Ejecución de Prueba — Infraestructura EKS

Infraestructura Central
cd terragrunt/environments/your-env-name
terragrunt plan -target="aws_vpc.main" \
  -target="aws_internet_gateway.main" \
  -target="aws_subnet.public" \
  -target="aws_subnet.private" \
  -target="aws_eip.nat" \
  -target="aws_nat_gateway.main" \
  -target="aws_route_table.public" \
  -target="aws_route_table.private" \
  -target="aws_route_table_association.public" \
  -target="aws_route_table_association.private"
Roles y Políticas IAM
terragrunt plan -target="aws_iam_role.cluster" \
  -target="aws_iam_role_policy_attachment.cluster_AmazonEKSClusterPolicy" \
  -target="aws_iam_openid_connect_provider.eks" \
  -target="aws_iam_role.node" \
  -target="aws_iam_role_policy_attachment.node_AmazonEKSWorkerNodePolicy" \
  -target="aws_iam_role_policy_attachment.node_AmazonEKS_CNI_Policy" \
  -target="aws_iam_role_policy_attachment.node_AmazonEC2ContainerRegistryReadOnly"
Cluster EKS y Grupos de Nodos
terragrunt plan -target="aws_eks_cluster.main" \
  -target="aws_eks_node_group.main" \
  -target="kubernetes_secret.regcred"

Usando un Registro Docker Personalizado / Privado

Por defecto, las imágenes de EKB se descargan de Docker Hub usando un secreto llamado regcred. Si el cliente aloja imágenes en un registro diferente, siga estos pasos antes de desplegar odin-services. Paso 1 — Crear el imagePullSecret en el namespace destino
# Registro privado genérico (Docker Hub, Quay, autoalojado, etc.)
kubectl create secret docker-registry regcred \
  --namespace default \
  --docker-server=<YOUR_REGISTRY_HOST> \
  --docker-username=<YOUR_REGISTRY_USERNAME> \
  --docker-password=<YOUR_REGISTRY_PASSWORD> \
  --docker-email=<YOUR_EMAIL>
 
# AWS ECR — el token expira cada 12h; actualizar mediante CronJob o usar la caché de paso de ECR
aws ecr get-login-password --region <YOUR_AWS_REGION> | \
  kubectl create secret docker-registry regcred \
    --namespace default \
    --docker-server=<YOUR_AWS_ACCOUNT_ID>.dkr.ecr.<YOUR_AWS_REGION>.amazonaws.com \
    --docker-username=AWS \
    --docker-password-stdin
Paso 2 — Establecer el nombre del secreto en values/odin-services.yaml
# values/odin-services.yaml
imagePullSecrets:
  - name: regcred          # debe coincidir con el nombre del secreto creado anteriormente
  # - name: customer-registry-secret  # agregar registros adicionales si es necesario
Paso 3 — Actualizar las referencias de imagen
web:
  image: <YOUR_REGISTRY_HOST>/<YOUR_ORG>/web:<TAG>
 
fastapiBackend:
  image: <YOUR_REGISTRY_HOST>/<YOUR_ORG>/server:<TAG>
Paso 4 — Verificar el acceso de descarga antes del despliegue completo
kubectl run registry-test \
  --image=<YOUR_REGISTRY_HOST>/<YOUR_ORG>/web:<TAG> \
  --overrides='{"spec":{"imagePullSecrets":[{"name":"regcred"}]}}' \
  --restart=Never --rm -it -- echo "Pull successful"

2.2 Desplegar Infraestructura EKS

Paso 1: Infraestructura Central
cd terragrunt/environments/your-env-name
terragrunt apply -target="aws_vpc.main" \
  -target="aws_internet_gateway.main" \
  -target="aws_subnet.public" \
  -target="aws_subnet.private" \
  -target="aws_eip.nat" \
  -target="aws_nat_gateway.main" \
  -target="aws_route_table.public" \
  -target="aws_route_table.private" \
  -target="aws_route_table_association.public" \
  -target="aws_route_table_association.private"
Después de este paso, actualice vpcId en values/infrastructure.yaml antes de desplegar el AWS Load Balancer Controller.
Paso 2: Cluster EKS, Roles y Políticas IAM
terragrunt apply -target="aws_iam_role.cluster" \
  -target="aws_iam_role_policy_attachment.cluster_AmazonEKSClusterPolicy" \
  -target="aws_iam_openid_connect_provider.eks" \
  -target="aws_iam_role.node" \
  -target="aws_iam_role_policy_attachment.node_AmazonEKSWorkerNodePolicy" \
  -target="aws_iam_role_policy_attachment.node_AmazonEKS_CNI_Policy" \
  -target="aws_iam_role_policy_attachment.node_AmazonEC2ContainerRegistryReadOnly"
Paso 3: Grupos de Nodos y Add-ons
terragrunt apply -target="aws_eks_cluster.main" \
  -target="aws_eks_node_group.main" \
  -target="kubernetes_secret.regcred"
Después de este paso, actualice CLUSTER_ENDPOINT en values/karpenter-values.yaml antes de desplegar Karpenter.
Verificar Conectividad del Cluster EKS
aws eks update-kubeconfig --region $AWS_REGION --name $CLUSTER_NAME
 
kubectl cluster-info
kubectl get nodes
kubectl get secret regcred -n default

Fase 3: Almacenamiento y Balanceo de Carga

Propósito: Controlador EBS CSI para volúmenes persistentes, AWS Load Balancer Controller ejecutándose en el grupo de nodos administrado.

3.1 Ejecución de Prueba — Almacenamiento y Balanceo de Carga

Controlador EBS CSI
cd terragrunt/environments/your-env-name
terragrunt plan -target="aws_iam_role.ebs_csi_driver" \
  -target="aws_iam_role_policy_attachment.ebs_csi_driver" \
  -target="helm_release.ebs_csi_driver"
AWS Load Balancer Controller
terragrunt plan -target="aws_iam_role.aws_load_balancer_controller" \
  -target="aws_iam_role_policy_attachment.aws_load_balancer_controller" \
  -target="aws_iam_policy.aws_load_balancer_controller" \
  -target="helm_release.infrastructure"

3.2 Desplegar Almacenamiento y Balanceo de Carga

Paso 1: Controlador EBS CSI
cd terragrunt/environments/your-env-name
terragrunt apply -target="aws_iam_role.ebs_csi_driver" \
  -target="aws_iam_role_policy_attachment.ebs_csi_driver" \
  -target="helm_release.ebs_csi_driver"
Verificación
helm list -n kube-system | grep ebs
kubectl get pods -n kube-system | grep ebs-csi
kubectl get storageclass
aws iam get-role --role-name $CLUSTER_NAME-ebs-csi-driver-role --region $AWS_REGION
kubectl get sa -n kube-system | grep ebs-csi
kubectl describe sa ebs-csi-controller-sa -n kube-system
Paso 2: AWS Load Balancer Controller
terragrunt apply -target="aws_iam_role.aws_load_balancer_controller" \
  -target="aws_iam_role_policy_attachment.aws_load_balancer_controller" \
  -target="aws_iam_policy.aws_load_balancer_controller" \
  -target="helm_release.infrastructure"
Verificación
helm list -n infrastructure
kubectl get pods -n infrastructure | grep aws-load-balancer-controller
kubectl get sa -n infrastructure
kubectl describe sa aws-load-balancer-controller -n infrastructure
aws iam get-role --role-name $CLUSTER_NAME-aws-load-balancer-controller --region $AWS_REGION
kubectl logs -n infrastructure -l app.kubernetes.io/name=aws-load-balancer-controller
kubectl get ingressclass

Fase 4: Autoescalado con Karpenter

Propósito: Roles IAM para Karpenter, manejo de interrupciones Spot, controlador Karpenter y pools de nodos.

4.1 Ejecución de Prueba — Karpenter

Recursos IAM de Karpenter
cd terragrunt/environments/your-env-name
terragrunt plan -target="aws_iam_role.karpenter_controller" \
  -target="aws_iam_policy.karpenter_controller" \
  -target="aws_iam_role_policy_attachment.karpenter_controller" \
  -target="aws_iam_role.karpenter_node" \
  -target="aws_iam_role_policy_attachment.karpenter_node_AmazonEKSWorkerNodePolicy" \
  -target="aws_iam_role_policy_attachment.karpenter_node_AmazonEKS_CNI_Policy" \
  -target="aws_iam_role_policy_attachment.karpenter_node_AmazonEC2ContainerRegistryReadOnly" \
  -target="aws_iam_role_policy_attachment.karpenter_node_AmazonEBSCSIDriverPolicy" \
  -target="aws_iam_instance_profile.karpenter_node"
Rol de Servicio Vinculado de EC2 Spot (si las instancias Spot están habilitadas)
terragrunt plan -target="aws_iam_service_linked_role.ec2_spot[0]"
Interrupción Spot de Karpenter (si está habilitada en terragrunt.hcl)
terragrunt plan -target="aws_sqs_queue.karpenter_interruption_queue" \
  -target="aws_sqs_queue_policy.karpenter_interruption_queue" \
  -target="aws_cloudwatch_event_rule.karpenter_interruption" \
  -target="aws_cloudwatch_event_target.karpenter_interruption"
Charts Helm de Karpenter
terragrunt plan -target="helm_release.karpenter"
Karpenter NodePools y EC2NodeClasses
terragrunt plan -target="kubernetes_manifest.karpenter_nodepool" \
  -target="kubernetes_manifest.karpenter_nodeclass" \
  -target="kubernetes_config_map.aws_auth"
Puede aparecer un error esperado durante el plan: API did not recognize GroupVersionKind from manifest (CRD may not be installed). Esto es seguro de ignorar — Kubernetes valida los recursos contra la API en vivo durante el plan, antes de que se instalen los CRDs.

4.2 Desplegar Karpenter

Paso 1: Recursos IAM de Karpenter
cd terragrunt/environments/your-env-name
terragrunt apply -target="aws_iam_role.karpenter_controller" \
  -target="aws_iam_policy.karpenter_controller" \
  -target="aws_iam_role_policy_attachment.karpenter_controller" \
  -target="aws_iam_role.karpenter_node" \
  -target="aws_iam_role_policy_attachment.karpenter_node_AmazonEKSWorkerNodePolicy" \
  -target="aws_iam_role_policy_attachment.karpenter_node_AmazonEKS_CNI_Policy" \
  -target="aws_iam_role_policy_attachment.karpenter_node_AmazonEC2ContainerRegistryReadOnly" \
  -target="aws_iam_role_policy_attachment.karpenter_node_AmazonEBSCSIDriverPolicy" \
  -target="aws_iam_instance_profile.karpenter_node"
Verificación
aws iam get-role --role-name $CLUSTER_NAME-karpenter-controller --region $AWS_REGION
aws iam get-role --role-name $CLUSTER_NAME-karpenter-node --region $AWS_REGION
aws iam get-instance-profile --instance-profile-name $CLUSTER_NAME-karpenter-node --region $AWS_REGION
aws iam list-attached-role-policies --role-name $CLUSTER_NAME-karpenter-node --region $AWS_REGION
Paso 2: Rol de Servicio Vinculado de EC2 Spot (si las instancias Spot están habilitadas)
El rol de servicio vinculado de EC2 Spot es a nivel de cuenta (solo uno por cuenta de AWS) y debe existir antes de que Karpenter pueda lanzar instancias Spot.
Opción A: Dejar que Terraform lo cree (recomendado para nuevos despliegues)
terragrunt apply -target="aws_iam_service_linked_role.ec2_spot[0]"
Opción B: Importar si el rol ya existe
# Verificar si el rol existe
aws iam get-role --role-name AWSServiceRoleForEC2Spot --region $AWS_REGION
 
# Si no existe, crearlo manualmente
aws iam create-service-linked-role --aws-service-name spot.amazonaws.com --region $AWS_REGION
 
# Importar en Terraform (reemplazar ACCOUNT_ID con su ID de cuenta de AWS de 12 dígitos)
terragrunt import 'aws_iam_service_linked_role.ec2_spot[0]' \
  arn:aws:iam::ACCOUNT_ID:role/aws-service-role/spot.amazonaws.com/AWSServiceRoleForEC2Spot
Paso 3: Interrupción Spot de Karpenter (si está habilitada en terragrunt.hcl)
terragrunt apply -target="aws_sqs_queue.karpenter_interruption_queue" \
  -target="aws_sqs_queue_policy.karpenter_interruption_queue" \
  -target="aws_cloudwatch_event_rule.karpenter_interruption" \
  -target="aws_cloudwatch_event_target.karpenter_interruption"
Verificación
aws events describe-rule --name $CLUSTER_NAME-karpenter-interruption --region $AWS_REGION
aws events list-targets-by-rule --rule $CLUSTER_NAME-karpenter-interruption --region $AWS_REGION
aws sqs get-queue-url --queue-name $CLUSTER_NAME-karpenter-interruption-queue --region $AWS_REGION
Paso 4: Chart Helm de Karpenter
terragrunt apply -target="helm_release.karpenter"
Verificación
helm list -n kube-system | grep karpenter
kubectl get pods -n kube-system | grep karpenter
kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter
kubectl describe sa karpenter -n kube-system
Paso 5: Manifestos de Kubernetes de Karpenter
terragrunt apply -target="kubernetes_manifest.karpenter_nodepool" \
  -target="kubernetes_manifest.karpenter_nodeclass"
 
# Importar el ConfigMap aws-auth existente
# Nota: Use comillas para evitar que zsh interprete los corchetes como patrones glob
terragrunt import 'kubernetes_config_map.aws_auth[0]' kube-system/aws-auth
 
# Luego aplicar
terragrunt apply -target='kubernetes_config_map.aws_auth[0]'
Verificación
kubectl get nodepools -o wide
kubectl describe nodepool general
kubectl describe nodepool application
kubectl describe nodepool database
kubectl get ec2nodeclasses -o wide
kubectl get configmap aws-auth -n kube-system -o jsonpath='{.data.mapRoles}' | grep karpenter-node
kubectl get nodepools -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.conditions[?(@.type=="Ready")].status}{"\n"}{end}'
kubectl get nodes -l karpenter.sh/nodepool --show-labels
kubectl get events -n kube-system --field-selector involvedObject.name=karpenter --sort-by='.lastTimestamp'

Fase 5: Autoescalado con KEDA

Propósito: KEDA para autoescalado a nivel de aplicación.

5.1 Ejecución de Prueba — KEDA

cd terragrunt/environments/your-env-name
terragrunt plan -target="helm_release.keda"

5.2 Desplegar KEDA

cd terragrunt/environments/your-env-name
terragrunt apply -target="helm_release.keda"
Verificación
helm list -n keda
kubectl get pods -n keda
kubectl get deployment -n keda
kubectl get crd | grep keda
kubectl get validatingwebhookconfigurations | grep keda
kubectl get svc -n keda

Fase 6: Servicios de Datos

Propósito: Supabase (base de datos), ElastiCache (Redis), RabbitMQ (cola de mensajes).
Despliegue primero el operador CloudNativePG, luego el clúster HA de Supabase DB, y luego la aplicación Supabase. El clúster de DB debe estar listo antes de que Supabase inicie.

6.1 Ejecución de Prueba — Servicios de Datos

Paso 1: Operador CloudNativePG (si está habilitado)
cd terragrunt/environments/your-env-name
ENABLE_CNPG=true terragrunt plan \
  --target='helm_release.additional_charts["cloudnative-pg"]'
Paso 2: DB HA de Supabase (si está habilitado)
ENABLE_HA_SUPABASE_DB=true terragrunt plan \
  --target='helm_release.additional_charts["ha-supabase-db"]'
Paso 3: Aplicación Supabase (si está habilitado)
if [ "${ENABLE_SUPABASE:-false}" = "true" ]; then
  ENABLE_SUPABASE=true terragrunt plan \
    --target='helm_release.supabase[0]'
fi
Paso 4: Servicios AWS — ElastiCache y RabbitMQ (si está habilitado)
if [ "${ENABLE_AWS_SERVICES:-false}" = "true" ]; then
  terragrunt plan -target="aws_elasticache_subnet_group.redis" \
    -target="aws_security_group.redis" \
    -target="aws_elasticache_replication_group.redis" \
    -target="aws_security_group.rabbitmq" \
    -target="aws_mq_broker.rabbitmq"
fi

6.2 Desplegar Servicios de Datos

Paso 1: Operador CloudNativePG (si está habilitado)
cd terragrunt/environments/your-env-name
ENABLE_CNPG=true terragrunt apply --auto-approve \
  --target='helm_release.additional_charts["cloudnative-pg"]'
Paso 2: DB HA de Supabase (si está habilitado)
ENABLE_HA_SUPABASE_DB=true terragrunt apply --auto-approve \
  --target='helm_release.additional_charts["ha-supabase-db"]'
Verificar el pooler PgBouncer y credenciales después del despliegue:
kubectl get svc -n ha-supabase-db | grep pooler
kubectl get secrets -n ha-supabase-db
kubectl get secret ha-supabase-db-authenticator-credentials -n ha-supabase-db \
  -o jsonpath='{.data.username}' | base64 -d && echo ""
Use el ClusterIP del pooler (o EXTERNAL-IP si es LoadBalancer) como el valor de SUPABASE_POSTGRES_HOST en values/odin-services.yaml y como secret.db.postgresHost en values/supabase.yaml. Paso 3: Aplicación Supabase (si está habilitado) Todos los pods del servicio Supabase se ejecutan exclusivamente en el NodePool application de Karpenter (solo bajo demanda) para prevenir interrupciones Spot.
if [ "${ENABLE_SUPABASE:-false}" = "true" ]; then
  ENABLE_SUPABASE=true terragrunt apply --auto-approve \
    --target='helm_release.supabase[0]'
fi
Paso 4: Servicios AWS — ElastiCache y RabbitMQ (si está habilitado)
if [ "${ENABLE_AWS_SERVICES:-false}" = "true" ]; then
  terragrunt apply \
    -target="aws_elasticache_subnet_group.redis" \
    -target="aws_security_group.redis" \
    -target="aws_elasticache_replication_group.redis" \
    -target="aws_security_group.rabbitmq" \
    -target="aws_mq_broker.rabbitmq"
fi
Verificación
# Obtener detalles de conexión de las salidas de Terraform
terragrunt output elasticache_endpoint
terragrunt output elasticache_port
terragrunt output rabbitmq_endpoint
terragrunt output rabbitmq_port
 
# Probar conectividad Redis desde el clúster EKS
kubectl run redis-test --image=redis:7-alpine --restart=Never -- \
  sh -c "redis-cli -h <redis-endpoint> -p 6379 --tls --insecure ping && echo 'Redis connection successful'"
kubectl logs redis-test
kubectl delete pod redis-test
 
# Verificar estado de cifrado de Redis
aws elasticache describe-replication-groups \
  --replication-group-id $CLUSTER_NAME-redis \
  --region $AWS_REGION \
  --query 'ReplicationGroups[0].{AtRestEncryption:AtRestEncryptionEnabled,TransitEncryption:TransitEncryptionEnabled}'
Antes de desplegar los Servicios Odin, actualice values/odin-services.yaml con el endpoint de Redis, el endpoint de RabbitMQ y todos los ARNs de certificados obtenidos en esta fase.

Fase 7: Servicios Odin

Propósito: Despliegue de la aplicación vía Helm.
Antes de desplegar, reduzca temporalmente las réplicas de fastapiBackend a una sola para la ejecución inicial de migración de base de datos — establezca replicaCount: 1, workers: 1 y keda.minReplicas: 1. Una vez que la migración se complete exitosamente, revierta estos valores a sus valores de producción predeterminados antes de volver a desplegar.

7.1 Ejecución de Prueba — Servicios Odin

cd terragrunt/environments/your-env-name
terragrunt plan -target="helm_release.odin_services"

7.2 Desplegar Servicios Odin

cd terragrunt/environments/your-env-name
terragrunt apply -target="helm_release.odin_services"
Verificación
kubectl get pods
kubectl get ingress  # Agregue los endpoints ALB a su proveedor de DNS

Fase 8: Observabilidad con SigNoz

Propósito: Monitoreo de logs y métricas.

8.1 Ejecución de Prueba — Charts de SigNoz

cd terragrunt/environments/your-env-name
terragrunt plan -target='helm_release.additional_charts["signoz"]'
terragrunt plan -target='helm_release.additional_charts["k8s-infra"]'

8.2 Desplegar Charts de SigNoz

cd terragrunt/environments/your-env-name
terragrunt apply -target='helm_release.additional_charts["signoz"]'
terragrunt apply -target='helm_release.additional_charts["k8s-infra"]'
Verificación
kubectl get pods -n monitoring
kubectl get ingress -n monitoring  # Agregue los endpoints ALB a su proveedor de DNS

Fase 9: Despliegue Final

9.1 Despliegue Completo

cd terragrunt/environments/your-env-name
terragrunt apply
Este apply final maneja cualquier recurso restante no explícitamente dirigido en fases anteriores.

9.2 Verificar Despliegue

# Actualizar kubeconfig
aws eks update-kubeconfig --region us-east-2 --name your-env-name
 
# Verificar estado del cluster
kubectl get nodes
kubectl get pods --all-namespaces
 
# Verificar Karpenter
kubectl get pods -n kube-system -l app.kubernetes.io/name=karpenter
kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter
 
# Verificar AWS Load Balancer Controller
kubectl get pods -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller
 
# Verificar KEDA
kubectl get pods -n keda
 
# Verificar Servicios Odin
kubectl get pods -n default
kubectl get services -n default
kubectl get ingress -n default
 
# Verificar todos los releases Helm
helm list --all-namespaces

Solución de Problemas

Problemas de bloqueo de estado
terragrunt force-unlock <lock-id>
Karpenter no funciona
kubectl describe nodes
kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter
Problemas con el Balanceador de Carga
kubectl describe ingress -n default
kubectl logs -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller
Problemas con charts Helm
helm status <release-name> -n <namespace>
helm rollback <release-name> <revision> -n <namespace>

Limpieza

# Destruir infraestructura
cd terragrunt/environments/your-env-name
terragrunt destroy -auto-approve
 
# Destruir bucket de estado (usar con precaución)
cd terragrunt/environments/your-env-name/state
terragrunt destroy -auto-approve

Monitoreo y Registro

# Recursos de AWS
aws eks describe-cluster --name your-env-name --region us-east-2
aws ec2 describe-instances --filters "Name=tag:kubernetes.io/cluster/your-env-name,Values=owned"
 
# Recursos de Kubernetes
kubectl top nodes
kubectl top pods --all-namespaces
kubectl get events --sort-by=.metadata.creationTimestamp

Referencia Rápida — Todos los Comandos de Despliegue

# Fase 1: Gestión de Estado
cd terragrunt/environments/your-env-name/state
terragrunt apply
 
# Fase 2: Infraestructura EKS
cd terragrunt/environments/your-env-name
 
terragrunt apply -target="aws_vpc.main" -target="aws_internet_gateway.main" \
  -target="aws_subnet.public" -target="aws_subnet.private" -target="aws_eip.nat" \
  -target="aws_nat_gateway.main" -target="aws_route_table.public" \
  -target="aws_route_table.private" -target="aws_route_table_association.public" \
  -target="aws_route_table_association.private" -auto-approve
 
terragrunt apply -target="aws_iam_role.cluster" \
  -target="aws_iam_role_policy_attachment.cluster_AmazonEKSClusterPolicy" \
  -target="aws_iam_openid_connect_provider.eks" -target="aws_iam_role.node" \
  -target="aws_iam_role_policy_attachment.node_AmazonEKSWorkerNodePolicy" \
  -target="aws_iam_role_policy_attachment.node_AmazonEKS_CNI_Policy" \
  -target="aws_iam_role_policy_attachment.node_AmazonEC2ContainerRegistryReadOnly" \
  -auto-approve
 
terragrunt apply -target="aws_eks_cluster.main" \
  -target="aws_eks_node_group.main" -target="kubernetes_secret.regcred" -auto-approve
 
# Fase 3: Almacenamiento y Balanceo de Carga
terragrunt apply -target="aws_iam_role.ebs_csi_driver" \
  -target="aws_iam_role_policy_attachment.ebs_csi_driver" \
  -target="helm_release.ebs_csi_driver" -auto-approve
 
terragrunt apply -target="aws_iam_role.aws_load_balancer_controller" \
  -target="aws_iam_role_policy_attachment.aws_load_balancer_controller" \
  -target="aws_iam_policy.aws_load_balancer_controller" \
  -target="helm_release.infrastructure" -auto-approve
 
# Fase 4: Autoescalado con Karpenter
terragrunt apply -target="aws_iam_role.karpenter_controller" \
  -target="aws_iam_policy.karpenter_controller" \
  -target="aws_iam_role_policy_attachment.karpenter_controller" \
  -target="aws_iam_role.karpenter_node" \
  -target="aws_iam_role_policy_attachment.karpenter_node_AmazonEKSWorkerNodePolicy" \
  -target="aws_iam_role_policy_attachment.karpenter_node_AmazonEKS_CNI_Policy" \
  -target="aws_iam_role_policy_attachment.karpenter_node_AmazonEC2ContainerRegistryReadOnly" \
  -target="aws_iam_role_policy_attachment.karpenter_node_AmazonEBSCSIDriverPolicy" \
  -target="aws_iam_instance_profile.karpenter_node" -auto-approve
 
# Manejo de interrupciones Spot (si spot_interruption_handling = true)
terragrunt apply -target="aws_sqs_queue.karpenter_interruption_queue" \
  -target="aws_sqs_queue_policy.karpenter_interruption_queue" \
  -target="aws_cloudwatch_event_rule.karpenter_interruption" \
  -target="aws_cloudwatch_event_target.karpenter_interruption" -auto-approve
 
terragrunt apply -target="helm_release.karpenter" -auto-approve
 
terragrunt apply -target="kubernetes_manifest.karpenter_nodepool" \
  -target="kubernetes_manifest.karpenter_nodeclass" \
  -target='kubernetes_config_map.aws_auth[0]' -auto-approve
 
# Fase 5: Autoescalado con KEDA
terragrunt apply -target="helm_release.keda" -auto-approve
 
# Fase 6: Servicios de Datos
ENABLE_CNPG=true terragrunt apply --target='helm_release.additional_charts["cloudnative-pg"]' -auto-approve
ENABLE_HA_SUPABASE_DB=true terragrunt apply --target='helm_release.additional_charts["ha-supabase-db"]' -auto-approve
 
if [ "${ENABLE_SUPABASE:-false}" = "true" ]; then
  ENABLE_SUPABASE=true terragrunt apply --target='helm_release.supabase[0]' -auto-approve
fi
 
if [ "${ENABLE_AWS_SERVICES:-false}" = "true" ]; then
  terragrunt apply -target="aws_elasticache_subnet_group.redis" \
    -target="aws_security_group.redis" \
    -target="aws_elasticache_replication_group.redis" \
    -target="aws_security_group.rabbitmq" \
    -target="aws_mq_broker.rabbitmq" -auto-approve
fi
 
# Fase 7: Servicios Odin
terragrunt apply -target="helm_release.odin_services" -auto-approve
 
# Fase 8: SigNoz (si está habilitado)
terragrunt apply -target='helm_release.additional_charts["signoz"]' -auto-approve
terragrunt apply -target='helm_release.additional_charts["k8s-infra"]' -auto-approve
 
# Fase 9: Despliegue Final
terragrunt apply -auto-approve
Reemplace your-env-name con el nombre real de su entorno en todo momento. Siempre ejecute ejecuciones de prueba (terragrunt plan) primero para validar su configuración antes de aplicar cambios.