> ## Documentation Index
> Fetch the complete documentation index at: https://ai-kb.automationanywhere.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Guia de Implantação Terragrunt

> Comandos passo a passo para implantar a infraestrutura completa EKB EKS usando Terragrunt, incluindo validação de execução seca e implantação real.

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:

1. **Gerenciamento de Estado** — Inicializa o bucket S3 usado para armazenar o estado do Terraform do ambiente.
2. **Infraestrutura EKS** — Provisiona a VPC, sub-redes, NAT gateways, funções IAM e o cluster EKS e node groups gerenciados.
3. **Armazenamento e Balanceamento de Carga** — Implantar o EBS CSI driver para volumes persistentes e o AWS Load Balancer Controller para ingress ALB.
4. **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.
5. **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.
6. **Serviços de Dados** — Provisionar Supabase (auto-hospedado ou Cloud), ElastiCache Redis e Amazon MQ RabbitMQ.
7. **Serviços Odin** — Implantar a pilha de aplicação EKB (Web, FastAPI, Celery, Automator) via Helm.
8. **Observabilidade SigNoz** — Implantar rastreamento distribuído, métricas e agregação de logs via SigNoz e o agente k8s-infra.
9. **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)**

```bash theme={null}
brew install terragrunt
```

**Linux (apt)**

```bash theme={null}
# 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)**

```bash theme={null}
choco install terragrunt
```

### Instalando o kubectl

**macOS (Homebrew)**

```bash theme={null}
brew install kubectl
```

**Linux**

```bash theme={null}
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)**

```bash theme={null}
choco install kubernetes-cli
```

### Instalando o Helm

**macOS (Homebrew)**

```bash theme={null}
brew install helm
```

**Linux**

```bash theme={null}
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
```

**Windows (Chocolatey)**

```bash theme={null}
choco install kubernetes-helm
```

### Verificando a Instalação

```bash theme={null}
terragrunt --version
terraform --version
kubectl version --client
helm version
```

### Configuração da AWS CLI

```bash theme={null}
# 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.

```bash theme={null}
# 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

```bash theme={null}
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**

1. Abra o console do AWS Certificate Manager
2. Mude para a região correta (canto superior direito) — deve corresponder a `<YOUR_AWS_REGION>`
3. Clique em **Request a certificate** → **Request a public certificate** → **Next**
4. Em **Fully qualified domain name**, insira o wildcard (por exemplo, `*.app.example.com`) ou um domínio específico
5. Defina **Validation method** como **DNS validation**
6. 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.` |

<Info>
  Inclua o ponto final (`.`) no final dos valores CNAME se seu provedor DNS exigir.
</Info>

**Cloudflare**

1. Faça login no Cloudflare → selecione seu domínio → vá para **DNS** → **Records** → **Add record**
2. Defina **Type** como `CNAME`
3. Cole o nome CNAME do ACM em **Name** e o valor CNAME do ACM em **Target**
4. Defina **Proxy status** como **DNS only** (ícone de nuvem cinza) — o certificado não será validado através do proxy Cloudflare
5. Clique em **Save**

**Route 53**

1. Abra o console Route 53 → **Hosted zones** → selecione sua zona → **Create record**
2. Defina **Record type** como `CNAME`
3. Cole o nome CNAME do ACM em **Record name** (apenas a parte do subdomínio) e o valor em **Value**
4. Defina TTL como `300` e clique em **Create records**

<Tip>
  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.
</Tip>

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()`.

```bash theme={null}
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`:

```hcl theme={null}
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`            |

```bash theme={null}
# 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

```bash theme={null}
nano terragrunt.hcl
```

| 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

```bash theme={null}
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

<Warning>
  Obtenha o ID da VPC **após** a criação do cluster EKS antes de implantar o AWS Load Balancer Controller.
</Warning>

```bash theme={null}
# 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
```

```bash theme={null}
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

<Warning>
  Obtenha o endpoint do cluster EKS **após** a criação do cluster EKS e antes de implantar o Karpenter.
</Warning>

```bash theme={null}
# 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
```

