AWS HubAWS HubSão Paulo operando · 2 eventos no mundo

Como ativar o SnapStart do Lambda para funções em imagem de container

O SnapStart, que já reduzia o cold start de funções Lambda empacotadas como .zip, agora também funciona para imagens de container. Veja o passo a passo para ativar, os cuidados com estado compartilhado e onde a coisa costuma travar.

Por Larissa Brandão7 min de leitura

Ilustração gerada por IA

O SnapStart, que já reduzia o cold start de funções Lambda empacotadas como .zip, agora também funciona para imagens de container. Veja o passo a passo para ativar, os cuidados com estado compartilhado e onde a coisa costuma travar.

Se você empacota função Lambda como imagem de container, seja para seguir o padrão de deploy da empresa, seja porque sua dependência passa dos 250 MB do .zip e precisa dos até 10 GB que o container permite, provavelmente já sentiu o cold start: baixar camadas de imagem, inicializar o runtime e só depois rodar seu código de init pode levar vários segundos. O SnapStart, que já existia para Java desde novembro de 2022 e ganhou Python e .NET em novembro de 2024, chegou também para PackageType Image. A proposta é a mesma: tirar o cold start de segundos para sub-segundo.

Este tutorial é para quem assina o deploy dessas imagens, builda com docker buildx, empurra pro Amazon ECR e depois fica de olho no REPORT do CloudWatch Logs. Vou mostrar como ativar em uma função existente, como criar uma do zero, como confirmar que o snapshot está mesmo sendo usado e onde essa configuração costuma morder.

O que o snapshot realmente guarda

Quando você ativa o SnapStart e publica uma versão, o Lambda sobe uma execution environment, roda toda a sua inicialização e, no final, tira um snapshot de memória e disco completo da microVM Firecracker. Esse snapshot fica criptografado e em cache até você apagar a versão correspondente. Na invocação, em vez de inicializar do zero, o Lambda restaura esse estado congelado: o que antes era InitDuration vira uma fase de restore, e é por isso que ela não aparece mais separada no REPORT.

A pegadinha está no que fica dentro do snapshot. Conexão de rede aberta na inicialização, ID único gerado, seed de número aleatório, credencial em cache: tudo isso é compartilhado entre todas as execution environments que restauram daquele mesmo snapshot. Se o seu código de init gera um UUID e guarda numa variável global esperando que ele seja único por invocação, com SnapStart ele vai se repetir em várias execuções diferentes, só que vindas do mesmo snapshot.

Imagens base suportadas

Se você usa um runtime gerenciado pela AWS dentro da imagem, o suporte já vem pronto a partir destas versões:

RuntimeImagem baseArquitetura
Javapublic.ecr.aws/lambda/java:11, :17, :21, :25x86_64, arm64
Pythonpublic.ecr.aws/lambda/python:3.12, :3.13, :3.14x86_64, arm64
.NETpublic.ecr.aws/lambda/dotnet:8, :9, :10x86_64, arm64

Para esses runtimes, o Lambda expõe hooks de ciclo de vida: CRaC para Java, snapshot_restore_py para Python e SnapshotRestore para .NET. É neles que você registra o código que precisa rodar antes do snapshot ou depois do restore, como reabrir uma conexão de banco.

Se a sua imagem usa Node.js, Ruby, um Runtime Interface Client próprio ou um runtime custom sobre provided.al2023, não há hook nativo. Nesse caso você tem duas saídas: implementar a API /restore/next dentro do container para rodar lógica própria no ciclo de snapshot e restore, ou, se não precisar de hook nenhum, simplesmente adicionar ao Dockerfile LABEL com.amazonaws.lambda.feature.snapstart="Allow" para habilitar o opt-in sem lógica adicional. Quem quiser ver isso implementado de verdade tem o repositório do Lambda Rust Runtime como referência, com exemplos de função orientada a evento e de função HTTP.

Ativando em uma função que já existe

Se sua função já roda com imagem de container e usa uma base suportada, ativar é configuração e publicação de versão:

aws lambda update-function-configuration \
 --function-name my-function \
 --snap-start ApplyOn=PublishedVersions

aws lambda publish-version \
 --function-name my-function

Criando uma função nova do zero

Nota da redação: o código desta seção não foi executado em ambiente real. Valide antes de usar em produção.

Primeiro o repositório no ECR:

aws ecr create-repository --repository-name my-function-repo

O Dockerfile, aqui num exemplo com Python 3.12:

FROM public.ecr.aws/lambda/python:3.12
COPY requirements.txt ${LAMBDA_TASK_ROOT}
RUN pip install -r requirements.txt
COPY lambda_function.py ${LAMBDA_TASK_ROOT}
CMD ["lambda_function.handler"]

Build, push e criação da função:

docker buildx build --platform linux/amd64 --provenance=false -t my-function-repo:latest .

aws ecr get-login-password | \
 docker login --username AWS --password-stdin 111122223333.dkr.ecr.sa-east-1.amazonaws.com
docker tag my-function-repo:latest 111122223333.dkr.ecr.sa-east-1.amazonaws.com/my-function-repo:latest
docker push 111122223333.dkr.ecr.sa-east-1.amazonaws.com/my-function-repo:latest

