Java + Gradle で Lambda レイヤー用のデプロイパッケージを作成する
Java での Lambda レイヤーを作成手順をまとめました。
Java + Gradle で Lambda レイヤー用のデプロイパッケージを作成する環境
- macOS: Catalina (10.15.7)
- JDK: Amazon Corretto 11 (Java 11 JDK)
- Gradle: 7.4.1
Java + Gradle で Lambda レイヤー用のデプロイパッケージを作成する際の前提
こちらの記事で実装した Lambda 関数をサンプルとして使います。
またこれから紹介する手順は以下の 2 つに分けて説明します。
- 外部の依存関係ライブラリを Lambda レイヤーに追加する手順
- 独自ライブラリを Lambda レイヤーに追加する手順
Java + Gradle で外部の依存関係ライブラリを追加した Lambda レイヤー用のデプロイパッケージを作成する手順
この手順ではサンプルで使用している以下の外部ライブラリを Lambda レイヤーに追加します。
- com.google.code.gson:gson:2.9.0
- com.amazonaws:aws-lambda-java-core:1.2.1
作業用ディレクトリの作成とプロジェクトの初期化
作業ディレクトリを作成して、gradle init
コマンドを実行します。
gradle init
コマンド実行時の各設問は最初の 2 つ以外はデフォルト値にしています。
$ mkdir ~/java-layer
$ cd ~/java-layer
$ gradle init
Starting a Gradle Daemon (subsequent builds will be faster)
Select type of project to generate:
1: basic
2: application
3: library
4: Gradle plugin
Enter selection (default: basic) [1..4] 1
Select build script DSL:
1: Groovy
2: Kotlin
Enter selection (default: Groovy) [1..2] 1
Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no]
Project name (default: java-layer):
> Task :init
Get more help with your project: Learn more about Gradle by exploring our samples at https://docs.gradle.org/7.4.1/samples
BUILD SUCCESSFUL in 16s
2 actionable tasks: 2 executed
gradle init
コマンド実行後、ディレクトリ構成は以下のようになります。
.
├── build.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
外部ライブラリの定義とデプロイパッケージ作成タスクの定義
build.gradle
ファイルを修正します。
dependencies
に Lambda レイヤーに追加する外部ライブラリを定義します。
また、task
でデプロイパッケージ(.zip ファイルアーカイブ)を作成するタスクを定義します。
外部ライブラリを保存するパスに注意してください。
Lambda のドキュメントでは以下のように記載しています。
Lambda レイヤーの作成と共有 - AWS Lambda
ライブラリの依存関係をレイヤーに含める
Lambda ランタイムごとに、PATH 変数に /opt ディレクトリ内の特定のフォルダが含まれます。レイヤー .zip ファイルアーカイブに同じフォルダ構造を定義すると、関数コードはパスを指定しなくても、レイヤーコンテンツにアクセスできます。
...
各 Lambda ランタイムのレイヤーパス
Java java/lib (CLASSPATH)
上記の記述のように、Lambda レイヤーにアップロードする zip ファイルアーカイブに特定のディレクトリにライブラリなどの依存関係を含めておくだけで、パスを指定しなくてもそれを利用することができます。
今回は Java なので、java/lib
ディレクトリに外部ライブラリを保存するよう必要があります。
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'com.google.code.gson:gson:2.9.0'
implementation 'com.amazonaws:aws-lambda-java-core:1.2.1'
}
task buildZip(type: Zip) {
into('java/lib') {
from configurations.runtimeClasspath
}
}
build.dependsOn buildZip
デプロイパッケージの作成その 1
gradle build
コマンドを実行してデプロイパッケージを作成します。
gradle build
build/distributions
ディレクトリに zip 形式のデプロイパッケージが作成されます。
作成されたデプロイパッケージには以下のように java/lib/
ディレクトリと外部ライブラリが格納されます。
$ unzip -l build/distributions/java-layer.zip
Archive: build/distributions/java-layer.zip
Length Date Time Name
--------- ---------- ----- ----
0 03-21-2022 23:02 java/
0 03-21-2022 23:02 java/lib/
249277 03-20-2022 23:20 java/lib/gson-2.9.0.jar
7515 03-20-2022 22:45 java/lib/aws-lambda-java-core-1.2.1.jar
--------- -------
256792 4 files
Lambda レイヤーの作成と Lambda 関数への紐付け 1
作成したデプロイパッケージを使って AWS マネジメントコンソールから Lambda レイヤーを作成と Lambda 関数への紐付けすることもできますが、今回は AWS CLI を使っていきます。
まず、publish-layer-version
コマンドで Lambda レイヤーを作成します。
次の手順で使用するので、publish-layer-version
コマンド実行結果から LayerVersionArn
をメモしておきましょう。
$ aws lambda publish-layer-version --layer-name java-basic-layer \
--zip-file fileb://build/distributions/java-layer.zip --compatible-runtimes java11
{
"Content": {
"Location": "https://awslambda-ap-ne-1-layers.s3.ap-northeast-1.amazonaws.com/xxxxxxxxxxxxx"
"CodeSha256": "V9RhiYR2QpTQEq7mKcl3pQnn+TTvMVyUKL9pbTi7Gd4=",
"CodeSize": 227065
},
"LayerArn": "arn:aws:lambda:ap-northeast-1:zzzzzzzzzzzzzz:layer:java-basic-layer",
"LayerVersionArn": "arn:aws:lambda:ap-northeast-1:zzzzzzzzzzzzzz:layer:java-basic-layer:1",
"Description": "",
"CreatedDate": "2022-03-21T14:04:36.472+0000",
"Version": 1,
"CompatibleRuntimes": [
"java11"
]
}
次に、作成した Lambda レイヤーをサンプルの Lambda 関数へ紐付けします。
--layers
オプションに先ほどメモした LayerVersionArn
を指定します。
aws lambda update-function-configuration --function-name java-sample \
--layers arn:aws:lambda:ap-northeast-1:zzzzzzzzzzzzzz:layer:java-basic-layer:1
Lambda 関数から外部ライブラリを排除する
サンプルの Lambda 関数のデプロイパッケージに外部ライブラリを含める必要がなくなるので、含めないように修正します。
build.gradle
ファイルを修正します。
task
でデプロイパッケージ(.zip ファイルアーカイブ)を作成するタスクから外部ライブラリに関連する定義を削除します。
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'com.google.code.gson:gson:2.9.0'
implementation 'com.amazonaws:aws-lambda-java-core:1.2.1'
}
task buildZip(type: Zip) {
from compileJava
from processResources
// 以下の定義が不要になります。
// into('lib') {
// from configurations.runtimeClasspath
// }
}
build.dependsOn buildZip
デプロイパッケージの作成その 2
gradle build
コマンドを実行してデプロイパッケージを作成します。
gradle build
build/distributions
ディレクトリに zip 形式のデプロイパッケージが作成されます。
作成されたデプロイパッケージには以下のように外部ライブラリがなくなり、クラスファイルのみが格納されます。
$ unzip -l build/distributions/java-sample.zip
Archive: build/distributions/java-sample.zip
Length Date Time Name
--------- ---------- ----- ----
0 03-21-2022 22:51 sample/
2686 03-21-2022 22:51 sample/App.class
1381 03-21-2022 22:51 previous-compilation-data.bin
--------- -------
4067 3 files
Lambda 関数の更新と実行その 1
作成したデプロイパッケージを使って AWS マネジメントコンソールから Lambda 関数を更新することもできますが、今回は AWS CLI を使って Lambda 関数の更新をします。
aws lambda update-function-code --function-name java-sample --zip-file fileb://build/distributions/java-sample.zip
Lambda 関数を実行して、エラーが出ず以下のように出力されれば成功です。
また、Lambda 関数の CloudWatch Logs のロググループに payload
の値などが出力されているはずです。
$ aws lambda invoke --function-name java-sample \
--payload '{"text":"Hello"}' response.txt --cli-binary-format raw-in-base64-out
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
Java + Gradle で独自ライブラリを追加した Lambda レイヤー用のデプロイパッケージを作成する手順
この手順ではサンプル内のログ処理を独自ライブラリとして Lambda レイヤーに追加します。
package sample;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.Map;
public class App implements RequestHandler<Map<String, String>, String> {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
@Override
public String handleRequest(Map<String, String> event, Context context) {
String response = new String("200 OK");
// ---ここから---
LambdaLogger logger = context.getLogger();
// log execution details
logger.log("ENVIRONMENT VARIABLES: " + gson.toJson(System.getenv()));
logger.log("CONTEXT: " + gson.toJson(context));
// process event
logger.log("EVENT: " + gson.toJson(event));
logger.log("EVENT TYPE: " + event.getClass().toString());
// ---ここまでの処理を独自ライブラリ化します。---
return response;
}
}
独自ライブラリ処理の実装
まず、独自ライブラリ処理を実装するためのファイルを作成します。
cd ~/java-layer
mkdir src/main/java/sample_lib
touch src/main/java/sample_lib/Util.java
サンプル内のログ処理を参考にというかほぼそのまま持ってきます。
package sample_lib;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.Map;
public class Util {
public static void log(Map<String, String> event, Context context) {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
LambdaLogger logger = context.getLogger();
// log execution details
logger.log("ENVIRONMENT VARIABLES: " + gson.toJson(System.getenv()));
logger.log("CONTEXT: " + gson.toJson(context));
// process event
logger.log("EVENT: " + gson.toJson(event));
logger.log("EVENT TYPE: " + event.getClass().toString());
}
}
デプロイパッケージ作成タスクに独自ライブラリを追加するように定義する
build.gradle
ファイルを修正します。
buildZip
タスクで外部ライブラリ(configurations.runtimeClasspath
)に加えコンパイルした独自ライブラリ(jar
)も Lambda レイヤーに追加するように定義します。
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'com.google.code.gson:gson:2.9.0'
implementation 'com.amazonaws:aws-lambda-java-core:1.2.1'
}
task buildZip(type: Zip) {
into('java/lib') {
from jar, configurations.runtimeClasspath
}
}
build.dependsOn buildZip
デプロイパッケージの作成その 3
gradle build
コマンドを実行してデプロイパッケージを作成します。
gradle build
build/distributions
ディレクトリに zip 形式のデプロイパッケージが作成されます。
作成されたデプロイパッケージには以下のように java/lib/
ディレクトリと独自ライブラリ(java-layer.jar
)と外部ライブラリが格納されます。
$ unzip -l build/distributions/java-layer.zip
Archive: build/distributions/java-layer.zip
Length Date Time Name
--------- ---------- ----- ----
0 03-21-2022 23:17 java/
0 03-21-2022 23:17 java/lib/
1446 03-21-2022 23:17 java/lib/java-layer.jar
249277 03-20-2022 23:20 java/lib/gson-2.9.0.jar
7515 03-20-2022 22:45 java/lib/aws-lambda-java-core-1.2.1.jar
--------- -------
258238 5 files
Lambda レイヤーの更新と Lambda 関数への紐付け 2
作成したデプロイパッケージを使って AWS マネジメントコンソールから Lambda レイヤーの更新と Lambda 関数への紐付けすることもできますが、今回は AWS CLI を使っていきます。
まず、publish-layer-version
コマンドで Lambda レイヤーを更新します。
次の手順で使用するので、publish-layer-version
コマンド実行結果から LayerVersionArn
をメモしておきましょう。
$ aws lambda publish-layer-version --layer-name java-basic-layer \
--zip-file fileb://build/distributions/java-layer.zip --compatible-runtimes java11
{
"Content": {
"Location": "https://awslambda-ap-ne-1-layers.s3.ap-northeast-1.amazonaws.com/xxxxxxxxxxxxx"
"CodeSha256": "V9RhiYR2QpTQEq7mKcl3pQnn+TTvMVyUKL9pbTi7Gd4=",
"CodeSize": 227065
},
"LayerArn": "arn:aws:lambda:ap-northeast-1:zzzzzzzzzzzzzz:layer:java-basic-layer",
"LayerVersionArn": "arn:aws:lambda:ap-northeast-1:zzzzzzzzzzzzzz:layer:java-basic-layer:2",
"Description": "",
"CreatedDate": "2022-03-21T14:04:36.472+0000",
"Version": 1,
"CompatibleRuntimes": [
"java11"
]
}
次に、作成した Lambda レイヤーをサンプルの Lambda 関数へ紐付けします。
--layers
オプションに先ほどメモした LayerVersionArn
を指定します。
aws lambda update-function-configuration --function-name java-sample \
--layers arn:aws:lambda:ap-northeast-1:zzzzzzzzzzzzzz:layer:java-basic-layer:2
独自ライブラリの取り込みと Lambda 関数から独自ライブラリの処理を呼び出す
まず、libs
ディレクトリを作成し、独自ライブラリの jar ファイルをコピーします。
mkdir ~/java-sample/libs
cp ~/java-layer/build/libs/java-layer.jar ~/java-sample/libs
次に、サンプルの Lambda 関数の build.gradle
ファイルを修正します。
独自ライブラリを使用するために dependencies
に libs
ディレクトリないの jar ファイルを依存関係ライブラリとして定義します。
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'com.google.code.gson:gson:2.9.0'
implementation 'com.amazonaws:aws-lambda-java-core:1.2.1'
implementation fileTree(dir: 'libs', include: '*.jar')
}
task buildZip(type: Zip) {
from compileJava
from processResources
}
build.dependsOn buildZip
最後に、サンプルの Lambda 関数から独自ライブラリの処理を呼び出すように修正します。
package sample;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
// 独自ライブラリのインポート
import sample_lib.Util;
import java.util.Map;
public class App implements RequestHandler<Map<String, String>, String> {
@Override
public String handleRequest(Map<String, String> event, Context context) {
// 独自ライブラリの処理の呼び出し
Util.log(event, context);
String response = new String("200 OK");
return response;
}
}
デプロイパッケージの作成その 4
gradle build
コマンドを実行してデプロイパッケージを作成します。
gradle build
build/distributions
ディレクトリに zip 形式のデプロイパッケージが作成されます。
作成されたデプロイパッケージの中身は以下のように外部ライブラリがなくなり、クラスファイルのみになります。
$ unzip -l build/distributions/java-sample.zip
Archive: build/distributions/java-sample.zip
Length Date Time Name
--------- ---------- ----- ----
0 03-21-2022 23:23 sample/
1392 03-21-2022 23:23 sample/App.class
528 03-21-2022 23:23 previous-compilation-data.bin
--------- -------
1920 3 files
Lambda 関数の更新と実行その 2
作成したデプロイパッケージを使って AWS マネジメントコンソールから Lambda 関数を更新することもできますが、今回は AWS CLI を使って Lambda 関数の更新をします。
aws lambda update-function-code --function-name java-sample --zip-file fileb://build/distributions/java-sample.zip
Lambda 関数を実行して、エラーが出ず以下のように出力されれば成功です。
また、Lambda 関数の CloudWatch Logs のロググループに payload
の値などが出力されているはずです。
$ aws lambda invoke --function-name java-sample \
--payload '{"text":"Hello"}' response.txt --cli-binary-format raw-in-base64-out
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
参考情報
個人開発したサービス