# Design Doc: EKS Multi-Tenant em Escala — Karpenter, Isolamento e Custo

Este documento descreve a arquitetura de uma plataforma EKS multi-tenant para dezenas de equipes, cobrindo isolamento de workloads via namespaces, network policies e RBAC, autoscaling e consolidação com Karpenter usando Spot, e alocação de custo por tenant via Kubecost. O objetivo é operar com segurança, previsibilidade de custo e eficiência de infraestrutura sem sacrificar a autonomia das equipes.

- URL: https://fernando.moretes.com/studies/design-doc-eks-multitenant-em-escala

- Markdown: https://fernando.moretes.com/studies/design-doc-eks-multitenant-em-escala/study.md?lang=pt

- Type: Design Doc / RFC

- Company: Plataforma multi-tenant (cenário)

- Domain: Kubernetes / Custo

- Date: 2026-03-06

- Tags: eks, kubernetes, multi-tenant, karpenter, spot, kubecost, irsa, cost-optimization

- Reading time: 10 min

---

Rodar dezenas de tenants num único cluster EKS é uma aposta de eficiência que só funciona se isolamento, custo e escala forem tratados como cidadãos de primeira classe desde o dia zero — não como ajustes tardios.

## O Problema

Plataformas internas de Kubernetes crescem de forma orgânica. Uma equipe cria um cluster, outra pede acesso, e em dezoito meses você tem quarenta times compartilhando infraestrutura que foi desenhada para cinco. O resultado típico é um cluster onde ninguém sabe exatamente o que custa o quê, workloads críticos competem com jobs de batch por nós, e o blast radius de um namespace mal configurado pode afetar vizinhos.

O cenário que este documento aborda é uma plataforma EKS gerenciada por uma equipe de plataforma central, servindo entre 30 e 60 times de produto. Cada time é um tenant: tem seus próprios namespaces, repositórios, pipelines e, idealmente, sua própria conta de custo. O cluster precisa escalar de forma reativa e econômica, manter isolamento forte o suficiente para satisfazer auditoria de segurança, e produzir dados de custo granulares o suficiente para showback ou chargeback.

Os três vetores de falha mais comuns nesse modelo são: (1) **isolamento insuficiente** — um tenant consome recursos de outro por ausência de quotas ou network policies, ou pior, consegue escalar privilégios via RBAC mal configurado; (2) **custo invisível** — sem labels consistentes e ferramentas de alocação, a fatura da AWS vira um número agregado que ninguém consegue contestar ou otimizar; (3) **autoscaling lento ou caro** — Cluster Autoscaler com node groups fixos não acompanha picos heterogêneos de workload e tende a superprovisionamento por segurança. Karpenter resolve o terceiro vetor, mas precisa de guardrails para não virar um vetor de custo descontrolado em mãos de tenants sem restrições.

## Objetivos e Não-Objetivos

- ✅ GOAL: Isolamento de tenant com namespace dedicado, ResourceQuota, LimitRange, NetworkPolicy padrão e RBAC com menor privilégio
- ✅ GOAL: Autoscaling reativo e consolidação de nós via Karpenter com suporte a Spot e fallback On-Demand
- ✅ GOAL: Alocação de custo por tenant com granularidade de namespace via Kubecost (showback no MVP, chargeback no futuro)
- ✅ GOAL: Identidade de workload via IRSA / EKS Pod Identity sem credenciais estáticas
- ✅ GOAL: Separação de node pools por classe de workload (sistema, produção, batch/spot) via NodePool e NodeClass do Karpenter
- ❌ NON-GOAL: Isolamento em nível de cluster por tenant (cluster-per-tenant não está no escopo desta iteração)

## Ficha Técnica