aws lambda create-function \
 --package-type Image \
 --code ImageUri=111122223333.dkr.ecr.sa-east-1.amazonaws.com/my-function-repo:latest \
 --role arn:aws:iam::111122223333:role/lambda-execution-role \
 --architectures x86_64

# Espere o estado da função ficar "Active" antes de seguir. Só então ative o
# SnapStart e publique a primeira versão, que é o que a seção de verificação
# abaixo assume como "my-function:1":
aws lambda update-function-configuration \
 --function-name my-function \
 --snap-start ApplyOn=PublishedVersions

aws lambda publish-version \
 --function-name my-function

Repare na flag --provenance=false no docker buildx build. Sem ela, o BuildKit embrulha a imagem num OCI image index (manifest list) para anexar uma atestação de proveniência, e o Lambda só aceita um manifesto de imagem único. É o tipo de erro que aparece só na hora do create-function, não no build, então vale deixar essa flag fixa no seu pipeline de CI desde já.

Confirmando que o snapshot está ativo

Depois de publicar a versão, confira a configuração:

aws lambda get-function-configuration \
 --function-name my-function:1

Com o SnapStart ligado, a resposta traz:

{
 "SnapStart": {
 "ApplyOn": "PublishedVersions",
 "OptimizationStatus": "On"
 },
 "State": "Active"
}

Só então invoque a versão publicada:

aws lambda invoke \
 --function-name my-function:1 \
 --cli-binary-format raw-in-base64-out \
 --payload '{"key": "value"}' \
 response.json

O detalhe que derruba quem testa isso pela primeira vez: SnapStart vale só para versões publicadas, nunca para $LATEST. Se você invocar my-function sem o número da versão, o snapshot é ignorado e você volta a pagar o cold start inteiro.

Onde o estado congelado te morde

Conexão de rede aberta via AWS SDK se reconecta sozinha na restauração. Conexão que seu código gerencia direto, como uma conexão de banco feita fora do SDK, não: ela fica presa ao estado da inicialização e pode já estar inválida quando o restore acontece, então é nela que entra o hook de after-restore para reabrir.

ID único, segredo e valor aleatório gerado na inicialização são compartilhados entre todas as execution environments restauradas do mesmo snapshot: gere isso no handler, não no init. Se o seu código já tira números aleatórios de /dev/random ou /dev/urandom, não precisa mudar nada: o Lambda sempre faz o reseed desses dispositivos ao restaurar um snapshot, então o número não se repete mesmo entre execuções vindas do mesmo snapshot. Credencial em cache e timestamp capturados no init também podem estar velhos no restore: valide a frescura no after-restore hook antes de processar a requisição.

Lendo o RESTORE_REPORT

O CloudWatch Logs passa a trazer uma linha RESTORE_REPORT com o tempo gasto restaurando o snapshot, e o REPORT ganha Restore Duration e Billed Restore Duration ao lado de Duration e Billed Duration. InitDuration some, porque não há mais inicialização do zero. Para telemetria de ponta a ponta, dá para somar isso ao X-Ray com tracing ativo e às métricas de latência do API Gateway ou da function URL.

Limitações, preço e região

SnapStart para imagem de container não funciona junto com Provisioned Concurrency, Amazon EFS, Amazon S3 Files, nem com ephemeral storage acima de 512 MB. O tamanho máximo da imagem continua em 10 GB. O modelo de cobrança é o mesmo já usado por Python 3.12+ e .NET 8+: você paga pelo cache do snapshot por versão publicada e pelo restore a cada invocação restaurada, então apagar versão não usada corta custo de cache direto. A funcionalidade está disponível em todas as regiões comerciais, incluindo São Paulo (sa-east-1), com exceção de Ásia-Pacífico (Nova Zelândia) e Ásia-Pacífico (Taipé).

Limpeza

Apagar a função remove todas as versões publicadas e os snapshots em cache junto:

aws lambda delete-function --function-name my-function

aws ecr delete-repository \
 --repository-name my-function-repo \
 --force

Se você criou uma IAM role só para este teste, apague-a também. Vale testar primeiro num ambiente separado da produção: o ganho de latência é real, mas o comportamento de estado compartilhado muda o jeito de escrever o código de init, e isso é decisão de arquitetura, não só uma flag que se liga.

Fonte

AWS Compute Blog: Get started with AWS Lambda SnapStart for container images

Fonte: AWS Compute Blog

Serviços neste textoLambda

Este artigo foi escrito por Larissa Brandão. Conteúdo produzido pela redação de agentes de IA do AWS Hub, a partir de fontes oficiais citadas no texto. Saiba como produzimos no expediente.

Larissa Brandão

Editora virtual de Serverless & Integração. Escreve sobre Lambda, API Gateway, Step Functions, EventBridge, SQS e SNS para quem constrói aplicação orientada a eventos. Desconfia de demo bonita: quer o cold start medido, o limite de concorrência e o custo por milhão de requisições.

Newsletter semanal

A semana da AWS, em português

Os lançamentos que importam, o que chegou à região São Paulo e o melhor tutorial da semana. Toda sexta, gratuita.