🙌

ASP.NET Core MVCアプリケーションをLambda Web Adapterを使ってLambda上で動かす

2024/04/01に公開

blogger方面から引っ越してきました。Zenn上では初記事です。
社内の勉強会用に小粒なネタができたので記事にまとめました。

きっかけ

Lambdaがコンテナイメージをサポートしたことは風の便りには聞いていたのですが、これまで実際に使う機会がありませんでした。10GBのイメージサイズまでサポートしているとのことですが、「Serverlessなんてサイズの小さなプログラムを動かすためのもの。250MBもあれば十分だぜ。」などと思っておりました。ところが、ちょっとしたWebフレームワークにパッケージを追加すると250MBの制限を超えそうになり、サイズを減らすために四苦八苦するケースが出てきました。他に抱えていた課題としては、ローカルの開発環境上でLambdaをデバッグするのはちょっとダルいんですよね。Lambdaに慣れていないチームで開発するときはなおさらです。
そんな中見つけたのが今回のネタであるLambda Web Adapterです。個人開発のアプリケーションでは利用し始めたところで、NestJSで実装したAPIサーバーをLambda上で動かしています。で、今回は伝統的なWebアプリケーションであるASP.NET Core MVCをLambda上で動かすのに挑戦してみました。(挑戦と言っても10分くらいで動いてしまったので、とてもお手軽です。)

やってみた

まずは最短距離でアプリケーションをLambda上で動かすまでの手順を説明します。

前提条件

以下を用意します。ちなみに、私の開発環境はMacですが、Windowsな方はWSLを使えば同じようなことができると思います。

ソースコードのクローン

今回Lambda上で動かすアプリケーションはASP.NET Core MVCの公式ドキュメントで紹介されているサンプル「MvcMovie」です。アプリケーション自体は変更しておらず、これにDockerfileを追加してGitHub上に公開しておいたので、これを使って手順を進めます。まずは、リポジトリをcloneします。

git clone https://github.com/awwa/dotnet-mvc-lambda.git
cd dotnet-mvc-lambda

ECR リポジトリの作成

環境変数ACCOUNT_IDにご自身のAWSアカウントのIDを設定します。その後、ECR にログインし、mvcmovieという名前でリポジトリを作成します。リージョンは東京(ap-northeast-1)を使っていますがお好きな場所で。

export ACCOUNT_ID=xxxxxxxxxxxxx
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com
aws ecr create-repository --repository-name mvcmovie --region ap-northeast-1 --image-scanning-configuration scanOnPush=true

Docker イメージのビルド

ローカル環境環境上でDocker イメージをビルドします。今回の記事のポイントであるDockerfileの中身は後で解説します。

docker build -f ./Dockerfile -t mvcmovie:latest .

ローカル環境での動作確認

Docker イメージをローカルで実行して動作確認します。
以下のコマンド実行後、ブラウザで http://localhost:8080 にアクセスします。
それっぽい画面(Welcome)が表示されれば成功です。
プロセスを停止するには Ctrl + C を押します。

docker run -p 8080:8080 mvcmovie:latest

ECR へプッシュ

ローカル環境でビルドした Docker イメージを ECR にプッシュします。

aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com
docker tag mvcmovie:latest $ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/mvcmovie:latest
docker push $ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/mvcmovie:latest

Lambda 関数用の IAM ロールの作成

Lambda 関数用の IAM ロールを作成します。この IAM ロールには、Lambda 関数が他の AWS サービスを呼び出すための権限が付与されます。IAMロールの内容はAWS公式ドキュメントを参考にしました。

aws iam create-role --role-name lambda-ex --assume-role-policy-document '{"Version": "2012-10-17","Statement": [{ "Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}'

Lambda 関数の作成

AWS Lambda に関数を作成します。関数名=mvcmovie、タイプ=イメージ、イメージ=先ほど作成したECR上のイメージ、IAMロール=先ほど作成したロールを指定しています。

aws lambda create-function \
  --function-name mvcmovie \
  --package-type Image \
  --code ImageUri=$ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/mvcmovie:latest \
  --role arn:aws:iam::$ACCOUNT_ID:role/lambda-ex

Lambda 関数 URL の追加

Lambda 関数 URL を使用して動作確認するため関数 URL を作成します。
今回は認証なしでアクセスできるように設定します。プロダクション環境であればAPI Gatewayなどを経由するんでしょうね。

aws lambda add-permission \
  --function-name mvcmovie \
  --action lambda:InvokeFunctionUrl \
  --principal "*" \
  --function-url-auth-type "NONE" \
  --statement-id url
aws lambda create-function-url-config \
  --function-name mvcmovie \
  --auth-type NONE

作成に成功したら、以下のようなレスポンスが返るはずです。ブラウザを開いてFunctionURLの URL にアクセスしてください。それっぽい画面(Welcome)が表示されたら成功です。

{
  "FunctionUrl": "https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws/",
  "FunctionArn": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxx:function:mvcmovie",
  "AuthType": "NONE",
  "CreationTime": "2024-03-31T13:11:24.319546Z"
}

解説

Dockerfile

今回作成したDockerfileは.NETの公式ドキュメントで公開されていたものに、以下の変更を加えたものです。

  • Lambda Web Adapterの記事で紹介されていたaws-lambda-webadapterCOPYする行を加える。
  • EXPOSE 8080を足す。
  • ENTRYPOINTの引数を今回のアプリケーションに合わせて修正する。
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
WORKDIR /App

# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore
# Build and publish a release
RUN dotnet publish -c Release -o out

# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /App
COPY --from=build-env /App/out .
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.1 /lambda-adapter /opt/extensions/lambda-adapter
EXPOSE 8080
ENTRYPOINT ["dotnet", "MvcMovie.dll"]

所感

  • NestJSで実装したAPIサーバーが動くのは見ていたので、特段驚くようなことはないと思っていたけど、伝統的な.NETのMVCアプリケーションが(全然ハマることなく)Lambda上で動くのを見てやっぱりビビった。
  • 開発する側がLambdaのことを全く気にしなくて良いのは素晴らしい。
  • とりあえず、クリティカルじゃないアプリケーションなら全然問題なさそう。ちゃんとしたアプリケーションであればもう少し色々なケースで検証してみたい。
  • .NETってDockerfileを書かなくてもコンテナイメージをビルドできるんですね(知らんかった)。Dockerのエコシステムを壊しそう&過去のDockerfileの資産が活きなさそう。
  • aws-lambda-adapterの更新頻度が意外と高い
  • 何気にaws-lambda-adapterのリポジトリを見たら結構いろいろなサンプルコードが整備されていた。aspnet-mvcもあった(最初からここを見ればよかった)。
  • 0.8.0のリリースノートSupport all non-HTTP event triggers, such as SQS, SNS, S3, DynamoDB, Kinesis, Kafka, EventBridge, and Bedrock Agents.の記載があった。S3とかEventBridgeからWebアプリを直接起動できるってこと?気になる。

参考情報

Discussion