Cluster Autoscaler no EKS compartilhado, com IRSA
TLDR: Instalar o Cluster Autoscaler no EKS compartilhado usando IRSA (OIDC), para que o managed node group escale entre
min=1emax=3automaticamente — resolvendo os podsPendinge deixando o OIDC pronto para uma futura migração para Karpenter.
Contexto
Depois de migrar o n8n (staging e produção) para o EKS compartilhado, réplicas de worker e webhook ficaram Pending com Insufficient cpu/memory — 0/1 nodes are available. O cluster tinha um único nó t3.medium.
O node group está declarado com min_nodes = 1 e max_nodes = 3, mas desired_size = min_nodes e nenhum autoscaler instalado. Um managed node group do EKS não escala sozinho: um Cluster Autoscaler (ou Karpenter) precisa observar pods Pending e ajustar a capacidade desejada. Sem isso o cluster nunca passa de 1 nó.
Decisão: instalar o Cluster Autoscaler (mais simples, funciona com o managed node group existente) usando IRSA (OIDC) para as permissões de IAM. IRSA foi escolhido em vez do atalho de anexar permissão à role do nó porque o Karpenter — o autoscaler pretendido no futuro — exige OIDC/IRSA de qualquer forma. Fazendo agora, o provider OIDC já fica pronto e a migração futura é só trocar a role e o release Helm.
Objetivos
- Cluster Autoscaler rodando, escalando o node group de 1 a 3 conforme pods pendentes.
- Provider OIDC do EKS criado, mais uma role IAM de menor privilégio para a service account do autoscaler.
- Node group com as tags de autodescoberta.
- Nenhum pod do n8n
Pendingdepois do rollout. - Provider OIDC reutilizável por uma futura migração para Karpenter.
Fora de escopo
- Migrar para Karpenter. Esta spec é o alicerce (o OIDC), não a migração.
- Apply local. Ver as restrições de execução abaixo.
Mudanças
src/modules/kubernetes/providers/aws/main.tf
- Provider OIDC:
data.tls_certificatedo issuer do cluster +aws_iam_openid_connect_provider(thumbprint, audiencests.amazonaws.com). ARN e URL expostos como outputs do módulo. - Tags de autodescoberta em
aws_eks_node_group.this:k8s.io/cluster-autoscaler/enabled = "true"ek8s.io/cluster-autoscaler/${cluster_name} = "owned". Elas precisam estar no ASG subjacente — o EKS não as propaga do node group. - Role IRSA:
aws_iam_rolecom trust policy federada ao provider OIDC, escopada asystem:serviceaccount:kube-system:cluster-autoscaler, com as açõesautoscaling:Describe*,SetDesiredCapacity,TerminateInstanceInAutoScalingGroup,ec2:DescribeLaunchTemplateVersions,ec2:DescribeInstanceTypeseeks:DescribeNodegroup.
src/modules/kubernetes/cluster-autoscaler.tf
Os outros addons (datadog, metrics-server, ingress) são helm_release no topo do módulo, usando o provider helm já cabeado no stack. O Cluster Autoscaler segue o mesmo padrão, com count = var.provider_type == "aws" ? 1 : 0: chart cluster-autoscaler do repo https://kubernetes.github.io/autoscaler, namespace kube-system, autoDiscovery.clusterName, a anotação eks.amazonaws.com/role-arn na service account, balance-similar-node-groups, skip-nodes-with-system-pods=false e image.tag pinado na release de CA correspondente ao minor do Kubernetes. depends_on = [module.aws], para que a role e as tags existam primeiro.
src/bootstrap/aws-account/terraform/main.tf
As ações de provider OIDC já estavam na TerraformPolicy. A role IRSA usa uma inline policy (aws_iam_role_policy), que exige iam:PutRolePolicy, iam:GetRolePolicy e iam:DeleteRolePolicy — adicionadas à statement IAMForEKS.
CI — corrigido junto nesta mudança
O CI do kubernetes compartilhado ainda estava cabeado para o layout antigo: apontava para src/shared/kubernetes/terraform com autenticação do provedor antigo, então nunca conseguiria aplicar o stack de EKS. Recabeado para o padrão do messagebroker: working-directory em src/shared/kubernetes/aws/terraform, secrets via ward exec --, sem os passos de SOPS e de kubeconfig do provedor antigo. infra-pr.yml ganhou um job kubernetes-plan — PRs antes não rodavam plan nenhum de k8s.
Bloqueios encontrados na implementação (corrigidos aqui)
Como o stack de EKS nunca havia aplicado via CI, dois problemas pré-existentes só apareceram quando o plan finalmente rodou:
- O EKS se auto-atualizou 1.33 → 1.34 fora do Terraform. A versão divergiu, marcando
aws_eks_clusterpara update in-place, o que tornavacluster_endpointknown after apply. Corrigido subindoTF_VAR_kubernetes_versionpara1.34no vault, para casar com a realidade. - Os providers kubernetes/helm/kubectl caíam em
http://localhost. Eles buscavamhost/tokennos outputs demodule.kubernetes, mas o módulo também contém data sources do Kubernetes, então o host podia ser desconhecido em tempo de plan. Oversions.tffoi refeito para buscar endpoint e CA de umdata.aws_eks_clusterpelo nome fixo do cluster e o token pelo pluginexec(aws eks get-token) — sem depender de output de módulo e sem ciclo.
Como verificar
- O job de
plando PR mostra os recursos novos (provider OIDC, role/policy IRSA, tags do node group, release do autoscaler) e nenhum destroy inesperado. - Depois do merge, o job de
applyfecha verde. kubectl -n kube-system get pods | grep cluster-autoscaler→Running.kubectl -n kube-system logs deploy/cluster-autoscaler→ descobre o ASG, sem erro de autenticação.- Com pods
Pending, um nó novo entra em 2–3 min;kubectl get nodesmostra 2 (até 3) e os pods agendam. - Ao remover carga, o autoscaler remove o nó extra respeitando
min=1. aws iam list-open-id-connect-providersmostra o provider OIDC do EKS.
Restrições de execução
- Tudo por Terraform. Nenhuma mutação manual de
aws/kubectl/helm. Provider OIDC, role/policy IRSA, tags do node group e o release Helm são todos recursos Terraform declarados. O único passo manual permitido é inspeção read-only para depurar. - Aplicado por CI, não localmente. Não rodar
terraform applyde estação de trabalho. O stack é aplicado pelo workflow: o PR rodaplan, o merge rodaapply. - Ordenação: o provider OIDC e a role IRSA (módulo) precisam aplicar antes do release Helm (stack), que consome o ARN da role. Um único apply no stack resolve pelo grafo de dependência.
Documentação
CLAUDE.md— seção de mudanças recentes.- Node group do EKS não escala sozinho — o aprendizado que saiu desta spec.