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 blog/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-ecrExportando 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
# Build do projeto
./mvnw clean install
# 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}:latestCriando 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-ecsExtraindo 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 $ALB_URL/actuator/healthVocê 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 $ALB_URL/api/filepack \
-F "files=@data/infrastructure_config.xml" \
-F "password=teste123" \
--output validation_pack.zipO 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 tableAtualizando 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:
./mvnw clean install
docker build -t filepack-api .
docker tag filepack-api:latest ${REPO_URI}:latest
docker push ${REPO_URI}:latestComo 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-deploymentEm poucos instantes após a atualização, sua nova versão responderá corretamente: curl $ALB_URL/api/filepack/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-1Ambiente 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ê!
