フォーム読み込み中
2022年9月29日掲載
AWS ECRを使ってNode.js/PythonのコンテナイメージをAWS Lambdaへデプロイしてみた。
AWS Lambdaはサーバレスコンピューティングプラットフォームであり、開発ユーザは小さな関数コードを作成するだけで、開発ユーザはサーバをプロビジョニングすることなく、その関数コードを実行することができます。
本記事では、AWS Lambdaのデプロイ方法の1つである、コンテナイメージとしてパッケージ化されたコンテナイメージファイルをECR(Amazon Elastic Container Registry)リポジトリにアップしながら、ECRイメージをLambdaコード関数としてデプロイする方法を説明します。ECR(Amazon Elastic Container Registry)はソースコードを管理するフルマネージドなコンテナレジストリです。ECRを使うと外部ネットワークを介せずにコンテナイメージを簡単に保持・管理できることや、Lambda以外にAWSサービスとの連携やIAMユーザーごとの操作権限等セキュリティ設定が簡単になります。
Node.jsおよびPythonそれぞれのコードをコンテナイメージにしたうえでECR経由でAWS Lambdaへデプロイする際の流れ、構成図としては次の通りになります。
ソースコードをDockerコンテナイメージでLambdaへデプロイするためのコンテナイメージを構築するために、Dockerの作業環境を準備します。Dockerは複数のプラットフォームをサポートしており、公式サイトよりインストールガイドラインに沿ってインストール、Docker作業環境を構築することができます。
著者はWindows PCを使用しているので、Docker Desktop for Windowsを使用します。
AWS CLIコマンドツールを導入します。AWS CLIコマンドツールを使用するには、Install Guidelineのガイドラインに従ってCLIコマンドツールをインストールし、設定します。著者はWindowsPCを使用しているので、Windowsを例に説明します。Windowsの場合、AWS CLI V2からインストールパッケージをダウンロードした後、ウィザードを使用してインストールプロセスをステップごとに完了できます。
<参考>
https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html
インストールが完了したら、`aws --version`コマンドを実行してステータスを確認できます。ツールが正常にインストールされていれば、関連するバージョン情報が表示されます。
`aws configure` コマンドを実行して、AWS CLIインストールをセットアップするための設定を行います。以下の情報が既にあることを確認します。
既存のアクセスキーがない場合は、IAM > User > Access Keys に移動し、`Create access key` ボタンをクリックして新しいアクセスキーを作成し、キー情報をコピーし、先ほどのセットアップ情報に入力します。
AWS LambdaでデプロイしたいNode.jsソースコードを準備します。本記事では、node_test_container_awsというソースコードフォルダを作成し、Lambdaのハンドラーが含まれるindex.jsを作成します。ハンドラー(handler)はサーバレスとして利用する言語の関数の開始位置となるもので、HTTPトリガー、イベントトリガーなどさまざまなトリガーによって実行される際に呼び出すエントリポイントです。このハンドラーは必須項目であり、ハンドラーを呼び出すことで、サーバレスとしての関数コード(以降スクリプト)が実行開始されます。ここでは、event handlerを例とします。
LambdaのNode.jsランタイム環境は、一般的なNode.jsモジュールと、使用可能な共通モジュールを提供します。カスタマイズされたScriptには、サードパーティの依存関係を必要とする場合があります。 上記のソースコードでは、日付を操作するためのmomentと呼ばれるパッケージが必要であり、デプロイ前にpackage.jsonで準備・管理する必要があります。これらのパッケージはnpmでローカルに管理します。node_test_container_awsフォルダにはindex.js、package.json が格納されています。
上記のソースコード index.js は次の通りです。
'use strict'; const moment = require('moment'); exports.handler = (event, context, callback) => { console.log('hello world'); callback(null, 'Container Node: ' + moment().utcOffset(9).format('YYYY-MM-DD HH:mm:ss')); }
上記のpackage.jsonは次の通りです。
{ "dependencies": { "moment": "^2.29.4" } }
AWS Lambda用コード関数のコンテナイメージを構築するには、AWSベースイメージからLambdaの特定のランタイム用イメージを作成する必要があります。AWSベースイメージには以下のような環境変数が用意されているので、Dockerfileで直接使用することができます。
今回、要件に基づいてDockerfileを変更するので、以下の対応をします。
1行目で特定のランタイムのベースイメージを設定します。ここではランタイムを Nodejs 16.x とします。他のベースイメージはAWS base images for Lambdaで確認することができます。
4行目で、プロジェクトフォルダ配下のファイル名情報に基づいて、ファイル名を変更します。node_test_container_awsフォルダ配下にはindex.js、package.json が格納されているので、index.js package.jsonのファイル名を記載します。このコード関数に属する全てのファイルは、必要に応じてpackage.jsonを含め、`LAMBDA_TASK_ROOT=/var/task` のディレクトリ配下へコピーされます。
7行目は、依存関係が必要ない場合は削除します。
9行目でコード関数のHandlerを設定します。この設定は、コンソール内のコンテナイメージに基づいて、作成・処理中に上書きすることができます。
以下はDockerfileの内容です。
FROM public.ecr.aws/lambda/nodejs:16 # Assumes your function is named "app.js", and there is a package.json file in the app directory COPY index.js package.json ${LAMBDA_TASK_ROOT} # Install NPM dependencies for function RUN npm install # Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile) CMD [ "index.handler" ]
Dockerfileの準備ができたら、`docker build -t <LOCAL_IMAGE> `コマンドを実行し、ローカルイメージをビルドします。AWSベースのイメージを取得し、サードパーティの依存関係をインストールするため多少時間がかかります。
AWSコマンドライン(CLI)とDockerコマンドを使用して、ビルドしたコンテナイメージをECRへアップロード(Push)します。
以下のコマンドでは、<AWS_ACCOUNT_ID>と<REGION>を自分用に変更します。次のコマンドでAWS ECRレジストリにログインします。
aws ecr get-login-password --region <REGION> | docker login --username AWS --password-stdin <AWS_ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com
ログインが無事完了すると、関連する成功メッセージが表示されます。
次に、以下のように `create-repository` コマンドを使用して、コンテナイメージを管理するリポジトリを作成します。
aws ecr create-repository --repository-name <REPOSITORY_NAME> --image-scanning-configuration scanOnPush=true --image-tag-mutability MUTABLE
AWSコンソール上のECR画面にて、作成されたリポジトリを確認します。
最後に、ビルドしたコンテナイメージにリポジトリ名と同じタグを付け、以下のように `docker push` コマンドで AWS ECR にデプロイします。
なお、<LOCAL_IMAGE>:<TAG>は、上記の手順でビルドしたイメージの情報と一致させる必要があります。
docker tag <LOCAL_IMAGE>:<LOCAL_TAG> <AWS_ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/<IMAGE>:<TAG>
docker push <AWS_ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/<IMAGE>:<TAG>
AWSコンソール上のECR画面にて、ECRリポジトリへアップロード(Push)したコンテナイメージを確認します。
AWS Lambda コンソール画面に移動し、ECRのコンテナイメージからコード関数を作成します。
`Create Function` ボタンをクリックして、コード関数を新規作成します。
タイプとして`Container Image` を選択します。
特定の関数名を入力します。
`Browse Images` ボタンをクリックして、ECR内のイメージをスキャンします。
上記の手順で作成したリポジトリを選択します。
上記の手順で作成したタグを持つターゲットイメージを選択します。
`Select Images` ボタンをクリックして、イメージURIを保存します。
`Create Function` ボタンをクリックして、作成プロセスを開始します。
`Container image overrides` 選定バーにて、以下の設定項目を更新することができます。
ENTRYPOINT
CMD
WORKDIR
Lambdaコンソール画面にて、作成したコード関数を確認するとコンテナイメージから作成されているため、インラインエディタでコードを表示することができません。
コンソールからテストイベントを使用してコード関数をテストします。
その結果、現在の日付情報が特定の形式で返されます。
以上が、AWS Elastic Container Registry (ECR) を使って、Node.jsソースコードのコンテナイメージをAWS Lambdaへデプロイする方法になります。
python_test_container_awsという名前のローカルプロジェクトフォルダを作成します。本記事では、Pythonスクリプトにrequestsというサードパーティパッケージが含まれています。pythonの場合、依存関係は requirements.txt で管理されます。
index.py と requirements.txt を次のように準備します。
上記の index.py ソースコードは次の通りです。
import requests def handler(event, context): response = requests.get('http://google.com/') print("Access Google home successfully!".format(response.status_code)) return "Response status_code: {0}".format(response.status_code)
上記の requirements.txt 依存関係ファイルは次の通りです。
requests
AWS Lambda関数のローカルイメージを構築するために、関連するDockerfileを準備します。
今回、要件に基づいてDockerfileを変更するので、以下の対応をします。
1行目で特定のランタイムのベースイメージを設定します。ここではランタイムを python 3.9 とします。他のベースイメージはAWS base images for Lambdaで確認することができます。
4行目で、プロジェクトフォルダ配下のファイル名情報に基づいて、ファイル名を変更します。python_test_container_awsフォルダ配下にはindex.py、requirements.txt が格納されているので、index.py requirements.txtのファイル名を記載します。このコード関数に属する全てのファイルは、必要に応じて9行目のrequirements.txtを含め、`LAMBDA_TASK_ROOT=/var/task` のディレクトリ配下へコピーされます。
10行目は、依存関係がもし必要ない場合は削除します。
13行目でコード関数のHandlerを設定します。この設定は、コンソール内のコンテナイメージに基づいて、作成・処理中に上書きすることができます。
以下はDockerfileの内容です。
FROM public.ecr.aws/lambda/python:3.9 # Copy function code COPY index.py ${LAMBDA_TASK_ROOT} # Install the function's dependencies using file requirements.txt # from your project folder. COPY requirements.txt . RUN pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}" # Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile) CMD [ "index.handler" ]
Dockerfileの準備ができたら、`docker build -t <LOCAL_IMAGE> `コマンドを実行し、ローカルイメージをビルドします。AWSベースのイメージを取得し、サードパーティの依存関係をインストールするため多少時間がかかります。
AWSコマンドライン(CLI)とDockerコマンドを使用して、ビルドしたコンテナイメージをECRへアップロード(Push)します。
以下のコマンドでは、<AWS_ACCOUNT_ID>と<REGION>を自分用に変更します。次のコマンドでAWS ECRレジストリにログインします。
aws ecr get-login-password --region <REGION> | docker login --username AWS --password-stdin <AWS_ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com
ログインが無事完了すると、関連する成功メッセージが表示されます。
次に、以下のように `create-repository` コマンドを使用して、コンテナイメージを管理するリポジトリを作成します。
aws ecr create-repository --repository-name <REPOSITORY_NAME> --image-scanning-configuration scanOnPush=true --image-tag-mutability MUTABLE
AWSコンソール上のECR画面にて、作成されたリポジトリを確認します。
最後に、ビルドしたコンテナイメージにリポジトリ名と同じタグを付け、以下のように `docker push` コマンドで AWS ECR にデプロイします。
なお、<LOCAL_IMAGE>:<TAG>は、上記の手順でビルドしたイメージの情報と一致させる必要があります。
docker tag <LOCAL_IMAGE>:<LOCAL_TAG> <AWS_ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/<IMAGE>:<TAG>
docker push <AWS_ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/<IMAGE>:<TAG>
AWSコンソール上のECR画面にて、ECRリポジトリへアップロード(Push)したコンテナイメージを確認します。
AWS Lambda コンソール画面に移動し、ECRのコンテナイメージからコード関数を作成します。
`Create Function` ボタンをクリックして、コード関数を新規作成します。
タイプとして`Container Image` を選択します。
特定の関数名を入力します。
`Browse Images` ボタンをクリックして、ECR内のイメージをスキャンします。
上記の手順で作成したリポジトリを選択します。
上記の手順で作成したタグを持つターゲットイメージを選択します。
`Select Images` ボタンをクリックして、イメージURIを保存します。
`Create Function` ボタンをクリックして、作成プロセスを開始します。
`Container image overrides` 選定バーにて、以下の設定項目を更新することができます。
ENTRYPOINT
CMD
WORKDIR
Lambdaコンソール画面にて、作成したコード関数を確認するとコンテナイメージから作成されているため、インラインエディタでコードを表示することができません。
コンソールからテストイベントを使用してコード関数をテストします。上記のデプロイする関数コードの中身は、Googleホームページにアクセスし、関連するHTTPステータスコードを返すものです。
以上が、AWS Elastic Container Registry (ECR) を使って、PythonソースコードのコンテナイメージをAWS Lambdaへデプロイする方法になります。
コード関数はコンテナイメージからデプロイされるので、コードを更新するためにコンテナイメージを更新することができます。例えば、上記のPythonサンプルの処理結果・応答メッセージにいくつかの単語を追加したとして、それに伴いコンテナイメージを更新構築します。コンテナイメージのタグが以前と同じ場合、前のコンテナイメージはリポジトリで「タグなし」としてマークされます。
Lambdaコンソール画面にて、作成したコード関数を確認するとコンテナイメージから作成されているため、インラインエディタでコードを表示することができません。
ECRの詳細ページに移動し、新規イメージからコード関数をデプロイします。
成功メッセージが表示されたら、コード関数を再度呼び出し、新たに単語を追加した応答メッセージを確認します。
Dockerfileで定義された`CMD [ "xxxxx" ]`がコード内の実際のエントリポイントと一致しなかった場合、関数を呼び出すとエラーが発生します。
これは、Dockerfileの設定がindex.handlerだが、index.pyの実際の関数はlambda_handlerと呼ばれるためです。
コンテナイメージを再構築したくない場合は、コード関数設定を使用してDockerfileの値をオーバーライドすることができます。
`Image` タブを開き、イメージの設定を確認します。
`Edit` ボタンをクリックします。
CMD に正しい値を設定します。ここでは index.lambda_handler を使用します。
`Save` ボタンをクリックして変更を処理します。
完了すると、成功メッセージが表示され、上記の手順と同様にコンテナイメージの構成設定が更新されます。
コード関数を再度呼び出すと、正しいPythonスクリプト内の正しいコード関数が呼び出されます。
コード関数作成中にKMS設定のエラーが発生する場合があります。
これは、ECRとLambda実行ロールとの同期によって発生する事象なので、しばらくしてからもう一度実行すると、正常に完了します。
それぞれをコンテナイメージファイルとしてAWS Lambdaへデプロイする方法をご紹介しました。参考として、この手法であれば、LambdaをEKS、あるいはECS+Fargateに置き換えてデプロイすることもできますし、または別記事で紹介しているようにServerless Devsを使って他クラウドのサーバレス基盤からコンテナイメージを使ってデプロイすることもできます。
サーバレスアプリケーションはコードを記述しながら実行できるクラウドサービスなので、エンジニアリングリソースを小さくしてくれるため非常に便利です。別途、AWS CLI版でLambdaをデプロイする方法も紹介していますが、依存関係を含めた開発・デバッグ・展開・運用・保守の完全なライフサイクルを満たしたい場合や、コンテナイメージを使ったCI/CD運用を目指している場合は本記事をご参考になれば幸いです。
サーバレスってなに?Alibaba Cloud, AWS, Azure, Google Cloud のサーバレスサービスを比べてみました
AWS
AWS LambdaにZIPファイル化したNode.jsおよびPythonのコードをデプロイしてみた(GUI編)
AWS LambdaをAWS CLIベースでNode.jsおよびPythonそれぞれをデプロイしてみた(CLI編)
AWS ECRを使ってNode.js/PythonのコンテナイメージをAWS Lambdaへデプロイしてみた ← 本記事
Google Cloud
Google Cloud FunctionにZIPファイル化したNode.jsおよびPythonのコードをデプロイしてみた(GUI編)
Google Cloud FunctionをGoogle Cloud CLIベースでNode.jsおよびPythonそれぞれをデプロイしてみた(CLI編)
Azure
Azure FunctionにZIPファイル化したNode.jsおよびPythonのコードをデプロイしてみた(GUI編)
Azure FunctionをAzure CLIベースおよびAzure Function Core ToolsでNode.jsおよびPythonそれぞれをデプロイしてみた(CLI編)
Azure FunctionをAzure CLIおよびAzure Function Core ToolsでNode.jsおよびPythonそれぞれをデプロイしてみた
Alibaba Cloud
Alibaba Cloud Function ComputeにZIPファイル化したNode.jsおよびPythonのコードをデプロイしてみた(GUI編)
Alibaba Cloud FunctionComputeをCLIベースでNode.jsおよびPythonそれぞれをデプロイしてみた(CLI編)
Serverless Devsを使ってNode.js/PythonコードをコンテナイメージにしながらAlibaba Cloud FunctionComputeへデプロイしてみた
条件に該当するページがございません