Um serviço Spring Boot virou imagem, chegou à AWS e agora precisa de um lugar para rodar. Nesse ponto, é comum a conversa saltar de “temos um JAR” para “precisamos de um cluster Kubernetes”. O salto parece arquitetural; a cobrança do control plane continua sendo bastante literal.
A comparação útil para um caminho gerenciado em 2026 é entre ECS Fargate e EKS Auto Mode. Não entre ECS e um EKS montado com peças escolhidas ao acaso, nem entre ECS Fargate e Fargate-on-EKS. A decisão ficará apoiada em duas variáveis observáveis:
- a aplicação exige algo no nível do nó, como
DaemonSet, modo privilegiado,HostNetworkou GPU? - o piso do control plane EKS é justificável diante das replica-horas e do tamanho de CPU e memória do workload?
Requisições HTTP, sozinhas, não respondem à segunda pergunta. Fargate cobra recursos alocados ao longo do tempo, não aplausos por endpoint acessado.
Pré-requisitos e recorte da comparação
Os exemplos usam:
- Spring Boot 4.1.1;
- Java 25 LTS com Eclipse Temurin 25;
- Linux x86_64;
- região
us-east-1; - uma imagem
spring-api:4.1.1-jre25já publicada no Amazon ECR; - AWS CDK 2.267.0, quando a infraestrutura é mostrada em código;
- Kubernetes 1.36 no EKS Auto Mode.
O Spring Boot 4.1.1 requer Java 17 ou mais recente, funciona com Java até a versão 26 e requer Spring Framework 7.0.9 ou mais recente. Java 25 é a escolha LTS conjunta para o exemplo; Java 26 não é LTS. O baseline, portanto, não volta ao Boot 3 apenas porque ele ainda aparece em muitos repositórios.
Também ficam fora da conta valores de ALB, NAT Gateway, IPv4 público, tráfego, CloudWatch e armazenamento adicional. Eles existem nos dois desenhos em combinações diferentes e devem entrar na estimativa do projeto, mas inventar uma fatura completa com meia tabela seria só uma maneira caprichada de errar.
Primeiro, o piso que aparece antes da aplicação
A página de preços do AWS Fargate fornece, em seus exemplos para Linux/x86 em US East (N. Virginia), os seguintes valores por segundo:
- US$ 0.000011244 por vCPU-segundo;
- US$ 0.000001235 por GB-segundo.
A cobrança começa no pull da imagem e termina quando a task ou o pod acaba, com arredondamento por segundo e mínimo de um minuto para Linux. Há 20 GB de armazenamento efêmero incluídos; armazenamento adicional é outra dimensão da cobrança.
Para tornar a comparação legível, a conversão abaixo é aritmética explícita, não um preço por hora impresso pela AWS:
vCPU-hora = 0.000011244 × 3.600 = US$ 0.0404784
GB-hora = 0.000001235 × 3.600 = US$ 0.004446
1 vCPU por 730 h = 0.0404784 × 730 = US$ 29.55
2 GB por 730 h = 0.004446 × 2 × 730 = US$ 6.49
compute Fargate/mês ≈ US$ 36.04
Essa tarefa de 1 vCPU e 2 GB ligada por 730 horas é uma unidade simples de comparação, não uma previsão de fatura. Se houver duas réplicas durante o mês inteiro, multiplica-se o compute por duas. Se houver escalabilidade ou desligamento fora do expediente, use as replica-horas reais.
O Amazon ECS não cobra uma taxa adicional pela orquestração. No caminho ECS Fargate, portanto, não existe uma linha equivalente à taxa de cluster EKS.
Já o Amazon EKS cobra pelo control plane:
- US$ 0.10 por cluster-hora durante o suporte padrão;
- US$ 0.60 por cluster-hora durante o suporte estendido.
Em 730 horas, isso representa US$ 73 no suporte padrão ou US$ 438 no estendido, antes do data plane. No Auto Mode, o data plane ainda inclui o custo das instâncias EC2 e a taxa do próprio Auto Mode. Os exemplos públicos dessa taxa que usam Oregon não devem ser transplantados para us-east-1; por isso, nenhum preço de instância ou de Auto Mode será improvisado aqui.
Para um único serviço de 1 vCPU e 2 GB executado 24×7, o piso de US$ 73 do control plane padrão já supera os aproximadamente US$ 36.04 de compute Fargate calculados acima. Isso não prova que EKS é sempre mais caro. Mostra que o cluster começa a conversa com um custo fixo que precisa ser amortizado entre workloads ou justificado por uma capacidade de Kubernetes.
| Dimensão | ECS Fargate | EKS Auto Mode |
|---|---|---|
| Orquestração/control plane | Sem taxa adicional do ECS | US$ 0.10/cluster-h padrão; US$ 0.60/h estendido |
| Data plane | vCPU, memória e demais dimensões Fargate | EC2 e taxa Auto Mode, além do control plane |
| Unidade útil para estimar | replica-horas × CPU/memória da task | cluster-horas + recursos e horas dos nós |
| API operacional | ECS task/service | Kubernetes API |
| Recurso por nó | Não há nó do cliente | Disponível conforme o tipo de nó e configuração |
O mesmo JAR como task do ECS Fargate
Uma definição mínima para 1 vCPU e 2 GB separa as duas identidades IAM que costumam ser misturadas:
{
"family": "spring-api",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "1024",
"memory": "2048",
"executionRoleArn": "arn:aws:iam::111122223333:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::111122223333:role/spring-api-task",
"runtimePlatform": {
"cpuArchitecture": "X86_64",
"operatingSystemFamily": "LINUX"
},
"containerDefinitions": [
{
"name": "app",
"image": "111122223333.dkr.ecr.us-east-1.amazonaws.com/spring-api:4.1.1-jre25",
"essential": true,
"portMappings": [
{"containerPort": 8080, "protocol": "tcp"}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/spring-api",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "app"
}
}
}
]
}
O task execution role é usado pelo agente ECS/Fargate para ações como baixar a imagem do ECR e enviar logs pelo driver awslogs. A aplicação dentro do container não recebe essas credenciais. O task role é a identidade do código Java para acessar S3, SQS, DynamoDB ou outro serviço permitido.
A trust policy do task role deve aceitar ecs-tasks.amazonaws.com e restringir a origem para reduzir o risco de confused deputy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"Service": "ecs-tasks.amazonaws.com"},
"Action": "sts:AssumeRole",
"Condition": {
"ArnLike": {
"aws:SourceArn": "arn:aws:ecs:us-east-1:111122223333:*"
},
"StringEquals": {
"aws:SourceAccount": "111122223333"
}
}
}
]
}
Se a task não consegue baixar a imagem ou inicializar o log driver, corrija permissões e configuração do execution role; observe a implantação pelos eventos do serviço ECS e pelos logs do container no CloudWatch. Se a aplicação recebe AccessDenied ao acessar um recurso, corrija a policy do task role, não aumente a policy de execução por reflexo.
Com CDK 2.267.0, o núcleo da definição deixa explícitos CPU e memória. Os defaults de 256 unidades de CPU e 512 MiB não representam a task calculada acima:
import * as ecs from 'aws-cdk-lib/aws-ecs';
const task = new ecs.FargateTaskDefinition(this, 'SpringTask', {
cpu: 1024,
memoryLimitMiB: 2048,
runtimePlatform: {
cpuArchitecture: ecs.CpuArchitecture.X86_64,
operatingSystemFamily: ecs.OperatingSystemFamily.LINUX,
},
});
task.addContainer('app', {
image: ecs.ContainerImage.fromEcrRepository(repo, '4.1.1-jre25'),
portMappings: [{ containerPort: 8080 }],
logging: ecs.LogDrivers.awsLogs({ streamPrefix: 'spring-api' }),
});
new ecs.FargateService(this, 'SpringService', {
cluster: ecsCluster,
taskDefinition: task,
});
Quando a capacidade subjacente falha, não há uma instância EC2 do cliente para reparar ou acessar por SSH. O scheduler do serviço ECS mantém a contagem desejada e coloca uma nova task. Para confirmar substituições e falhas de placement sem adivinhar o que ocorreu, consulte os eventos do serviço ECS e, se habilitado, as métricas do CloudWatch Container Insights.
O mesmo JAR no EKS Auto Mode
No EKS, a unidade passa a ser um Deployment. O manifesto abaixo mantém o mesmo pedido e limite de 1 CPU e 2 GiB por réplica:
apiVersion: v1
kind: ServiceAccount
metadata:
name: spring-api
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-api
spec:
replicas: 2
selector:
matchLabels:
app: spring-api
template:
metadata:
labels:
app: spring-api
spec:
serviceAccountName: spring-api
containers:
- name: app
image: 111122223333.dkr.ecr.us-east-1.amazonaws.com/spring-api:4.1.1-jre25
ports:
- containerPort: 8080
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "1"
memory: "2Gi"
Para acesso da aplicação a serviços AWS, o EKS Pod Identity associa uma role IAM ao ServiceAccount. A trust policy da role usa o principal pods.eks.amazonaws.com:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowEksAuthToAssumeRoleForPodIdentity",
"Effect": "Allow",
"Principal": {"Service": "pods.eks.amazonaws.com"},
"Action": ["sts:AssumeRole", "sts:TagSession"]
}
]
}
Pod Identity é o caminho mais simples para código novo e não exige anotação no ServiceAccount. IRSA continua disponível e usa o provedor OIDC do cluster. Nos dois casos, a correção para um pod sem autorização é revisar a associação, a role e a policy da aplicação; kubectl describe pod e a inspeção do ServiceAccount mostram identidade, eventos e configuração observável no cluster.
No CDK, o módulo aws-eks-v2 cria Auto Mode por padrão. O módulo clássico aws-eks continua existindo; não é preciso inventar uma depreciação para justificar código novo no v2.
import * as eks from 'aws-cdk-lib/aws-eks-v2';
const cluster = new eks.Cluster(this, 'Apps', {
version: eks.KubernetesVersion.V1_36,
});
Se um nó do Auto Mode se torna não saudável, os recursos de monitoramento e reparo gerenciados pelo serviço atuam sobre o nó, e o scheduler volta a posicionar os pods conforme as restrições do workload. A aplicação, os requests, os limites e um PodDisruptionBudget que bloqueia a movimentação continuam sendo responsabilidade do time. Use kubectl describe node, kubectl describe pod e os eventos do cluster para observar falhas de agendamento; habilite Container Insights quando precisar correlacionar a saúde dos workloads no CloudWatch.
Um Managed Node Group muda essa divisão: as instâncias ficam na conta, o grupo é apoiado por Auto Scaling e o reparo automático é opcional. O EKS gerencia o control plane, mas o time ainda escolhe estratégia de atualização, capacidade e configuração dos nós. Auto Mode reduz esse trabalho; não elimina a API Kubernetes nem seu ciclo de versões.
Quando o cluster realmente vale o que custa
A primeira variável é booleana e técnica: há requisito no nível do nó? Um agente obrigatório entregue como DaemonSet, uso de privileged, HostNetwork, HostPort, GPU ou uma integração de rede/armazenamento dependente do nó aponta para EKS com nós Auto Mode ou Managed Node Groups. Um sidecar comum não basta para concluir isso: ECS Fargate aceita vários containers na mesma task, e Kubernetes aceita sidecars no pod.
A segunda variável é econômica: qual é a relação entre o piso do control plane e o compute do workload? Monte a conta com dados que possam ser medidos:
piso EKS padrão = horas do cluster × US$ 0.10
compute Fargate = vCPU-segundos × US$ 0.000011244
+ GB-segundos × US$ 0.000001235
Depois compare usando as replica-horas esperadas, não “tráfego mensal”. Dois serviços com o mesmo número de requests podem reservar CPU e memória muito diferentes. Métrica de negócio não se transforma em unidade de cobrança por força de vontade.
EKS tende a fazer sentido quando o time já opera uma plataforma Kubernetes com vários Deployments e namespaces, precisa de CRDs, operators, GitOps ou políticas compartilhadas, ou precisa das capacidades de nó citadas acima. O custo fixo pode então ser amortizado, e a Kubernetes API é parte do requisito, não decoração da arquitetura.
ECS Fargate tende a ser a escolha firme para um JAR interno isolado, sem necessidade de nó e mantido por um time que não opera Kubernetes. Há menos ciclos de atualização e menos objetos de plataforma entre a imagem e o serviço.
A régua de recuo fica objetiva:
- de EKS para ECS Fargate: recue quando não houver requisito de nó e o piso do control plane for igual ou maior que o compute Fargate dos workloads que realmente usariam o cluster;
- de ECS Fargate para EKS Auto Mode ou MNG: recue quando surgir um requisito de nó que sidecar não resolve, ou quando uma plataforma multi-tenant já depender honestamente da Kubernetes API;
- antes de aceitar suporte estendido: atualize a minor do EKS; deixar o cluster parado eleva a taxa de US$ 0.10/h para US$ 0.60/h.
A opção que combina as duas contas
Fargate-on-EKS existe, mas é uma escolha ruim para um único JAR interno neste recorte. O time paga o control plane EKS, paga o compute Fargate e ainda não pode executar DaemonSet em pods Fargate. Também não há suporte ali para modo privilegiado, HostNetwork, HostPort ou GPU.
Se um pod destinado ao perfil Fargate fica Pending por depender dessas capacidades, a correção é transformar o agente em sidecar quando isso for tecnicamente válido ou mover o workload para nós Auto Mode/MNG. kubectl describe pod mostra eventos e razões de agendamento sem exigir uma sessão de adivinhação coletiva.
Fargate-on-EKS pode servir quando uma plataforma Kubernetes já existe e alguns pods isolados combinam com o modelo Fargate. Usá-lo para obter “Kubernetes sem nós” em um serviço único junta o piso do cluster às limitações do Fargate. É o tipo de composição que fica muito elegante no diagrama e um pouco menos na fatura.
Limitações e verificação sem deploy
Os exemplos não medem desempenho e não afirmam diferença de throughput, p95 ou p99. Também não precificam instâncias EC2, taxa Auto Mode, rede, load balancer, logs ou armazenamento. A decisão deve ser refeita com a região, o número de réplicas e os componentes reais do ambiente.
Antes de qualquer deploy, valide os artefatos localmente:
jq empty task-definition.json
kubectl apply --dry-run=client --validate=true -f deployment.yaml
npx cdk synth
cdk synth produz CloudFormation localmente para inspeção. kubectl --dry-run=client valida a construção do manifesto sem falar com um cluster, embora a validação completa de APIs e políticas do ambiente ainda dependa do cluster real.
Não use aws ecs register-task-definition como “dry-run”: o comando registra uma definição de task na conta. Para revisar o JSON, compare-o com os parâmetros oficiais de task definition do ECS e mantenha a etapa local.
Na primeira implantação controlada, acompanhe:
- ECS: eventos do serviço para placement e substituição de tasks; logs e Container Insights para a aplicação;
- EKS:
kubectl describe podekubectl describe nodepara scheduling, identidade e interrupções; eventos do cluster e Container Insights quando habilitado; - IAM: execution role e task role separadas no ECS; associação Pod Identity ou IRSA conferida no EKS.
Se o deploy for automatizado, autentique o GitHub Actions sem chaves longas com OIDC na AWS. Isso resolve a identidade do pipeline; não substitui as roles de runtime da aplicação.
Próximo passo
Pegue um serviço real e anote apenas quatro valores: réplicas mínimas e máximas, horas em cada faixa, vCPU e memória por réplica. Calcule o compute Fargate com os preços por segundo e coloque ao lado o piso EKS de US$ 0.10 por cluster-hora. Depois marque “sim” ou “não” para requisito de nó.
Sem requisito de nó e com o piso do cluster dominando a conta, comece no ECS Fargate. Com requisito de nó ou uma plataforma Kubernetes já compartilhada por vários workloads, avalie EKS Auto Mode. O cluster deve entrar porque resolve uma necessidade observável — não porque o JAR ganhou um Dockerfile e ficou ambicioso.