はじめに
コンテナイメージからLambda関数のデプロイにトライ。
(コンテナイメージからのデプロイ自体は2020年のアップデート)
コンテナイメージからLambdaをデプロイする
コンテナイメージからLambda関数をデプロイすることでデプロイ可能なMaxのサイズが10GBになる。当時のre:Inventの動画を見ても機械学習などパッケージが大きいケースでもLambdaを使いやすく、というのが念頭に置かれている印象。
2020年の記事
zipファイルアーカイブからLambda関数をデプロイする方法では、解凍後のzipファイルのサイズが250MBまでに制限される
コンテナイメージからデプロイすれば依存関係の管理も楽ではあるけど、単純にWebアプリをLambdaに移植しても入出力インターフェースの違いから正常に動かないことがある。(Lambdaにはコードのエントリポイントとなるhandler関数があってイベントオブジェクトとコンテキストオブジェクトを受け取っているけど。そこがLambda独自仕様の部分)
この状態だとコンテナ化しても移植性はいまひとつになってしまう……のでHTTPを話すフレームワークであればLambda Web Adapterを使うとよさげ。
Lambda Web Adapter
Lambda Extensionに追加することでNext.jsやFlaskなどHTTPを話すフレームワークのWebアプリをそのままLambdaでも動かせる、というもの。
Web AdapterにLambdaイベント->HTTPリクエスト変換、HTTPレスポンス->Lambdaレスポンス変換を担ってもらい、インターフェースの違いを意識せずに動かせるようにする。
Lambda Web AdapterはAWS公式OSSで、dockerイメージ、zipパッケージ両方に対応している。
詳細は以下のREADMEで。
実際にデプロイする
EC2にDocker入れて操作する。
EC2にDockerインストール
$ sudo yum install -y docker
$ sudo service docker start
lambda_function.pyを作成する(Pythonの場合)
中身は好きなコードでどうぞ。
サンプルコード
requirements.txtを作成する
インストールしたいパッケージをrequirements.txtに記載する。
Dockerfileを作成
簡単なコードであれば参考リンクのサンプル通り5文で大丈夫。Lambda Web Adapterを使いたい場合だとCOPY文が一行増える。
FROM public.ecr.aws/lambda/python:3.12
COPY requirements.txt ${LAMBDA_TASK_ROOT}
RUN pip install -r requirements.txt
COPY lambda_function.py ${LAMBDA_TASK_ROOT}
CMD [ "lambda_function.lambda_handler" ]
参考
上記リンクのサンプルでは
CMD [ "lambda_function.lambda_handler" ]
の部分は
CMD [ "lambda_function.handler" ]
になっているけど、ここは自分のコードに合わせて修正する。
コンソールから関数作るとハンドラはlambda_function.lambda_handler
になるはず。(Pythonの場合)
関数コードのファイル名がlambda_function.pyでなければCOPY lambda_function.py ${LAMBDA_TASK_ROOT}
も修正する。
dockerイメージを構築
docker build --platform linux/amd64 -t docker-image:test .
ECRに対してdocker CLIを認証する
aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <your_account_id>.dkr.ecr.us-east-1.amazonaws.com
以下のようなエラーが出たときは/var/run/docker.sock
でpermission deniedになっているので属性を変更する必要がある。(参考リンクでは666)
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json": dial unix /var/run/docker.sock: connect: permission denied
参考
ECRにリポジトリを作成する
リポジトリはLambda関数と同じリージョンを指定する。
aws ecr create-repository --repository-name <repository name> --region <region> --image-scanning-configuration scanOnPush=true --image-tag-mutability MUTABLE
repositoryUriが出力されるので控えておく。
{
"repository": {
"repositoryArn": "arn:aws:ecr:<region>:<accountID>:repository/<repository name>",
"registryId": "xxxxxxxxxxx",
"repositoryName": "<repository name>",
"repositoryUri": "<accountID>.dkr.ecr.us-east-1.amazonaws.com/<repository name>",
"createdAt": "2024-06-02T06:10:26.698000+00:00",
"imageTagMutability": "MUTABLE",
"imageScanningConfiguration": {
"scanOnPush": true
},
"encryptionConfiguration": {
"encryptionType": "AES256"
}
}
}
ローカルイメージのタグ付け
ローカルイメージをECRリポジトリにタグ付け(latest)
docker tag docker-image:test <ECRrepositoryUri>:latest
ECRリポジトリにローカルイメージをpushする
docker push
docker push <accountID>.dkr.ecr.<region>.amazonaws.com/<repository name>:latest
最初に上記のコマンドを実行したらno basic auth credentialsと言われてしまって、原因は認証してるリージョン違いとかトークンの有効期限切れとか色々あるらしいけど、今回は以下のコマンドを実行して再度Docker CLIを認証したらpushできた。
aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <your_account_id>.dkr.ecr.us-east-1.amazonaws.com
Lambda側でも関数の概要などは見れるけど、コードの表示はできない。
Lambda関数を作成する
GUIからでもaws lambda create-functionコマンドのどちらでも
Lambda関数を実行する
response.jsonに実行結果(returnした戻り値)が出力される
aws lambda invoke --function-name <function-name> response.json
依存関係のファイル作ってないと実行したときにエラーが出る。(今回はコードの中でboto3使ってたのにrequirements.txtが空のままビルドしていた)
"Unable to import module 'lambda_function': No module named 'boto3'", "errorType": "Runtime.ImportModuleError", "requestId": "f03937c4-719e-4bf4-85a7-dbf769282d06", "stackTrace": []}
LambdaがBedrockのAPIにリクエスト投げるのにLambdaに何の権限もついてない、とかだともちろん関数実行時エラーになるので、実行に必要な権限があればつけておく。
Lambda関数を更新する
新しいイメージをECRリポジトリにアップロードしてからupdate-function-codeを実行する。
update-function-code --function-name <function name> --image-uri <imageUri>
おわりに
Lambdaはちょっとしたコードを実行する、といった用途で使っていたけどもう少しスケールの大きいこともできそう、という印象。手持ちのLambda関数コードだとLambda Web Adapterの出る幕が無かったのでサンプルコードでWeb Adapter試したい。