```bash theme={null}
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

```bash theme={null}
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

```bash theme={null}
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

```bash theme={null}
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`)

<Warning>
  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.
</Warning>

```bash theme={null}
# 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
```

```bash theme={null}
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`)

<Warning>
  Os segredos aqui devem corresponder a `supabase.yaml`. Use os mesmos valores gerados para `postgresPassword`, `jwtSecret`, `anonKey` e `serviceRoleKey`.
</Warning>

```bash theme={null}
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

<Warning>
  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.
</Warning>

```bash theme={null}
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     |

```bash theme={null}
# 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`      |

```bash theme={null}
# 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                     |

```bash theme={null}
# 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`)

```bash theme={null}
nano values/signoz.yaml
```

| 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`)

```bash theme={null}
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:

1. **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
2. **Após criação do cluster EKS** — Defina: `<YOUR_VPC_ID>` (`infrastructure.yaml`), `<YOUR_EKS_CLUSTER_ENDPOINT>` (`karpenter-values.yaml`)
3. **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

```bash theme={null}
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).

```bash theme={null}
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**

```bash theme={null}
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**

```bash theme={null}
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**

```bash theme={null}
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**

```bash theme={null}
# 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`**

```yaml theme={null}
# 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**

```yaml theme={null}
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**

```bash theme={null}
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**

```bash theme={null}
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"
```

<Warning>
  Após este passo, atualize o `vpcId` em `values/infrastructure.yaml` antes de implantar o AWS Load Balancer Controller.
</Warning>

**Passo 2: Cluster EKS e Funções e Políticas IAM**

```bash theme={null}
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**

```bash theme={null}
terragrunt apply -target="aws_eks_cluster.main" \
  -target="aws_eks_node_group.main" \
  -target="kubernetes_secret.regcred"
```

<Warning>
  Após este passo, atualize `CLUSTER_ENDPOINT` em `values/karpenter-values.yaml` antes de implantar o Karpenter.
</Warning>

**Verificar Conectividade do Cluster EKS**

```bash theme={null}
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**

```bash theme={null}
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**

```bash theme={null}
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**

```bash theme={null}
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**

```bash theme={null}
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**

```bash theme={null}
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**

```bash theme={null}
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**

```bash theme={null}
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)**

```bash theme={null}
terragrunt plan -target="aws_iam_service_linked_role.ec2_spot[0]"
```

**Interrupção Spot do Karpenter (se habilitada em `terragrunt.hcl`)**

```bash theme={null}
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**

```bash theme={null}
terragrunt plan -target="helm_release.karpenter"
```

**Karpenter NodePools e EC2NodeClasses**

```bash theme={null}
terragrunt plan -target="kubernetes_manifest.karpenter_nodepool" \
  -target="kubernetes_manifest.karpenter_nodeclass" \
  -target="kubernetes_config_map.aws_auth"
```

<Info>
  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.
</Info>

### 4.2 Implantar Karpenter

**Passo 1: Recursos IAM do Karpenter**

```bash theme={null}
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**

