.NET Lambda 関数コードをネイティブランタイム形式にコンパイルする
.NET 8 はネイティブ ahead-of-time (AOT) コンパイルをサポートします。ネイティブ AOT を使用することで、Lambda 関数コードをネイティブなランタイム形式にコンパイルできるので、実行時に .NET コードをコンパイルする必要がなくなります。ネイティブ AOT コンパイルは、.NET で記述する Lambda 関数のコールドスタート時間を短縮できます。詳細については、AWS コンピューティングブログの「Introducing the .NET 8 runtime for AWS Lambda
Lambda ランタイム
ネイティブ AOT コンパイルで構築された Lambda 関数をデプロイするには、 マネージド .NET 8 Lambda ランタイムを使用します。このランタイムは、x86_64 アーキテクチャと arm64 アーキテクチャの両方の使用をサポートします。
AOT を使用せずに .NET Lambda 関数をデプロイすると、アプリケーションが中間言語 (IL) コードにコンパイルされます。実行時、Lambda ランタイム just-in-time (JIT) コンパイラが、必要に応じて IL コードを取得してマシンコードにコンパイルします。ネイティブ AOT で事前にコンパイルされた Lambda 関数では、関数をデプロイするときにコードをマシンコードにコンパイルするため、Lambda ランタイムの .NET ランタイムや SDK に依存しずに、実行前にコードをコンパイルできます。
AOT の制限の 1 つは、アプリケーションコードを、.NET 8 ランタイムが使用するのと同じ Amazon Linux 2023 (AL2023) オペレーティングシステムを使用する環境でコンパイルする必要があることです。.NET Lambda CLI には、AL2023 イメージを使用して Docker コンテナでアプリケーションをコンパイルする機能があります。
アーキテクチャ間の互換性に関する潜在的な問題を避けるため、関数用に設定したのと同じプロセッサアーキテクチャの環境でコードをコンパイルすることを強くお勧めします。クロスアーキテクチャコンパイルの制限の詳細については、Microsoft .NET ドキュメントの「クロスコンパイル
前提条件
- Docker
-
ネイティブ AOT を使用するには、関数コードを、 ランタイムと同じ AL2023 オペレーティングシステムの環境でコンパイルする必要があります。以下のセクションで説明する .NET CLI コマンドは、Docker を使用して AL2023 環境で Lambda 関数を開発および構築します。
- .NET 8 SDK
-
ネイティブ AOT コンパイルは .NET 8 の機能です。ビルドマシンには、ランタイムだけでなく、.NET 8 SDK
もインストールする必要があります。 - Amazon.Lambda.Tools
-
Lambda 関数を作成するには、Amazon.Lambda.Tools
.NET Global Tools 拡張機能 を使用します。Amazon.Lambda.Tools をインストールするには、以下のコマンドを実行します。 dotnet tool install -g Amazon.Lambda.Tools
Amazon.Lambda.Tools .NET CLI 拡張機能の詳細については、GitHub の「AWS Extensions for .NET CLI
」リポジトリを参照してください。 - Amazon.Lambda.Templates
-
Lambda 関数コードを生成するには、Amazon.Lambda.Templates
NuGet パッケージを使用します。このテンプレートパッケージをインストールするには、以下のコマンドを実行します。 dotnet new install Amazon.Lambda.Templates
開始
.NET Global CLI と AWS Serverless Application Model (AWS SAM) はどちらも、ネイティブ AOT を使用してアプリケーションを構築するための開始用テンプレートを提供します。最初のネイティブ AOT Lambda 関数を構築するには、以下の手順を実行します。
ネイティブ AOT コンパイル済み Lambda 関数を初期化してデプロイするには
-
ネイティブ AOT テンプレートを使用して新しいプロジェクトを初期化し、作成した
.cs
ファイルと.csproj
ファイルが含まれるディレクトリに移動します。この例では、関数にNativeAotSample
という名前を付けています。dotnet new lambda.NativeAOT -n NativeAotSample cd ./NativeAotSample/src/NativeAotSample
ネイティブ AOT テンプレートによって作成された
Function.cs
ファイルには、次の関数コードが含まれています。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
ネイティブ AOT は、アプリケーションを単一のネイティブバイナリにコンパイルします。このバイナリのエントリポイントは
static Main
メソッドです。static Main
内部では、Lambda ランタイムがブートストラップされ、FunctionHandler
メソッドが設定されます。ランタイムブートストラップの一部として、ソース生成のシリアライザーはnew SourceGeneratorLambdaJsonSerializer<LambdaFunctionJsonSerializerContext>()
を使用して設定されます。 -
アプリケーションを Lambda にデプロイするには、Docker がローカル環境で実行されていることを確認し、次のコマンドを実行します。
dotnet lambda deploy-function
バックグラウンドでは、.NET Global CLI が AL2023 Docker イメージをダウンロードし、実行中のコンテナ内でアプリケーションコードをコンパイルしています。コンパイルされたバイナリは、Lambda にデプロイされる前にローカルファイルシステムに出力されます。
-
次のコマンドを実行して関数をテストします。
<FUNCTION_NAME>
を、デプロイウィザードで関数に選択した名前に置き換えます。dotnet lambda invoke-function <FUNCTION_NAME> --payload "hello world"
CLI からの応答には、コールドスタートのパフォーマンスの詳細 (初期化時間) と関数呼び出しの合計実行時間が含まれます。
-
前の手順で作成した AWS リソースを削除するには、次のコマンドを実行します。
<FUNCTION_NAME>
を、デプロイウィザードで関数に選択した名前に置き換えます。使用しなくなった AWS リソースを削除することで、AWS アカウントに請求される料金の発生を防ぎます。dotnet lambda delete-function <FUNCTION_NAME>
シリアル化
ネイティブ AOT を使用して Lambda に関数をデプロイするには、関数コードでソース生成のシリアル化Product
タイプを返す Lambda 関数には、次のように定義されたシリアライザーが含まれます。
[JsonSerializable(typeof(APIGatewayProxyRequest))] [JsonSerializable(typeof(APIGatewayProxyResponse))] [JsonSerializable(typeof(Product))] public partial class CustomSerializer : JsonSerializerContext { }
トリミング
ネイティブ AOT は、バイナリができるだけ小さくなるように、コンパイルの一部としてアプリケーションコードをトリミングします。.NET 8 for Lambda は、以前のバージョンの .NET と比較して、トリミングサポートが改善されています。Lambda ランタイムライブラリ
これらの改善により、ビルド時のトリミング警告を排除できる可能性がありますが、.NET が完全にトリムセーフになることはありません。つまり、関数が依存するライブラリの一部が、コンパイルステップの一部としてトリミングされる可能性があるということです。これを管理するには、次の例に示すように、TrimmerRootAssemblies
を .csproj
ファイルの一部として定義することでこれを管理できます。
<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>
トリミング警告が表示された場合は、警告を生成するクラスを TrimmerRootAssembly
に追加しても、問題は解決しない可能性があります。トリミング警告は、ランタイムまで決定できない他のクラスにクラスがアクセスしようとしていることを示します。ランタイムエラーを回避するには、この 2 番目のクラスを TrimmerRootAssembly
に追加します。
トリミング警告の管理の詳細については、Microsoft .NET ドキュメントの「Introduction to trim warnings
トラブルシューティング
- Error: Cross-OS native compilation is not supported.(エラー: クロス OS のネイティブコンパイルはサポートされません。)
-
使用している Amazon.Lambda.Tools .NET Core グローバルツールのバージョンが古くなっています。最新のバージョンにアップデートしてから、再試行してください。
- Docker: image operating system "linux" cannot be used on this platform. (Docker: このプラットフォームでイメージオペレーティングシステム「linux」を使用することはできません。)
-
システム上の Docker は、Windows コンテナを使用するように設定されています。Linux コンテナに切り替えて、ネイティブ AOT 構築環境を実行してください。
一般的なエラーの詳細については、GitHub の AWS NativeAOT for .NET