O blog da AWS

Apresentando o runtime do .NET 8 para o AWS Lambda

Este post foi escrito por Beau Gosse, engenheiro de software sênior e Paras Jain, gerente técnico sênior de contas.

O AWS Lambda agora oferece suporte ao .NET 8 como ambiente de execução gerenciado e imagem base de contêiner. Com esta versão, os desenvolvedores do Lambda podem se beneficiar dos recursos do .NET 8, incluindo aprimoramentos de API, suporte aprimorado ao Native Ahead of Time (AOT nativo) e melhor desempenho. O .NET 8 é compatível com C# 12, F# 8 e PowerShell 7.4. Você pode desenvolver funções do Lambda no .NET 8 usando o AWS Toolkit for Visual Studio, o AWS Extensions for .NET CLI, o AWS Serverless Application Model (AWS SAM), o AWS CDK e outras infraestruturas como ferramentas de código.

Creating .NET 8 function in the console

Criando a função .NET 8 no console

O que há de novo

Sistema operacional atualizado

O runtime do .NET 8 é baseado na imagem de contêiner mínimo do Amazon Linux 2023 (AL2023). Isso proporciona um footprint de implantação menor do que os tempos de execução anteriores baseados no Amazon Linux 2 (AL2) e as versões atualizadas de bibliotecas comuns, como glibc 2.34 e OpenSSL 3.

A nova imagem também usa microdnf como gerenciador de pacotes, com link simbólico como dnf. Isso substitui o gerenciador de pacotes yum usado em imagens anteriores baseadas em AL2. Se você implantar suas funções do Lambda como imagens de contêiner, deverá atualizar seus Dockerfiles para usar dnf em vez de yum ao atualizar para a imagem base do .NET 8. Para obter mais informações, consulte Apresentando o runtime do Amazon Linux 2023 para o AWS Lambda.

Desempenho

Há várias melhorias de desempenho de linguagem disponíveis como parte do .NET 8. O tempo de inicialização pode afetar o desempenho, pois o Lambda cria novos ambientes de execução para escalar sua função automaticamente. Há várias maneiras de otimizar o desempenho de cargas de trabalho do .NET baseadas em Lambda, incluindo o uso de geradores de origem em System.Text.Json ou o AOT nativo.

O Lambda aumentou o tamanho da memória padrão de 256 MB para 512 MB nos esquemas e modelos para melhorar o desempenho com o .NET 8. Execute seus próprios testes funcionais e de desempenho em seus aplicativos .NET 8. Você pode usar o AWS Compute Optimizer ou o AWS Lambda Power Tuning para criar perfis de desempenho.

No lançamento, os novos runtimes do Lambda recebem menos uso do que os já estabelecidos. Isso pode resultar em tempos de inicialização a frio (cold start) mais longos devido à redução da residência do cache nos subsistemas internos do Lambda. Os tempos de inicialização a frio geralmente melhoram nas semanas seguintes ao lançamento, à medida que o uso aumenta. Como resultado, a AWS recomenda não tirar conclusões de comparação de desempenho com outros tempos de execução do Lambda até que o desempenho se estabilize.

AOT nativo

A Lambda introduziu o suporte AOT nativo do .NET em novembro de 2022. Os benchmarks mostram uma melhoria de até 86% nos tempos de inicialização a frio ao eliminar a compilação do JIT. A implantação de funções AOT nativas do .NET 8 usando o runtime dotnet8 gerenciado em vez do tempo de provided.al2023 fornecido somente pelo sistema operacional dá à sua função acesso às bibliotecas do sistema .NET. Por exemplo, libicu, que é usada para globalização, não está incluída por padrão no runtime provided.al2023, mas está no runtime dotnet8.

Embora o AOT nativo não seja adequado para todas as funções do .NET, o .NET 8 melhorou o suporte ao trimming. Isso permite que você execute as APIs do ASP.NET com mais facilidade. O suporte aprimorado ao trimming ajuda a eliminar os avisos de ajuste do tempo de construção, que destacam possíveis erros de runtime. Isso pode lhe dar a certeza de que sua função AOT nativa se comporta como uma função compilada com JIT. O suporte ao trimming foi adicionado às bibliotecas de runtime do Lambda, ao SDK do .NET da AWS, às anotações do .NET Lambda e ao próprio .NET 8.

Usando o .NET 8 com Lambda

Para usar o .NET 8 com o Lambda, você deve atualizar suas ferramentas.

  1. Instale ou atualize o SDK do .NET 8.
  2. Se você estiver usando o AWS SAM, instale ou atualize para a versão mais recente.
  3. Se você estiver usando o Visual Studio, instale ou atualize o AWS Toolkit for Visual Studio.
  4. Se você usa a extensão .NET Lambda Global Tools (Amazon.Lambda.Tools), instale a extensão e os modelos da CLI. Você pode atualizar as ferramentas existentes com dotnet tool update -g Amazon.Lambda.Tools e os modelos existentes com dotnet new install Amazon.Lambda.Templates.

