はじめに
QuarkusはコンテナファーストのJavaフレームワークですが、AWS Lambdaにも対応しています。当記事では、AWS Lambda上で、QuarkusベースのREST APIを作成・動作させる手順を説明します。
前提環境
当記事では以下を前提とします(ツールのバージョンは一致しなくても実施は可能)。
- AWSアカウントを保有していること
- Quarkus CLI 3.2.3 Final
- Maven 3.8.7
- AWS CLI 2.13.10
実施手順
基本的には、Quarkusのドキュメントをベースに進めていきます。
Mavenプロジェクトの作成
まず、Mavenでプロジェクトを作成します。
mvn archetype:generate \
-DarchetypeGroupId=io.quarkus \
-DarchetypeArtifactId=quarkus-amazon-lambda-archetype \
-DarchetypeVersion=3.2.4.Final
上記のコマンドを実行後、groupIdなど、それぞれ以下の値を入力します。
- groupId:org.example
- artifactId:quarkus-lambda-example
- version:<ブランク>
- package:<ブランク>
以下が実行例です。
Define value for property 'groupId': org.example
Define value for property 'artifactId': quarkus-lambda-example
Define value for property 'version' 1.0-SNAPSHOT: :
Define value for property 'package' org.example: :
Confirm properties configuration:
groupId: org.example
artifactId: quarkus-lambda-example
version: 1.0-SNAPSHOT
package: org.example
Y: :
上記により、quarkus-lambda-example
というディレクトリが作成されるので、そのディレクトリに移動します(以降はquarkus-lambda-example
ディレクトリで作業します)。
作成されたpom.xml
ファイルの中を見てみます。Quarkusではquarkus-amazon-lambda
エクステンションが提供されており、これがLambda独特の要素を吸収してくれます。
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-amazon-lambda</artifactId>
</dependency>
<!-- 中略 -->
</dependencies>
JavaでLambda関数を作成する際、ハンドラクラスを作成しますが、Quarkusのquarkus-amazon-lambda
エクステンションでは、1つのプロジェクト内に複数のハンドラクラスを用意し、application.properties
による設定、または環境変数によって、Lambda関数で実行されるハンドラクラスを切り替えることが可能になっています。
Mavenでのプロジェクト作成によるデフォルトで、application.properties
ではtest
が指定されているので、@Named("test")
が設定されているハンドラクラス(TestLambda.java
)が実行されます。
quarkus.lambda.handler=test
TestLambda.java
を見てみます。ここでは、ProcessingService
クラスをインジェクトしており、実質的な処理はProcessingService
クラスが担っています。
package org.example;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
@Named("test")
public class TestLambda implements RequestHandler<InputObject, OutputObject> {
@Inject
ProcessingService service;
@Override
public OutputObject handleRequest(InputObject input, Context context) {
return service.process(input).setRequestId(context.getAwsRequestId());
}
}
ProcessingService
クラスは以下のようになっています。Lambdaの入力で与えられたgreeting
、name
の値を文字列連結して返すという単純な内容です。
package org.example;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class ProcessingService {
public static final String CAN_ONLY_GREET_NICKNAMES = "Can only greet nicknames";
public OutputObject process(InputObject input) {
if (input.getName().equals("Stuart")) {
throw new IllegalArgumentException(CAN_ONLY_GREET_NICKNAMES);
}
String result = input.getGreeting() + " " + input.getName();
OutputObject out = new OutputObject();
out.setResult(result);
return out;
}
}
ビルド
では次に、ソースコードをビルドし、デプロイパッケージを作成します。以下のコマンドを実行します。
quarkus build
これにより、target
ディレクトリが作成され、以下のパッケージ群がビルドされます。特に赤枠で囲ったfunction.zip
とmanage.sh
が重要で、それぞれ以下の内容になっています。
manage.sh
の中身を見ると、ファイルの中程に以下のような行があります。変数で、Lambda関数の名前、ハンドラー、ランタイムを指定しています。
FUNCTION_NAME=QuarkusLambdaExample
HANDLER=io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest
RUNTIME=java11
AWS CLIの設定
manage.sh
ではAWS CLIを使用しますが、AWS CLIを使用するにあたり、事前の設定を実施します。まず、以下のコマンドを実行し、AWS CLIを使用できるようにします。
aws configure
上記のコマンドでは、AWSのIAMユーザのAccess Key、Secret Access Keyが聞かれます。これらは、AWSコンソールの「IAM」→「ユーザー」→自身のIAMユーザー→「セキュリティ認証情報」→「アクセスキーを作成」で作成しておきます(詳細な手順は割愛します)。
Lambda関数の実行ロール作成
次に、Lambda関数がAWSのリソースにアクセスできるようアクセス許可を付与するための実行ロールを作成します。詳細はAWSのドキュメントを参照ですが、以下に手順のみを記載します。
# `lambda-ex`という名前のロールを作成する。
aws iam create-role --role-name lambda-ex --assume-role-policy-document '{"Version": "2012-10-17","Statement": [{ "Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}'
# 作成したロールに AWSLambdaBasicExecutionRole ポリシーを付与する
# これにより、Lambda関数がログをCloudWatch Logsに出力することが可能になる
aws iam attach-role-policy --role-name lambda-ex --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
作成したlambda-ex
ロールのARNを取得します。下記のコマンドで出力されたJSONのRole.Arn
の値をメモしておきます。
aws iam get-role --role-name lambda-ex
Lambda関数の作成
それではいよいよLambda関数を作成します。以下のコマンドを実行します。
LAMBDA_ROLE_ARN="<上記で取得したlambda-exロールのARN>" sh target/manage.sh create
コマンドが正常終了することを確認し、AWSコンソールのLambdaのページにアクセスすると、manage.sh
で関数名として指定されていた「QuarkusLambdaExample」が作成されています。
では次に、作成した関数をテストしてみます。上記の関数一覧の「QuarkusLambdaExample」をクリックし、「テスト」タブを選択します。「イベント名」に適当な名前を入力し、「イベントJSON」に以下の内容を入力し、「テスト」ボタンをクリックします。
{
"name": "Bill",
"greeting": "hello"
}
テスト結果が以下の通り成功となり、レスポンスのJSONのresult
にhello Bill
が出力されていればOKです。
API Gatewayの作成 & 外部からのAPIアクセス
次に、Lambda関数をAPI化し、外部(インターネット)からアクセスできるようにします。具体的にはAPI Gatewayを作成します。AWSコンソールのAPI Gatewayのページにアクセスし、「APIを作成」ボタンをクリックします。
「API名」に適当な名前を入力し、「APIの作成」ボタンをクリックします。
「POST」メソッドを選択し、チェックマークをクリックします。
「統合タイプ」で「Lambda関数」を選択し、「Lambda関数」で先ほど作成したLambda関数「QuarkusLambdaExample」を選択し、「保存」ボタンをクリックします。
「リクエスト本文」に前述のJSONを入力し、「テスト」ボタンをクリックします。
「ステータス」で200
が返り、「レスポンス本文」でLambda関数本体のテスト実施時と同様のレスポンスが出力されていればOKです。
「アクション」→「APIのデプロイ」を選択し、APIのデプロイを行います。
「デプロイされるステージ」で「新しいステージ」を選択し、「ステージ名」を「example」とし、「デプロイ」ボタンをクリックします。
すると、APIがデプロイされ、APIのエンドポイントが表示される(赤枠部分)のでメモします。
ここまででAPIがデプロイされ、外部からアクセスできるようになりました。最後にAPIの稼働確認を実施します。以下のcurl
コマンドを実行します(URL部分は先ほどメモしたAPIのエンドポイントに置き換えてください)。以下のように、Lambda関数本体のテスト実施時と同様のレスポンスが出力されていればOKです。
curl -X POST -H "Content-Type: application/json" -d '{"name" : "Bill", "greeting" : "hello"}' https://xxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/example
{"result":"hello Bill","requestId":"5aadf1cb-9460-47d5-90b2-8f6845817a0a"}