Funciones de .NET con compilación nativa anticipada - AWS Lambda

Funciones de .NET con compilación nativa anticipada

.NET 8 admite la compilación nativa anticipada (AOT). Con el enfoque nativo anticipado, puede compilar el código de la función de Lambda en un formato de tiempo de ejecución nativo, lo que elimina la necesidad de compilar código .NET en el tiempo de ejecución. La compilación nativa anticipada puede reducir el tiempo de inicio en frío de las funciones de Lambda que crea con .NET. Para obtener más información, consulte Introducción al tiempo de ejecución de .NET 8 para AWS Lambda en el Blog de informática de AWS.

Tiempo de ejecución de Lambda

Utilice el tiempo de ejecución administrado de .NET 8 en Lambda para implementar una función de Lambda que se cree con la compilación nativa anticipada (AOT). Este tiempo de ejecución es compatible con las arquitecturas x86_64 y arm64.

Cuando implementa una función de Lambda en .NET sin utilizar AOT, la aplicación primero se compila en código de lenguaje intermedio (IL). Durante el tiempo de ejecución, la compilación en tiempo de ejecución (JIT) de Lambda toma el código IL y lo compila en código de máquina según sea necesario. Con una función de Lambda que se compila con antelación mediante una AOT nativa, el código se compila en código de máquina al implementar la función. Por lo tanto, no se depende del tiempo de ejecución de .NET ni del SDK en el tiempo de ejecución de Lambda para compilar el código antes de la ejecución.

Una limitación de AOT implica que el código de la aplicación debe compilarse en un entorno con el mismo sistema operativo Amazon Linux 2023 (AL2023) que utiliza el tiempo de ejecución de .NET 8. La CLI de Lambda .NET proporciona la funcionalidad para compilar la aplicación en un contenedor de Docker mediante una imagen AL2023.

Para evitar posibles problemas de compatibilidad entre arquitecturas, le recomendamos encarecidamente que compile el código en un entorno con la misma arquitectura de procesador que configuró para la función. Para obtener más información sobre las limitaciones de la compilación entre arquitecturas, consulte Compilación entre arquitecturas en la documentación de Microsoft .NET.

Requisitos previos

Docker

Para utilizar la AOT nativa, el código de la función debe compilarse en un entorno con el mismo sistema operativo AL2023 que el tiempo de ejecución de .NET 8. Los comandos de la CLI de .NET de las siguientes secciones utilizan Docker para desarrollar y crear funciones de Lambda en un entorno AL2023.

SDK para .NET 8

La compilación nativa anticipada es una característica de .NET 8. Debe instalar el SDK para .NET 8 en su máquina de compilación, no solo el tiempo de ejecución.

Amazon.Lambda.Tools

Para crear sus funciones de Lambda, utilice la extensión de herramientas globales de .NET Amazon.Lambda.Tools. Ejecute el siguiente comando para instalar Amazon.Lambda.Tools:

dotnet tool install -g Amazon.Lambda.Tools

Para obtener más información sobre la Amazon.Lambda.Tools de la extensión de la CLI de .NET, consulte el repositorio en GitHub Extensiones de AWS para la CLI de .NET.

Amazon.Lambda.Templates

Para generar el código de la función de Lambda, utilice el paquete NuGet Amazon.Lambda.Templates. Para instalar este paquete de plantillas, ejecute el siguiente comando:

dotnet new install Amazon.Lambda.Templates

Introducción

Tanto la CLI global de .NET como la AWS Serverless Application Model (AWS SAM) proporcionan plantillas de introducción para crear aplicaciones mediante la AOT nativa. Para crear su primera función de Lambda AOT nativa, lleve a cabo los pasos de las siguientes instrucciones.