- **Plataforma:** EKS (cenário composto, baseado em práticas reais)
- **Tenants:** 30–60 times de produto
- **Escala estimada:** 500–1500 pods em produção, picos de batch até 3x
- **Região principal:** us-east-1 (multi-AZ: 3 AZs)
- **Versão EKS:** 1.29+ (gerenciado, auto-upgrade via pipeline)
- **Autoscaler:** Karpenter v1.x (substituindo Cluster Autoscaler)
- **Custo / observabilidade:** Kubecost + AWS Cost Allocation Tags + Container Insights
- **Identidade de workload:** EKS Pod Identity (preferido) + IRSA (legado)
- **Rede:** VPC CNI + Network Policies (Calico ou VPC CNI nativo)
- **Economia Spot estimada:** 50–70% vs On-Demand para workloads de batch (estimativa de mercado)

## Design Proposto: Isolamento em Camadas

O modelo de isolamento adotado é **namespace-as-tenant-boundary**, com reforço em quatro camadas independentes. Nenhuma camada sozinha é suficiente; a defesa em profundidade é o princípio.

**Camada 1 — Namespace e identidade:** Cada tenant recebe um namespace dedicado (ou um conjunto deles, por ambiente: `team-x-prod`, `team-x-staging`). O provisionamento é automatizado via GitOps (ArgoCD ou Flux): um PR no repositório de plataforma cria o namespace, aplica ResourceQuota e LimitRange padrão, e cria o RoleBinding que concede ao ServiceAccount da equipe permissões de `edit` dentro do namespace, nunca `cluster-admin`. A política de RBAC segue o princípio de menor privilégio: equipes não conseguem criar ClusterRoles, acessar segredos de outros namespaces, ou modificar NetworkPolicies — essas operações são reservadas à equipe de plataforma.

**Camada 2 — Rede:** Uma NetworkPolicy `default-deny-all` é aplicada a cada namespace no momento da criação. Equipes precisam declarar explicitamente as políticas de ingress e egress que seus workloads necessitam. O tráfego entre namespaces é bloqueado por padrão; exceções são concedidas via política revisada pela equipe de plataforma. Isso elimina o risco de um tenant acessar inadvertidamente APIs internas de outro tenant. O DNS interno do cluster (`kube-dns`) permanece acessível para todos os namespaces, mas o acesso à API do Kubernetes a partir de pods é restrito via NetworkPolicy para o CIDR do control plane.

**Camada 3 — Compute e quotas:** ResourceQuota define limites de CPU, memória, número de pods, e contagem de objetos (Services, ConfigMaps, Secrets) por namespace. LimitRange define defaults de `requests` e `limits` para containers sem especificação explícita — isso é crítico para que o scheduler do Kubernetes e o Karpenter tomem decisões corretas de bin-packing. Workloads sem `requests` definidos são tratados como BestEffort e podem ser evicted primeiro sob pressão de memória. A equipe de plataforma define três tiers de quota (small, medium, large) e os tenants solicitam o tier adequado via PR.

**Camada 4 — Identidade de workload:** Nenhum pod deve carregar credenciais AWS estáticas. O modelo preferido é **EKS Pod Identity**, que associa um ServiceAccount Kubernetes a uma IAM Role via o agente `eks-pod-identity-agent` rodando como DaemonSet. A role é criada com escopo mínimo e revisada pelo time de segurança. IRSA permanece suportado para workloads legados, mas novos onboardings usam Pod Identity. Isso elimina a classe de vulnerabilidade onde credenciais são expostas em variáveis de ambiente ou ConfigMaps.

## Arquitetura: EKS Multi-Tenant com Karpenter

Visão do cluster EKS mostrando separação de node pools por classe de workload, isolamento de namespace por tenant, fluxo de autoscaling via Karpenter e integração com serviços AWS de identidade e custo.

### 👤 Tenants / GitOps

- Dev Teams PR → Git (user)
- ArgoCD / Flux GitOps Controller (ci)

### 🔐 AWS Control Plane

- EKS Control Plane API Server / etcd (compute)
- IAM Pod Identity / IRSA (security)
- ECR Container Registry (storage)

