Este guia orienta sobre a implantação completa da infraestrutura EKB EKS no AWS usando Terragrunt. Ele cobre instalação de ferramentas, configuração do ambiente e uma sequência de implantação em fases projetada para garantir a ordem adequada de dependências em todos os componentes da infraestrutura.
As implantações são organizadas em nove fases:
- Gerenciamento de Estado — Inicializa o bucket S3 usado para armazenar o estado do Terraform do ambiente.
- Infraestrutura EKS — Provisiona a VPC, sub-redes, NAT gateways, funções IAM e o cluster EKS e node groups gerenciados.
- Armazenamento e Balanceamento de Carga — Implantar o EBS CSI driver para volumes persistentes e o AWS Load Balancer Controller para ingress ALB.
- Escalação Automática Karpenter — Configura provisionamento dinâmico de nós com suporte a instâncias Spot e tratamento de interrupção via SQS e EventBridge.
- Escalação Automática KEDA — Implantar o KEDA para escalação automática em nível de pod baseada em limites de CPU e memória.
- Serviços de Dados — Provisionar Supabase (auto-hospedado ou Cloud), ElastiCache Redis e Amazon MQ RabbitMQ.
- Serviços Odin — Implantar a pilha de aplicação EKB (Web, FastAPI, Celery, Automator) via Helm.
- Observabilidade SigNoz — Implantar rastreamento distribuído, métricas e agregação de logs via SigNoz e o agente k8s-infra.
- Implantação Final — Executar um
terragrunt apply completo para reconciliar quaisquer recursos restantes.
Antes de começar, complete a lista de verificação de pré-requisitos com o cliente e certifique-se de que todos os placeholders <YOUR_*> no template do ambiente estejam preenchidos. Vários valores — incluindo o ID da VPC, endpoint do cluster EKS e endpoints Redis e RabbitMQ — só estão disponíveis após a conclusão de fases específicas, portanto o guia indica exatamente quando capturá-los e aplicá-los.
Pré-requisitos
- AWS CLI configurada com permissões apropriadas
- Terraform (>= 1.0)
- Terragrunt (versão mais recente)
kubectl para gerenciamento do Kubernetes
helm para gerenciamento de charts Helm
Guia de Instalação
Instalando o Terragrunt
macOS (Homebrew)
Linux (apt)
# Adicionar chave GPG da HashiCorp
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
# Adicionar repositório 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
# Atualizar e instalar
sudo apt update
sudo apt install terragrunt
Windows (Chocolatey)
Instalando o kubectl
macOS (Homebrew)
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 o Helm
macOS (Homebrew)
Linux
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
Windows (Chocolatey)
choco install kubernetes-helm
Verificando a Instalação
terragrunt --version
terraform --version
kubectl version --client
helm version
Configuração da 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 credenciais AWS
aws configure
# Verificar configuração
aws sts get-caller-identity
Criando um Novo Ambiente
Passo 1: Copie o Template do Ambiente
A pasta env-template-folder contém arquivos pré-estruturados com placeholders <YOUR_*> prontos para serem preenchidos. Copie-a inteiramente para criar sua nova pasta de ambiente.
# Navegue até o diretório de ambientes do terragrunt
cd terragrunt/environments
# Copie a pasta template completa para um novo ambiente (substitua 'nome-do-seu-ambiente')
cp -r env-template-folder nome-do-seu-ambiente
# A estrutura de pastas está pronta:
# nome-do-seu-ambiente/
# ├── terragrunt.hcl # Configuração principal do cluster
# ├── state/
# │ └── terragrunt.hcl # Configuração do bucket de estado S3
# └── values/
# ├── infrastructure.yaml # AWS Load Balancer Controller
# ├── karpenter-values.yaml # Configurações do controlador Karpenter
# ├── karpenter-nodeclasses.yaml # Definições EC2NodeClass
# ├── karpenter.yaml # Definições Karpenter NodePool
# ├── keda.yaml # Escalonador KEDA
# ├── aws-ebs-csi-driver.yaml # EBS CSI driver
# ├── odin-services.yaml # Serviços de aplicação Odin
# ├── supabase.yaml # Supabase (se auto-hospedado)
# ├── ha-supabase-db.yaml # Supabase HA DB (se auto-hospedado)
# ├── cloudnative-pg.yaml # Operador CloudNativePG (se auto-hospedado)
# ├── signoz.yaml # Observabilidade SigNoz (opcional)
# └── signoz-k8s-infra.yaml # Métricas SigNoz k8s (opcional)
Passo 2: Verifique se Todos os Placeholders Estão Presentes
cd nome-do-seu-ambiente
# Listar todos os placeholders que precisam ser preenchidos
grep -r "<YOUR_" . --include="*.hcl" --include="*.yaml" | sort
Todos os placeholders seguem a convenção <YOUR_*>. Os passos abaixo orientam sobre como preenchê-los arquivo por arquivo.
Passo 3: Provisionar Certificados SSL (AWS ACM)
Antes de definir variáveis de ambiente, você precisa dos ARNs dos certificados. Use o Console AWS para solicitar certificados SSL no AWS Certificate Manager (ACM) para todos os domínios que seu ambiente servirá.
Opção A: Certificado wildcard único (recomendado)
Um único certificado wildcard cobre todos os subdomínios com um único ARN. Por exemplo, se seu domínio base for app.example.com, um único certificado *.app.example.com cobre:
| Serviço | Domínio |
|---|
| Web | app.example.com |
| FastAPI | api-app.example.com |
| Automator | automations-app.example.com |
| Supabase | supabase-app.example.com |
| SigNoz | signoz-app.example.com |
Opção B: Certificados por serviço
Solicite um certificado por domínio se não puder usar um wildcard. Repita os passos abaixo para cada domínio: <YOUR_WEB_DOMAIN>, <YOUR_API_DOMAIN>, <YOUR_AUTOMATOR_DOMAIN>, <YOUR_SUPABASE_DOMAIN> (apenas se ENABLE_SUPABASE=true), <YOUR_SIGNOZ_DOMAIN> (apenas se ENABLE_SIGNOZ=true).
Solicitando um certificado no Console AWS
- Abra o console do AWS Certificate Manager
- Mude para a região correta (canto superior direito) — deve corresponder a
<YOUR_AWS_REGION>
- Clique em Request a certificate → Request a public certificate → Next
- Em Fully qualified domain name, insira o wildcard (por exemplo,
*.app.example.com) ou um domínio específico
- Defina Validation method como DNS validation
- Clique em Request — o certificado é criado no estado
Pending validation
Adicionando o registro de validação DNS CNAME
O ACM gera um registro CNAME que você deve adicionar ao seu provedor DNS para provar a titularidade do domínio. Obtenha os valores no Console ACM abrindo o certificado e expandindo o domínio em Domains.
| Campo DNS | Valor |
|---|
| Tipo de registro | CNAME |
| Nome / Host | por exemplo, _fa187f22ac17bce6f508bf3c56439c61.signoz-app.example.com. |
| Valor / Aponta para | por exemplo, _c7c97325fe38061e168e232d122c7ff3.jkddzztszm.acm-validations.aws. |
Inclua o ponto final (.) no final dos valores CNAME se seu provedor DNS exigir.
Cloudflare
- Faça login no Cloudflare → selecione seu domínio → vá para DNS → Records → Add record
- Defina Type como
CNAME
- Cole o nome CNAME do ACM em Name e o valor CNAME do ACM em Target
- Defina Proxy status como DNS only (ícone de nuvem cinza) — o certificado não será validado através do proxy Cloudflare
- Clique em Save
Route 53
- Abra o console Route 53 → Hosted zones → selecione sua zona → Create record
- Defina Record type como
CNAME
- Cole o nome CNAME do ACM em Record name (apenas a parte do subdomínio) e o valor em Value
- Defina TTL como
300 e clique em Create records
No ACM, você também pode clicar em Create records in Route 53 para que o ACM adicione o registro automaticamente se a hosted zone estiver na mesma conta.
Uma vez que o DNS propagar (tipicamente 1–5 minutos), o status do certificado muda para Issued. Copie o ARN do topo do certificado — ele se parece com arn:aws:acm:<regiao>:<account-id>:certificate/<uuid>. Mantenha o(s) ARN(s) à mão para o próximo passo.
Passo 4: Defina as Variáveis de Ambiente
Defina estas variáveis de ambiente do shell antes de executar quaisquer comandos Terragrunt. Elas são lidas diretamente por terragrunt.hcl via get_env().
export AWS_REGION="<YOUR_AWS_REGION>" # por exemplo, "eu-west-2", "us-east-2"
export CLUSTER_NAME="<env_folder_name>" # por exemplo, "env-template-folder"
# Configuração de domínio
export WEB_DOMAIN="<YOUR_WEB_DOMAIN>" # por exemplo, "app.example.com"
export FASTAPI_DOMAIN="<YOUR_API_DOMAIN>" # por exemplo, "api-app.example.com"
export AUTOMATOR_DOMAIN="<YOUR_AUTOMATOR_DOMAIN>" # por exemplo, "automations-app.example.com"
export SUPABASE_DOMAIN="<YOUR_SUPABASE_DOMAIN>" # por exemplo, "supabase-app.example.com"
export SIGNOZ_DOMAIN="<YOUR_SIGNOZ_DOMAIN>" # por exemplo, "signoz-app.example.com"
# ARNs de Certificados SSL — Opção A: Certificado wildcard único (recomendado)
export WILDCARD_CERTIFICATE_ARN="arn:aws:acm:<YOUR_AWS_REGION>:<YOUR_AWS_ACCOUNT_ID>:certificate/<YOUR_WILDCARD_CERT_ID>"
# ARNs de Certificados SSL — Opção B: Certificados por serviço
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>"
# Flags de habilitação de serviços
export ENABLE_ALB_CONTROLLER="true"
export ENABLE_AWS_SERVICES="true" # Defina como "true" para habilitar ElastiCache e AmazonMQ
# Pilha Supabase (auto-hospedado) — habilite os três juntos se auto-hospedando o Supabase
export ENABLE_CNPG="true" # Operador CloudNativePG (namespace: cnpg-system)
export ENABLE_HA_SUPABASE_DB="true" # Supabase HA database (namespace: ha-supabase-db)
export ENABLE_SUPABASE="true" # Aplicação Supabase (namespace: supabase)
export ENABLE_SIGNOZ="true" # Defina como "true" para habilitar observabilidade SigNoz
export SSL_TERMINATION="alb"
Instâncias Spot e Cargas de Trabalho Estatais
As instâncias Spot são configuradas por NodePool em values/karpender.yaml, não via variáveis de ambiente. Cada NodePool declara sua própria estratégia de capacidade:
| NodePool | Rótulo workload-type | Tipo de Capacidade | Justificativa |
|---|
general | general | Spot → fallback On-Demand | Otimizado em custo para cargas de trabalho em lote/segundo plano sem estado |
compute-intensive | compute-intensive | Spot → fallback On-Demand | Otimizado em custo para cargas de trabalho limitadas por CPU |
memory-intensive | memory-intensive | On-Demand → fallback Spot | Estabilidade priorizada para pods de alta memória |
gpu | gpu | Spot → fallback On-Demand | Otimizado em custo para cargas de trabalho em lote de IA/ML |
application | application | Apenas On-Demand | Serviços estáveis面向 ao usuário (Supabase, Kong, etc.) — sem interrupções Spot |
database | database / node-type: database-dedicated | Apenas On-Demand | Estatal — interrupção Spot é insegura para bancos de dados |
O NodePool application usa famílias de instâncias m/c (geração 5+) com apenas On-Demand. Os pods do serviço Supabase são fixados aqui via nodeSelector: workload-type: "application" para garantir que nunca sejam interrompidos por um evento de reivindicação Spot.
O NodePool database-dedicated nunca usa Spot. Ele usa consolidationPolicy: WhenEmpty para que o Karpenter não evicta um nó que ainda tem um pod em execução, tornando-o seguro para cargas de trabalho estatais como réplicas PostgreSQL e CloudNativePG.
Diretrizes para aplicações estatais no Spot:
- Não agende bancos de dados, filas persistentes ou qualquer pod com
PersistentVolumeClaim em NodePools Spot.
- Use um
nodeSelector direcionando node-type: database-dedicated com a toleração correspondente database-workload: "true" para pods de banco de dados.
- Use
nodeSelector: workload-type: "application" para serviços sem estado面向 ao usuário que devem permanecer disponíveis sem interrupção.
- Para cargas de trabalho em segundo plano (Web, API, Celery, Automator), o NodePool Spot
general é apropriado — o tratador de interrupção SQS do Karpenter drena nós Spot graciosamente antes que a AWS os reivindique, e a contagem mínima de réplicas do KEDA (≥ 2) garante disponibilidade durante a substituição de nós.
- Para desabilitar Spot globalmente, remova
"spot" da lista de valores em cada NodePool dentro de values/karpenter.yaml.
Como o Karpenter lida com avisos de interrupção Spot:
A AWS fornece um aviso de interrupção de 2 minutos antes de terminar uma instância Spot. O Karpenter usa EventBridge e SQS para agir automaticamente:
Evento de Interrupção Spot da AWS
│
▼
Amazon EventBridge (CloudWatch Events)
Rule: EC2 Spot Instance Interruption Warning
│
▼
Fila SQS (Fila de interrupção do Karpenter)
│
▼
Controlador Karpenter (verifica a SQS continuamente)
│
├── Marca o nó como cordoned (nenhum novo pod agendado)
├── Drena pods existentes (respeita PodDisruptionBudgets)
├── Provisiona um nó de substituição em paralelo
└── Pods reagendam para o novo nó antes que a janela de 2 minutos feche
Isto é configurado no bloco karpenter em terragrunt.hcl:
karpenter = {
spot_interruption_handling = true # cria a fila SQS e a regra EventBridge
enable_spot_instances = true # permite Spot nos requisitos de capacidade do NodePool
}
Passo 5: Atualize os Valores Específicos do Ambiente
Faça uma busca e substituição em todos os arquivos na sua nova pasta de ambiente para os seguintes placeholders:
| Placeholder | Descrição | Exemplo |
|---|
<YOUR_ENV_NAME> | Identificador único do ambiente | app-eks-prod |
<YOUR_AWS_REGION> | Região AWS do cluster | eu-west-2, us-east-2 |
<YOUR_AWS_ACCOUNT_ID> | ID da conta AWS de 12 dígitos | 123456789012 |
<YOUR_ENVIRONMENT> | Valor da tag de ambiente | prod, staging, dev |
<YOUR_PROJECT> | Valor da tag de projeto | odin, ekb |
# Execute a partir da sua nova pasta de ambiente para encontrar todos os placeholders restantes
grep -r "<YOUR_" .
5.1 terragrunt.hcl — Configuração principal do cluster
| Campo | Placeholder | Notas |
|---|
cluster_name | <YOUR_ENV_NAME> | Deve corresponder ao nome do cluster EKS |
cluster_region | <YOUR_AWS_REGION> | Região AWS |
aws_account_id | <YOUR_AWS_ACCOUNT_ID> | ID da conta de 12 dígitos |
vpc_cidr | <YOUR_VPC_CIDR> | por exemplo, 192.168.0.0/16 |
availability_zones | <YOUR_REGION>a/b/c | 3 AZs na sua região |
tags.Environment | <YOUR_ENVIRONMENT> | por exemplo, prod |
tags.Project | <YOUR_PROJECT> | por exemplo, odin |
aws_services.amazon_mq.rabbitmq.username | <YOUR_RABBITMQ_USERNAME> | Nome de usuário admin do RabbitMQ (apenas quando ENABLE_AWS_SERVICES=true) |
aws_services.amazon_mq.rabbitmq.password | <YOUR_RABBITMQ_PASSWORD> | Mín. 12 caracteres; deve incluir maiúsculas, minúsculas, dígitos e caracteres especiais |
5.2 state/terragrunt.hcl — Bucket de estado S3
nano state/terragrunt.hcl
| Campo | Placeholder | Notas |
|---|
bucket_name | odin-terraform-state-<YOUR_ENV_NAME> | Deve ser globalmente único |
region | <YOUR_AWS_REGION> | Mesma região do cluster |
5.3 values/infrastructure.yaml — AWS Load Balancer Controller
Obtenha o ID da VPC após a criação do cluster EKS antes de implantar o AWS Load Balancer Controller.
# Obter o ID da VPC após a criação do cluster EKS
aws eks describe-cluster --name <YOUR_ENV_NAME> \
--query "cluster.resourcesVpcConfig.vpcId" --output text
nano values/infrastructure.yaml
| Campo | Placeholder | Notas |
|---|
clusterName | <YOUR_ENV_NAME> | Nome do cluster EKS |
region | <YOUR_AWS_REGION> | Região AWS |
vpcId | <YOUR_VPC_ID> | Necessário antes da implantação do ALB |
serviceAccount.annotations.eks.amazonaws.com/role-arn | <YOUR_AWS_ACCOUNT_ID>, <YOUR_ENV_NAME> | Função IAM para o controlador ALB |
5.4 values/karpenter-values.yaml — Controlador Karpenter
Obtenha o endpoint do cluster EKS após a criação do cluster EKS e antes de implantar o Karpenter.
# Obter o endpoint do cluster após a criação do cluster EKS
aws eks describe-cluster --name <YOUR_ENV_NAME> \
--query "cluster.endpoint" --output text
nano values/karpenter-values.yaml
| Campo | Placeholder | Notas |
|---|
serviceAccount.annotations.eks.amazonaws.com/role-arn | <YOUR_AWS_ACCOUNT_ID>, <YOUR_ENV_NAME> | Função IAM para o Karpenter |
env.CLUSTER_NAME | <YOUR_ENV_NAME> | Nome do cluster EKS |
env.CLUSTER_ENDPOINT | <YOUR_EKS_CLUSTER_ENDPOINT> | Necessário antes da implantação do Karpenter |
settings.aws.defaultInstanceProfile | <YOUR_ENV_NAME> | Perfil de instância do nó do Karpenter |
5.5 values/karpenter-nodeclasses.yaml — Classes de nós Karpenter
nano values/karpenter-nodeclasses.yaml
| Campo | Placeholder | Notas |
|---|
Todas as tags kubernetes.io/cluster/<YOUR_ENV_NAME> | <YOUR_ENV_NAME> | Tag de cluster para seletores de sub-rede/SG |
Nome do cluster bootstrap user_data | <YOUR_ENV_NAME> | Script de bootstrap do nó |
tags.Environment | <YOUR_ENVIRONMENT> | por exemplo, prod |
tags.Project | <YOUR_PROJECT> | por exemplo, odin |
5.6 values/aws-ebs-csi-driver.yaml — EBS CSI Driver
nano values/aws-ebs-csi-driver.yaml
| Campo | Placeholder | Notas |
|---|
controller.serviceAccount.annotations.eks.amazonaws.com/role-arn | <YOUR_AWS_ACCOUNT_ID>, <YOUR_ENV_NAME> | Função IAM para o controlador EBS CSI |
node.serviceAccount.annotations.eks.amazonaws.com/role-arn | <YOUR_AWS_ACCOUNT_ID>, <YOUR_ENV_NAME> | Função IAM para o nó EBS CSI |
controller.env.AWS_DEFAULT_REGION | <YOUR_AWS_REGION> | Região AWS |
controller.env.AWS_REGION | <YOUR_AWS_REGION> | Região AWS |
node.env.AWS_DEFAULT_REGION | <YOUR_AWS_REGION> | Região AWS |
node.env.AWS_REGION | <YOUR_AWS_REGION> | Região AWS |
5.7 values/karpenter.yaml — Karpenter NodePools
nano values/karpenter.yaml
| Campo | Placeholder | Notas |
|---|
*.labels.Environment | <YOUR_ENVIRONMENT> | Aplicado a todos os rótulos de NodePool |
*.requirements topology.kubernetes.io/zone | ["<YOUR_REGION>a", "<YOUR_REGION>b", "<YOUR_REGION>c"] | AZs para todos os NodePools |
Os nomes das classes de nós (general, compute-intensive, memory-intensive, gpu, database) devem corresponder às entradas em karpenter-nodeclasses.yaml.
5.8 values/keda.yaml — Escalonador KEDA
Nenhum placeholder específico do ambiente é necessário. Limites de recursos e contagens de réplicas são pré-configurados com padrões sensatos. Revise e ajuste se necessário.
5.9 values/supabase.yaml — Aplicação Supabase (apenas se ENABLE_SUPABASE=true)
Todas as chaves abaixo devem ser geradas consistentemente e compartilhadas com ha-supabase-db.yaml. Gere-as uma vez e use os mesmos valores em ambos os arquivos.
# Gerar segredo JWT
openssl rand -hex 32
# Gerar JWTs de anon/service (requer Supabase CLI)
brew install supabase/tap/supabase
supabase gen-keys
# Gerar senhas e tokens
openssl rand -hex 24 # para senhas
openssl rand -base64 64 # para secretKeyBase
nano values/supabase.yaml
| Campo | Placeholder | Notas |
|---|
secret.jwt.anonKey | <YOUR_SUPABASE_ANON_KEY> | Deve corresponder ao anonKey em ha-supabase-db.yaml |
secret.jwt.serviceKey | <YOUR_SUPABASE_SERVICE_ROLE_KEY> | Deve corresponder ao serviceRoleKey em ha-supabase-db.yaml |
secret.jwt.secret | <YOUR_SUPABASE_JWT_SECRET> | Deve corresponder ao jwtSecret em ha-supabase-db.yaml |
secret.db.password | <YOUR_SUPABASE_DB_PASSWORD> | Deve corresponder ao postgresPassword em ha-supabase-db.yaml |
secret.analytics.publicAccessToken | <YOUR_SUPABASE_ANALYTICS_PUBLIC_TOKEN> | Token interno Logflare |
secret.analytics.privateAccessToken | <YOUR_SUPABASE_ANALYTICS_PRIVATE_TOKEN> | Token interno Logflare |
secret.dashboard.username | <YOUR_SUPABASE_DASHBOARD_USERNAME> | Login da UI Studio |
secret.dashboard.password | <YOUR_SUPABASE_DASHBOARD_PASSWORD> | Login da UI Studio |
secret.realtime.secretKeyBase | <YOUR_SUPABASE_REALTIME_SECRET_KEY_BASE> | Chave secreta Phoenix |
secret.meta.cryptoKey | <YOUR_SUPABASE_META_CRYPTO_KEY> | openssl rand -hex 32 |
secret.s3.keyId | <YOUR_MINIO_KEY_ID> | Deve corresponder ao secret.minio.user (openssl rand -hex 16) |
secret.s3.accessKey | <YOUR_MINIO_ACCESS_KEY> | Deve corresponder ao secret.minio.password (openssl rand -hex 32) |
secret.minio.user | <YOUR_MINIO_KEY_ID> | Mesmo valor que secret.s3.keyId |
secret.minio.password | <YOUR_MINIO_ACCESS_KEY> | Mesmo valor que secret.s3.accessKey |
5.10 values/ha-supabase-db.yaml — Supabase HA Database (apenas se ENABLE_HA_SUPABASE_DB=true)
Os segredos aqui devem corresponder a supabase.yaml. Use os mesmos valores gerados para postgresPassword, jwtSecret, anonKey e serviceRoleKey.
nano values/ha-supabase-db.yaml
| Campo | Placeholder | Notas |
|---|
secrets.inline.postgresPassword | <YOUR_SUPABASE_DB_PASSWORD> | Deve corresponder ao secret.db.password em supabase.yaml |
secrets.inline.authenticatorPassword | <YOUR_SUPABASE_DB_PASSWORD> | Deve ser idêntico ao postgresPassword |
secrets.inline.pgbouncerPassword | <YOUR_SUPABASE_DB_PASSWORD> | Deve ser idêntico ao postgresPassword |
secrets.inline.jwtSecret | <YOUR_SUPABASE_JWT_SECRET> | Deve corresponder ao secret.jwt.secret em supabase.yaml |
secrets.inline.anonKey | <YOUR_SUPABASE_ANON_KEY> | Deve corresponder ao secret.jwt.anonKey em supabase.yaml |
secrets.inline.serviceRoleKey | <YOUR_SUPABASE_SERVICE_ROLE_KEY> | Deve corresponder ao secret.jwt.serviceKey em supabase.yaml |
A classe de armazenamento (ebs-csi-gp2), contagens de instâncias e limites de recursos são pré-configurados. Ajuste postgres.storage.size e postgres.walStorage.size para o volume de dados esperado.
5.11 values/cloudnative-pg.yaml — Operador CloudNativePG (apenas se ENABLE_CNPG=true)
Nenhum placeholder específico do ambiente é necessário. Isto implanta apenas o controlador do operador CNPG. As configurações padrão (3 réplicas, limites de recursos) são adequadas para a maioria dos ambientes.
5.12 values/odin-services.yaml — Serviços de aplicação Odin
Os endpoints Redis e RabbitMQ só estão disponíveis após o Terraform criar esses recursos AWS. Os ARNs de certificado devem ser provisionados no ACM antes da implantação.
nano values/odin-services.yaml
Configurações gerais:
| Campo | Placeholder | Notas |
|---|
server | <YOUR_WEB_DOMAIN> | Domínio web principal |
toolkitEncryptionKey | <YOUR_TOOLKIT_ENCRYPTION_KEY> | Gerar: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" |
Supabase (dataServiceConfig) — auto-hospedado (ENABLE_SUPABASE=true):
| Campo | Placeholder | Fonte |
|---|
supabase.projectUrl | http://supabase-kong:8000 | Fixo — Supabase Kong interno |
supabase.key | <YOUR_SUPABASE_SERVICE_ROLE_KEY> | Mesmo que secret.jwt.serviceKey em supabase.yaml |
supabase.postgres.user | postgres | Fixo para auto-hospedado |
supabase.postgres.host | ha-supabase-db-postgres-pooler-rw.ha-supabase-db.svc.cluster.local | Fixo — Serviço DB Pool dentro do cluster |
supabase.postgres.password | <YOUR_SUPABASE_DB_PASSWORD> | Mesmo que secret.db.password em supabase.yaml |
supabase.projectId | (deixe vazio) | Não usado no modo auto-hospedado |
Supabase (dataServiceConfig) — Supabase Cloud (ENABLE_SUPABASE=false):
| Campo | Placeholder | Fonte |
|---|
supabase.projectUrl | <YOUR_SUPABASE_PROJECT_URL> | Supabase dashboard → Project Settings → API |
supabase.key | <YOUR_SUPABASE_SERVICE_ROLE_KEY> | Supabase dashboard → API → chave service_role |
supabase.postgres.user | <YOUR_SUPABASE_DB_USER> | Supabase dashboard → Project Settings → Database |
supabase.postgres.host | <YOUR_SUPABASE_DB_HOST> | Supabase dashboard → Database (por exemplo, aws-0-eu-west-2.pooler.supabase.com) |
supabase.postgres.password | <YOUR_SUPABASE_DB_PASSWORD> | Supabase dashboard → Project Settings → Database |
supabase.projectId | <YOUR_SUPABASE_PROJECT_ID> | Da URL do seu projeto Supabase |
Redis:
| Campo | Placeholder | Notas |
|---|
redis.url | rediss://<YOUR_REDIS_HOST>:6379?ssl_cert_reqs=none | Após o Terraform criar o ElastiCache |
redis.host | <YOUR_REDIS_HOST> | Endpoint primário do ElastiCache |
# Obter endpoint do Redis após 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:
| Campo | Placeholder | Notas |
|---|
rabbitmq.url | amqps://<YOUR_RABBITMQ_USERNAME>:<YOUR_RABBITMQ_PASSWORD>@<YOUR_RABBITMQ_HOST>:5671 | Após o Terraform criar o AmazonMQ |
rabbitmq.host | <YOUR_RABBITMQ_HOST> | Endpoint do broker AmazonMQ |
rabbitmq.username | <YOUR_RABBITMQ_USERNAME> | Definido em terragrunt.hcl |
rabbitmq.password | <YOUR_RABBITMQ_PASSWORD> | Definido em terragrunt.hcl |
# Obter endpoint do RabbitMQ após 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
ARNs de SSL / Certificado:
| Campo | Placeholder | Notas |
|---|
ssl.services.web.domain | <YOUR_WEB_DOMAIN> | por exemplo, app.example.com |
ssl.services.web.certificateArn | <YOUR_WEB_CERTIFICATE_ARN> | ARN do certificado ACM |
ssl.services.fastapiBackend.domain | <YOUR_API_DOMAIN> | por exemplo, api-app.example.com |
ssl.services.fastapiBackend.certificateArn | <YOUR_API_CERTIFICATE_ARN> | ARN do certificado ACM |
ssl.services.automator.domain | <YOUR_AUTOMATOR_DOMAIN> | por exemplo, automations-app.example.com |
ssl.services.automator.certificateArn | <YOUR_AUTOMATOR_CERTIFICATE_ARN> | ARN do certificado ACM |
ssl.services.supabase.domain | <YOUR_SUPABASE_DOMAIN> | por exemplo, supabase-app.example.com |
ssl.services.supabase.certificateArn | <YOUR_SUPABASE_CERTIFICATE_ARN> | ARN do certificado ACM |
# Listar certificados ACM na sua região
aws acm list-certificates --region <YOUR_AWS_REGION> \
--query "CertificateSummaryList[*].[DomainName,CertificateArn]" --output table
Chaves Supabase do frontend web — auto-hospedado (ENABLE_SUPABASE=true):
| Campo | Placeholder | Fonte |
|---|
web.supabase.url | https://<YOUR_SUPABASE_DOMAIN> | URL externa roteada via ingress ALB |
web.supabase.anonKey | <YOUR_SUPABASE_ANON_KEY> | Mesmo que secret.jwt.anonKey em supabase.yaml |
web.supabase.serviceRoleKey | <YOUR_SUPABASE_SERVICE_ROLE_KEY> | Mesmo que secret.jwt.serviceKey em supabase.yaml |
web.supabase.clientanonKey | <YOUR_SUPABASE_SERVICE_ROLE_KEY> | Mesmo que secret.jwt.serviceKey em supabase.yaml |
Chaves Supabase do frontend web — Supabase Cloud (ENABLE_SUPABASE=false):
| Campo | Placeholder | Fonte |
|---|
web.supabase.url | <YOUR_SUPABASE_PROJECT_URL> | Supabase dashboard → Project Settings → API |
web.supabase.anonKey | <YOUR_SUPABASE_ANON_KEY> | Supabase dashboard → API → chave anon |
web.supabase.serviceRoleKey | <YOUR_SUPABASE_SERVICE_ROLE_KEY> | Supabase dashboard → API → chave service_role |
web.supabase.clientanonKey | <YOUR_SUPABASE_CLIENT_ANON_KEY> | Mesmo que a chave service_role |
5.13 values/signoz.yaml — Observabilidade SigNoz (apenas se ENABLE_SIGNOZ=true)
| Campo | Placeholder | Notas |
|---|
global.clusterName | <YOUR_ENV_NAME> | Nome do 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> | por exemplo, signoz-app.example.com |
5.14 values/signoz-k8s-infra.yaml — Métricas SigNoz K8s (apenas se ENABLE_SIGNOZ=true)
nano values/signoz-k8s-infra.yaml
| Campo | Placeholder | Notas |
|---|
global.clusterName | <YOUR_ENV_NAME> | Nome do cluster EKS para rotulagem de métricas |
O endpoint do coletor OTel (signoz-otel-collector.monitoring.svc.cluster.local:4317) é pré-configurado assumindo que tanto SigNoz quanto k8s-infra são implantados no namespace monitoring. Nenhuma alteração é necessária, a menos que você use um nome de release personalizado.
Lembrete da Ordem de Implantação
Alguns valores só estão disponíveis após a implantação de certa infraestrutura. Siga esta ordem:
- Antes de qualquer implantação — Defina:
<YOUR_ENV_NAME>, <YOUR_AWS_REGION>, <YOUR_AWS_ACCOUNT_ID>, <YOUR_ENVIRONMENT>, <YOUR_PROJECT>, <YOUR_VPC_CIDR>, todos os nomes de domínio, todos os ARNs de certificado, todos os valores do Supabase, <YOUR_TOOLKIT_ENCRYPTION_KEY>, nome de usuário/senha do RabbitMQ
- Após criação do cluster EKS — Defina:
<YOUR_VPC_ID> (infrastructure.yaml), <YOUR_EKS_CLUSTER_ENDPOINT> (karpenter-values.yaml)
- Após
terraform apply para serviços AWS — Defina: <YOUR_REDIS_HOST>, <YOUR_RABBITMQ_HOST> (odin-services.yaml)
Passo 6: Verifique se Não Há Placeholders Restantes
grep -r "<YOUR_" . --include="*.hcl" --include="*.yaml"
A saída esperada deve estar vazia ou conter apenas referências a recursos prestes a serem criados (VPC, Redis, MQ, EKS). Se houver placeholders restantes, consulte as subseções do Passo 5 acima.
Lista de verificação de arquivos:
| Arquivo | Passo | Obrigatório |
|---|
terragrunt.hcl | 5.1 | Sempre |
state/terragrunt.hcl | 5.2 | Sempre |
values/infrastructure.yaml | 5.3 | Sempre |
values/karpenter-values.yaml | 5.4 | Sempre |
values/karpenter-nodeclasses.yaml | 5.5 | Sempre |
values/karpenter.yaml | 5.7 | Sempre |
values/keda.yaml | 5.8 | Sempre |
values/aws-ebs-csi-driver.yaml | 5.6 | Sempre |
values/odin-services.yaml | 5.12 | Sempre |
values/cloudnative-pg.yaml | 5.11 | Apenas se ENABLE_CNPG=true |
values/ha-supabase-db.yaml | 5.10 | Apenas se ENABLE_HA_SUPABASE_DB=true |
values/supabase.yaml | 5.9 | Apenas se ENABLE_SUPABASE=true |
values/signoz.yaml | 5.13 | Apenas se ENABLE_SIGNOZ=true |
values/signoz-k8s-infra.yaml | 5.14 | Apenas se ENABLE_SIGNOZ=true |
Fase 1: Configuração do Gerenciamento de Estado
Finalidade: Criação do bucket S3 para o estado do Terraform.
O módulo de gerenciamento de estado de cada ambiente cria um bucket S3 com o padrão odin-terraform-state-{nome-do-ambiente}, configura criptografia, versionamento e bloqueio de acesso público, e usa estado local para o próprio módulo de estado (padrão de inicialização).
cd terragrunt/environments/{nome-do-seu-ambiente}/state
terragrunt init
terragrunt plan
terragrunt apply
Fase 2: Implantação da Infraestrutura EKS
Finalidade: Rede principal (VPC, sub-redes, NAT gateway), funções e políticas IAM, cluster EKS e node groups gerenciados.
2.1 Execução Seca — Infraestrutura EKS
Infraestrutura Principal
cd terragrunt/environments/nome-do-seu-ambiente
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"
Funções e 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 e Node Groups
terragrunt plan -target="aws_eks_cluster.main" \
-target="aws_eks_node_group.main" \
-target="kubernetes_secret.regcred"
Usando um Registro Docker Personalizado / Privado
Por padrão, as imagens EKB são puxadas do Docker Hub usando um segredo chamado regcred. Se o cliente hospeda imagens em um registro diferente, siga estes passos antes de implantar odin-services.
Passo 1 — Criar o imagePullSecret no namespace alvo
# Registro privado genérico (Docker Hub, Quay, auto-hospedado, 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 — o token expira a cada 12h; atualize via CronJob ou use cache pull-through do 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
Passo 2 — Definir o nome do segredo em values/odin-services.yaml
# values/odin-services.yaml
imagePullSecrets:
- name: regcred # deve corresponder ao nome do segredo criado acima
# - name: customer-registry-secret # adicionar registros adicionais se necessário
Passo 3 — Atualizar referências de imagem
web:
image: <YOUR_REGISTRY_HOST>/<YOUR_ORG>/web:<TAG>
fastapiBackend:
image: <YOUR_REGISTRY_HOST>/<YOUR_ORG>/server:<TAG>
Passo 4 — Verificar acesso de pull antes da implantação completa
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 Implantar Infraestrutura EKS
Passo 1: Infraestrutura Principal
cd terragrunt/environments/nome-do-seu-ambiente
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"
Após este passo, atualize o vpcId em values/infrastructure.yaml antes de implantar o AWS Load Balancer Controller.
Passo 2: Cluster EKS e Funções e 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"
Passo 3: Node Groups e Add-ons
terragrunt apply -target="aws_eks_cluster.main" \
-target="aws_eks_node_group.main" \
-target="kubernetes_secret.regcred"
Após este passo, atualize CLUSTER_ENDPOINT em values/karpenter-values.yaml antes de implantar o Karpenter.
Verificar Conectividade do 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: Armazenamento e Balanceamento de Carga
Finalidade: EBS CSI driver para volumes persistentes, AWS Load Balancer Controller rodando no node group gerenciado.
3.1 Execução Seca — Armazenamento e Balanceamento de Carga
EBS CSI Driver
cd terragrunt/environments/nome-do-seu-ambiente
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 Implantar Armazenamento e Balanceamento de Carga
Passo 1: EBS CSI Driver
cd terragrunt/environments/nome-do-seu-ambiente
terragrunt apply -target="aws_iam_role.ebs_csi_driver" \
-target="aws_iam_role_policy_attachment.ebs_csi_driver" \
-target="helm_release.ebs_csi_driver"
Verificação
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
Passo 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"
Verificação
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: Escalação Automática Karpenter
Finalidade: Funções IAM para Karpenter, tratamento de interrupção Spot, controlador Karpenter e node pools.
4.1 Execução Seca — Karpenter
Recursos IAM do Karpenter
cd terragrunt/environments/nome-do-seu-ambiente
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"
Função Vinculada ao Serviço EC2 Spot (se instâncias Spot estiverem habilitadas)
terragrunt plan -target="aws_iam_service_linked_role.ec2_spot[0]"
Interrupção Spot do Karpenter (se habilitada em 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 do Karpenter
terragrunt plan -target="helm_release.karpenter"
Karpenter NodePools e EC2NodeClasses
terragrunt plan -target="kubernetes_manifest.karpenter_nodepool" \
-target="kubernetes_manifest.karpenter_nodeclass" \
-target="kubernetes_config_map.aws_auth"
Um erro esperado pode aparecer durante o plan: API did not recognize GroupVersionKind from manifest (CRD may not be installed). Isto pode ser ignorado com segurança — o Kubernetes valida recursos contra a API ao vivo durante o plan, antes que os CRDs sejam instalados.
4.2 Implantar Karpenter
Passo 1: Recursos IAM do Karpenter
cd terragrunt/environments/nome-do-seu-ambiente
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"
Verificação
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
Passo 2: Função Vinculada ao Serviço EC2 Spot (se instâncias Spot estiverem habilitadas)
A função vinculada ao serviço EC2 Spot é por conta (apenas uma por conta AWS) e deve existir antes que o Karpenter possa iniciar instâncias Spot.
Opção A: Deixe o Terraform criá-la (recomendado para novas implantações)
terragrunt apply -target="aws_iam_service_linked_role.ec2_spot[0]"
Opção B: Importar se a função já existir
# Verificar se a função existe
aws iam get-role --role-name AWSServiceRoleForEC2Spot --region $AWS_REGION
# Se não existir, criá-la manualmente
aws iam create-service-linked-role --aws-service-name spot.amazonaws.com --region $AWS_REGION
# Importar para o Terraform (substitua ACCOUNT_ID pelo seu ID de conta 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
Passo 3: Interrupção Spot do Karpenter (se habilitada em 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"
Verificação
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
Passo 4: Chart Helm do Karpenter
terragrunt apply -target="helm_release.karpenter"
Verificação
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
Passo 5: Manifestos Kubernetes do Karpenter
terragrunt apply -target="kubernetes_manifest.karpenter_nodepool" \
-target="kubernetes_manifest.karpenter_nodeclass"
# Importar o ConfigMap aws-auth existente
# Nota: Use aspas para evitar que o zsh interprete colchetes como padrões glob
terragrunt import 'kubernetes_config_map.aws_auth[0]' kube-system/aws-auth
# Em seguida, aplique
terragrunt apply -target='kubernetes_config_map.aws_auth[0]'
Verificação
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: Escalação Automática KEDA
Finalidade: KEDA para escalação automática em nível de aplicação.
5.1 Execução Seca — KEDA
cd terragrunt/environments/nome-do-seu-ambiente
terragrunt plan -target="helm_release.keda"
5.2 Implantar KEDA
cd terragrunt/environments/nome-do-seu-ambiente
terragrunt apply -target="helm_release.keda"
Verificação
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: Serviços de Dados
Finalidade: Supabase (banco de dados), ElastiCache (Redis), RabbitMQ (fila de mensagens).
Implante primeiro o operador CloudNativePG, depois o cluster HA Supabase DB, e então a aplicação Supabase. O cluster DB deve estar pronto antes que o Supabase inicie.
6.1 Execução Seca — Serviços de Dados
Passo 1: Operador CloudNativePG (se habilitado)
cd terragrunt/environments/nome-do-seu-ambiente
ENABLE_CNPG=true terragrunt plan \
--target='helm_release.additional_charts["cloudnative-pg"]'
Passo 2: HA Supabase DB (se habilitado)
ENABLE_HA_SUPABASE_DB=true terragrunt plan \
--target='helm_release.additional_charts["ha-supabase-db"]'
Passo 3: Aplicação Supabase (se habilitada)
if [ "${ENABLE_SUPABASE:-false}" = "true" ]; then
ENABLE_SUPABASE=true terragrunt plan \
--target='helm_release.supabase[0]'
fi
Passo 4: Serviços AWS — ElastiCache e RabbitMQ (se habilitados)
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 Implantar Serviços de Dados
Passo 1: Operador CloudNativePG (se habilitado)
cd terragrunt/environments/nome-do-seu-ambiente
ENABLE_CNPG=true terragrunt apply --auto-approve \
--target='helm_release.additional_charts["cloudnative-pg"]'
Passo 2: HA Supabase DB (se habilitado)
ENABLE_HA_SUPABASE_DB=true terragrunt apply --auto-approve \
--target='helm_release.additional_charts["ha-supabase-db"]'
Verificar o pooler PgBouncer e credenciais após a implantação:
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 o ClusterIP do pooler (ou EXTERNAL-IP se LoadBalancer) como valor de SUPABASE_POSTGRES_HOST em values/odin-services.yaml e como secret.db.postgresHost em values/supabase.yaml.
Passo 3: Aplicação Supabase (se habilitada)
Todos os pods do serviço Supabase rodam exclusivamente no NodePool application do Karpenter (apenas On-Demand) para prevenir interrupções Spot.
if [ "${ENABLE_SUPABASE:-false}" = "true" ]; then
ENABLE_SUPABASE=true terragrunt apply --auto-approve \
--target='helm_release.supabase[0]'
fi
Passo 4: Serviços AWS — ElastiCache e RabbitMQ (se habilitados)
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
Verificação
# Obter detalhes de conexão das saídas do Terraform
terragrunt output elasticache_endpoint
terragrunt output elasticache_port
terragrunt output rabbitmq_endpoint
terragrunt output rabbitmq_port
# Testar conectividade Redis a partir do cluster 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 status de criptografia do Redis
aws elasticache describe-replication-groups \
--replication-group-id $CLUSTER_NAME-redis \
--region $AWS_REGION \
--query 'ReplicationGroups[0].{AtRestEncryption:AtRestEncryptionEnabled,TransitEncryption:TransitEncryptionEnabled}'
Antes de implantar os Serviços Odin, atualize values/odin-services.yaml com o endpoint Redis, endpoint RabbitMQ e todos os ARNs de certificado obtidos nesta fase.
Fase 7: Serviços Odin
Finalidade: Implantação de aplicação via Helm.
Antes de implantar, reduza temporariamente o fastapiBackend para uma única réplica para a execução inicial de migração do banco de dados — defina replicaCount: 1, workers: 1 e keda.minReplicas: 1. Uma vez que a migração seja concluída com sucesso, reverta esses valores para seus padrões de produção antes de reimplementar.
7.1 Execução Seca — Serviços Odin
cd terragrunt/environments/nome-do-seu-ambiente
terragrunt plan -target="helm_release.odin_services"
7.2 Implantar Serviços Odin
cd terragrunt/environments/nome-do-seu-ambiente
terragrunt apply -target="helm_release.odin_services"
Verificação
kubectl get pods
kubectl get ingress # Adicione os endpoints ALB ao seu provedor DNS
Fase 8: Observabilidade SigNoz
Finalidade: Monitoramento de logs e métricas.
8.1 Execução Seca — Charts SigNoz
cd terragrunt/environments/nome-do-seu-ambiente
terragrunt plan -target='helm_release.additional_charts["signoz"]'
terragrunt plan -target='helm_release.additional_charts["k8s-infra"]'
8.2 Implantar Charts SigNoz
cd terragrunt/environments/nome-do-seu-ambiente
terragrunt apply -target='helm_release.additional_charts["signoz"]'
terragrunt apply -target='helm_release.additional_charts["k8s-infra"]'
Verificação
kubectl get pods -n monitoring
kubectl get ingress -n monitoring # Adicione os endpoints ALB ao seu provedor DNS
Fase 9: Implantação Final
9.1 Implantação Completa
cd terragrunt/environments/nome-do-seu-ambiente
terragrunt apply
Este apply final lida com quaisquer recursos restantes não direcionados explicitamente nas fases anteriores.
9.2 Verificar Implantação
# Atualizar kubeconfig
aws eks update-kubeconfig --region us-east-2 --name nome-do-seu-ambiente
# Verificar status do 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 Serviços Odin
kubectl get pods -n default
kubectl get services -n default
kubectl get ingress -n default
# Verificar todas as releases Helm
helm list --all-namespaces
Solução de Problemas
Problemas de lock de estado
terragrunt force-unlock <lock-id>
Karpenter não funciona
kubectl describe nodes
kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter
Problemas com balanceador de carga
kubectl describe ingress -n default
kubectl logs -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller
Problemas com charts Helm
helm status <release-name> -n <namespace>
helm rollback <release-name> <revision> -n <namespace>
Limpeza
# Destruir infraestrutura
cd terragrunt/environments/nome-do-seu-ambiente
terragrunt destroy -auto-approve
# Destruir bucket de estado (use com cuidado)
cd terragrunt/environments/nome-do-seu-ambiente/state
terragrunt destroy -auto-approve
Monitoramento e Logging
# Recursos AWS
aws eks describe-cluster --name nome-do-seu-ambiente --region us-east-2
aws ec2 describe-instances --filters "Name=tag:kubernetes.io/cluster/nome-do-seu-ambiente,Values=owned"
# Recursos Kubernetes
kubectl top nodes
kubectl top pods --all-namespaces
kubectl get events --sort-by=.metadata.creationTimestamp
Referência Rápida — Todos os Comandos de Implantação
# Fase 1: Gerenciamento de Estado
cd terragrunt/environments/nome-do-seu-ambiente/state
terragrunt apply
# Fase 2: Infraestrutura EKS
cd terragrunt/environments/nome-do-seu-ambiente
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: Armazenamento e Balanceamento 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: Escalação Automática 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
# Tratamento de interrupção Spot (se 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: Escalação Automática KEDA
terragrunt apply -target="helm_release.keda" -auto-approve
# Fase 6: Serviços de Dados
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: Serviços Odin
terragrunt apply -target="helm_release.odin_services" -auto-approve
# Fase 8: SigNoz (se habilitado)
terragrunt apply -target='helm_release.additional_charts["signoz"]' -auto-approve
terragrunt apply -target='helm_release.additional_charts["k8s-infra"]' -auto-approve
# Fase 9: Implantação Final
terragrunt apply -auto-approve
Substitua nome-do-seu-ambiente pelo nome do seu ambiente real ao longo de todo o processo. Sempre execute execuções secas (terragrunt plan) primeiro para validar sua configuração antes de aplicar alterações.