🐳

AWS Lambda Web Adapterで.NET WEB APIをLambdaで動かす

2023/04/02に公開

はじめに

Lambda Web Adapterの登場により素のWebフレームワークがLambdaで動かせるようになりました。
これまでコンテナのデプロイ先としてはECS on FargateやAWS App Runnerがありましたが、Lambdaも有力な候補になってきました。

Lambda Web Adapterとは

LambdaイベントをHTTPリクエストへ変換してコンテナ内のWebアプリへ伝えてくれます。
詳細な説明は公式GitHubが分かりやすいです。
https://github.com/awslabs/aws-lambda-web-adapter

Lambda Web Adapterを利用すると、通常のWebアプリコンテナをLambdaで実行することが(原理的には)可能になります。使い慣れたWebフレームワークでLambdaの恩恵が受けられるというわけです。

GitHub上では

  • Python
  • Node.js
  • Java
  • PHP
  • Rust
  • Go

などの例は紹介されていますが、C#(.NET)での使用例がなかったので、動くかどうか試してみます。.NETに興味がない方も意外と簡単だねと眺めていただけると幸いです。

作ってみよう

前提として下記のセットアップは済ませています。

  • Dockerのインストール
  • AWSアカウントの作成、権限設定
  • Visual Studioのインストール
  • AWS Toolkit for Visual Studioのインストール

今回の構成ではLambda 関数 URLでLambdaコンテナへアクセスします。
コンテナはC#(.NET6)で作成し、AWS Toolkit for Visual StudioでECRに発行します。

プロジェクトの作成

Visual Studioを起動し、「新しいプロジェクトの作成」を選択します。

新しいプロジェクトの作成で「ASP.NET Core Web API」を選択します。

プロジェクト名は「LambdaWebAPI」としました。次へ。

追加情報は「HTTPS用の構成」チェックを外します。「Dockerを有効にする」のチェックが入っていることを確認し、「作成」をクリック。

おなじみの天気予報テンプレートが出来上がります。

Lambda Web Adapter機能追加

Lambda Web Adapterを使用するためにいくつか修正を加えます。
下記ファイルを修正していきます。

  • Dockerfile
  • appsettings.json
  • Program.cs

Dockerfile修正

.NET6ではデフォルトポートが80になっているので、8080に変更します。(.NET8からはデフォルトが8080になるようなので今後はこの修正は不要になるかも)
また、今回のポイントとなるaws-lambda-adapterのコピーを追加しています。(バージョンは公式GitHubを参考に最新バージョンを利用しました。)他の設定はデフォルトテンプレートのままにしています。

Dockerfile
 #See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
 
 FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
 WORKDIR /app
-EXPOSE 80
+EXPOSE 8080
 
 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
 WORKDIR /src
 COPY ["LambdaWebAPI/LambdaWebAPI.csproj", "LambdaWebAPI/"]
 RUN dotnet restore "LambdaWebAPI/LambdaWebAPI.csproj"
 COPY . .
 WORKDIR "/src/LambdaWebAPI"
 RUN dotnet build "LambdaWebAPI.csproj" -c Release -o /app/build
 
 FROM build AS publish
 RUN dotnet publish "LambdaWebAPI.csproj" -c Release -o /app/publish /p:UseAppHost=false
 
 FROM base AS final
+COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.6.4 /lambda-adapter /opt/extensions/lambda-adapter
 WORKDIR /app
 COPY --from=publish /app/publish .
 ENTRYPOINT ["dotnet", "LambdaWebAPI.dll"]

appsettings.json修正

urlsプロパティを追加し、デフォルトポートを8080へ変更します。(.NET8以降不要になるかも)

appsettings.json
 {
   "Logging": {
     "LogLevel": {
       "Default": "Information",
       "Microsoft.AspNetCore": "Warning"
     }
   },
-  "AllowedHosts": "*"
+  "AllowedHosts": "*",
+  "urls": "http://*:8080"
 }

Program.cs修正

Lambda Web AdapterはWebアプリの準備状況をチェックしているようです。AddHealthChecksでルートパスMapHealthChecks("/")のヘルスチェックを有効にしておきます。

Program.cs
 var builder = WebApplication.CreateBuilder(args);
 
 // Add services to the container.
 
 builder.Services.AddControllers();
 // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
 builder.Services.AddEndpointsApiExplorer();
 builder.Services.AddSwaggerGen();
+builder.Services.AddHealthChecks();
 
 var app = builder.Build();
 
 // Configure the HTTP request pipeline.
 if (app.Environment.IsDevelopment())
 {
     app.UseSwagger();
     app.UseSwaggerUI();
 }
 
 app.UseAuthorization();
 
 app.MapControllers();
+app.MapHealthChecks("/");
 
 app.Run();

作成したプロジェクトをECRに発行

AWS Toolkit for Visual Studioの機能を使い、コンテナをECRへ発行します。
Visual Studioのプロジェクトを右クリックし、「Publish Container to AWS」を選択します。

Deployment Targetは「Push only the Docker image to Amazon Elastic Container Registry」を選択し、Publish。資格情報やリージョンは適宜変更してください。

コンテナのビルドが始まります。ECRへのPushが完了するまでしばらく待ちます。

Lambdaにデプロイ

ここからはAWSの設定となります。画面でポチポチとデプロイしていきます。コンテナイメージからLambdaを作成し、Lambda関数URLで公開します。

Lambda関数の作成

AWSへログインし、AWS Lambdaのコンソールへアクセスします。
Lambdaのダッシュボードから「関数の作成」をクリックします。
関数の作成では「コンテナイメージ」を選択します。

先ほどアップロードしたコンテナを選択し、「関数の作成」をクリック。

Lambda関数URLを設定

Lambdaの設定 → 関数URL → 関数URLを作成 を選択します。

関数URLの認証タイプは「NONE」を選択し、「保存」。

関数URLが作成されました。

動作確認

関数URLにアクセスしてみます。

ルートパスに設定したヘルスチェックHealthyが表示されました。

次に他のパスも動作するか確認するため、{先ほど確認したURL}/WeatherForecastにアクセスしてみます。
デフォルトテンプレートのWeatherForecastの結果が表示されました。

タイムアウトするかも

Lambdaのデフォルト設定では、タイムアウトが3秒になっていました。
私が実行した際、初回のアクセスはコンテナイメージがキャッシュされていないためか、タイムアウトになってしまいました。(2回目以降は正しい結果が返りました)
実際に利用する場合はタイムアウト値やメモリの調整が必要です。

まとめ

Lambda Web Adapterで.NETコンテナを動作させることができました。
Lambda Web Adapterの登場によりサーバレスコンテナの幅が広がったのではないでしょうか?
検証環境や日中のわずかな時間しか利用されないようなワークロードではコスト削減に貢献しそうです。
Lambdaの特性上、アクセスのたびにコンテナが起動されるため、重たいWebフレームワークでは起動タイムアウトが発生する可能性があります。(一度キャッシュに乗ればそれほど気になりませんでしたが)実用に耐えられるかは十分に検証を行う必要があります。
.NET8ではNative AOT対応やArm64へのサポート強化が進んでいるようなので、将来的にはこういった問題も解消されていくのでしょうか。今後が楽しみです。

Discussion