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

Como testar a resiliência de uma função Lambda com AWS FIS sem tocar no código

O AWS Fault Injection Service ganhou ações específicas para Lambda que injetam erro, atraso e resposta HTTP falsa via uma extensão mantida pela AWS. Um tutorial completo com Serverless Framework mostra como montar o experimento, definir o stop condition e ler o resultado sem acordar de madrugada.

Por Danilo Barros9 min de leitura

Ilustração gerada por IA

Toda aplicação serverless parece resiliente no papel: tem retry aqui, dead-letter queue (DLQ) ali, alarme em algum lugar. Se essas peças realmente funcionam juntas numa emergência, só se descobre quando algo quebra de verdade, geralmente de madrugada. É esse o problema que o AWS Fault Injection Service (FIS) ataca quando aplicado a funções Lambda, como mostra um tutorial publicado por Sascha M. na DEV Community.

Desde outubro de 2024 o FIS oferece ações nativas para Lambda que injetam erro, atraso de inicialização e resposta HTTP falsa diretamente na invocação, sem exigir deploy de código alterado. Para quem desenha e opera ambiente de produção em AWS, isso muda a forma de validar hipótese de resiliência: em vez de simular timeout com sleep() hardcoded ou mockar erro em staging, o experimento roda contra a função real, com extensão e configuração via S3, e com alarme do CloudWatch como stop condition.

O ângulo interessante aqui não é só "a AWS lançou mais um serviço de chaos engineering". É que a técnica tem implicação direta em arquitetura: ela expõe comportamento que só aparece na integração entre serviços, como SQS event source mapping, visibility timeout da fila e retry count, que é exatamente onde moram os incidentes que o Well-Architected chama de "falha em cascata".

O que você vai construir

O tutorial da fonte monta um exemplo completo e executável: uma locadora de bicicletas fictícia chamada radstation-chaos, com uma API HTTP que recebe POST /rentals, uma fila SQS e um worker que grava o contrato de aluguel no DynamoDB de forma idempotente. Por trás disso, dois experimentos de FIS: um que faz 30% das invocações do worker falharem por cinco minutos, e outro que atrasa 50% das invocações da API em três segundos.

A arquitetura é deliberadamente simples para isolar a variável que importa: o comportamento do sistema sob falha controlada, não a complexidade do domínio. Isso é relevante para quem for adaptar o exemplo a um ambiente real, porque o ganho do experimento está na hipótese testada, não no tamanho da aplicação.

Em resumo: o objetivo final não é "ver o FIS funcionando", é sair com uma resposta mensurável para perguntas como "se o worker falhar em 30% das tentativas, alguma mensagem vai parar na DLQ?" e "se a API atrasar três segundos, um cliente com timeout de cinco segundos ainda recebe resposta?".

Pré-requisitos

O tutorial lista o que é necessário antes de começar:

  • Uma conta AWS de teste ou staging, nunca produção, com permissões para CloudFormation, Lambda, IAM, S3, SQS, DynamoDB, CloudWatch e FIS
  • Node.js 24 e npm
  • Serverless Framework v4 (instalado com npm i -g serverless, com serverless login executado uma vez)
  • AWS CLI v2, usada para iniciar os experimentos depois do deploy

