O blog da AWS

Construindo aplicações SaaS multi-tenant com o novo modo de isolamento de tenant do AWS Lambda

Por Anton Aleksandrov, Prin. SSA, Serverless e Ayush Kulkarni, Sr Product Mgr Tech.

Hoje, a AWS anunciou um novo modo de isolamento de tenant para o AWS Lambda, que permite processar invocações de função em ambientes de execução separados para cada usuário final ou tenant da aplicação que invoca sua função Lambda. Essa capacidade simplifica a construção de aplicações SaaS multi-tenant seguras ao gerenciar o isolamento do ambiente de computação no nível do tenant e o roteamento de requisições para você. Como resultado, você pode se concentrar na sua lógica de negócio principal em vez de implementar seu próprio isolamento de ambiente de computação com reconhecimento de tenant.

Visão geral

O Lambda executa o código da sua função em ambientes de execução seguros que aproveitam a virtualização Firecracker para fornecer isolamento. Esses ambientes de execução nunca compartilham ou reutilizam recursos virtuais (como vCPU, disco ou memória) entre funções, ou mesmo entre diferentes versões da mesma função. No entanto, o Lambda pode reutilizar ambientes de execução para múltiplas invocações da mesma versão de função, pois esses ambientes de execução estão totalmente configurados e, portanto, podem fornecer processamento de requisições mais rápido para suas funções.

Figura 1. Invocações recebidas processadas por uma coleção de ambientes de execução que pertencem a uma única função.

Figura 1. Invocações recebidas processadas por uma coleção de ambientes de execução que pertencem a uma única função.

Aplicações SaaS multi-tenant que lidam com dados sensíveis específicos do tenant ou executam código fornecido dinamicamente pelos tenants podem precisar de um grau mais alto de isolamento—no nível do tenant individual da aplicação em vez de no nível da função—para execução segura de código e para reduzir o risco de acesso a dados entre tenants.

Antes do lançamento de hoje, os desenvolvedores implementavam soluções personalizadas, como SDKs ou lógica de aplicação para gerenciar o isolamento dentro do código da função. Essa abordagem era propensa a erros, exigia mais trabalho das equipes de desenvolvimento de aplicações e não garantia isolamento no nível do ambiente de computação.

Alternativamente, os desenvolvedores adotavam a abordagem de criar funções separadas por tenant da aplicação, replicando o mesmo código em centenas ou milhares de tenants. Essa abordagem fornecia isolamento de ambiente de computação mais forte do que compartilhar ambientes de computação entre múltiplos tenants da mesma função, mas aumentava a sobrecarga de implementação e a complexidade operacional à medida que as cargas de trabalho cresciam para suportar um número maior de tenants ao longo do tempo.

Figura 2. Usando o modelo de função por tenant, as requisições de cada tenant são processadas por uma função separada.

Figura 2. Usando o modelo de função por tenant, as requisições de cada tenant são processadas por uma função separada.

A partir de hoje, o AWS Lambda oferece um novo modo de isolamento de tenant que permite isolar ambientes de execução usados em diferentes tenants de suas aplicações SaaS multi-tenant, mesmo quando todos os tenants invocam a mesma função. Quando você habilita o novo modo de isolamento de tenant, você inclui um identificador de tenant com cada invocação de função. O Lambda usa esse identificador para rotear a requisição para o ambiente de execução correto. Como resultado, cada ambiente de execução é reutilizado apenas para invocações do mesmo tenant. Isso significa que você ainda obtém os benefícios de desempenho de ambientes de execução aquecidos, enquanto garante que as cargas de trabalho de cada tenant permaneçam isoladas.

Figura 3. Com a nova capacidade de isolamento de tenant, o Lambda cria ambientes de execução separados por tenant para uma única função.

Figura 3. Com a nova capacidade de isolamento de tenant, o Lambda cria ambientes de execução separados por tenant para uma única função.

