これは、なにをしたくて書いたもの?
AWS SAM Local(ベータ版) – サーバーレスアプリケーションをローカルに構築してテストする | Amazon Web Services ブログ
aws-sam-localから、今はaws-sam-cliになったみたいです。
以前にLocalStackを使って、ローカルでAWS Lambdaを動かしたことはありましたが、今度はAWSの提供するツールで
行ってみたいと思います。
LocalStackを使って、AWS Lambdaを試してみる - CLOVER🍀
aws-sam-cliのインストール
まずは、aws-sam-cliのインストールを行いましょう。
最初に、Linux用のインストール方法を見たのですが、Linuxbrew…。
Installing the AWS SAM CLI on Linux - AWS Serverless Application Model
他に見ると、pipでインストールする方法もあるようなので、こちらを使うことにしました。
Additional Installation Topics - AWS Serverless Application Model
venvを使って、仮想環境にインストールすることにしました。
$ python3 -m venv venv $ source venv/bin/activate $ pip3 install aws-sam-cli
確認。
$ sam --version SAM CLI, version 0.11.0
なお、aws-sam-cliを使ってローカルでAWS Lambdaを動かす場合、Dockerが必要になるのでインストールしておきましょう。
使い方については、GitHubにドキュメントがあるので、こちらを参照。
https://github.com/awslabs/aws-sam-cli/blob/v0.11.0/docs/usage.md
https://github.com/awslabs/aws-sam-cli/blob/v0.11.0/docs/advanced_usage.md
JavaでAWS Lambdaを書いてみる
このあたりを見つつ、まずはJavaでAWS Lambdaをを使うアプリケーションを書いてみましょう。
環境。
$ java -version openjdk version "1.8.0_191" OpenJDK Runtime Environment (build 1.8.0_191-8u191-b12-2ubuntu0.18.04.1-b12) OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode) $ mvn -version Apache Maven 3.6.0 (97c98ec64a1fdfee7767ce5ffb20918da4f719f3; 2018-10-25T03:41:47+09:00) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 1.8.0_191, vendor: Oracle Corporation, runtime: /usr/lib/jvm/java-8-openjdk-amd64/jre Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "4.15.0-45-generic", arch: "amd64", family: "unix"
Mavenの設定は、こちらを参考に。
Maven および Eclipse IDE を使用した .jar デプロイパッケージの作成 (Java) - AWS Lambda
Maven依存関係。
<dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> <version>1.2.0</version> </dependency>
ライブラリ依存関係も含めてひとつのJARファイルにするために、Maven Shade Pluginも適用しておきます。
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
実装方法は、以下の2つがあるようです。
ハンドラーの入出力タイプ (Java) - AWS Lambda
ハンドラ作成用に事前定義されているインターフェイスの利用 (Java) - AWS Lambda
今回は、RequestHandlerインターフェースを実装して作成してみましょう。
こんな感じで。
src/main/java/org/littlewings/aws/lambda/MyLambdaFunction.java
package org.littlewings.aws.lambda; import java.time.LocalDateTime; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; public class MyLambdaFunction implements RequestHandler<MyLambdaFunction.Request, MyLambdaFunction.Response> { @Override public Response handleRequest(Request request, Context context) { System.out.printf("[%s] request = %s %s%n", LocalDateTime.now(), request.getLastName(), request.getFirstName()); return new Response("Hello, " + request.getLastName() + " " + request.getFirstName() + "!!"); } public static class Request { String firstName; String lastName; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } } public static class Response { String message; public Response() { } public Response(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } }
パッケージング。
$ mvn package
aws-sam-cliでAWS Lambdaをローカルで動かすには、テンプレートファイルが必要なようです。
今回は、このようなものを作成。 template.yml
AWSTemplateFormatVersion: 2010-09-09 Transform: AWS::Serverless-2016-10-31 Resources: MyLambdaJavaFunction: Type: AWS::Serverless::Function Properties: Handler: org.littlewings.aws.lambda.MyLambdaFunction CodeUri: ./target/aws-lambda-local-java-example-0.0.1-SNAPSHOT.jar Runtime: java8
Resources配下のFunction名は任意の名前です。PropertiesのHandlerには、AWS Lambdaから呼び出される起点となる
クラスを、CodeUriには作成したJARファイルのパスを、Runtimeには実行環境となるDockerイメージのタグを指定します。
使用できる言語のランタイム、バージョンは、GitHubのREADME.mdで確認することができます。
実行はsam local invokeで行います。今回はAWS CLIなしで実行しているので、「--region」でリージョンを指定しています。
リクエストの内容は、標準出力経由でJSONで渡せばOKです。
$ echo '{"firstName": "カツオ", "lastName": "磯野"}' | sam local invoke --region us-west-1
ランタイムのDockerイメージが起動して、AWS Lambdaが動きます。
Fetching lambci/lambda:java8 Docker container image...... 2019-03-01 00:03:52 Mounting /tmp/tmp9j3o9vgu as /var/task:ro inside runtime container START RequestId: dcf625f3-f02d-45bf-8803-e270e282679b Version: $LATEST END RequestId: dcf625f3-f02d-45bf-8803-e270e282679b REPORT RequestId: dcf625f3-f02d-45bf-8803-e270e282679b Duration: 28.98 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 17 MB [2019-02-28T15:03:54.057] request = 磯野 カツオ {"message":"Hello, 磯野 カツオ!!"}
OKですね。
sam local invokeで指定できるオプションは、以下のページや
sam local invoke - AWS Serverless Application Model
ヘルプで確認できます。
$ sam local invoke --help
なお、テンプレートファイルは「--template」で指定することもできますし(デフォルトは「template.yaml」または「template.yml」)、
-t, --template PATH AWS SAM template file [default: template.[yaml|yml]]
-e, --event PATH JSON file containing event data passed to the Lambda function during invoke. If this option is not specified, we will default to reading JSON from stdin
環境変数を渡したりもできるので、確認してみるとよいでしょう。
特に実行が高速なわけではないですが、ローカルでも動かせるというのはよいですね。
オマケ:Node.js
オマケとして、Node.jsでも動かしてみました。
Node.js の AWS Lambda 関数ハンドラ - AWS Lambda
作成したLambda関数。
lambda.js
exports.myHandler = async (event, context) => { console.log(`event = ${JSON.stringify(event)}`); console.log(`context = ${JSON.stringify(context)}`); return {"message": `Hello ${event.lastName} ${event.firstName}!!`}; }
テンプレートファイル。
template.yml
AWSTemplateFormatVersion: 2010-09-09 Transform: AWS::Serverless-2016-10-31 Resources: MyLambdaNodeJsFunction: Type: AWS::Serverless::Function Properties: Handler: lambda.myHandler Runtime: nodejs8.10
実行。
$ echo '{"firstName": "カツオ", "lastName": "磯野"}' | sam local invoke --region us-west-1
結果。
Fetching lambci/lambda:nodejs8.10 Docker container image...... 2019-03-01 00:12:51 Mounting /path/to as /var/task:ro inside runtime container START RequestId: 24ca0313-f3fd-110d-7525-c885abc6bdb7 Version: $LATEST 2019-02-28T15:12:54.894Z 24ca0313-f3fd-110d-7525-c885abc6bdb7 event = {"firstName":"カツオ","lastName":"磯野"} 2019-02-28T15:12:54.895Z 24ca0313-f3fd-110d-7525-c885abc6bdb7 context = {"callbackWaitsForEmptyEventLoop":true,"logGroupName":"/aws/lambda/test","logStreamName":"2019/02/28/[$LATEST]776c5662a876fe3374d5bc59b2b82f58","functionName":"test","memoryLimitInMB":"128","functionVersion":"$LATEST","invokeid":"24ca0313-f3fd-110d-7525-c885abc6bdb7","awsRequestId":"24ca0313-f3fd-110d-7525-c885abc6bdb7","invokedFunctionArn":"arn:aws:lambda:us-east-1:426374449862533:function:test"} END RequestId: 24ca0313-f3fd-110d-7525-c885abc6bdb7 REPORT RequestId: 24ca0313-f3fd-110d-7525-c885abc6bdb7 Duration: 2.33 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 31 MB {"message":"Hello 磯野 カツオ!!"}