A permissão IAM mínima para a role da função Lambda que vai sofrer o experimento inclui s3:GetObject restrito ao prefixo FisConfigs/* do bucket de configuração e s3:ListBucket com a condição s3:prefix limitada ao mesmo caminho. Já a role assumida pelo FIS (FisExperimentRole) precisa de s3:PutObject, s3:DeleteObject no mesmo bucket, lambda:GetFunction, tag:GetResources e permissões de logs:CreateLogDelivery para escrever o log do experimento.

Passo a passo

1. Criar o projeto

bash
mkdir radstation-chaos && cd radstation-chaos
npm init -y
npm install @aws-sdk/client-sqs @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
npm install -D typescript @types/node @types/aws-lambda

O Serverless Framework v4 compila TypeScript com esbuild durante o deploy, então o tsc instalado serve só para os testes e para o script de carga.

2. Escrever a função da API

A validação de entrada fica isolada em src/validate.ts, testável sem depender de nenhum recurso da AWS. A função parseRentalRequest valida bikeId e customerId contra um padrão de regex e devolve um ValidationResult tipado.

A função src/create-rental.ts usa esse validador, publica a mensagem na fila SQS com SendMessageCommand e responde 202 (aceito, processamento assíncrono) assim que a mensagem é enfileirada:

typescript
export const handler = async (
 event: APIGatewayProxyEventV2,
): Promise<APIGatewayProxyResultV2> => {
 const parsed = parseRentalRequest(event.body);
 if (!parsed.ok) {
 return { statusCode: 400, body: JSON.stringify({ message: parsed.error }) };
 }
 const rentalId = randomUUID();
 await sqs.send(
 new SendMessageCommand({
 QueueUrl: queueUrl,
 MessageBody: JSON.stringify({ rentalId, ...parsed.value }),
 }),
 );
 return { statusCode: 202, body: JSON.stringify({ rentalId }) };
};

3. Escrever o worker

O src/issue-contract.ts consome a fila com functionResponseType: ReportBatchItemFailures, reportando falhas por mensagem individual em vez de reprocessar o lote inteiro. A gravação no DynamoDB usa ConditionExpression: attribute_not_exists(rentalId), o que torna a escrita idempotente: uma segunda entrega da mesma mensagem (algo comum sob FIS, já que o experimento provoca exatamente esse redelivery) não cria contrato duplicado.

4. Configurar o serverless.yml com layer e experimentos

Este é o núcleo técnico do tutorial. A extensão do FIS para Lambda não tem ARN fixo: ela vem de um parâmetro público no SSM mantido pela própria AWS, resolvido no deploy com a sintaxe ${ssm:...}:

yaml
layers:
 - ${ssm:/aws/service/fis/lambda-extension/AWS-FIS-extension-arm64/1.x.x}
environment:
 AWS_FIS_CONFIGURATION_LOCATION: arn:aws:s3:::${self:custom.fisBucketName}/FisConfigs/
 AWS_LAMBDA_EXEC_WRAPPER: /opt/aws-fis/bootstrap
 AWS_FIS_EXTENSION_METRICS: all
 AWS_FIS_POLL_MAX_WAIT_MILLISECONDS: "2000"

Três pontos merecem atenção de quem for reproduzir isso em produção (ainda que a recomendação explícita do tutorial seja rodar só em staging). Primeiro: AWS_LAMBDA_EXEC_WRAPPER só deve ser setado junto com a layer, porque sem a extensão presente toda invocação falha.

Segundo: a role da função Lambda só pode ler do prefixo FisConfigs/, enquanto a role do FIS só pode escrever e apagar ali, uma separação de permissão que limita o raio de ação de cada lado. Terceiro: o bucket S3 criado tem PublicAccessBlockConfiguration com todas as quatro flags em true, porque é por esse bucket que o FIS distribui a configuração do experimento, e exposição dele é superfície de ataque desnecessária.

O serverless.yml também define dois stop conditions, o mecanismo de segurança do FIS que interrompe o experimento automaticamente:

yaml
DlqNotEmptyAlarm:
 Type: AWS::CloudWatch::Alarm
 Namespace: AWS/SQS
 MetricName: ApproximateNumberOfMessagesVisible
 Statistic: Maximum
 Period: 60
 EvaluationPeriods: 1
 Threshold: 0
 ComparisonOperator: GreaterThanThreshold

Esse alarme dispara assim que uma única mensagem aparece na DLQ. O segundo stop condition monitora 5xx na métrica AWS/ApiGateway com threshold de 5 erros por minuto. Os dois AWS::FIS::ExperimentTemplate (um para cada experimento) referenciam esses alarmes em StopConditions, com Source: aws:cloudwatch:alarm.

O experimento do worker usa a ação aws:lambda:invocation-error com invocationPercentage: "30" e preventExecution: "true" (a invocação é marcada como falha sem rodar o código), por duration: PT5M. O experimento da API usa aws:lambda:invocation-add-delay com startupDelayMilliseconds: "3000" em 50% das invocações.

5. Script de carga

Sem carga não há experimento: o FIS injeta a falha, mas é preciso tráfego real batendo na função para observar o efeito. O scripts/load-test.ts dispara um número fixo de requisições com paralelismo limitado e reporta percentis de latência (p50, p95, p99) e contagem por status code, incluindo status 0 para timeout de cliente.

Testando

Com o stack implantado (serverless deploy), os experimentos já existem como AWS::FIS::ExperimentTemplate, mas não rodam sozinhos. A AWS CLI inicia cada um apontando o ID do template de saída do CloudFormation:

bash
aws fis start-experiment --experiment-template-id <WorkerErrorExperimentId>

Para o experimento do worker, a hipótese documentada pela fonte é: "se 30% das invocações do worker falharem por cinco minutos, nenhuma mensagem deve terminar na DLQ, e todos os contratos de aluguel existem no máximo dez minutos após o fim do experimento". É esse tipo de frase, testável e com métrica clara, que separa chaos engineering de "quebrar coisas aleatoriamente": o experimento só vale a pena se a resposta puder ser sim ou não sem ambiguidade.

Durante a execução, vale rodar o script de carga em paralelo e observar o CloudWatch Logs Insights ou o log group dedicado (/aws/fis/radstation-staging) para acompanhar o progresso. Um detalhe de timing citado pela fonte merece atenção de quem vai operar isso: sem experimento ativo, a extensão consulta o S3 a cada 60 segundos; durante um experimento, a cada 20 segundos. Isso significa que o início e o fim do efeito não são exatos ao segundo, e a duração do experimento, o VisibilityTimeout da fila e o maxReceiveCount do redrive precisam estar dimensionados com essa folga em mente, ou o teste acaba medindo outra coisa que não a hipótese original.

No experimento de latência, a leitura do resultado é simples de errar: um atraso de três segundos parece um cold start, mas na verdade atinge qualquer execution environment, inclusive os já aquecidos, e conta como billed duration cobrada. Se o atraso configurado ultrapassar o timeout da função, o resultado é um timeout real, o que é útil justamente para simular uma dependência downstream travada.

Limpando os recursos

Terminado o teste, o comando que remove todo o stack é:

bash
serverless remove --stage staging

Isso apaga função Lambda, fila SQS (incluindo a DLQ), tabela DynamoDB, os dois alarmes do CloudWatch, o log group do FIS, a FisExperimentRole e os dois AWS::FIS::ExperimentTemplate. Um ponto que o serverless remove não cobre sozinho: o bucket S3 radstation-fis-<accountId>-staging usado para a configuração do FIS pode reter objetos residuais da execução dos experimentos, e buckets S3 não vazios não são removidos automaticamente pelo CloudFormation. Vale conferir manualmente com aws s3 ls s3://radstation-fis-<accountId>-staging/FisConfigs/ antes de considerar a limpeza completa, e esvaziar o bucket antes do remove se ele reclamar.

O tutorial não cita preço do FIS em si (a AWS cobra por duração de experimento ativo, modelo separado do Lambda), mas vale lembrar que o Lambda com atraso injetado conta como billed duration normal: um experimento de atraso de três segundos em 50% de um volume alto de invocações pode sair do Free Tier do Lambda dependendo da escala do teste, algo que a fonte não quantifica e que cabe a quem reproduzir calcular para o próprio volume.

Próximos passos

Para quem desenha ambiente de produção, o valor do FIS para Lambda está em transformar suposição de resiliência em experimento recorrente, algo que cabe bem num pipeline de game day trimestral ou numa rotina pós-incidente, testando a correção antes do próximo deploy. A fonte linka dois artigos complementares da mesma autora sobre os componentes usados no exemplo: resposta parcial de lote em SQS (SQS partial batch response) e idempotência em Lambda, que valem leitura para quem for adaptar o padrão a uma aplicação real com volume de produção.

Um ponto que fica em aberto no material: o tutorial roda tudo em eu-central-1, e a ARN do parâmetro SSM da layer é por região; antes de reproduzir em sa-east-1 vale confirmar se o parâmetro /aws/service/fis/lambda-extension/AWS-FIS-extension-arm64/1.x.x existe na região de São Paulo, já que a extensão do FIS para Lambda não necessariamente está disponível em todas as regiões simultaneamente ao lançamento.

Fonte: DEV Community — #aws

Serviços neste textoStep Functions

Este artigo foi escrito por Danilo Barros. 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.

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.