Lambda関数(+Docker)でPythonのFastAPIを動かしてみよう

本サイトでは過去の記事でLambda関数の作成方法を確認し、Lambda関数でDockerを利用する手順などを公開しました。本記事ではさらにDocker上でPythonのWebフレームワークであるFastAPIをインストールして複数のエンドポイントを持つサーバレスなアプリケーションを構築する方法を確認していきます。
最終的にLambda関数の関数URL(FunctionURL)を利用してインターネット上から複数のエンドポイントを使ってLambda関数が実行できるようになります。
目次
FastAPIとは
FastAPIは主にAPIを構築する際に利用され、高速で構築が簡単に行えるPythonベースのWEBフレームワークです。
FastAPIは名前にAPIが含まれていることから分かる通りAPIを構築するためのフレームワークです。そのためFastAPIには複数のエンドポイントの設定を行います。FastAPIで構築する複数のエンドポイントはLambda上でASGIアプリケーションを実行するためのAdapterであるMangumを利用して実現します。
事前準備
本文書はmacOSを利用して動作確認を行っています。
Dockerを利用するためDockerがインストール済みである必要があります。インストールが完了している場合にはdockerコマンドを利用してバージョンを確認することができます。
Dockerのイメージ、コンテナを利用して動作確認を行うので任意の場所にディレクトリを作成してください。ここではlambda_fastapiという名前のディレクトリを作成しています。
Dockerイメージの作成
lambda_fastapiディレクトリに移動後、app.pyファイルを作成します。app.pyファイル内でFastAPIを動作させるためのコードを記述します。FastAPI, Mangumをimportして異なる方法で動作確認を行うため3つのルーティングを追加しています。
1つ目のルーティングはGETリクエストがあるとJSONデータを戻します。2つ目のルーティングuppercaseはGETリクエストからクエリパラメータを受け取り大文字に変換してJSONデータとして戻します。3つ目はルーティングitemsはPOSTリクエストでnameとpriceを含んだJSONデータを受け取り、そのまま受け取ったデータをJSONデータで戻します。
app.pyファイルでimportしたFastAPI, Mangumをインストールする必要があるのでrequirements.txtファイルを作成します。
Dockerイメージを作成するためにDockerfileを作成します。FROMではAWSが提供しているPythonのDockerイメージpublic.ecr.aws/lambda/python:3.12を指定しています。COPYで作成済みのapp.py、requirements.txtファイルをイメージにコピーしています。RUNではpipを利用してrequirements.txtに記述したパッケージのインストールを行っています。CMDではapp.pyファイルのhandler関数を実行しています。
Dockerfileの作成が完了したらイメージのビルドを行います。
イメージのビルドが完了したらdocker imagesコマンドを実行して作成したイメージを確認しておきます。
動作確認
作成したイメージを元にコンテナを作成して起動を行い、ローカル環境で設定通りの動作が行われるのか確認を行います。public.ecr.aws/lambda/python:3.12のイメージの中にLambda Runtime Interface Emulator(RIE)が含まれているためローカルでLambda関数の動作確認を行うことができます。RIE が8080ポートでリスニングをするためポートマッピングで 9000 ポートにマッピングしています。
コマンドを実行するとhttp://localhost:9000/2015-03-31/functions/function/invocationsのエンドポイントが作成されます。ローカル環境の動作確認ではそのエンドポイントに対してcurlを利用してリクエストを送信します。
別のターミナルを開いてdocker psコマンドを実行すると起動しているコンテナの情報を確認することができます。コンテナ名はfervent_burnellと自動で命名されています。
コンテナが起動していることが確認できたら、curlを利用して3つのルーティングに対してリクエストを送信します。
GETリクエスト
最初に”/”のルーティングに対してGETリクエストを送信します。-dオプションに設定したresource, path, httpMethod, requestContextのいずれかが抜けているとエラーメッセージが表示されます。実行するとResponseのbodyの中に”Hello from lamdba”が入っていることがわかります。
/uppercaseルーティングはクエリパラメータを利用してKeyとValueを渡す必要があるためqueryStringParametersを設定します。queryStringParametersに設定した”Hello from lambda”が大文字になって戻されていることが確認できます。
POSTリクエスト
/itemsルーティングに対してはこれまでの2つは異なりPOSTリクエスト(httpMethodの値がPOST)を利用します。/itemsのルーティングに送信するデータはbodyで設定を行います。実行するとbodyに設定したnameとpriceを含めJSONデータがそのまま戻されていることが確認できます。
httpMethodの値をGETにして送信すると”Method Not Allowed”というメッセージが戻されます。
bodyにnameにしか設定していない場合にはバリデーションが行われ必須項目が不足しているということでField requiredのメッセージが戻されます。ステータスコードは422 Unprocessable Entityです。
ローカルの動作確認では期待通りの動作になることが確認できたのでこれからビルドしたイメージを利用してLambda関数を設定していきます。
Lambda関数の設定
ECRのリポジトリの作成
Lambda関数はAWS ECR(Elastic Container Registry)に保存したDockerイメージを利用します。そのため作成したDockerイメージを保存するリポジトリの作成を行います。ここでは新たにリポジトリを作成して作成したイメージをアップロードします。
ECRにリポジトリを作成する前にECRにログインを行います。123456789012の部分を各自のAWSのアカウントIDに変更して実行してください。
IAMユーザに権限(ecr:GetAuthorizationToken)がない場合には上記のようにエラーメッセージが表示されます。
権限があり正しいコマンドで実行した場合には”Login Succeded”のメッセージが表示されます。
権限がない場合はECRを利用できるポリシー”AmazonEC2ContainerRegistryFullAccess”をユーザにアタッチします。

