これは、なにをしたくて書いたもの?
要するに、AWS Lambdaを代替でもいいからとりあえず手元で動かしてみたくて書いたエントリです。
お題
Node.jsでLambda関数を作成することとし、ペイロードで与えられたパラメータを含めて、メッセージを返却する
コードを作成します。
また、その時にnpmでモジュールを引き込んで使用します。
環境
コードを作成した環境は、Ubuntu Linux 18.04 LTS。
Node.js、npmのバージョンは、こちら。
$ node -v v8.12.0 $ npm -v 6.4.1
ですが、実行環境はまた別の話ですね。
LocalStackは、Docker Composeで立てました。
オフィシャルのものを使用しても良かったのですが
localstack/docker-compose.yml at master · localstack/localstack · GitHub
自分で作ろうかなとこんな感じに。
docker-compose.yml
version: "3" services: localstack: image: localstack/localstack:0.8.7 ports: - "4567-4584:4567-4584" - "8080:8080" environment: - SERVICES=${SERVICES- } - DEBUG=${DEBUG- } - DATA_DIR=${DATA_DIR- } - PORT_WEB_UI=${PORT_WEB_UI- } - LAMBDA_EXECUTOR=docker - KINESIS_ERROR_PROBABILITY=${KINESIS_ERROR_PROBABILITY- } - DOCKER_HOST=unix:///var/run/docker.sock volumes: - "/var/run/docker.sock:/var/run/docker.sock"
LocalStackで、LambdaをNode.jsをランタイムとして動かす場合は、LAMBDA_EXECUTORにDockerを要求するみたいです。
Dockerを指定しない場合は、Lambdaの実行時にこんなエラーを見ることになります。
Exception: Unable to find executor for Lambda function "xxx". Note that Node.js and .NET Core Lambdas currently require LAMBDA_EXECUTOR=docker
なので、ホスト側のdocker.sockをVolumeとして見るようにしておきます。
起動。
$ docker-compose up
Lambdaのエンドポイントのポートは、4574です。また、このDockerが起動しているホストのIPアドレスは、「192.168.0.3」とします。
これで、準備は完了です。
Lambda関数を作成する
それでは、Lambda関数を作成しましょう。
とりあえず、なにかライブラリを使いたいので、文字列操作のライブラリでも使うことにします。
今回は、Vocaを使うことにしました。
Voca: The JavaScript string library
インストール。
$ npm i --save voca
バージョン。
"dependencies": { "voca": "^1.4.0" }
ソースコードは、こんな感じで。
lambda.js
const voca = require('voca'); exports.myHandler = async (event, context) => { console.log(`event = ${JSON.stringify(event)}`); console.log(`context = ${JSON.stringify(context)}`); return `${voca.repeat(event.word, event.count)}!!!`; };
このあたりを参考にしています。
Node.js の AWS Lambda 関数ハンドラー - AWS Lambda
Node.js の AWS Lambda context オブジェクト - AWS Lambda
Node.js の AWS Lambda 関数ログ作成 - AWS Lambda
AWS Lambda 関数を使用するためのベストプラクティス - AWS Lambda
Lambda関数の登録
では、この作成したLambda関数を、LocalStackのLambdaにデプロイしてみます。
まずは、デプロイパッケージの作成。
.zip ファイルアーカイブで Node.js Lambda 関数をデプロイする - AWS Lambda
とりあえず、全部まとめてzipに放り込みました。zipファイル名は、「dist.zip」とします。
$ zip -r dist.zip lambda.js node_modules package.json package-lock.json
では、こちらをLambdaに登録します。
参考にしたのは、Lambdaのドキュメントのユースケースより。
Lambda関数の登録。
$ aws --endpoint-url=http://192.168.0.3:4574 lambda create-function \ --function-name hello-lambda \ --runtime nodejs8.10 \ --zip-file fileb://`pwd`/dist.zip \ --role test-role \ --handler lambda.myHandler \ --region us-east-1 { "TracingConfig": {}, "FunctionName": "hello-lambda", "VpcConfig": { "SubnetIds": [ null ], "SecurityGroupIds": [ null ] }, "FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:hello-lambda", "Environment": { "Variables": {}, "Error": {} }, "Handler": "lambda.myHandler", "Role": "test-role", "Runtime": "nodejs8.10" }
LocalStackなので、「--endpoint-url」を指定する必要があります。
あとは、「--function-name」で登録する関数の名前、「--runtime」でランタイムを、「--zip-file」でデプロイ対象のzipファイルを、
「--role」でロールを、「--handler」で実行する関数を指定します。
実行する関数は、今回は「lambda.js」の「myHandler」という関数を登録したので「lambda.myHandler」となります。
exports.myHandler = async (event, context) => {
登録したLambda関数の一覧。
$ aws --endpoint-url=http://192.168.0.3:4574 lambda list-functions { "Functions": [ { "Version": "$LATEST", "FunctionName": "hello-lambda", "CodeSize": 50, "FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:hello-lambda", "Environment": {}, "Handler": "lambda.myHandler", "Runtime": "nodejs8.10" } ] }
Lambda関数の呼び出し。「--payload」でパラメーターを渡します。
$ aws --endpoint-url=http://192.168.0.3:4574 lambda invoke --function-name hello-lambda --region us-east1 --payload '{"word": "Lambda", "count": 5}' result.log
結果は、このコマンド例だと「result.log」に出力されます。
$ cat result.log "LambdaLambdaLambdaLambdaLambda!!!"
次に、Lambda関数を更新してみましょう。returnする値を、少し変更してみます。
return `${voca.repeat(event.word, event.count)}!!!???`;
再度zipにまとめて
$ zip -r dist.zip lambda.js node_modules package.json package-lock.json
Lambda関数定義の更新。
$ aws --endpoint-url=http://192.168.0.3:4574 lambda update-function-code --function-name hello-lambda --zip-file fileb://`pwd`/dist.zip
実行。
$ aws --endpoint-url=http://192.168.0.3:4574 lambda invoke --function-name hello-lambda --region us-east1 --payload '{"word": "Lambda", "count": 5}' result.log
定義が変更されました。
$ cat result.log "LambdaLambdaLambdaLambdaLambda!!!???"
Lambda関数の削除。
$ aws --endpoint-url=http://192.168.0.3:4574 lambda delete-function --function-name hello-lambda
ところで、最初の方でLocalStackでのLambdaでNode.jsを使う場合はDockerが必要だという話なのですが、Lambdaの実行の度に
コンテナが起動し、終了していきます。これ、「docker container ps -a」で見るとどんどん残っていくので、適当に削除が
必要ですね。
Lambdaのログを見るには、「docker container logs」で見ることになります。
$ docker container logs 430d2b75a324 START RequestId: 1d0e0e82-6c90-1718-3755-d4cd9431bbeb Version: $LATEST 2018-10-23T15:20:12.415Z 1d0e0e82-6c90-1718-3755-d4cd9431bbeb event = {"count":5,"word":"Lambda"} 2018-10-23T15:20:12.416Z 1d0e0e82-6c90-1718-3755-d4cd9431bbeb context = {"callbackWaitsForEmptyEventLoop":true,"logGroupName":"/aws/lambda/hello-lambda","logStreamName":"2018/10/23/[$LATEST]4ac47aebf4367f612e6e4d8a25b5f078","functionName":"hello-lambda","memoryLimitInMB":"1536","functionVersion":"$LATEST","invokeid":"1d0e0e82-6c90-1718-3755-d4cd9431bbeb","awsRequestId":"1d0e0e82-6c90-1718-3755-d4cd9431bbeb","invokedFunctionArn":"arn:aws:lambda:us-east-1:000000000000:function:hello-lambda"} END RequestId: 1d0e0e82-6c90-1718-3755-d4cd9431bbeb REPORT RequestId: 1d0e0e82-6c90-1718-3755-d4cd9431bbeb Duration: 13.83 ms Billed Duration: 100 ms Memory Size: 1536 MB Max Memory Used: 32 MB "LambdaLambdaLambdaLambdaLambda!!!???"
「console.log」などの内容は、こんな感じで確認できます。
コンテナが残るので消すのは面倒なのですが、かといって終了時に消えてしまうと、ログを確認する手段がなくなって
しまうような気も…。難しい…。
まあ、確認としてはこんなところですね。
まとめ
LocalStackを使って、AWS Lambdaを試してみました。
AWS Lambda自体使ったことがなかったので、まずはとっかかりということで。Lambdaの実行にDockerを使うところで
若干つまづきましたが、それ以外は割とあっさり。
良い素振りになりました。