Você também pode usar o .NET 8 com Powertools for AWS Lambda (.NET), um kit de ferramentas para desenvolvedores para implementar as melhores práticas sem servidor, como observabilidade, processamento em lote, recuperação de parâmetros, idempotência e sinalizadores de recursos.

Criando novas funções do.NET 8

Usando o AWS SAM

  1. Execute sam init.
  2. Escolha 1- AWS Quick Start Templates..
  3. Escolha um dos modelos disponíveis, como Hello World Example.
  4. Selecione N para Use the most popular runtime and package type?
  5. Selecione dotnet8 como runtime. O exemplo dotnet8 Hello World também inclui uma opção de modelo AOT nativo.
  6. Siga o restante das instruções para criar a função .NET 8.

AWS SAM .NET 8 init options

AWS SAM .NET 8 init options

Você pode alterar o código da função gerado e usar sam deploy –guided para implantar a função.

Usando o AWS Toolkit para Visual Studio

  1. No assistente Criar um novo projeto, filtre os modelos para o tipo de projeto Lambda ou Serverless e selecione um modelo. Use o Lambda para implantar uma única função. Use o Serverless para implantar uma coleção de funções usando o AWS CloudFormation.
  2. Continue com as etapas para concluir a criação do seu projeto.
  3. Você pode alterar o código da função gerada.
  4. Para implantar, clique com o botão direito do mouse no projeto no Solution Explorer e selecione Publish to AWS Lambda.

Usando extensões da AWS para a CLI do.NET

  1. Execute dotnet new list --tag Lambda para obter uma lista dos modelos disponíveis do Lambda.
  2. Escolha um modelo e execute dotnet new <template name>. Para criar uma função usando o AOT nativo, use dotnet new lambda.NativeAOT ou dotnet new serverless.NativeAOT ao usar o .NET Lambda Annotations Framework.
  3. Localize a função Lambda gerada no diretório em src que contém o arquivo .csproj. Você pode alterar o código da função gerada.
  4. Para implantar, execute dotnet lambda deploy-function e siga as instruções.
  5. Você pode testar a função na nuvem usando dotnet lambda invoke-function ou usando a funcionalidade de teste no console Lambda.

Você pode criar e implantar funções do .NET Lambda usando imagens de contêiner. Siga as instruções na documentação.

Migrando do .NET 6 para o .NET 8 sem AOT nativo

Usando o AWS SAM

  1. Abra o arquivo template.yaml.
  2. Atualize o Runtime para dotnet8.
  3. Abra uma janela de terminal e reconstrua o código usando o sam build.
  4. Execute sam deploy para implantar as alterações.

Usando o AWS Toolkit para Visual Studio

  1. Abra o arquivo de projeto .csproj e atualize o TargetFramework para net8.0. Atualize os pacotes NuGet para suas funções do Lambda para a versão mais recente para obter atualizações do .NET 8.
  2. Verifique se o comando de compilação que você está usando tem como alvo o runtime do .NET 8.
  3. Pode haver etapas adicionais, dependendo da ferramenta de construção/implantação que você está usando. Atualizar o runtime da função pode ser suficiente.

.NET function in AWS Toolkit for Visual Studio

Usando extensões da AWS para a CLI do .NET ou o AWS Toolkit for Visual Studio

  1. Abra o arquivo aws-lambda-tools-defaults.json, se ele existir.
    1. Defina o campo da estrutura como net8.0. Se não for especificado, o valor será inferido do arquivo do projeto.
    2. Defina o campo function-runtime como dotnet8.
  2. Abra o arquivo serverless.template, se ele existir. Para qualquer recurso AWS::Lambda::Function ou AWS::Servereless::Function, defina a propriedade Runtime como dotnet8.
  3. Abra o arquivo de projeto .csproj, se ele existir, e atualize o TargetFramework para net8.0. Atualize os pacotes NuGet para suas funções do Lambda para a versão mais recente para obter atualizações do .NET 8.

Migrando do .NET 6 para o .NET 8 AOT nativo

O exemplo a seguir migra uma função de biblioteca de classes do .NET 6 para uma função executável AOT nativa do .NET 8. Isso usa a Lambda Annotations framework, que fornece padrões idiomáticos de codificação do .NET.

Atualize seu arquivo de projeto

  1. Abra o arquivo do projeto.
  2. Defina TargetFramework como net8.0.
  3. Defina OutputType como exe.
  4. Remova PublishReadyToRun se existir.
  5. Adicione PublishAot e defina como true.
  6. Adicione ou atualize as referências do pacote NuGet para Amazon.Lambda.Annotations e Amazon.Lambda.RuntimeSupport. Você pode atualizar usando a interface do usuário do NuGet em seu IDE, manualmente ou executando dotnet add package Amazon.Lambda.RuntimeSupport e dotnet add package Amazon.Lambda.Annotations a partir do diretório do seu projeto.