ログイン完了後はcreate-repositoryコマンドを利用してlambda_fastapi_repoという名前のリポジトリを作成しています。リポジトリ名には任意の名前をつけることができます。ECRはプライベートとパブリック用のリポジトリを作成することができますが下記のコマンドではプライベート用のリポジトリを作成しています。
イメージのアップロード
リポジトリを作成後は作成済みのイメージにタグをつけるためdocker tagコマンドを実行します。イメージをECRにアップロードするためにはタグ付けは必須です。123456789012の箇所は各自のAWSのアカウントIDを指定してください。
docker tagを実行後にdocker imagesコマンドを実行すると名前の異なる2つのイメージが同じIDで作成されていることがわかります。
タグ付けしたイメージのアップロードにはdocker pushコマンドを利用します。タグ付け後に作成されているイメージの名前を指定してください。
作成後はdescribe-repositoriesで作成したリポジトリの情報を確認することができます。
Lambda関数の作成
ECRにイメージがアップロードが完了したのでManagement ConsoleからLambda関数を作成します。
関数の作成画面のオプションではコンテナイメージを選択してください。コンテナイメージを選択するとイメージを参照ボタンから作成したリポジトリのイメージが選択できるようになるので選択してください。Lambda関数の名前はlambdaFastAPIとしています。
動作確認でLambda関数から別のサービスを利用するわけではないので設定するロールがない場合はロールには基本的なLambdaアクセス権限で新しいロールを作成を選択してください。自動で命名されたロールが追加されて設定されます。

Lambda関数を設定後、AWS CLIを利用して作成したLambd関数を実行するとUnhandledエラーが発生します。
関数URLの設定
URLを利用してLambda関数が実行できるように関数URLの設定を行います。Management Consoleから作成したLambda関数のページに移動して設定タブから関数URLを設定クリックします。
関数URLのタブが開いたら関数URLを作成ボタンをクリックします。

関数URLの設定画面が表示されるのでLambda関数のURLからパブリックからアクセスできるように”NONE”を選択して”保存”ボタンをクリックします。

設定したURLが表示されているので関数URLのリンクをクリックしてください。

ブラウザ上にapp.pyで設定した”/”の戻り値が表示されればLambda関数が正常に動作していることになります。

/docsにアクセスするとルーティングの一覧を確認することができます。ここから各ルーティングに対しての動作確認を行うこともできます。

パブリックで公開しているのでどこからでもアクセスすることができるのでローカルのPCからcurlを利用して公開済みのLambda関数のURL+/itemsにPOSTリクエストを送信します。送信したデータがJSONデータで戻されることが確認できます。
app.pyファイルで設定した複数のエンドポイントに対してLambda関数を利用して実行できるようになりました。