05 - Serverless (AWS Lambda, Fargate)
video: https://youtu.be/... no frontmatter da nota.Serverless (AWS Lambda, Fargate)
Serverless é o modelo de execução em que o provedor de nuvem gerencia servidores, capacidade e disponibilidade, cobrando apenas pelo uso efetivo; o time entrega código ou imagem de contêiner e a plataforma escala. Na AWS, o modelo tem duas expressões complementares: o Lambda, funções de curta duração disparadas por eventos, e o Fargate, contêineres executados sem administração de instâncias. Em produção, a relevância é econômica e operacional ao mesmo tempo: cargas com tráfego variável podem custar uma fração do que custariam em capacidade provisionada, e o time elimina patching de sistema operacional e dimensionamento manual. O trade-off central é o controle: a plataforma impõe limites de execução, tempo de inicialização a frio e modelos de cobrança que premiam cargas intermitentes. Escolher bem entre Lambda, Fargate e contêineres administrados é uma decisão arquitetural sobre perfil de carga, não uma moda.
Conceito e modelo de responsabilidade
O termo serverless descreve a abstração do servidor, não a ausência dele; existem servidores, mas o provedor administra. A fronteira de responsabilidade desloca-se: o time responde por código, configuração e dados, e a plataforma responde por sistema operacional, runtime, capacity planning e disponibilidade do serviço. Essa mudança reduz o trabalho operacional em duas frentes: não há instâncias para atualizar e não há dimensionamento fixo para justificar.
O modelo de cobrança reforça o conceito. O Lambda cobra por invocação, pela duração em milissegundos e pela memória alocada; o Fargate cobra por vCPU e GB de memória por segundo, enquanto a task roda. Capacidade ociosa não é cobrada, o que inverte a lógica de clusters tradicionais, em que se paga para estar pronto. A consequência prática é que cargas esporádicas ficam baratas, e cargas constantes e saturadas podem ficar mais caras que EC2 reservadas; a análise custo-benefício exige o perfil de tráfego real.
Por que serverless em produção
O problema que o modelo resolve é o custo de manter capacidade para o pico durante todo o tempo. Em uma arquitetura tradicional, o cluster dimensionado para a carga de sexta-feira à tarde permanece rodando, e cobrando, nas madrugadas de domingo. Serverless alinha custo à demanda em granularidade fina: segundos de execução, não horas de instância. Além do custo, elimina categorias inteiras de trabalho operacional: não há patch de kernel, planejamento de capacidade nem gestão de grupos de instâncias.
O segundo ganho é a elasticidade automática diante de picos. Uma função Lambda com fonte de eventos em fila absorve um surto multiplicando ambientes paralelos até o limite de concorrência da conta; um service Fargate com autoscaling reage a métricas de CPU e de fila. A contrapartida aparece em três frentes: limites rígidos da plataforma, dependência mais profunda do provedor e novos modos de falha, como throttling de concorrência e cold start no caminho crítico. As seções seguintes detalham cada modelo e as fronteiras de decisão.
AWS Lambda: funções orientadas a eventos
O Lambda executa funções em resposta a eventos: requisições do API Gateway, mensagens do SQS, mudanças no S3, eventos do EventBridge, entre dezenas de fontes. A plataforma gerencia um pool de execution environments por versão do runtime; quando chega uma invocação sem ambiente livre, ocorre um cold start, que provisiona sandbox, inicia o runtime e executa o código de inicialização. Cold starts variam de dezenas de milissegundos em runtimes interpretados a alguns segundos em JVM e .NET, e podem ser mitigados com provisioned concurrency, que mantém ambientes pré-inicializados.
A memória configurada é o único ajuste de escala da função e funciona como proxy de CPU: a potência alocada cresce proporcionalmente à memória, de 128 MB até 10 GB. Dobrar memória dobra aproximadamente a CPU, e o tempo de execução cai; o custo total pode até diminuir, pois duração menor compensa preço maior por segundo. Ferramentas de benchmark por função encontram o ponto ótimo, e a tabela resume os limites que moldam o desenho.
| Limite do Lambda | Valor padrão | Implicação de desenho |
|---|---|---|
| Duração por invocação | Até 15 minutos | Processamento longo precisa de Step Functions ou Fargate |
| Memória | 128 MB a 10 GB | Proxy de CPU; ajuste com benchmark |
| Payload síncrono | 6 MB | Transferências grandes usam S3 presigned |
| Tamanho do pacote | 50 MB zipado, 250 MB descomprimido | Camadas e imagens de contêiner ampliam |
| Concurrency por região | 1000 por padrão | Ajustável; reserve para funções críticas |
| Tempo de cold start | Milissegundos a segundos | Provisioned concurrency para latência sensível |
O desenho de funções segue princípios específicos: statelessness total, com estado em DynamoDB, S3 ou ElastiCache; funções pequenas com responsabilidade única; inicialização fora do handler para reutilizar conexões entre invocações; e idempotência, porque fontes como SQS podem entregar mensagens mais de uma vez.
# Lambda otimizada: inicialização fora do handler reutiliza conexões
import json
import boto3
# Executado uma vez por ambiente; a plataforma reusa entre invocações
dynamodb = boto3.resource("dynamodb")
pedidos = dynamodb.Table("pedidos")
def lambda_handler(event, context):
# Idempotência: chave de deduplicação vinda da mensagem SQS
message_id = event["Records"][0]["messageId"]
# Escrita condicional garante que processamento repetido não duplique efeito
pedidos.put_item(
Item={"id": message_id, "status": "processado"},
ConditionExpression="attribute_not_exists(id)",
)
return {"statusCode": 200, "body": json.dumps({"processado": message_id})}
Fargate: contêineres sem servidor
O Fargate executa contêineres Docker em tasks ECS ou pods EKS sem que o time administre servidores. A unidade de alocação é declarada em vCPU e memória, com combinações fixas de 0,25 vCPU e 512 MB até 16 vCPU e 120 GB, e a plataforma posiciona a tarefa em infraestrutura da AWS. Como cada task recebe capacidade dedicada, não há contenção por vizinho barulhento, e o isolamento é mais forte que em hosts compartilhados.
A operação típica combina service, autoscaling e ALB, como no exemplo a seguir.
# Cria o service Fargate com 2 tasks iniciais e registro no Target Group do ALB
aws ecs create-service \
--cluster producao \
--service catalogo \
--task-definition catalogo-api \
--launch-type FARGATE \
--desired-count 2 \
--network-configuration "awsvpcConfiguration={subnets=[subnet-abc,subnet-def],securityGroups=[sg-app],assignPublicIp=DISABLED}" \
--load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:sa-east-1:123456789012:targetgroup/catalogo/abc,containerName=api,containerPort=8080"
# Executa uma task avulsa de batch, sem serviço e sem servidor, e aguarda o fim
aws ecs run-task \
--cluster producao \
--task-definition etl-diario \
--launch-type FARGATE \
--overrides '"containerOverrides":[{"name":"etl","environment":[{"name":"DATA_REF","value":"2025-06-01"}]}]'
O Fargate brilha em três perfis: serviços HTTP com escala e escala para zero desejáveis, tarefas batch de duração arbitrária e workloads de contêiner que não cabem nos limites do Lambda. Um job de ETL que roda 40 minutos custa o mesmo modelo por segundo, sem janela de timeout; um endpoint de baixo tráfego escala a zero durante a madrugada e cobra apenas os segundos de requisição. O custo é maior por unidade de computação que EC2 administrada, e a ausência de suporte a GPU e de containers privileged restrinjia casos especializados, lacuna que o ECS Managed Instances lançado em 2025 passou a cobrir mantendo o modelo simplificado do Fargate, com instâncias dedicadas incluindo GPU, privileged e memória acima do teto de 120 GB.
Escolha por perfil de carga
A decisão entre os modelos deve partir do comportamento da carga, e não da preferência por tecnologia. Alguns sinais concretos orientam a escolha.
- Tráfego esporádico e imprevisível, com picos curtos: Lambda tende a vencer em custo e simplicidade.
- Tráfego constante e saturado na maior parte do tempo: capacidade provisionada com EC2 ou Managed Instances costuma ser mais barata que pagar por segundo sempre.
- Latência de cauda sensível, como APIs públicas com SLA rígido: Fargate com réplicas aquecidas elimina o cold start do caminho crítico.
- Processamento de eventos em fila com paralelismo natural: Lambda consumindo SQS com batch size ajustado escala de forma elegante.
- Jobs de longa duração, ETL e workloads que exigem IP fixo ou conexões persistentes: Fargate, pois o limite de 15 minutos e o modelo sem estado do Lambda inviabilizam.
- Dependência de GPU, privileged ou memória acima de 120 GB: ECS Managed Instances, que preserva a simplicidade do modelo com controle de host.
Lambda versus Fargate
Os dois modelos são serverless, mas servem perfis de carga distintos. A comparação ajuda a escolher por critério objetivo.
| Critério | Lambda | Fargate |
|---|---|---|
| Unidade de implantação | Código ou imagem de contêiner | Imagem de contêiner OCI |
| Duração máxima | 15 minutos por invocação | Sem limite de duração |
| Escala | Automática e instantânea por evento | Por contagem de tasks com Service Auto Scaling |
| Scale to zero | Nativo | Nativo em tasks para zero com custo de escala mínima |
| Modelo de custo | Por invocação, duração e memória | Por vCPU e memória por segundo |
| Estado em memória | Desencorajado; ambientes reciclados | Mantido durante a vida da task |
| Rede fixa | Não; conexões reutilizadas por ambiente | IP próprio em awsvpc, grupos de segurança |
| Latência pico a pico | Cold start pode penalizar | Estável, com prewarm possível |
| Melhor perfil | Eventos curtos e paralelizáveis | Serviços HTTP e batch em contêiner |
A combinação também é válida e comum: um API Gateway encaminha requisições de baixa latência para um serviço em Fargate, e eventos assíncronos do mesmo domínio são processados por funções Lambda. O critério de fronteira é a natureza do trabalho: requisições com SLA rígido e conexões persistentes tendem ao contêiner; transformações curtas e disparadas por evento tendem à função. Ambientes de teste e carga simulada revelam o comportamento real de cold start e escala antes do compromisso de produção.
Erros comuns e boas práticas
Os incidentes recorrentes em serverless vêm de desenho inadequado ao modelo de execução, e não de falhas da plataforma. Os pontos abaixo concentram as ocorrências.
- Funções monolíticas que fazem tudo, impossibilitando escala independente e testes; separe por responsabilidade e tamanho de pacote.
- Inicialização de conexões dentro do handler, multiplicando latência e consumindo pool do banco; inicialize fora e reutilize com connection pooling.
- Vazamento de estado em variáveis globais, gerando comportamento imprevisível entre invocações reutilizadas.
- Ausência de idempotência em consumidores de fila, duplicando efeitos colaterais em redeliveries do SQS.
- Ignorar o timeout padrão de 3 segundos em funções atrás de API Gateway, resultando em cortes silenciosos; defina timeout explícito por função.
- Uso excessivo de provisioned concurrency sem análise, comprando de volta o cold start ao custo do modelo sempre-ligado que o serverless evitava.
- Exceder o limite de concorrência da conta em função secundária e bloquear funções críticas; reserve concurrency para as funções essenciais e monitore throttling.
- No Fargate, subestimar o custo de imagens grandes e de pull em cada task nova; imagens enxutas em ECR aceleram cold start de contêiner.
Boas práticas formam um pacote coerente. Modele funções como unidades de negócio pequenas; configure memória com benchmark e reveja após mudanças de código; adote observabilidade com structured logs, AWS X-Ray e métricas por função; proteja segredos no Secrets Manager ou Parameter Store, nunca em variáveis versionadas; e monitore throttling e erros com alarmes por função. No Fargate, trate a task como gado: sem estado local, com drain adequado no scale down e health checks coerentes com o ALB. Serverless recompensa quem respeita o contrato da plataforma, e castiga quem tenta reproduzir dentro dele o desenho de um servidor tradicional.