### ⚙️ Platform Namespace (kube-system / platform)

- Karpenter NodePool Controller (compute)
- Kubecost Cost Allocation (data)
- Pod Identity Agent DaemonSet (security)
- OPA / Kyverno Admission Webhook (security)

### 🟦 Tenant A — team-a-prod

- Namespace: team-a-prod ResourceQuota + LimitRange (compute)
- Pods (team-a) ServiceAccount → IAM Role A (compute)
- NetworkPolicy default-deny + allow rules (security)

### 🟩 Tenant B — team-b-prod

- Namespace: team-b-prod ResourceQuota + LimitRange (compute)
- Pods (team-b) ServiceAccount → IAM Role B (compute)
- NetworkPolicy default-deny + allow rules (security)

### 🖥️ EC2 Node Pools

- NodePool: system On-Demand, m6i, tainted (compute)
- NodePool: production On-Demand + Spot fallback m6i / m7i / c6i (compute)
- NodePool: batch Spot-first, diverse r6i / m6i / c6i (compute)

### 📊 Observabilidade / Custo

- CloudWatch Container Insights (data)
- S3 + CUR Cost & Usage Report (storage)
- AWS Cost Allocation Tags team / env / service (data)

### Fluxos

- dev -> gitops: PR / merge
- gitops -> eks-cp: apply manifests
- eks-cp -> karpenter: pending pods
- karpenter -> np-prod: provision node
- karpenter -> np-batch: provision spot
- karpenter -> np-system: system nodes
- eks-cp -> ns-a
- eks-cp -> ns-b
- ns-a -> pod-a
- ns-a -> netpol-a
- ns-b -> pod-b
- ns-b -> netpol-b
- pod-a -> pod-identity-agent: assume role A
- pod-b -> pod-identity-agent: assume role B
- pod-identity-agent -> iam
- admission -> ns-a: validar / mutar
- admission -> ns-b: validar / mutar
- kubecost -> ns-a: scrape metrics
- kubecost -> ns-b: scrape metrics
- kubecost -> s3-cost: exportar CUR
- cost-tags -> s3-cost
- np-prod -> cw
- np-batch -> cw

## Karpenter: NodePools, Spot e Consolidação

Karpenter substitui o Cluster Autoscaler com uma abordagem fundamentalmente diferente: em vez de escalar node groups pré-definidos, ele observa pods `Unschedulable` e provisiona o nó mais adequado diretamente via EC2 Fleet API, respeitando os constraints declarados no pod (nodeSelector, affinity, tolerations, topology spread). Isso permite bin-packing mais eficiente e latência de provisionamento significativamente menor.

**Estrutura de NodePools:** Defino três NodePools com responsabilidades distintas:

1. **system** — nós On-Demand dedicados para workloads de plataforma (Karpenter, CoreDNS, Kubecost, admission webhooks). Esses nós têm um taint `CriticalAddonsOnly` e são excluídos da consolidação agressiva. Uso instâncias `m6i.large` ou `m6i.xlarge` fixas para previsibilidade.

2. **production** — nós para workloads de produto com SLA. Estratégia: On-Demand como base, com capacidade Spot para até 30% dos nós quando disponível. O `EC2NodeClass` define uma lista diversificada de tipos de instância (`m6i`, `m7i`, `c6i`, `c7i`) para maximizar disponibilidade de Spot. `karpenter.sh/capacity-type: [on-demand, spot]` com `spot-to-on-demand-ratio` controlado via weight. PodDisruptionBudgets são obrigatórios para workloads neste pool — sem PDB, o Karpenter pode consolidar nós e derrubar todos os pods de um Deployment de uma vez.

3. **batch** — nós Spot-first para jobs de ML, ETL e CI. Aqui aceito interrupção com tolerância declarada (`karpenter.sh/interruption-queue` integrado ao SQS para receber notificações de interrupção EC2 e drenar nós graciosamente antes da terminação). A diversidade de tipos de instância é máxima neste pool — `r6i`, `m6i`, `c6i`, `r7i`, `m7i` em múltiplos tamanhos — para garantir que o Spot Placement Score seja alto.

