AWS Lambda Web Adapterで.NET WEB APIをLambdaで動かす
はじめに
Lambda Web Adapterの登場により素のWebフレームワークがLambdaで動かせるようになりました。
これまでコンテナのデプロイ先としてはECS on FargateやAWS App Runnerがありましたが、Lambdaも有力な候補になってきました。
Lambda Web Adapterとは
LambdaイベントをHTTPリクエストへ変換してコンテナ内のWebアプリへ伝えてくれます。
詳細な説明は公式GitHubが分かりやすいです。
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を参考に最新バージョンを利用しました。)他の設定はデフォルトテンプレートのままにしています。
#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 /lambda-adapter /opt/extensions/lambda-adapter
WORKDIR /app
COPY /app/publish .
ENTRYPOINT ["dotnet", "LambdaWebAPI.dll"]
appsettings.json修正
urls
プロパティを追加し、デフォルトポートを8080
へ変更します。(.NET8以降不要になるかも)
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
- "AllowedHosts": "*"
+ "AllowedHosts": "*",
+ "urls": "http://*:8080"
}
Program.cs修正
Lambda Web AdapterはWebアプリの準備状況をチェックしているようです。AddHealthChecks
でルートパスMapHealthChecks("/")
のヘルスチェックを有効にしておきます。
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