Para inicializar e implementar una función de Lambda nativa compilada en AOT
  1. Inicialice un nuevo proyecto con la plantilla AOT nativa y, a continuación, navegue hasta el directorio que contiene los archivos .cs y .csproj creados. En este ejemplo, asignamos un nombre a nuestra función NativeAotSample.

    dotnet new lambda.NativeAOT -n NativeAotSample cd ./NativeAotSample/src/NativeAotSample

    El archivo Function.cs creado por la plantilla AOT nativa contiene el siguiente código de función.

    using Amazon.Lambda.Core; using Amazon.Lambda.RuntimeSupport; using Amazon.Lambda.Serialization.SystemTextJson; using System.Text.Json.Serialization; namespace NativeAotSample; public class Function { /// <summary> /// The main entry point for the Lambda function. The main function is called once during the Lambda init phase. It /// initializes the .NET Lambda runtime client passing in the function handler to invoke for each Lambda event and /// the JSON serializer to use for converting Lambda JSON format to the .NET types. /// </summary> private static async Task Main() { Func<string, ILambdaContext, string> handler = FunctionHandler; await LambdaBootstrapBuilder.Create(handler, new SourceGeneratorLambdaJsonSerializer<LambdaFunctionJsonSerializerContext>()) .Build() .RunAsync(); } /// <summary> /// A simple function that takes a string and does a ToUpper. /// /// To use this handler to respond to an AWS event, reference the appropriate package from /// https://github.com/aws/aws-lambda-dotnet#events /// and change the string input parameter to the desired event type. When the event type /// is changed, the handler type registered in the main method needs to be updated and the LambdaFunctionJsonSerializerContext /// defined below will need the JsonSerializable updated. If the return type and event type are different then the /// LambdaFunctionJsonSerializerContext must have two JsonSerializable attributes, one for each type. /// // When using Native AOT extra testing with the deployed Lambda functions is required to ensure // the libraries used in the Lambda function work correctly with Native AOT. If a runtime // error occurs about missing types or methods the most likely solution will be to remove references to trim-unsafe // code or configure trimming options. This sample defaults to partial TrimMode because currently the AWS // SDK for .NET does not support trimming. This will result in a larger executable size, and still does not // guarantee runtime trimming errors won't be hit. /// </summary> /// <param name="input"></param> /// <param name="context"></param> /// <returns></returns> public static string FunctionHandler(string input, ILambdaContext context) { return input.ToUpper(); } } /// <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(string))] public partial class LambdaFunctionJsonSerializerContext : 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

    La AOT nativa compila la aplicación en un único binario nativo. El punto de entrada de ese binario es el método static Main. Dentro de static Main, se inicia el tiempo de ejecución de Lambda y se configura el método FunctionHandler. Como parte del arranque del tiempo de ejecución, se configura un serializador generado en la fuente mediante new SourceGeneratorLambdaJsonSerializer<LambdaFunctionJsonSerializerContext>()

  2. Para implementar la aplicación en Lambda, asegúrese de que Docker se esté ejecutando en su entorno local y ejecute el siguiente comando.

    dotnet lambda deploy-function

    Entre bastidores, la CLI global de .NET descarga una imagen de Docker de AL2023 y compila el código de la aplicación dentro de un contenedor en ejecución. El binario compilado se devuelve a su sistema de archivos local antes de implementarlo en Lambda.

  3. Pruebe la función al ejecutar el siguiente comando. Reemplace <FUNCTION_NAME> por el nombre que eligió para la función en el asistente de implementación.

    dotnet lambda invoke-function <FUNCTION_NAME> --payload "hello world"

    La respuesta de la CLI incluye detalles de rendimiento para el arranque en frío (duración de la inicialización) y el tiempo total de ejecución de la invocación de la función.

  4. Para eliminar los recursos de AWS que creó siguiendo los pasos anteriores, ejecute el siguiente comando. Reemplace <FUNCTION_NAME> por el nombre que eligió para la función en el asistente de implementación. Si elimina los recursos de AWS que ya no utiliza, evitará que se facturen gastos innecesarios en su Cuenta de AWS.

    dotnet lambda delete-function <FUNCTION_NAME>

Serialización

Para implementar funciones en Lambda mediante la AOT nativa, el código de la función debe usar la serialización generada en la fuente. En lugar de utilizar la reflexión en tiempo de ejecución para recopilar los metadatos necesarios para acceder a las propiedades de los objetos con fines de serialización, los generadores de fuentes generan archivos fuente en C# que se compilan al crear la aplicación. Para configurar correctamente el serializador generado en código fuente, asegúrese de incluir todos los objetos de entrada y salida que utilice su función, así como cualquier tipo personalizado. Por ejemplo, una función de Lambda que recibe eventos de una API de REST de API Gateway y devuelve un tipo de Product personalizado incluiría un serializador definido de la siguiente manera.

[JsonSerializable(typeof(APIGatewayProxyRequest))] [JsonSerializable(typeof(APIGatewayProxyResponse))] [JsonSerializable(typeof(Product))] public partial class CustomSerializer : JsonSerializerContext { }

Recorte

La AOT nativa recorta el código de la aplicación como parte de la compilación para garantizar que el binario sea lo más pequeño posible. .NET 8 para Lambda proporciona un mejor soporte de reducción de tamaño en comparación con las versiones anteriores de .NET. Se ha añadido compatibilidad con las bibliotecas de tiempo de ejecución de Lambda, SDK de .NET de AWS, las anotaciones de Lambda para .NET y .NET 8.

Estas mejoras ofrecen la posibilidad de eliminar las advertencias de recorte durante el tiempo de compilación, pero .NET nunca será completamente seguro para la reducción. Esto significa que las partes de las bibliotecas en las que se basa su función pueden recortarse como parte del paso de compilación. Para gestionarlo, puede definir TrimmerRootAssemblies en su archivo .csproj, tal como se muestra en el ejemplo siguiente.

<ItemGroup> <TrimmerRootAssembly Include="AWSSDK.Core" /> <TrimmerRootAssembly Include="AWSXRayRecorder.Core" /> <TrimmerRootAssembly Include="AWSXRayRecorder.Handlers.AwsSdk" /> <TrimmerRootAssembly Include="Amazon.Lambda.APIGatewayEvents" /> <TrimmerRootAssembly Include="bootstrap" /> <TrimmerRootAssembly Include="Shared" /> </ItemGroup>

Tenga en cuenta que cuando reciba una advertencia de recorte, es posible que añadir la clase que genera la advertencia a TrimmerRootAssembly no resuelva el problema. Una advertencia de recorte indica que la clase está intentando acceder a otra clase que no se puede determinar hasta el tiempo de ejecución. Para evitar errores de tiempo de ejecución, añada esta segunda clase a TrimmerRootAssembly.

Para obtener más información sobre la administración de las advertencias de recorte, consulte Introducción a las advertencias de recorte en la documentación de Microsoft .NET.

Solución de problemas

Error: no se admite la compilación nativa entre sistemas operativos.

Su versión de la herramienta global de .NET Core Amazon.Lambda.Tools está desactualizada. Actualice la herramienta a la versión más reciente e inténtelo de nuevo.

Docker: el sistema operativo de imágenes “Linux” no se puede utilizar en esta plataforma.

Docker está configurado en su sistema para usar contenedores de Windows. Cambie a contenedores de Linux para ejecutar el entorno de compilación nativa anticipada.

Para obtener más información sobre los errores comunes, consulte el repositorio en GitHub Enfoque nativo anticipado de AWS para .NET.