Seu arquivo de projeto deve ser semelhante ao seguinte:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <AWSProjectType>Lambda</AWSProjectType>
    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
    <!-- Generate native aot images during publishing to improve cold start time. -->
    <PublishAot>true</PublishAot>
	  <!-- StripSymbols tells the compiler to strip debugging symbols from the final executable if we're on Linux and put them into their own file. 
		This will greatly reduce the final executable's size.-->
	  <StripSymbols>true</StripSymbols>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Amazon.Lambda.Core" Version="2.2.0" />
    <PackageReference Include="Amazon.Lambda.RuntimeSupport" Version="1.10.0" />
    <PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.4.0" />
  </ItemGroup>
</Project>

Atualizando seu código de função

    1. Faça referência à biblioteca de anotações using Amazon.Lambda.Annotations;
    2. Adicione [assembly:LambdaGlobalProperties(GenerateMain = true)] para permitir que o annotations framework crie o método principal. Isso é necessário, pois o projeto agora é um executável em vez de uma biblioteca.
    3. Adicione a classe parcial abaixo e inclua um atributo JsonSerializable para qualquer tipo que você precise serializar, inclusive para a entrada e saída da função. Essa classe parcial é usada no momento da construção para gerar código livre de reflexão dedicado à serialização dos tipos listados. Veja a seguir um exemplo:
      /// <summary>
      /// This class is used to register the input event and return type for the FunctionHandler method with the System.Text.Json source generator.
      /// There must be a JsonSerializable attribute for each type used as the input and return type or a runtime error will occur 
      /// from the JSON serializer unable to find the serialization information for unknown types.
      /// </summary>
      [JsonSerializable(typeof(APIGatewayHttpApiV2ProxyRequest))]
      [JsonSerializable(typeof(APIGatewayHttpApiV2ProxyResponse))]
      public partial class MyCustomJsonSerializerContext : JsonSerializerContext
      {
          // By using this partial class derived from JsonSerializerContext, we can generate reflection free JSON Serializer code at compile time
          // which can deserialize our class and properties. However, we must attribute this class to tell it what types to generate serialization code for
          // See https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-source-generation
      }
    4. Após a instrução using, adicione o seguinte para especificar o serializador a ser usado. [assembly: LambdaSerializer(typeof(SourceGeneratorLambdaJsonSerializer<LambdaFunctionJsonSerializerContext>))]

Troque LambdaFunctionJsonSerializerContext pelo seu contexto se você não estiver usando a classe parcial da etapa anterior.

Atualizando sua configuração de função

Se você estiver usando aws-lambda-tools-defaults.json.

  1. Defina function-runtime como dotnet8.
  2. Defina a function-architecture para corresponder à sua máquina de compilação — x86_64 ou arm64.
  3. Defina (ou atualize) environment-variables para incluir ANNOTATIONS_HANDLER=<YourFunctionHandler> Substitua <YourFunctionHandler> pelo nome do método da sua função handler, para que annotations framework saiba qual método chamar a partir do método main gerado.
  4. Defina function-handler como o nome do conjunto executável em seu diretório bin. Por padrão, esse é o nome do seu projeto, que diz ao script bootstrap do .NET Lambda que execute seu binário nativo em vez de iniciar o runtime do .NET. Se o arquivo do seu projeto tiver AssemblyName, use esse valor para a função handler.
{
  "function-architecture": "x86_64",
  "function-runtime": "dotnet8",
  "function-handler": "<your-assembly-name>",
  "environment-variables",
  "ANNOTATIONS_HANDLER=<your-function-handler>",
}

Implemente e teste

  1. Implante sua função. Se você estiver usando Amazon.Lambda.Tools, execute dotnet lambda deploy-function. Verifique os avisos de corte durante a construção e refatore para eliminá-los.
  2. Teste sua função para garantir que as chamadas nativas para o AL2023 estejam funcionando corretamente. Por padrão, a execução de testes de unidade local em sua máquina de desenvolvimento não será executada de forma nativa e ainda usará o compilador JIT. A execução com o compilador JIT não permite que você detecte erros de runtime específicos do AOT nativo.

Conclusão

O Lambda está introduzindo o novo runtime gerenciado .NET 8. Esta postagem destaca os novos recursos do .NET 8. Você pode criar novas funções do Lambda ou migrar funções existentes para o .NET 8 ou .NET 8 Native AOT.

Para obter mais informações, consulte o repositório AWS Lambda for .NET, a documentação do e .Net no Serverless Land.

Para obter mais recursos de aprendizado sem servidor, visite Serverless Land.

 

Este blog é uma tradução do blog original em inglês (link aqui).

Biografia do Autor

Beau Gosse, engenheiro de software sênior
Paras Jain, gerente técnico sênior de contas

Biografia do tradutor

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.