EC2, Lambda ou container: onde diabos eu coloco minha aplicação?

Colocar uma aplicação .NET no ar na AWS não tem uma resposta única, e faz sentido que não tenha: EC2, contêineres via ECS/Fargate e AWS Lambda resolvem o mesmo problema de hospedagem partindo de modelos de execução completamente diferentes.

A resposta curta

Se a carga de trabalho é uma API que fica no ar o tempo todo, com tráfego relativamente previsível, contêiner (ECS com Fargate) costuma ser o ponto de partida mais equilibrado. Se existe necessidade de controle total sobre o sistema operacional, EC2 ainda faz sentido. Se a carga é orientada a evento, esporádica ou com picos bem definidos, Lambda tende a sair mais barato e exigir menos operação.

Isso não é regra fixa, é ponto de partida.

EC2: controle total, responsabilidade total

Amazon EC2 fornece uma máquina virtual. A escolha do sistema operacional, a instalação do runtime do .NET, a configuração do servidor web, o patch de segurança e o escalonamento (manual ou via Auto Scaling Groups) ficam por conta de quem opera a aplicação. É o modelo com menos abstração e, por isso, o que dá mais liberdade para configurar exatamente o que for necessário, ao custo de mais responsabilidade operacional.

Faz sentido quando a aplicação depende de alguma configuração de sistema operacional específica, de um software instalado que não se encaixa bem em contêiner, ou quando a equipe já tem processo maduro de gerenciar VM e prefere manter esse controle.

Contêineres com ECS e Fargate: menos servidor para gerenciar

Amazon ECS é o orquestrador de contêineres da AWS. Rodar uma aplicação .NET nele significa empacotar a aplicação numa imagem de contêiner e descrever, numa task definition, como ela deve rodar: quanto de CPU, memória, quais portas expor.

A escolha que mais muda a experiência é o launch type. Com EC2 como launch type, ainda é preciso gerenciar as instâncias por trás dos contêineres. Com AWS Fargate, a AWS gerencia essa camada, e a preocupação passa a ser apenas a imagem e a definição da tarefa, sem provisionar servidor manualmente.

Esse modelo tende a equilibrar bem controle e operação para uma API tradicional, que fica no ar o tempo todo esperando requisição.

Lambda: paga por execução, mas não é gratuito em complexidade

AWS Lambda roda uma função sob demanda, cobrando por tempo de execução e não por servidor ligado. Isso é vantajoso para carga orientada a evento (processar uma mensagem de fila, responder a um upload no S3, executar uma tarefa agendada), onde manter um servidor ligado o tempo todo para atender eventos esporádicos seria desperdício.

O ponto que costuma pegar quem vem do mundo de aplicação web tradicional é o cold start: quando uma função não é chamada há um tempo, a próxima invocação precisa inicializar o ambiente de execução antes de processar a requisição, o que adiciona latência. Em .NET, isso historicamente pesava mais que em runtimes mais leves, e a compilação Native AOT ajuda a reduzir esse tempo de inicialização quando o cenário exige baixa latência mesmo em cold start.

Para uma API tradicional, síncrona, com tráfego constante o tempo todo, Lambda tende a fazer menos sentido que contêiner: o modelo de cobrança por execução perde vantagem quando o volume de chamadas já justificaria manter um servidor ligado o tempo inteiro de qualquer forma.

Uma comparação rápida

CenárioEC2ECS/FargateLambda
Controle total do SOSimNãoNão
Sem gerenciar servidorNãoCom Fargate, simSim
Cobrança por execuçãoNãoNãoSim
Ideal para evento esporádicoNãoNãoSim
Ideal para API sempre ativaDependeSimDepende do volume

A pergunta que decide, na prática

Mais do que preferência entre os três, a pergunta que costuma decidir bem é: essa carga de trabalho fica ligada o tempo todo, esperando requisição, ou ela só existe quando algo específico acontece? A primeira aponta para EC2 ou contêiner. A segunda aponta para Lambda. Dali em diante, o resto é ajuste fino de custo e operação, não uma escolha certa ou errada de largada.