**Consolidação:** Habilito `consolidationPolicy: WhenUnderutilized` para o pool de batch e `WhenEmpty` para produção. A diferença é importante: `WhenUnderutilized` permite ao Karpenter mover pods entre nós para liberar nós subutilizados (bin-packing ativo), enquanto `WhenEmpty` só remove nós completamente vazios. Para produção, a consolidação agressiva pode causar disruption excessiva; prefiro `WhenEmpty` com PDBs bem configurados.

**Guardrails de custo:** Sem restrições, um tenant pode criar um Job que solicita 1000 pods de 4 vCPU cada, e o Karpenter vai provisionar 250 nós `c6i.xlarge` em segundos. Para prevenir isso, ResourceQuota no namespace limita o total de CPU/memória requisitável. Adicionalmente, o `NodePool` define `limits.cpu` e `limits.memory` globais — o Karpenter não provisiona além desses limites, mesmo que haja pods pendentes. Isso cria um teto de custo por pool.

## Alternativas Avaliadas

### Cluster por Tenant (Hard Multi-Tenancy)

**Pros**
- Isolamento máximo: blast radius contido por cluster
- Upgrade e configuração independentes por tenant
- Adequado para requisitos de conformidade rígidos (PCI, HIPAA)

**Cons**
- Custo de controle plane: ~$0.10/h por cluster EKS = overhead significativo para muitos tenants
- Overhead operacional: upgrades, patches e monitoramento multiplicados por N clusters
- Fragmentação de capacidade: cada cluster precisa de nós mínimos para sistema

**Verdict:** Reservado para tenants com requisitos de conformidade que o cluster compartilhado não pode satisfazer

### Cluster Autoscaler (manter legado)

**Pros**
- Maduro e amplamente documentado
- Sem necessidade de migração

**Cons**
- Latência de scale-up: 2–5 min vs ~30s do Karpenter
- Node groups fixos: difícil diversificar tipos de instância para Spot
- Consolidação limitada: não realiza bin-packing ativo
- Overhead de configuração: dezenas de ASGs para cobrir diversidade de workloads

**Verdict:** Rejeitado: o custo de oportunidade de não migrar para Karpenter é alto em escala

### Namespace-as-Tenant (proposta)

**Pros**
- Eficiência de custo: bin-packing máximo, um único control plane
- Operação centralizada: upgrades, políticas e observabilidade unificados
- Onboarding rápido: GitOps automatiza criação de namespace em minutos

**Cons**
- Isolamento de kernel compartilhado: vulnerabilidades de container escape afetam todos os tenants
- Noisy neighbor em CPU/memória se quotas não forem bem calibradas
- Não adequado para conformidade PCI/HIPAA sem controles adicionais significativos

**Verdict:** Adotado: melhor trade-off para o perfil de tenants descrito (equipes de produto sem requisitos de conformidade rígidos)

### Service Mesh (Istio) para isolamento de rede

**Pros**
- mTLS automático entre serviços, observabilidade de tráfego L7
- Políticas de autorização mais expressivas que NetworkPolicy

**Cons**
- Overhead de sidecar: ~50–100MB RAM e ~0.5 vCPU por pod (estimativa)
- Complexidade operacional alta: curva de aprendizado, debugging de tráfego
- Latência adicional no data path

**Verdict:** Adiado: NetworkPolicy cobre o modelo de ameaça atual; revisitar quando mTLS entre serviços for requisito explícito

