Amazon Web Services ブログ
AWS LambdaサポートのコンテナイメージとAWS CDKを利用したマイクロサービス開発
前提条件
このソリューションのデプロイに以下が必要です:
- Administrator権限を持つAWS account
- AWS Command Line Interface (AWS CLI)のインストール
ソリューション
ローカルの開発マシンを利用して環境をセットアップするか、AWS Cloud9利用できます。 このブログでは、AWS Cloud9を利用した例を紹介します。AWS Cloud9環境を作成するために、GitHubの手順に従います。
AWS Cloud9で新しいターミナルを開いて、jq
のインストールを実行してください:
sudo yum install jq -y
サンプルコードの入ったGitHubリポジトリをクローンします:
git clone https://github.com/aws-samples/aws-cdk-lambda-container.git
サンプルデータセットの作成
このソリューションは、データセットとしてサンプルのmoviesテーブルを利用します。手順については、AWSドキュメントのCreate a DynamoDB Table with the AWS SDK for JavaScriptを参照してください。
テーブルをセットアップするために、コードリポジトリのDynamoDBディレクトリに移動します。create-MoviesTable.sh
と load-MovieTable.sh
のスクリプトを実行します。
cd ~/environment/aws-cdk-lambda-container/DynamoDB
./create-MoviesTable.sh
./load-MoviesTable.sh
Lambda関数の作成
2つのLambda関数、list.js
と get.js
を作成します。このコードはGitHubリポジトリに含まれています。
list.js
関数: list関数はmoviesテーブルから全てのmovieデータを取得します。コードはGitHubを参照してください。
get.js
関数: get関数は2つのパラメータ、yearとtitleを条件にmoviesテーブルから項目を取得します。これらのパラメータは、Amazon API Gateway経由のHTTP APIを通して後ほど関数に渡されます。get.js
関数のコードはGitHubを参照してください。
Dockerfileの作成
Dockerfileはテキストドキュメントで、コンテナイメージをアセンブルするためにユーザーが実行するコマンドラインコマンドを含める事ができます。Dockerfileは現在のワークスペースの ~/environment/aws-cdk-lambda-container/src/movie-service/Dockerfileに置いてあります。
FROM public.ecr.aws/lambda/nodejs:12
# Alternatively, you can pull the base image from Docker Hub: amazon/aws-lambda-nodejs:12
# Copy the Lambda functions
COPY list.js get.js package.json package-lock.json ${LAMBDA_TASK_ROOT}/
# Install NPM dependencies for functions
RUN npm install
このDockerfileは、 Node.js12のLambda用に公開されているAWSベースイメージ、public.ecr.aws/lambda/nodejs:12
を指定しています。list.js
, get.js
, package.json
と package-lock.json
ファイルを${LAMBDA_TASK_ROOT}
ディレクトリにコピーしています。その後、Lambda関数の依存関係をインストールするため、npm install
を実行しています。 ${LAMBDA_TASK_ROOT}
は、AWSドキュメントのUsing AWS Lambda environment variablesに記載されているLambda関数のパスを表しています。
コンテナイメージのビルド
Dockerfileと2つのLambda関数を利用して、コンテナイメージをビルドします。 コンテナイメージはLambda関数のハンドラー実行に必要な以下の全てが必要です: 関数コードと依存関係、ランタイムやベースイメージから継承されたオペレーティングシステム。
ターミナルで以下のコマンドを実行してください:
cd ~/environment/aws-cdk-lambda-container/src/movie-service docker
build -t movie-service .
docker images | grep movie-service
以下がアウトプットです:
ローカルでのLambda関数のテスト
パッケージングしたLambda関数をローカルでテストするため、AWS Lambda Runtime Interface Emulator (RIE)を利用します。Lambda Runtime Interface Emulatorは軽量なウェブサーバで、HTTPリクエストをJSONイベントに変換し、コンテナイメージのLambda関数に渡してくれます。
コンテナイメージでは、以下の環境変数を設定する必要があります:
- AWS SDK認証用の
AWS_ACCESS_KEY_ID
,AWS_SECRET_ACCESS_KEY
,AWS_SESSION_TOKEN
とAWS_REGION
。AWS CLIを利用して現在の環境のクレデンシャルを取得し、aws configure get
を利用してローカルのコンテナにこのクレデンシャル情報を渡します。 DYNAMODB_TABLE
を使用してMovies
テーブルの情報をLambda関数に渡します。
1. list.js
関数を実行します。このコマンドはイメージをmovie-service
コンテナとして起動し、ローカルでlist.js
関数用のエンドポイント、 http://localhost:9080/2015-03-31/functions/function/invocations
を起動します:
2. 新しいターミナルを開いて後続のコマンドを実行します:
3. list.js
関数を呼び出します:
4. get.js
関数を実行します。以下のコマンドはmovie-service
イメージをコンテナとして起動し、ローカルでget.js
関数用のエンドポイント、 http://localhost:9080/2015-03-31/functions/function/invocations
を起動します:
5. get.js
関数をテストします。このコマンドは2つの環境変数をyear=”2013”
と title=”Rush”
として指定して get.js
関数を呼び出し、API Gatewayのリクエストをシミューレートしています:
アプリケーションのデプロイ
新しいターミナルを開いて、以下を実行します:
このコマンドにより、最新のAWS CDKモジュールをnode_modules
ディレクトリにインストールします。
AWS CDKでのAWSリソースの作成
AWS CDKはTypeScriptで記述された1つのCDKスタックからアーキテクチャをデプロイします。cdk/lib
ディレクトリにある、http-api-aws-lambda-container-stack.ts
ファイルを開き、次に示すCDKコンストラクトを見ていきましょう。
DynamoDBテーブル
AWS CDKを利用して(ドキュメントに記載されているように)既存のDynamoDBテーブルをインポートします:
const table = dynamodb.Table.fromTableName(this, 'MoviesTable', 'Movies');
Lambda関数
AWS CDKのDockerImageFunction
クラスを利用してLambda関数を作成します。コードはDockerImageCode
クラスのメソッドのstatic fromImageAsset(directory, props?)
を利用しています。これは、src/movie-service
ディレクトリのDockerfileで使用します。
listMovieFunction:
const listMovieFunction = new lambda.DockerImageFunction(this, 'listMovieFunction', {
functionName: 'listMovieFunction',
code: lambda.DockerImageCode.fromImageAsset(path.join(__dirname, '../../src/movie-service'), {
cmd: [ "list.list" ],
entrypoint: ["/lambda-entrypoint.sh"],
}),
environment: {
DYNAMODB_TABLE: this.table.tableName
},
});
getMovieFunction:
const getMovieFunction = new lambda.DockerImageFunction(this, 'getMovieFunction',{
functionName: 'getMovieFunction',
code: lambda.DockerImageCode.fromImageAsset(path.join(__dirname, '../../src/movie-service'), {
cmd: [ "get.get" ],
entrypoint: ["/lambda-entrypoint.sh"],
}),
environment: {
DYNAMODB_TABLE: this.table.tableName
},
});
Lambdaプロキシ統合
Amazon API Gateway Lambdaプロキシ統合により、クライアントがLambda関数を呼び出す事が可能です。クライアントがAPIリクエストを送信すると、API Gatewayはrawリクエストを統合されたLambda関数に渡します。
LambdaProxyIntegration
クラスを利用して2つのLambda関数用の2つのLambdaプロキシ統合を作成します。このクラスは引数として、LambdaProxyIntegrationProps
を受け取ります。
listMovieFunctionIntegration:
const listMovieFunctionIntegration = new apigintegration.LambdaProxyIntegration({
handler: listMovieFunction,
});
getMovieFunctionIntegration:
const getMovieFunctionIntegration = new apigintegration.LambdaProxyIntegration({
handler: getMovieFunction,
});
HTTP API
Amazon API GatewayのHTTP APIにより開発者はREST APIよりも低遅延で低コストなRESTful APIを作成できます(HTTP APIの詳細は、Building faster, lower cost, better APIs – HTTP APIs now generally availableを参照してください)。HTTP APIを利用してLambda関数にリクエストを送る事が可能です。
バックエンドの2つLambda関数と統合するHTTP APIを作成します。クライアントはこのAPIを呼び出す際に、API GatewayがリクエストをLambda関数に送信し、関数のレスポンスをクライアントに返します。以下がdefault stageのHTTP API のコードです。
const httpApi = new apig.HttpApi(this, "httpApi", {
apiName: "httpApi",
createDefaultStage: true,
});
HTTP API ルート
HTTP APIのルートは2つの要素、HTTPメソッドとリソースパスで構成されています。ルートは、例えばAWS Lambda関数のようなバックエンドに直接APIリクエストをルーティングします。
listMovieFunction
Lambda関数と統合したGET /list
ルートと、getMovieFunction
Lambda関数と統合したGET /{year}/{title}
ルートを作成します。詳細は、HttpRouteクラスのドキュメント
を参照してください。
httpApi.addRoutes({
integration: listMovieFunctionIntegration,
methods: [apig.HttpMethod.GET],
path: '/list',
});
httpApi.addRoutes({
integration: getMovieFunctionIntegration,
methods: [apig.HttpMethod.GET],
path: '/get/{year}/{title}',
});
AWS CDKでのAWSリソースのプロビジョニング
1. TypeScriptのコンパイル:
2. 特定のリージョン (今回の例では、us-east-1 )で最初のCDKのインフラストラクチャを作成するために、cdk bootstrapコマンドを実行します:
AWS CDKはリージョン内で、全てのプロジェクトのCDKデプロイ管理用の環境(S3バケット)を利用します。 bootstrapコマンドはCDKスタックを作成するリージョンで、1回だけ実行する必要があります。
3. 以下のコマンドでスタックをデプロイします:
(Do you wish to deploy all these changes (y/n)?にはyと応答してください)
注意: エラーが出た場合、package.json
をチェックし、全てのCDKライブラリが同じバージョン番号か確認してください(先頭のキャレット ^以外)。多くのCDKプロジェクトのエラーは、バージョンの不一致が原因です。必要に応じて、バージョン番号を修正し、package-lock.json
ファイルとnode_modules
ツリーを削除して、npm install
を実行してください。
これらのコマンドの構文や詳細はドキュメントに記載してあります。
HTTP APIのテスト
Outputs
セクションのlist
Lambda関数のHTTP APIエンドポイントをメモしておいてください:
HttpApiAwsLambdaContainerStack.HttpApiendpointlistMovieFunction
.
APIエンドポイントのテスト:
API GatewayコンソールでLambda関数と統合されたHTTP APIを確認できます。
Cleanup
AWS CDKで作成したリソースをクリーンアップするため、以下を実行します:
(Are you sure you want to delete (y/n)?には、y と応答してください)
DynamoDB のMovies テーブルを削除するため、以下を実行してください:
まとめ
このブログでは、AWS CDKとAWS Lambdaを利用したサーバーレスアプリケーションのデプロイ方法を紹介しました。Infrastructure as Code (IaC) を利用したLambda関数の開発とデプロイにより、AWSマネージメントコンソールやAWS CLIでのマニュアルステップを削減する事ができます。
AWS CDKを更に学びたい場合は、CDKWorkshop siteを参照ください。
翻訳はPublic Sector Prototyping Solutions Architect 小泉 秀徳が担当しました。原文はこちらです。