```bash theme={null}
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)**

<Warning>
  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.
</Warning>

**Opção A: Deixe o Terraform criá-la (recomendado para novas implantações)**

```bash theme={null}
terragrunt apply -target="aws_iam_service_linked_role.ec2_spot[0]"
```

**Opção B: Importar se a função já existir**

```bash theme={null}
# 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
```

<details>
  <summary>Solução de Problemas na Criação de Instâncias Spot</summary>

  **Verificar logs do Karpenter para erros relacionados a Spot:**

  ```bash theme={null}
  kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter -f | grep -i spot
  ```

  Erros comuns: `AuthFailure.ServiceLinkedRoleCreationNotPermitted`, `UnfulfillableCapacity`, `InsufficientInstanceCapacity`.

  **Verificar se a função vinculada ao serviço existe:**

  ```bash theme={null}
  aws iam get-role --role-name AWSServiceRoleForEC2Spot --region $AWS_REGION
  ```

  **Verificar se a política IAM inclui permissão de Spot:**

  ```bash theme={null}
  aws iam get-policy-version \
    --policy-arn $(aws iam list-policies --query 'Policies[?PolicyName==`YOUR_CLUSTER_NAME-karpenter-controller`].Arn' --output text) \
    --version-id $(aws iam get-policy --policy-arn $(aws iam list-policies --query 'Policies[?PolicyName==`YOUR_CLUSTER_NAME-karpenter-controller`].Arn' --output text) --query 'Policy.DefaultVersionId' --output text) \
    --region $AWS_REGION | grep -i "CreateServiceLinkedRole"
  ```

  **Verificar disponibilidade de instâncias Spot:**

  ```bash theme={null}
  aws ec2 describe-spot-price-history \
    --instance-types r6a.4xlarge r6a.large \
    --product-descriptions "Linux/UNIX" \
    --region $AWS_REGION \
    --max-items 10
  ```

  **Verificar tipos de capacidade dos NodePool:**

  ```bash theme={null}
  kubectl get nodepool -o yaml | grep -A 5 "capacity-type"
  ```

  **Verificar nós Spot vs On-Demand:**

  ```bash theme={null}
  kubectl get nodes -o json | jq -r '.items[] | "\(.metadata.name)\t\(.metadata.labels."karpenter.sh/capacity-type")\t\(.metadata.labels."node.kubernetes.io/instance-type")"'
  ```
</details>

**Passo 3: Interrupção Spot do Karpenter (se habilitada em `terragrunt.hcl`)**

```bash theme={null}
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**

```bash theme={null}
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**

```bash theme={null}
terragrunt apply -target="helm_release.karpenter"
```

**Verificação**

```bash theme={null}
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**

```bash theme={null}
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**

```bash theme={null}
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

```bash theme={null}
cd terragrunt/environments/nome-do-seu-ambiente
terragrunt plan -target="helm_release.keda"
```

### 5.2 Implantar KEDA

```bash theme={null}
cd terragrunt/environments/nome-do-seu-ambiente
terragrunt apply -target="helm_release.keda"
```

**Verificação**

```bash theme={null}
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).

<Info>
  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.
</Info>

### 6.1 Execução Seca — Serviços de Dados

**Passo 1: Operador CloudNativePG (se habilitado)**

```bash theme={null}
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)**

```bash theme={null}
ENABLE_HA_SUPABASE_DB=true terragrunt plan \
  --target='helm_release.additional_charts["ha-supabase-db"]'
```

**Passo 3: Aplicação Supabase (se habilitada)**

```bash theme={null}
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)**

```bash theme={null}
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)**

```bash theme={null}
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)**

```bash theme={null}
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:**

```bash theme={null}
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.

```bash theme={null}
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)**

```bash theme={null}
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**

```bash theme={null}
# 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}'
```

<Warning>
  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.
</Warning>

***

## Fase 7: Serviços Odin

**Finalidade:** Implantação de aplicação via Helm.

<Info>
  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.
</Info>

### 7.1 Execução Seca — Serviços Odin

```bash theme={null}
cd terragrunt/environments/nome-do-seu-ambiente
terragrunt plan -target="helm_release.odin_services"
```

### 7.2 Implantar Serviços Odin

```bash theme={null}
cd terragrunt/environments/nome-do-seu-ambiente
terragrunt apply -target="helm_release.odin_services"
```

**Verificação**

```bash theme={null}
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

```bash theme={null}
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

```bash theme={null}
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**

```bash theme={null}
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

```bash theme={null}
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

```bash theme={null}
# 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**

```bash theme={null}
terragrunt force-unlock <lock-id>
```

**Karpenter não funciona**

```bash theme={null}
kubectl describe nodes
kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter
```

**Problemas com balanceador de carga**

```bash theme={null}
kubectl describe ingress -n default
kubectl logs -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller
```

**Problemas com charts Helm**

```bash theme={null}
helm status <release-name> -n <namespace>
helm rollback <release-name> <revision> -n <namespace>
```

***

## Limpeza

```bash theme={null}
# 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

```bash theme={null}
# 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

```bash theme={null}
# 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
```

<Info>
  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.
</Info>