## Modelos de Alocação de Custo
| Critério | Modelo | Granularidade | Precisão | Complexidade Operacional | Adequado para Chargeback? |
| --- | --- | --- | --- | --- | --- |
| Tags de Custo AWS (puro) | Conta / Serviço AWS | Baixa (não vê pod) | Baixa | Não — muito agregado | — |
| Kubecost (proposta) | Namespace / Label / Pod | Alta (modela CPU+RAM+net+storage) | Média (requer labels consistentes) | Sim — com labels corretos | — |
| OpenCost (alternativa OSS) | Namespace / Label | Média | Baixa (sem UI enterprise) | Parcialmente — requer integração custom | — |
| Split Cost Allocation (AWS nativo) | EKS namespace (via CUR) | Média (proporcional a requests) | Baixa (configuração no console) | Sim — integrado ao CUR/Cost Explorer | — |

## Decisão: EKS Pod Identity sobre IRSA para novos workloads

**Status:** accepted

**Contexto**

IRSA (IAM Roles for Service Accounts) requer anotação no ServiceAccount e configuração de OIDC provider. EKS Pod Identity, lançado em 2023, simplifica o modelo: o agente no nó intercepta chamadas ao IMDS e injeta credenciais sem necessidade de anotação OIDC. Suporta múltiplas roles por ServiceAccount e não tem o limite de 100 OIDC providers por conta.

**Decisão**

Novos workloads usam EKS Pod Identity. Workloads legados com IRSA são migrados oportunisticamente. O agente eks-pod-identity-agent é instalado como add-on gerenciado em todos os node groups.

**Consequências**
- Simplificação do onboarding: equipes não precisam configurar OIDC trust policies manualmente
- Redução de risco: sem dependência de OIDC endpoint externo para autenticação de workload
- Período de coexistência IRSA + Pod Identity durante migração: documentar claramente qual mecanismo cada workload usa

## Plano de Rollout

1. **Fase 0 — Fundação (Semanas 1–2)** — Instalar Karpenter via Helm no cluster existente (sem remover Cluster Autoscaler ainda). Criar NodePool `system` com taints. Configurar EC2NodeClass com AMI family AL2023, subnets privadas e security groups corretos. Validar que workloads de plataforma (CoreDNS, Kubecost, admission webhooks) são agendados nos nós system. Instalar eks-pod-identity-agent como add-on gerenciado.

2. **Fase 1 — Migração de Autoscaling (Semanas 3–4)** — Criar NodePools `production` e `batch`. Migrar 20% dos workloads de produção para nós gerenciados pelo Karpenter (via nodeSelector). Monitorar latência de provisionamento, taxa de Spot interruption e comportamento de consolidação. Após validação, escalar para 100% dos workloads e remover Cluster Autoscaler. Configurar fila SQS para interrupção Spot e validar drenagem graciosa.

3. **Fase 2 — Isolamento de Tenant (Semanas 5–7)** — Implementar pipeline GitOps de onboarding de namespace: PR cria namespace, aplica ResourceQuota (tier selecionado), LimitRange, NetworkPolicy default-deny, RoleBinding. Instalar e configurar OPA Gatekeeper ou Kyverno com políticas de: (a) rejeitar pods sem resource requests, (b) rejeitar imagens sem digest ou de registries não aprovados, (c) rejeitar pods com `hostNetwork: true` ou `privileged: true` fora do namespace de plataforma. Auditar namespaces existentes e remediar violações.

4. **Fase 3 — Alocação de Custo (Semanas 8–10)** — Configurar Kubecost com integração ao CUR (Cost & Usage Report) via S3. Definir taxonomia de labels obrigatórios: `team`, `env`, `service`, `cost-center`. Implementar política Kyverno que rejeita Deployments sem os labels obrigatórios. Habilitar AWS Split Cost Allocation para EKS no Cost Explorer como validação cruzada. Publicar dashboard de showback por tenant. Definir processo de revisão mensal de custo com equipes.

5. **Fase 4 — Hardening e Observabilidade (Semanas 11–12)** — Migrar workloads legados de IRSA para EKS Pod Identity. Habilitar EKS control plane logging (API, audit, authenticator) para CloudWatch. Configurar alertas de custo por namespace no Kubecost (threshold por tier de quota). Implementar runbook de resposta a Spot interruption. Realizar exercício de chaos: simular interrupção de nós Spot em produção e validar que PDBs previnem indisponibilidade. Documentar e publicar guia de onboarding para equipes.