Para organizações que lidam com dados sensíveis específicos do tenant ou executam código não confiável fornecido dinamicamente pelos usuários finais, o novo modo de isolamento de tenant do Lambda fornece os benefícios de segurança da separação de ambiente de computação por tenant sem a complexidade operacional de gerenciar funções ou infraestrutura individuais para cada tenant.

Cenário de exemplo

Considere a construção de uma aplicação SaaS serverless multi-tenant. Para otimizar o desempenho, seu manipulador de função pode recuperar configuração e dados específicos do tenant, armazená-los em cache na memória e reutilizá-los para invocações subsequentes do mesmo tenant. Por exemplo, você pode armazenar em cache a localização do banco de dados específico do tenant, feature flags ou regras de negócio que são frequentemente acessadas durante o processamento de requisições. Você pode armazenar essas informações dentro do processo de runtime da aplicação como variáveis globais ou como arquivos no diretório /tmp. No entanto, se o ambiente de execução subjacente for usado para servir múltiplos tenants, essa abordagem pode potencialmente expor dados entre tenants.

Com o modo de isolamento de tenant, você pode abordar esse risco com uma arquitetura e configuração muito mais simples. Essa capacidade integrada torna o Lambda uma excelente escolha para aplicações SaaS multi-tenant que precisam de ambientes de computação isolados para tenants individuais.

Começando com o Modo de Isolamento de Tenant do Lambda

Use o novo parâmetro tenancy-config para configurar o modo de isolamento de tenant quando você criar sua função. Você só pode aplicar essa configuração no momento da criação da função; ela não pode ser atualizada para funções existentes. O seguinte trecho cria uma função com configuração de tenancy usando a AWS CLI.

aws lambda create-function \
   --function-name my-function1 \
   --runtime nodejs22.x \
   --zip-file fileb://my-function1.zip \
   --handler index.handler \
   --role arn:aws:iam:1234567890:role/my-function-role \
   --tenancy-config '{"TenantIsolationMode": "PER_TENANT"}'

Após a função ser criada, você deve fornecer o parâmetro de ID do tenant com cada invocação. O Lambda usa esse identificador para garantir que o ambiente de execução usado para um tenant específico nunca seja reutilizado para outros tenants. Para invocações subsequentes do mesmo tenant, o Lambda pode reutilizar o ambiente de execução para otimizar o desempenho. Especifique este parâmetro tenant-id conforme ilustrado abaixo:

aws lambda invoke \
   --function-name my-function \
   --tenant-id BlueTenant \
   response.json

O novo parâmetro tenant-id é obrigatório para funções que usam o modo de isolamento de tenant. Invocações de função que omitem este parâmetro falharão com um erro de invocação, conforme mostrado abaixo:

aws lambda invoke --function-name multitenant-function out.json

An error occurred (InvalidParameterValueException) when calling the Invoke operation:
The invoked function is enabled with tenancy configuration. 
Add a valid tenant ID in your request and try again.

O Lambda disponibiliza o parâmetro de ID do tenant através do objeto de contexto do manipulador da sua função. Isso permite que você acesse informações específicas do tenant no seu código, por exemplo, se você deseja implementar lógica personalizada baseada na identidade do tenant, conforme mostrado abaixo:

exports.handler = async function (event, context) {
   const tenantId = context.tenantId;

   // Process tenant-specific logic

   return {
      statusCode: 200,
      body: `OK for tenantId=${tenantId}`
   };
};

A tabela a seguir descreve as diferenças entre funções Lambda com e sem o modo de isolamento de tenant habilitado:

Recurso Sem o novo
modo de isolamento de tenant
Com o novo
modo de isolamento de tenant
Isolamento do ambiente de execução Isolado por versão de função. Isolado por usuário final ou tenant invocando uma versão de função.
Reutilização do ambiente de execução Pode ser reutilizado para processar todas as invocações de uma versão de função. Pode ser reutilizado apenas para processar invocações do mesmo tenant invocando uma versão de função.
Dados armazenados em disco local e na memória Potencialmente acessíveis em todas as invocações de uma versão de função. Potencialmente acessíveis em invocações do mesmo tenant. Não acessíveis para invocações de outros tenants.
Cold starts Ocorrem quando não há ambientes de execução aquecidos disponíveis para processar a invocação recebida. Ocorrem quando não há ambientes de execução aquecidos específicos do tenant disponíveis para processar a invocação recebida. Mais cold starts são esperados devido aos ambientes de execução específicos do tenant.

Integração com o Amazon API Gateway

O Amazon API Gateway usa a API Invoke do Lambda para invocar funções Lambda. Ao usar a API Invoke, o Lambda espera que o parâmetro de ID do tenant seja passado usando o cabeçalho HTTP X-Amz-Tenant-Id. Você pode configurar o API Gateway para injetar este cabeçalho HTTP na requisição de invocação do Lambda com um valor obtido das propriedades da requisição do cliente, como cabeçalho HTTP, parâmetro de consulta ou parâmetro de caminho. Ao usar Lambda Authorizers, você pode obter o valor das informações de contexto de autorização retornadas pelo autorizador, como ID principal ou claim JWT. Consulte a documentação do API Gateway para aprender como você pode retornar informações de autorização dos Lambda authorizers para serem usadas no valor do cabeçalho X-Amz-Tenant-Id.

Figura 4. Obtendo o valor do cabeçalho X-Amz-Tenant-Id de fontes de autenticação.

Figura 4. Obtendo o valor do cabeçalho X-Amz-Tenant-Id de fontes de autenticação.

A captura de tela a seguir ilustra a configuração de integração do Lambda no API Gateway, onde a requisição recebida no API Gateway inclui um cabeçalho x-tenant-id que é mapeado para o cabeçalho de requisição X-Amz-Tenant-Id para invocar uma função Lambda usando o modo de isolamento de tenant.

Figura 5. Mapeando o cabeçalho de requisição do cliente para o cabeçalho tenant-id do Lambda.

Figura 5. Mapeando o cabeçalho de requisição do cliente para o cabeçalho tenant-id do Lambda.

O seguinte trecho de código ilustra essa configuração implementada com o AWS CDK.

const lambdaIntegration = new ApiGw.LambdaIntegration(fn, {
   requestParameters: {
      // This configures API Gateway to inject X-Amz-Tenant-Id header
      // into downstream requests. The header value is obtained from 
      // x-tenant-id header in the client request.
      'integration.request.header.X-Amz-Tenant-Id': 'method.request.header.x-tenant-id'
   }
});

resource.addMethod('GET', lambdaIntegration, {
   requestParameters: {
      // This enables API Gateway to use the x-tenant-id header value 
      // obtained from the client request. The header name is arbitrary.
      // you can use any other header name. 
      'method.request.header.x-tenant-id': true
   }
});

Observabilidade com reconhecimento de tenant

Para funções que usam isolamento de tenant, o Lambda inclui automaticamente o ID do tenant nos logs da função quando você tem o log JSON habilitado, facilitando o monitoramento e a depuração de problemas específicos do tenant. Observe que a propriedade tenantId está disponível durante a invocação da função, em vez de durante a inicialização da função. A propriedade tenantId é incluída tanto para eventos de plataforma (como platform.start e platform.report) quanto para logs personalizados que você imprime no código da sua função, conforme mostrado na seguinte captura de tela:

Figura 6. Logs de função Lambda com tenantId.

Figura 6. Logs de função Lambda com tenantId.

O Lambda cria um fluxo de log do CloudWatch separado para cada ambiente de execução. Você pode usar o CloudWatch Log Insights para encontrar fluxos de log que pertencem a um tenant específico filtrando por ID do tenant:

fields @logStream, @message
| filter tenantId=='BlueTenant' or record.tenantId=='BlueTenant'
| stats count() as logCount by @logStream
| sort @timestamp desc

Você também pode recuperar logs específicos do tenant em todos os fluxos de log:

fields @message
| filter tenantId=='BlueTenant' or record.tenantId=='BlueTenant'
| limit 1000

Cada fluxo de log começa com logs de inicialização da função seguidos pelos logs de invocação. Essa estrutura ajuda você a depurar problemas específicos do tenant e entender o ciclo de vida dos ambientes de execução de cada tenant.

Considerações

Ao usar o novo isolamento de tenant para funções Lambda, considere o seguinte:

  • Os ambientes de execução de cada tenant são isolados de outros tenants para que dados específicos do tenant armazenados em disco ou na memória permaneçam separados de outros tenants invocando a mesma função Lambda.
  • Todos os tenants compartilham a função de execução da função. Para permissões mais refinadas para tenants individuais, considere propagar credenciais com escopo de tenant dos componentes de aplicação upstream que invocam sua função Lambda.
  • Sua aplicação pode experimentar uma porcentagem maior de cold starts, pois o Lambda processa requisições em ambientes de execução separados para cada tenant invocando suas funções.
  • Você paga uma taxa por cada novo ambiente de execução específico do tenant criado, dependendo da memória configurada para sua função. Consulte a página de preços do Lambda para detalhes.

Melhores práticas

Ao usar o novo modo de isolamento de tenant para funções Lambda, a AWS recomenda as seguintes melhores práticas:

  • Implemente validação robusta de ID do tenant na camada de aplicação para prevenir acesso não autorizado através de manipulação de ID do tenant. Considere usar um serviço dedicado ou banco de dados para manter IDs de tenant válidos.
  • Monitore e audite padrões de acesso de tenant regularmente para detectar anomalias de segurança potenciais ou tentativas de acesso não autorizado entre tenants.
  • Esteja ciente das cotas de concorrência do Lambda ao construir aplicações multi-tenant. Você pode precisar solicitar aumentos de cota com base na contagem de tenants e padrões de uso.

Código de exemplo

Siga as instruções neste repositório do GitHub para provisionar um projeto de exemplo em sua própria conta e ver o novo modo de isolamento de tenant do Lambda em ação. O projeto de exemplo ilustra como integrar uma função usando o novo modo de isolamento de tenant com o Amazon API Gateway e propagar a identidade do tenant das requisições do cliente.

Conclusão

O novo modo de isolamento de tenant para o Lambda simplifica a construção de aplicações SaaS multi-tenant serverless na AWS. Ao gerenciar automaticamente o isolamento do ambiente de computação no nível do tenant da aplicação, essa capacidade elimina a necessidade de lógica de isolamento personalizada ou funções de tenant separadas, permitindo que você se concentre na lógica de negócio principal enquanto a AWS lida com as complexidades do isolamento de ambiente de computação com reconhecimento de tenant.

Combinado com os recursos de segurança existentes no Lambda, escalabilidade rápida e preços de pagamento por uso, o modo de isolamento de tenant torna o Lambda uma escolha ainda mais atraente para aplicações SaaS modernas, seja você construindo novas soluções ou aprimorando as existentes.

Para saber mais, consulte a documentação para isolamento de tenant. Para detalhes sobre preços, consulte a página de preços do Lambda.


Este conteúdo foi traduzido do post original do blog, que pode ser encontrado aqui.

Autores

Anton Aleksandrov, Prin. SSA, Serverless
Ayush Kulkarni, Sr Product Mgr Tech

Tradutores

Rodrigo Peres é Arquiteto de Soluções na AWS, com mais de 20 anos de experiência trabalhando com arquitetura de soluções, desenvolvimento de sistemas e modernização de sistemas legados.

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.

https://www.linkedin.com/in/danielabib/