O blog da AWS
Entendendo as técnicas para reduzir os custos do AWS Lambda em aplicações Serverless
Este post foi escrito por Josh Kahn, líder técnico de Serverless, e Chloe Jeon, PMTes sênior do AWS Lambda.
As aplicações Serverless podem reduzir o custo total de propriedade (TCO) quando comparados a um modelo de execução em nuvem baseado em servidor, pois transferem efetivamente as responsabilidades operacionais, como o gerenciamento de servidores, para um provedor de nuvem. A pesquisa da Deloitte sobre o TCO de Serverless com clientes da Fortune 100 em todos os setores mostra que aplicações Serverless podem oferecer até 57% de economia de custos em comparação com soluções baseadas em servidor.
Aplicações Serverless podem oferecer custos mais baixos em:
- Desenvolvimento inicial: Serverless permite que os desenvolvedores sejam mais ágeis, forneçam recursos com mais rapidez e se concentrem em uma lógica comercial diferenciada.
- Manutenção e infraestrutura contínuas: Serverless transfere a carga operacional para a AWS. Tarefas de manutenção contínua, incluindo patches e atualizações do sistema operacional.
Este post se concentra nas opções disponíveis para reduzir os custos diretos da AWS ao criar aplicações Serverless. O AWS Lambda geralmente é a camada computacional dessas cargas de trabalho e pode representar uma parte significativa do custo geral.
Para ajudar a otimizar seus custos relacionados ao Lambda, este post discute algumas das técnicas de otimização de custos mais usadas, com ênfase nas alterações de configuração em vez de atualizações de código. Este post é destinado a arquitetos e desenvolvedores iniciantes no desenvolvimento com Serverless.
Construir aplicações Serverless facilita a experimentação e o aprimoramento iterativo. Todas as técnicas descritas aqui podem ser aplicadas antes do desenvolvimento da aplicação ou após a implantação da aplicação em produção. As técnicas são basicamente por aplicabilidade: a primeira pode ser aplicada a qualquer carga de trabalho; a última se aplica a um número menor de cargas de trabalho.
Dimensionando corretamente suas funções Lambda
O Lambda usa um modelo de pagamento por uso (pay-per-use) que é orientado por três métricas:
- Configuração de memória: memória alocada de 128MB a 10.240MB. A CPU e outros recursos disponíveis para a função são alocados proporcionalmente à quantidade de memória.
- Duração da função: tempo de execução da função, medidas em milissegundos.
- Número de invocações: o número de vezes que sua função é executada.
O provisionamento excessivo de memória é um dos principais fatores do aumento de custos do Lambda. Isso é particularmente grave entre desenvolvedores que começaram a usar Lambda recentemente e que estão acostumados a provisionar hosts que executam vários processos. O Lambda é dimensionado de forma que cada ambiente de execução de uma função processe apenas uma solicitação por vez. Cada ambiente de execução tem acesso a recursos fixos (memória, CPU, armazenamento) para concluir o trabalho na solicitação.
Ao dimensionar corretamente a configuração da memória, a função tem os recursos para concluir seu trabalho e você está pagando somente pelos recursos necessários. Embora você também tenha controle direto da duração da função, essa é uma otimização de custo menos eficaz de implementar. Os custos de engenharia para criar alguns milissegundos de economia podem superar a economia de custos. Dependendo da carga de trabalho, o número de vezes que sua função é invocada pode estar fora de seu controle. A próxima seção discute uma técnica para reduzir o número de invocações para alguns tipos de cargas de trabalho.
A configuração da memória pode ser acessada por meio do AWS Management Console ou da sua opção favorita de infraestrutura como código (IaC). A configuração da memória define a memória alocada, mas não a memória usada pela sua função. O dimensionamento correto da memória é um ajuste que pode reduzir o custo (ou aumentar o desempenho) de sua função. No entanto, reduzir a memória funcional nem sempre resulta em economia de custos. Reduzir a memória funcional significa reduzir a CPU disponível para a função Lambda, o que pode aumentar a duração da função, resultando em nenhuma economia de custos ou em custos mais altos. É importante identificar a configuração de memória ideal para reduzir custos e, ao mesmo tempo, preservar o desempenho.
A AWS oferece duas abordagens para o dimensionamento correto da alocação de memória: AWS Lambda Power Tuning e AWS Compute Optimizer.
O AWS Lambda Power Tuning é uma ferramenta de código aberto que pode ser usada para encontrar empiricamente a configuração de memória ideal para sua função, comparando o custo com o tempo de execução. A ferramenta executa várias versões simultâneas de sua função em dados de entrada de exemplo (mock inputs) em diferentes alocações de memória. O resultado é um gráfico que pode ajudá-lo a encontrar o “ponto ideal” entre custo e duração/desempenho. Dependendo da carga de trabalho, você pode priorizar uma em detrimento da outra. O AWS Lambda Power Tuning é uma boa opção para novas funções e também pode ajudar a selecionar entre as duas arquiteturas de conjunto de instruções oferecidas pelo Lambda.
O AWS Compute Optimizer usa aprendizado de máquina para recomendar uma configuração de memória ideal com base em dados históricos. O Compute Optimizer exige que sua função seja invocada pelo menos 50 vezes nos últimos 14 dias para fornecer uma recomendação com base na utilização anterior, portanto, é mais eficaz quando sua função está em produção.
Tanto o Lambda Power Tuning quanto o Compute Optimizer ajudam a obter a alocação de memória do tamanho certo para sua função. Use esse valor para atualizar a configuração da sua função usando o AWS Management Console ou IaC.
Este post inclui um exemplo de código do AWS Serverless Application Model (AWS SAM) para demonstrar como implementar otimizações. Você também pode usar o AWS Cloud Development Kit (AWS CDK), o Terraform, o Serverless Framework e outras ferramentas de IaC para implementar as mesmas mudanças.
MyFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: nodejs18.x
Handler: app.handler
MemorySize: 1024 # Set memory configuration to optimize for cost or performance
Definindo um tempo limite de função mais realista
Funções Lambda são configuradas com um tempo máximo de execução de cada invocação, de até 15 minutos. Definir um tempo limite apropriado pode ser benéfico para conter os custos da sua aplicaçãp que roda em Lambda. Exceções não tratadas, ações de bloqueio (por exemplo, abrir uma conexão de rede), dependências lentas e outras condições podem levar a funções com execução mais longa ou funções que são executadas até o tempo limite configurado. Os tempos limite adequados são a melhor proteção contra códigos lentos e errôneos. Em algum momento, o trabalho que a função está executando e o custo por milissegundo desse trabalho são desperdiçados.
Nossa recomendação é definir um tempo limite de menos de 29 segundos para todas as invocações síncronas ou aquelas nas quais o chamador está aguardando uma resposta. Tempos limite mais longos são apropriados para invocações assíncronas, mas considere tempos limite maiores que 1 minuto como uma exceção que requer análise e teste.
Usando Graviton
O Lambda oferece duas arquiteturas de conjunto de instruções na maioria das regiões da AWS: x86 e arm64.
Escolher o Graviton pode economizar dinheiro de duas maneiras. Primeiro, suas funções podem ser executadas com mais eficiência devido à arquitetura do Graviton2. Em segundo lugar, você pode pagar menos pelo tempo em que eles funcionam. As funções Lambda desenvolvidas pelo Graviton2 foram projetadas para oferecer um desempenho até 19% melhor a um custo 20% menor. Considere começar com o Graviton ao desenvolver novas funções do Lambda, especialmente aquelas que não exigem binários compilados nativamente.
Se sua função depende de binários compilados nativos ou está empacotada como uma imagem de contêiner, você deve fazer um rebuild para alternar entre arm64 e x86. As camadas Lambda também podem incluir dependências direcionadas para uma arquitetura ou outra. Recomendamos que você revise as dependências e teste sua função antes de alterar a arquitetura. A ferramenta AWS Lambda Power Tuning também permite comparar o preço e o desempenho do arm64 e do x86 em diferentes configurações de memória.
Você pode modificar a configuração da arquitetura de sua função no console ou no IaC de sua escolha. Por exemplo, no AWS SAM:
MyFunction:
Type: AWS::Serverless::Function
Properties:
Architectures:
- arm64 # Set architecture to use Graviton2
Runtime: nodejs18.x
Handler: app.handler
Filtrando eventos recebidos
O Lambda é integrado a mais de 200 fontes de eventos, incluindo Amazon SQS, Amazon Kinesis Data Streams, Amazon DynamoDB Streams, Amazon Managed Streaming for Apache Kafka e Amazon MQ. O serviço Lambda se integra a essas fontes de eventos para recuperar mensagens e invocar sua função conforme necessário para processar essas mensagens.
Ao trabalhar com uma dessas fontes de eventos, os desenvolvedores podem configurar filtros para limitar os eventos enviados à sua função. Essa técnica pode reduzir consideravelmente o número de vezes que sua função é invocada, dependendo do número de eventos e da especificidade de seus filtros. Quando não estiver usando a filtragem de eventos, a função deve ser invocada para determinar primeiro se um evento deve ser processado antes de realizar o trabalho real. A filtragem de eventos alivia a necessidade de realizar essa verificação inicial e, ao mesmo tempo, reduz o número de invocações.
Por exemplo, talvez você queira que uma função seja executada somente quando pedidos acima de USD 200 forem encontrados em uma mensagem em um stream de dados do Kinesis. Você pode configurar um padrão de filtragem de eventos usando o console ou IaC de maneira semelhante à configuração da memória.
Para implementar um filtro de stream do Kinesis usando o AWS SAM:
MyFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: nodejs18.x
Handler: app.handler
Events:
Type: Kinesis
Properties:
StartingPosition: LATEST
Stream: "arn:aws:kinesis:us-east-1:0123456789012:stream/orders"
FilterCriteria:
Filters:
- Pattern: '{ "data" : { "order" : { "value" : [{ "numeric": [">", 200] }] } } }'
Se um evento satisfizer um dos filtros de eventos associados à fonte do evento, o Lambda enviará o evento para sua função para ser processado. Caso contrário, o evento será descartado como processado com sucesso sem invocar a função.
Se você estiver criando ou executando uma função do Lambda que é invocada por uma das fontes de eventos mencionadas anteriormente, é recomendável revisar as opções de filtragem disponíveis. Essa técnica não exige alterações de código em sua função do Lambda — mesmo que a função execute alguma verificação de pré-processamento, essa verificação ainda será concluída com êxito com a filtragem implementada.
Para saber mais, leia Filtragem de fontes de eventos para funções do AWS Lambda.
Evitando a recursão
Você pode estar familiarizado com o conceito de programação de uma função recursiva ou de uma função/rotina que chama a si mesma. Embora seja raro, os clientes às vezes criam recursão involuntariamente em sua arquitetura, então uma função do Lambda se chama continuamente.
O padrão recursivo mais comum é entre o Lambda e o Amazon S3. As ações em um bucket do S3 podem acionar uma função do Lambda, e a recursão pode ocorrer quando essa função do Lambda grava de volta no mesmo bucket.
Considere um caso de uso em que uma função Lambda é usada para gerar uma thumbnail das imagens enviadas pelo usuário. Você configura o bucket para acionar a função de geração de thumbnail quando um novo objeto é colocado no bucket. O que acontece se a função Lambda gravar a thumbnail no mesmo bucket? O processo começa de novo e a função Lambda é executada na própria imagem thumbnail. Isso é recursão e pode levar a uma condição de loop infinito.
Embora existam várias maneiras de evitar essa condição, é uma prática recomendada usar um segundo bucket do S3 para armazenar thumbnails. Essa abordagem minimiza as alterações na arquitetura, pois você não precisa alterar as configurações de notificação nem o bucket principal do S3. Para saber mais sobre outras abordagens, leia Como evitar invocação recursiva com o Amazon S3 e o AWS Lambda.
Se você encontrar recursão em sua arquitetura, defina a concorrência reservada do Lambda como zero para impedir a execução da função. Aguarde de minutos a horas antes de levantar o limite de simultaneidade reservado. Como os eventos do S3 são invocações assíncronas que têm novas tentativas (retry) automáticas, você pode continuar vendo a recursão até resolver o problema ou até que todos os eventos tenham expirado.
Conclusão
O Lambda oferece várias técnicas que você pode usar para minimizar os custos de infraestrutura, esteja você apenas começando com o Lambda ou tenha várias funções já implantadas em produção. Quando combinado com os custos baixos de desenvolvimento inicial e manutenção contínua, Serverless pode oferecer um baixo custo total de propriedade (TCO). Experimente essas técnicas e muito mais com o Serverless Optimization Workshop.
Para saber mais sobre arquiteturas sem servidor, encontrar padrões reutilizáveis e manter-se atualizado, visite Serverless Land.
Este blog é uma tradução do conteúdo original em inglês (link aqui).
Biografia dos autores
Josh Kahn é líder técnico de Serverless. | |
Chloe Jeon é PMTes sênior do AWS Lambda. |
Biografia da tradutora
Bianca Mota é arquiteta de soluções para o segmento de startups e iniciou sua jornada na AWS em 2019 ajudando clientes em suas jornadas na nuvem. Além disso, Bianca é parte da comunidade Serverless na AWS e já apresentou sobre o assunto em diversos eventos, como o AWS Summit São Paulo e o AWS re:Invent. |
Biografia do revisor
Daniel Abib é arquiteto de soluções sênior na AWS, com mais de 25 anos trabalhando com gerenciamento de projetos, arquiteturas de soluções escaláveis, desenvolvimento de sistemas e CI/CD, microsserviços, arquitetura Serverless & Containers e segurança. Ele trabalha apoiando clientes corporativos, ajudando-os em sua jornada para a nuvem. |