> **Riscos e Mitigações:** **R1 — Consolidação Karpenter derruba workloads sem PDB:** O Karpenter pode consolidar nós e evictar todos os pods de um Deployment de réplica única. Mitigação: política Kyverno que rejeita Deployments com `replicas: 1` em produção sem PDB correspondente. Alertar equipes durante onboarding.

**R2 — Spot interruption em pico de produção:** Se o NodePool de produção tiver proporção Spot alta e o mercado EC2 estiver apertado, múltiplos nós podem ser interrompidos simultaneamente. Mitigação: limitar Spot a 30% do NodePool de produção; diversificar tipos de instância; configurar `topologySpreadConstraints` para distribuir pods entre AZs e tipos de nó.

**R3 — Tenant escapa de namespace via RBAC misconfiguration:** Um RoleBinding incorreto pode conceder acesso a recursos fora do namespace. Mitigação: auditoria periódica de RBAC com `kubectl-who-can`; política de admission que bloqueia criação de ClusterRoleBindings por ServiceAccounts de tenant.

**R4 — Kubecost com dados imprecisos por labels inconsistentes:** Workloads sem labels corretos aparecem como custo não alocado, invalidando o showback. Mitigação: política Kyverno obrigatória + período de grace de 30 dias para equipes existentes migrarem.

**R5 — Versão EKS desatualizada aumenta superfície de ataque:** CVEs em versões antigas do Kubernetes são exploráveis. Mitigação: pipeline de upgrade automatizado com janela de manutenção; política de não suportar versões com mais de 2 minor releases de distância da versão atual do EKS.

## Avaliação Well-Architected

- **security**: Forte: RBAC com menor privilégio, NetworkPolicy default-deny, EKS Pod Identity sem credenciais estáticas, admission webhooks bloqueando configurações inseguras, audit logging habilitado.
- **reliability**: Boa: PDBs obrigatórios, topologySpreadConstraints, Spot com fallback On-Demand, consolidação conservadora em produção. Risco residual: interrupção Spot simultânea em múltiplos nós.
- **performance**: Alta: Karpenter provisiona nós em ~30s vs 2–5min do CAS; bin-packing ativo reduz fragmentação de recursos; LimitRange garante que todos os pods têm requests definidos para scheduling correto.
- **sustainability**: Positivo: consolidação de nós reduz instâncias ociosas; Spot utiliza capacidade EC2 excedente; bin-packing eficiente reduz footprint total do cluster.

## Métricas de Sucesso e Targets

- **Latência de provisionamento de nó (p95):** < 60 segundos (baseline CAS: ~3 minutos)
- **Utilização média de CPU dos nós:** > 60% (baseline típico sem consolidação: 30–40%)
- **% de custo alocado (Kubecost):** > 90% (custo não alocado < 10%)
- **Proporção Spot em workloads batch:** > 70% dos nós batch rodando em Spot
- **Violações de NetworkPolicy bloqueadas:** 100% de namespaces com default-deny aplicado (auditado mensalmente)
- **Tempo de onboarding de novo tenant:** < 30 minutos (PR aprovado → namespace pronto)
- **Incidentes de cross-tenant data access:** 0 (medido via audit log do EKS)
- **Redução de custo EC2 vs baseline (estimativa):** 25–40% via consolidação + Spot (estimativa; depende do mix de workload)

> **Minha Perspectiva Senior:** O erro mais comum que vejo em plataformas multi-tenant Kubernetes não é técnico — é de sequenciamento. Equipes instalam Karpenter primeiro (porque é empolgante e os resultados são imediatos) e deixam isolamento e alocação de custo para depois. Seis meses depois, você tem um cluster eficiente que ninguém consegue auditar e onde um bug de quota pode causar um bill surpresa de cinco dígitos.

