Conhecer orquestradores de containers se tornou requisito mínimo para Engenheiros de Software. Ferramentas como o ECS (Elastic Container Service) resolvem os desafios de ambiente e deploy que máquinas virtuais tradicionais não conseguem entregar sozinhas.
Como o AWS ECS transforma a maneira de enviar aplicações Spring Boot para produção? Qual a diferença dele para o Kubernetes? Vamos entender de perto como usar e responder essas dúvidas.
Até o final desse artigo teremos criado uma aplicação Spring Boot, provisionado a infra na AWS e feito o deploy no ECS com Fargate. Você vai precisar de uma conta AWS. Caso ainda não possua, siga o passo a passo oficial.
ℹ️ Cobranças podem ser geradas, porém seguindo o passo a passo iremos excluir a infraestrutura ao final para evitar custos extras.
Orquestração de Containers
Em ambientes tradicionais, o deploy acontecia diretamente em máquinas virtuais onde você configurava o sistema operacional, instalava dependências e subia a aplicação. Containers mudaram esse cenário ao empacotar a aplicação junto com tudo que ela precisa, garantindo consistência entre ambientes.
Mas rodar containers em produção traz novos desafios: garantir disponibilidade, escalar sob demanda e substituir instâncias que falhem. O Orquestrador de Containers resolve isso, gerenciando o ciclo de vida de forma declarativa, cuidando de provisionamento, escalonamento e recuperação automática.
O que é o AWS ECS?
O AWS ECS (Elastic Container Service) é o orquestrador de containers nativo da AWS. Ele se integra diretamente com serviços como ALB (Application Load Balancer), CloudWatch e IAM, eliminando a necessidade de configurações extras para conectar essas peças. No ECS, você trabalha com cinco conceitos principais:
- Cluster: agrupamento lógico que reúne seus services e recursos.
- Service: garante que as tasks estejam sempre rodando na quantidade desejada.
- Task Definition: receita do container onde você define imagem, CPU, memória e variáveis de ambiente.
- Task: instância em execução de uma Task Definition.
- Container: processo que roda dentro de cada Task, empacotando sua aplicação.
Ao criar um Service, você escolhe o tipo de infraestrutura: EC2 ou Fargate.
ECS com EC2
No modo EC2, você provisiona e gerencia as instâncias (máquinas virtuais). Isso dá controle total sobre o ambiente, mas você assume a responsabilidade de patching, dimensionamento e manutenção das máquinas. Útil se você precisa controlar o Sistema Operacional ou incluir máquinas virtuais fora da AWS para suportar seu cluster.
ECS com Fargate
No modo Fargate, a AWS cuida de toda a infraestrutura. Você define apenas CPU e memória da task, e o container roda sem que você precise saber em qual servidor ele está. Não há instâncias para gerenciar, não há sistema operacional para atualizar. Usaremos o Fargate neste artigo justamente por essa simplicidade, que nos permite focar na aplicação e não na infraestrutura.
ECS vs K8S
O Kubernetes (K8S) é o líder de mercado para orquestração multi-cloud. Ele oferece um ecossistema gigante e enorme flexibilidade, mas cobra seu preço em alta complexidade operacional e curva de aprendizado íngreme.
Já o AWS ECS vai na direção oposta, priorizando a simplicidade. Sendo nativo da nuvem AWS, ele elimina a gestão do orquestrador. Combinado com Fargate, a equipe foca apenas em definir a imagem e os recursos da aplicação, enquanto a AWS cuida de toda infraestrutura subjacente.
O AWS EKS (Elastic Kubernetes Service) é o meio-termo: gerencia o control plane pela AWS, mantendo a estrutura padrão do Kubernetes para quem não abre mão do ecossistema K8S (como Helm e operadores).
| Critério | AWS ECS | AWS EKS (Kubernetes gerenciado) | Kubernetes (K8S puro) |
|---|---|---|---|
| Operação e Gestão | Baixa (Foco total na aplicação) | Média (Maior manutenção de componentes K8S) | Alta (Gestão completa do ambiente e control plane) |
| Portabilidade | Restrito à nuvem AWS | Alta (Compatível com padrão K8S global) | Total (Roda em qualquer infra ou cloud) |
| Curva de aprendizado | Baixa (Ideal para desenvolvedores) | Alta | Muito Alta |
| Integração AWS | Imediata e transparente | Requer configuração extra | Complexa e manual |
| Extensibilidade | Soluções nativas da AWS (CloudWatch, Auto Scaling, CodeDeploy) | Add-ons e operadores de terceiros (Prometheus, Karpenter, ArgoCD) | Add-ons e operadores de terceiros (Prometheus, Karpenter, ArgoCD) |
Em resumo: Se o foco do time é Multi-Cloud ou casos de uso mais complexos, vá de EKS/K8S. Se o objetivo é resolver o problema e entregar rápido, sem dor de cabeça com gestão de infraestrutura e com integração nativa AWS, o ECS com Fargate é a decisão mais objetiva.
Nosso projeto
Para colocar o ECS em prática, vamos construir a FilePack API: uma aplicação Spring Boot onde o usuário envia vários arquivos, informa uma senha e solicita a geração do pacote. A API recebe os uploads, compacta tudo em um .zip protegido por senha e devolve o artefato para download.
A aplicação não guarda estado: recebe uma requisição multipart/form-data junto com a senha, empacota e devolve para o consumidor. Cenário particularmente útil para transferir arquivos pela rede. Além disso, operações de compactação e criptografia têm custo computacional alto, o que vai permitir explorar uso de CPU e memória futuramente.
Aplicação
Nossa aplicação vai utilizar:
- Java 25 como versão da JDK
- Spring Boot 4.x como base do projeto
- Spring Web para receber uploads e devolver o download do
.zip - Spring Boot Actuator para expor health checks e métricas básicas
- Zip4j para gerar arquivos
.zipprotegidos por senha - Docker para empacotar a aplicação em uma imagem de container
Infraestrutura
Usaremos IaC (Infrastructure as Code) com AWS CloudFormation para provisionar todos esses recursos de forma automatizada e replicável:
- Amazon ECR para armazenar a imagem Docker da aplicação
- Amazon ECS Fargate como orquestrador de containers
- Application Load Balancer para expor a aplicação por HTTP na internet
- Amazon CloudWatch Logs para centralizar os logs da aplicação
- IAM para papéis e permissões mínimas entre ECS, ECR e CloudWatch
AWS CLI
Para fazer o deploy de novas versões e visualizar as configurações da AWS, usaremos o AWS CLI V2. Faça o download aqui.
⚠️ A partir daqui você precisa estar logado na AWS para continuar.
Mão na Massa
A aplicação está disponível no repositório do blog, clique aqui para acessar.
Nosso foco não vai ser o código, todavia é importante que você saiba que essa classe de serviço é o coração da nossa aplicação. O FilePackService recebe os arquivos, aplica criptografia AES e gera o pacote final:
private File createEncryptedZip(List<File> files, String password) throws IOException { ZipParameters params = new ZipParameters(); params.setEncryptFiles(true); // AES é a opção mais segura suportada pelo formato zip params.setEncryptionMethod(EncryptionMethod.AES); // arquivo de saída no diretório temporário File target = File.createTempFile("archive-", ".zip"); ZipFile zipFile = new ZipFile(target, password.toCharArray()); zipFile.addFiles(files, params); return target; }
De forma simples:
- Recebemos uma lista de arquivos
- Compactamos em um Zip
- Protegemos com senha AES
- Retornamos o zip para download
- Esvaziamos os arquivos temporários do disco
A dependência que usamos para gerar o Zip com senha é uma das melhores dentro do Java, chama-se Zip4j, referência aqui.
Lembre-se de realizar o clone do repositório e entrar na pasta do projeto, execute os comandos via GitBash:
git clone https://github.com/devsuperior/blog.git cd articles/deploy-de-aplicacoes-na-aws-com-ecs-fargate/projects/filepack-api
⚠️ Você precisa estar logado na AWS e com o AWS CLI instalado para continuar daqui em diante. Sempre execute os comandos no GitBash mais atualizado quando estiver no Windows.
Obtendo as credenciais
- Acesse o Console da AWS e faça login.
- No GitBash, digite
aws login:
Caso seja solicitado região, digite us-east-1.
- Verifique o login:
aws ec2 describe-regions --region-names us-east-1 --output table

Criando a infraestrutura
Agora que você está logado, podemos iniciar a criação da infraestrutura. Na pasta de infraestrutura do nosso projeto temos 2 arquivos .yml do CloudFormation:
infra/1-ecr.yml: Faz a criação da imagem ECR. ECR é similar ao Dockerhub, é onde versionamos as imagens Docker da nossa aplicação.infra/2-ecs.yml: Faz a criação do restante da infraestrutura (ECS, LoadBalancer, etc).
Entre o deploy do 1-ecr.yml e do 2-ecs.yml faremos a geração da imagem Docker e envio para o repositório remoto criado. Então, vamos iniciar a criação da nossa Infra em 3 etapas.
Criando repositório ECR (1-ecr.yml)
Inicie criando o repositório onde armazenaremos nossa imagem Docker:
aws cloudformation create-stack \ --stack-name filepack-ecr \ --template-body file://infra/1-ecr.yml aws cloudformation wait stack-create-complete \ --stack-name filepack-ecr
Exportando os outputs da stack para variáveis locais. Em vez de depender de adivinhações manuais, vamos filtrar os Outputs que nossa infraestrutura enviou como resposta nativa da stack:
# 1. Fazemos uma única chamada e trazemos os parâmetros como texto formatado, ganhando performance OUTPUTS_ECR=$(aws cloudformation describe-stacks --stack-name filepack-ecr --query "Stacks[0].Outputs[*].[OutputKey,OutputValue]" --output text) # 2. Extraímos as chaves localmente usando o 'awk', que é nativo no Linux, Mac e no Git Bash (Windows) export ACCOUNT_ID=$(echo "$OUTPUTS_ECR" | awk '$1 == "AccountId" {print $2}') export REPO_NAME=$(echo "$OUTPUTS_ECR" | awk '$1 == "RepositoryName" {print $2}') export REPO_URI=$(echo "$OUTPUTS_ECR" | awk '$1 == "RepositoryUri" {print $2}') # Conferindo a extração: echo "Injetando no repositório $REPO_NAME via DNS: $REPO_URI"
Gerando e subindo imagem Docker para ECR
O próximo passo é autenticar o Docker local no ECR recém-criado, fazer o build da aplicação e enviá-la para o repositório:
# Autentica e loga o Docker no ECR aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com # Cria o build da imagem Docker docker build -t filepack-api . # Associa a tag remota e envia a aplicação docker tag filepack-api:latest ${REPO_URI}:latest docker push ${REPO_URI}:latest
Criando restante da infraestrutura do ECS (2-ecs.yml)
Com nossa imagem disponível no ECR, usaremos o CloudFormation para provisionar a infraestrutura em volta da aplicação. O arquivo 2-ecs.yml será o responsável por criar:
- AWS::ECS::Cluster: Agrupamento lógico da infraestrutura ECS, onde ficam os serviços.
- AWS::ECS::Service: Gerenciador que garante a quantidade desejada de Tasks Fargate sempre rodando.
- AWS::ElasticLoadBalancingV2::LoadBalancer: Application Load Balancer que serve de entrada às requisições HTTP.
- AWS::Logs::LogGroup: Centralizador de logs do CloudWatch.
- AWS::IAM::Role: Criação das Task e Execution Roles com permissões essenciais.
Como a stack está criando permissões de segurança (os IAM Roles), o comando exige o uso explícito da flag de --capabilities:
aws cloudformation create-stack \ --stack-name filepack-ecs \ --template-body file://infra/2-ecs.yml \ --capabilities CAPABILITY_NAMED_IAM aws cloudformation wait stack-create-complete \ --stack-name filepack-ecs
Extraindo a URL do Load Balancer. Após a conclusão do deploy, o CloudFormation retorna nas saídas a URL pública:
# Extraindo o DNS público do nosso Load Balancer com query export ALB_URL=$(aws cloudformation describe-stacks --stack-name filepack-ecs --query "Stacks[0].Outputs[?OutputKey=='LoadBalancerUrl'].OutputValue" --output text) echo "Essa é a URL da noss aplicação: $ALB_URL"
Testando a saúde do serviço. Com o endereço devidamente exportado em $ALB_URL, já podemos testar imediatamente invocando o endpoint /actuator/health do Spring Actuator:
curl -k -v http://$ALB_URL/actuator/health
Você deve ver "status": "UP". Seu back-end e infraestrutura AWS estão prontos para receber requisições diretamente da internet! 🎉
Testando o FilePack
O repositório do projeto conta com uma pasta data que possui arquivos de teste. Pelo terminal, vamos disparar uma requisição referenciando esses arquivos e gerando nosso pacote .zip protegido:
curl -X POST $SERVICE_URL/api/filepack \ -F "files=@data/infrastructure_config.xml" \ -F "password=teste123" \ --output validation_pack.zip
O validation_pack.zip foi baixado. Verifique seu diretório e note que os arquivos internos só podem ser extraídos fornecendo a senha correta!
Manuseando o ECS
Saber como acompanhar a sua infraestrutura é essencial. Com o AWS CLI, conseguimos visualizar detalhes de execução no formato de tabela:
# Verifica o status do Service: quantas tasks estamos pedindo x quantas estão rodando aws ecs describe-services \ --cluster filepack-cluster \ --services filepack-service \ --query "services[*].{Status:status, Desired:desiredCount, Running:runningCount, TaskDefinition:taskDefinition}" \ --output table # Identifica o status atual e a versão associada para todos os containers ativos no momento aws ecs describe-tasks \ --cluster filepack-cluster \ --tasks $(aws ecs list-tasks --cluster filepack-cluster --query "taskArns" --output text) \ --query "tasks[*].{LastStatus:lastStatus, Version:taskDefinitionArn}" \ --output table
Atualizando a Aplicação (Deploy)
A maior vantagem da orquestração é a fluidez na entrega contínua. Vamos testar adicionando um novo endpoint no controller Java:
import org.springframework.web.bind.annotation.GetMapping; import java.util.Map; @GetMapping("/hello") public Map<String, String> hello() { return Map.of("hello", "world"); }
Faça o build da imagem contendo a nova funcionalidade e envie para o ECR substituindo a tag latest:
docker build -t filepack-api . docker tag filepack-api:latest ${REPO_URI}:latest docker push ${REPO_URI}:latest
Como mantivemos a mesma tag (latest) já mapeada em nossa Task Definition, não precisamos criar uma nova revisão. Basta ordenar ao ECS que force um novo deploy no nosso Service, fazendo o orquestrador baixar a imagem recém-atualizada:
# Dispara o Rolling Update, matando containers da versão antiga e subindo a nova imagem gradualmente aws ecs update-service \ --cluster filepack-cluster \ --service filepack-service \ --force-new-deployment
Em poucos instantes após a atualização, sua nova versão responderá corretamente: curl http://$ALB_URL/hello.
Limpeza dos Recursos (Clean Up)
Para evitar surpresas nos custos, é imprescindível limpar o ambiente após os testes. Você precisa destruir o que foi criado operando de modo inverso à criação:
- Desprovisionar a Stack Principal (ECS/ALB/Roles)
- Forçar deleção das imagens no ECR via CLI (o CloudFormation protegerá e abortará a exclusão se existirem imagens residuais)
- Desprovisionar o repositório base ECR
# 1. Desprovisiona a Stack Principal aws cloudformation delete-stack --stack-name filepack-ecs --region us-east-1 aws cloudformation wait stack-delete-complete --stack-name filepack-ecs --region us-east-1 # 2. Limpa o repositório ECR das imagens associadas aws ecr delete-repository --repository-name ${REPO_NAME:-filepack-api} --force --region us-east-1 # 3. Finaliza deletando o resíduo do ECR e sua stack aws cloudformation delete-stack --stack-name filepack-ecr --region us-east-1 aws cloudformation wait stack-delete-complete --stack-name filepack-ecr --region us-east-1
Ambiente limpo! Você executou com sucesso todo o ciclo de vida e usufruiu da flexibilidade nativa entre AWS Fargate e ECS.
Conclusão
Você já tem o básico para colocar seu serviço online, que tal continuar experimento a os serviços em Cloud da AWS?
Embora conteinerizar e expor serviços imponha desafios, pudemos ver como o AWS ECS aliado ao modelo Fargate quebra barreiras e tira o fardo do provisionamento direto de máquinas. Ele absorve grande parte da complexidade operacional, entregando uma plataforma robusta e escalável, para que a engenharia de software seja o grande e único foco.
Companhias com operações gigantescas no Brasil e no mundo adotam ativamente a nuvem AWS como base. Temos líderes nacionais de mercado como Nubank e iFood rodando workloads resilientes no ecossistema de infraestrutura AWS. Dominar essas ferramentas te posiciona fortemente em um excelente nicho de mercado.
E não precisamos estacionar nesse tutorial. Você pode levar a mesma arquitetura para um novo patamar ativando o Application Auto Scaling para crescer a quantidade de instâncias quando a API ficar sob estresse, ou integrar o uso da aplicação com o Amazon S3 para armazenamento persistente. O avanço na nuvem agora depende apenas de você!