Minha recomendação é inverter a ordem de prioridade: **fundação de isolamento primeiro, autoscaling depois, custo em paralelo**. ResourceQuotas e NetworkPolicies são baratas de implementar e difíceis de retroagir — cada namespace sem default-deny é uma dívida técnica de segurança que cresce com o número de tenants.

Sobre Karpenter especificamente: a feature de consolidação é poderosa, mas precisa ser calibrada com cuidado em produção. Eu sempre começo com `WhenEmpty` e só movo para `WhenUnderutilized` depois de ter PDBs validados em todos os workloads críticos. A diferença entre os dois modos é a diferença entre 'remover nós vazios' e 'mover pods ativamente' — o segundo pode causar disruption inesperada se PDBs estiverem mal configurados ou ausentes.

Sobre custo: Kubecost é bom, mas a precisão dele depende inteiramente da disciplina de labels. Sem uma política de admission que force labels obrigatórios, você vai ter 30% do custo como 'unallocated' e nenhuma equipe vai confiar nos números. A política Kyverno de labels obrigatórios não é opcional — é o que torna o showback crível.

Por fim, sobre EKS Pod Identity vs IRSA: migre. O modelo é mais simples, mais seguro (sem OIDC thumbprint para gerenciar), e o agente trata da rotação de credenciais automaticamente. O único motivo para manter IRSA é compatibilidade com workloads que não conseguem ser migrados no curto prazo — e mesmo esses devem ter uma data de migração definida.

## Veredicto

A arquitetura proposta é viável e representa o estado da arte para plataformas EKS multi-tenant em 2024–2025. A combinação de namespace-as-tenant-boundary com NetworkPolicy default-deny, ResourceQuota por tier, Karpenter com NodePools segregados e Kubecost com labels obrigatórios cobre os três vetores de falha identificados: isolamento insuficiente, custo invisível e autoscaling ineficiente.

O design não é perfeito — nenhum cluster compartilhado é. O isolamento de kernel é o limite fundamental: um exploit de container escape afeta todos os tenants. Para organizações com requisitos de conformidade rígidos (PCI-DSS, HIPAA, tenants externos), cluster-per-tenant é a resposta correta, e o custo operacional adicional é justificado pelo modelo de ameaça. Para o perfil descrito — equipes de produto internas sem requisitos de conformidade rígidos — o trade-off de eficiência vs isolamento está bem calibrado.

O risco de execução mais alto é o sequenciamento do rollout. A tentação de pular a Fase 2 (isolamento) para chegar logo na economia de custo da Fase 3 é real. Resista. Um incidente de cross-tenant causado por NetworkPolicy ausente ou RBAC mal configurado custa mais — em tempo de investigação, confiança das equipes e potencial impacto de segurança — do que qualquer economia de infraestrutura que você antecipou.

## Referências

- [Amazon EKS Best Practices Guide](https://docs.aws.amazon.com/eks/latest/best-practices/)
- [Karpenter Documentation](https://karpenter.sh/)
- [EKS Best Practices: Multi-tenancy](https://docs.aws.amazon.com/eks/latest/best-practices/multi-tenancy.html)
- [EKS Best Practices: Security](https://docs.aws.amazon.com/eks/latest/best-practices/security.html)
- [Karpenter: NodePool API Reference](https://karpenter.sh/docs/concepts/nodepools/)
- [Karpenter: Disruption (Consolidation)](https://karpenter.sh/docs/concepts/disruption/)
- [AWS EKS Pod Identity](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html)
- [AWS Split Cost Allocation for EKS](https://docs.aws.amazon.com/cur/latest/userguide/split-cost-allocation-data.html)

## Fontes do caso

- [Amazon EKS — Best Practices Guide](https://docs.aws.amazon.com/eks/latest/best-practices/)
- [Karpenter](https://karpenter.sh/)
