Quarkus REST、Undertow、またはReactive Routesを使用したAWS Lambda
With Quarkus you can deploy your favorite Java HTTP frameworks as AWS Lambda’s using either the AWS Gateway HTTP API or AWS Gateway REST API. This means that you can deploy your microservices written with Quarkus REST (our Jakarta REST implementation), Undertow (servlet), Reactive Routes, Funqy HTTP or any other Quarkus HTTP framework as an AWS Lambda.
You should only use single HTTP framework together with AWS Lambda extension to avoid unexpected conflicts and errors. |
Lambda を純粋な Java jar としてデプロイすることもできますし、プロジェクトをネイティブイメージにコンパイルしてデプロイすることで、より少ないメモリーフットプリントと起動時間を実現することもできます。また、統合により、 AmazonのSAM framework で使用できるSAMデプロイメントファイルも生成されます。
Quarkusでは、ゲートウェイAPIごとに異なるエクステンションを用意しています。HTTP Gateway APIは、 quarkus-amazon-lambda-http
のエクステンションで実装されています。REST Gateway APIは、 quarkus-amazon-lambda-rest
のエクステンションで実装されています。どのGateway製品を使用すべきか迷っている場合は、Amazonが決断を助ける為に 素晴らしいガイドを提供しています。
Quarkus AWS Lambda HTTP/REST エクステンションは、大概の Quarkus エクステンションと同様、ライブコーディングをサポートしています。
この技術は、previewと考えられています。 preview では、下位互換性やエコシステムでの存在は保証されていません。具体的な改善には設定や API の変更が必要になるかもしれませんが、 stable になるための計画は現在進行中です。フィードバックは メーリングリスト や GitHub の課題管理 で受け付けています。 とりうるステータスの完全なリストについては、 FAQの項目 を参照してください。 |
前提条件
このガイドを完成させるには、以下が必要です:
-
ざっと 30 minutes
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.9.9
-
使用したい場合は、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
はじめに
This guide walks you through generating an example Java project via a Maven archetype. Later on, it walks through the structure of the project so you can adapt any existing projects you have to use AWS Lambda.
AWS ビットのインストール
AWSのすべてのツールをインストールすることは、おそらくこのガイドでは最も難しいことです。AWS CLIをインストールするためのすべての手順に従っていることを確認してください。
デプロイ用のMavenプロジェクトを作成する
Maven Archetype を使用して Quarkus AWS Lambda Maven プロジェクトを作成します。
AWS Gateway HTTP APIを使用する場合は、このスクリプトでプロジェクトを生成します。
mvn archetype:generate \
-DarchetypeGroupId=io.quarkus \
-DarchetypeArtifactId=quarkus-amazon-lambda-http-archetype \
-DarchetypeVersion=3.17.5
AWS Gateway REST APIを使用する場合は、このスクリプトでプロジェクトを生成します。
mvn archetype:generate \
-DarchetypeGroupId=io.quarkus \
-DarchetypeArtifactId=quarkus-amazon-lambda-rest-archetype \
-DarchetypeVersion=3.17.5
ビルドとデプロイ
プロジェクトのビルド
quarkus build
./mvnw install
これでコードがコンパイルされ、生成されたプロジェクト内に含まれるユニットテストが実行されます。ユニットテストは他のJavaプロジェクトと同じで、Amazonで実行する必要はありません。Quarkus dev モードもこのエクステンションで利用できます。
ネイティブ実行可能ファイルをビルドする場合は、GraalVM が正しくインストールされていることを確認し、ビルドに native
プロパティーを追加してください。
quarkus build --native
./mvnw install -Dnative
Linux 以外のシステムでビルドしている場合は、Amazon Lambda が Linux バイナリーを必要とするため、Docker ビルドを使用するように Quarkus に指示するプロパティーも渡す必要があります。これを行うには、ビルドコマンドに -Dquarkus.native.container-build=true を渡します。ただし、これには Docker をローカルにインストールする必要があります。
|
According to the AWS documentation, AL2023
x86-64 binaries are built for the x86-64-v2 revision of the x86-64 architecture.
In Quarkus, the default value of quarkus.native.march is x86-64-v3.
This could cause
issues if AWS Lambda provisions older
hardware.
To maximize Lambda compatibility, you can set |
quarkus build --native --no-tests -Dquarkus.native.container-build=true
# The --no-tests flag is required only on Windows and macOS.
./mvnw install -Dnative -DskipTests -Dquarkus.native.container-build=true
ビルド時に追加生成されるファイル
ビルドを実行すると、Quarkus lambda エクステンションで生成されるいくつかの追加ファイルがあります。これらのファイルはビルドディレクトリーにあります: Maven なら target/
、Gradle なら build/
です。
-
function.zip
- Lambda デプロイファイル -
sam.jvm.yaml
- SAM CLI デプロイメントスクリプト -
sam.native.yaml
- ネイティブ用の SAM CLI デプロイメントスクリプト
AWS Lambda 環境のローカルでのライブコーディングとシミュレーション
開発モードおよびテストモードでは、Quarkus は模擬 AWS Lambda イベントサーバーを起動し、対応する API Gateway イベントタイプに HTTP リクエストを変換し、処理のために基盤となる Quarkus HTTP Lambda 環境に送信します。これにより、Docker や SAM CLI などのツールを必要とせずに、AWS Lambda 環境を可能な限りローカルでシミュレートします。
Quarkus Dev Mode を使用する場合は、REST エンドポイントをテストする場合と同じように、http://localhost:8080
で HTTP リクエストを呼び出すだけです。このリクエストは Mock Event Server にヒットし、Quarkus Lambda Poll ループによって消費される API Gateway json メッセージに変換されます。
テストのために、Quarkus はポート 8081 で別の Mock Event サーバーを起動します。Rest Assured のデフォルトポートは Quarkus によって自動的に 8081 に設定されるため、これを設定する必要はありません。
テストでより複雑な API Gateway イベントをシミュレートする場合は、生の API Gateway json イベントを使用して http://localhost:8080/_lambda_
(テストモードではポート 8081) に対して手動で HTTP POST を実行します。これらのイベントは、処理のために Quarkus Lambda ポーリングループに直接配置されます。その例を次に示します。
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.equalTo;
import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent;
import io.quarkus.test.junit.QuarkusTest;
@QuarkusTest
public class AmazonLambdaSimpleTestCase {
@Test
public void testJaxrsCognitoJWTSecurityContext() throws Exception {
APIGatewayV2HTTPEvent request = request("/security/username");
request.getRequestContext().setAuthorizer(new APIGatewayV2HTTPEvent.RequestContext.Authorizer());
request.getRequestContext().getAuthorizer().setJwt(new APIGatewayV2HTTPEvent.RequestContext.Authorizer.JWT());
request.getRequestContext().getAuthorizer().getJwt().setClaims(new HashMap<>());
request.getRequestContext().getAuthorizer().getJwt().getClaims().put("cognito:username", "Bill");
given()
.contentType("application/json")
.accept("application/json")
.body(request)
.when()
.post("/_lambda_")
.then()
.statusCode(200)
.body("body", equalTo("Bill"));
}
上記の例は、HTTP リクエストを含む Cognito プリンシパルを HTTP Lambda に送信することをシミュレートしています。
AWS HTTP API の生のイベントをハンドコードする場合、AWS Lambda ライブラリーのリクエストイベントタイプは com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent
で、レスポンスイベントタイプは com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPResponse
です。これは、quarkus-amazon-lambda-http
エクステンションおよび AWS HTTP API に対応します。
AWS REST API の生のイベントをハンドコードする場合、Quarkus には独自の実装 (io.quarkus.amazon.lambda.http.model.AwsProxyRequest
および io.quarkus.amazon.lambda.http.model.AwsProxyResponse
) があります。これは、quarkus-amazon-lambda-rest
エクステンションおよび AWS REST API に対応します。
モックイベントサーバは @QuarkusIntegrationTest
のテストでも起動されるので、ネイティブバイナリでも動作します。このように、SAM CLI のローカルテストと同様の機能を、Docker のオーバーヘッドなしに提供します。
最後に、ポート 8080 またはポート 8081 がコンピューターで使用できない場合は、application.properties を使用して開発モードとテストモードのポートを変更できます。
quarkus.lambda.mock-event-server.dev-port=8082
quarkus.lambda.mock-event-server.test-port=8083
ポート値がゼロの場合、ポートはランダムに割り当てられます。
モック・イベント・サーバーをオフにする方法:
quarkus.lambda.mock-event-server.enabled=false
Simulate AWS Lambda Deployment with SAM CLI
AWS SAM CLI を使用すると、ラップトップ上で Lambda をシミュレートした環境でローカルに実行することができます。このためには Docker をインストールする必要があります。Maven プロジェクトをビルドした後、以下のコマンドを実行してください
sam local start-api --template target/sam.jvm.yaml
これで Amazon の Lambda のデプロイ環境を模した Docker コンテナーが起動します。環境が起動したら、ブラウザで以下を開くとサンプルの Lambda を実行できます。
コンソールには、ラムダからの起動メッセージが表示されます。この特定のデプロイメントでは、JVM を起動し、純粋な Java として Lambda をロードします。
AWS にデプロイする
sam deploy -t target/sam.jvm.yaml -g
すべての質問に答えると、 Lambda がデプロイされ、API Gatewayへの必要なフックが設定されます。すべてが正常にデプロイされると、マイクロサービスのルート URL がコンソールに出力されます。このような感じです。
Key LambdaHttpApi Description URL for application Value https://234asdf234as.execute-api.us-east-1.amazonaws.com/
Value
属性は Lambda のルート URL です。これをブラウザにコピーして、最後に hello
を追加します。
バイナリー型のレスポンスは自動的にbase64でエンコードされます。これは、生のバイトを返す quarkus:dev を使用した動作とは異なります。Amazon の API には、base64 エンコーディングを必要とする追加の制限があります。一般的に、クライアントコードは自動的にこのエンコーディングを処理しますが、特定のカスタムな状況では、手動でエンコーディングを管理する必要があるかもしれないことを認識しておく必要があります。
|
ネイティブ実行可能ファイルのデプロイ
ネイティブ実行ファイルをデプロイするには、GraalVM を使用してビルドする必要があります。
quarkus build --native --no-tests -Dquarkus.native.container-build=true
# The --no-tests flag is required only on Windows and macOS.
./mvnw install -Dnative -DskipTests -Dquarkus.native.container-build=true
その後、ローカルで sam local を使って実行ファイルをテストすることができます。
sam local start-api --template target/sam.native.yaml
AWS Lambda へのデプロイ
sam deploy -t target/sam.native.yaml -g
POM を検証する
POM には quarkus-amazon-lambda-http
エクステンション(AWS Gateway HTTP APIでデプロイしている場合) か quarkus-amazon-lambda-rest
エクステンション( AWS Gateway REST APIでデプロイしている場合)が依存関係として含まれている以外に特別なことは何もありません。これらのエクステンションは Lambda のデプロイに必要なものをすべて自動的に生成します。
Also, at least in the generated Maven archetype pom.xml
, the quarkus-rest
, quarkus-reactive-routes
, and quarkus-undertow
dependencies are all optional. Pick which HTTP framework(s) you want to use (Jakarta REST, Reactive Routes, and/or Servlet) and
remove the other dependencies to shrink your deployment.
sam.yaml を調べる
sam.yaml
の構文はこのドキュメントの範囲を超えています。しかし、カスタムの sam.yaml
デプロイメントファイルを作成しようとしている場合、幾つかの強調しなければならないことがあります。
最初に注意することは、純粋なJavaラムダデプロイメントには特定のハンドラークラスが必要であるということです。 Lambdaハンドラー名は変更しないでください。
Properties:
Handler: io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest
Runtime: java17
このハンドラは、lambdaランタイムと使用しているQuarkus HTTPフレームワーク(Jakarta REST、Servletなど)の間のブリッジとなります。
ネイティブを利用する場合は、ネイティブのGraalVMデプロイメント用に設定する必要のある環境変数があります。 sam.native.yaml
を見ると、次のことがわかります。
Environment:
Variables:
DISABLE_SIGNAL_HANDLERS: true
This environment variable resolves some incompatibilities between Quarkus and the AWS Lambda Custom Runtime environment.
最後に、AWS Gateway RESTAPIへのデプロイに固有のことが一つあります。そのAPIは、設定でどのメディアタイプがバイナリーであるかを明示的に指定しない限り、HTTPレスポンスボディはテキストであることを想定しています。より簡単にするために、QuarkusエクステンションはすべてのHTTP応答メッセージのバイナリー(ベース64)エンコーディングを強制します。 sam.yaml
ファイルでは、すべてのメディアタイプがバイナリーであると仮定するようにAPI Gatewayを設定する必要があります。
Globals:
Api:
EndpointConfiguration: REGIONAL
BinaryMediaTypes:
- "*/*"
注入可能なAWSコンテキスト変数
If you are using Quarkus REST and Jakarta REST, you can inject various AWS Context variables into your Jakarta REST resource classes
using the Jakarta REST @Context
annotation or anywhere else with the CDI @Inject
annotation.
AWS HTTP APIでは、AWS変数 com.amazonaws.services.lambda.runtime.Context
および com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent
を注入することができます。以下はその例です。
import jakarta.ws.rs.core.Context;
import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent;
@Path("/myresource")
public class MyResource {
@GET
public String ctx(@Context com.amazonaws.services.lambda.runtime.Context ctx) { }
@GET
public String event(@Context APIGatewayV2HTTPEvent event) { }
@GET
public String requestContext(@Context APIGatewayV2HTTPEvent.RequestContext req) { }
}
AWS REST APIでは、AWS変数 com.amazonaws.services.lambda.runtime.Context
と io.quarkus.amazon.lambda.http.model.AwsProxyRequestContext
を注入することができます。以下はその例です。
import jakarta.ws.rs.core.Context;
import io.quarkus.amazon.lambda.http.model.AwsProxyRequestContext;
import io.quarkus.amazon.lambda.http.model.AwsProxyRequest;
@Path("/myresource")
public class MyResource {
@GET
public String ctx(@Context com.amazonaws.services.lambda.runtime.Context ctx) { }
@GET
public String reqContext(@Context AwsProxyRequestContext req) { }
@GET
public String req(@Context AwsProxyRequest req) { }
}
AWS XRay と GraalVM を使用したトレース
ネイティブイメージをビルドしていて、Lambda で AWS X-Ray Tracing を使いたい場合は、 quarkus-amazon-lambda-xray
を依存関係として pom に含める必要があります。AWS X-Ray ライブラリは GraalVM との完全な互換性がないため、これを動作させるためにいくつかの統合作業をしなければなりませんでした。
セキュリティー統合
API GatewayでHTTPリクエストを呼び出すと、GatewayはそのHTTPリクエストをJSONイベントドキュメントに変換し、Quarkus Lambdaに転送されます。Quarkus LambdaはこのJSONを解析し、QuarkusがサポートするあらゆるHTTPフレームワーク(Jakarta REST、サーブレット、Reactive Routes)で使用できるHTTPリクエストの内部表現に変換します。
API Gateway supports many ways to securely invoke on your HTTP endpoints that are backed by Lambda and Quarkus.
If you enable it, Quarkus will automatically parse relevant parts of the event json document
and look for security based metadata and register a java.security.Principal
internally that can be looked up in Jakarta REST
by injecting a jakarta.ws.rs.core.SecurityContext
, via HttpServletRequest.getUserPrincipal()
in servlet, and RouteContext.user()
in Reactive Routes.
If you want more security information, the Principal
object can be typecast to
a class that will give you more information.
このセキュリティー機能を有効にするには、これを application.properties
ファイルに追加します。
quarkus.lambda-http.enable-security=true
マッピング方法は次のとおりです。
認証タイプ | プリンシパルクラス | プリンシパル名の Json パス |
---|---|---|
Cognito JWT |
|
|
IAM |
|
|
カスタム Lambda |
|
|
認証タイプ | プリンシパルクラス | プリンシパル名の Json パス |
---|---|---|
Cognito |
|
|
IAM |
|
|
カスタム Lambda |
|
|
cognito:groups
が存在する場合、Quarkus はこれらのグループを抽出して Quarkus のロールにマッピングし、 @RolesAllowed
のようなアノテーションを使用して認証に使用することができます。 cognito:groups
をQuarkusのロールにマッピングしたくない場合は、設定で明示的に無効にする必要があります:
quarkus.lambda-http.map-cognito-to-roles=false
また、ロールを抽出するために、別のCognitoクレームを指定することもできます:
quarkus.lambda-http.cognito-role-claim=cognito:roles
デフォルトでは、ロールが括弧で囲んだスペース区切りのリスト(例: [ user admin ]
)に入っていることを想定しています。クレーム文字列から個々のロールを検索するために使用する正規表現を指定することも可能です:
quarkus.lambda-http.cognito-claim-matcher=[^\[\] \t]+
カスタムセキュリティーの統合
AWSセキュリティのデフォルトサポートは、プリンシパル名をQuarkusセキュリティAPIにマッピングするだけで、クレームやロール、パーミッションのマッピングは何も行いません。 io.quarkus.amazon.lambda.http.LambdaIdentityProvider
インターフェースの実装を使用して、ラムダHTTPイベントのセキュリティメタデータがQuarkus Security APIにマッピングされる方法を完全に制御することができます。このインターフェースを実装することで、プリンシパルのロールマッピングを定義したり、IAMやCognito、カスタムLambdaセキュリティ統合によって提供される追加属性を公開したりすることができます。
quarkus-amazon-lambda-http
package io.quarkus.amazon.lambda.http;
/**
* Helper interface that removes some boilerplate for creating
* an IdentityProvider that processes APIGatewayV2HTTPEvent
*/
public interface LambdaIdentityProvider extends IdentityProvider<LambdaAuthenticationRequest> {
@Override
default public Class<LambdaAuthenticationRequest> getRequestType() {
return LambdaAuthenticationRequest.class;
}
@Override
default Uni<SecurityIdentity> authenticate(LambdaAuthenticationRequest request, AuthenticationRequestContext context) {
APIGatewayV2HTTPEvent event = request.getEvent();
SecurityIdentity identity = authenticate(event);
if (identity == null) {
return Uni.createFrom().optional(Optional.empty());
}
return Uni.createFrom().item(identity);
}
/**
* You must override this method unless you directly override
* IdentityProvider.authenticate
*
* @param event
* @return
*/
default SecurityIdentity authenticate(APIGatewayV2HTTPEvent event) {
throw new IllegalStateException("You must override this method or IdentityProvider.authenticate");
}
}
HTTP の場合、オーバーライドする重要なメソッドは LambdaIdentityProvider.authenticate(APIGatewayV2HTTPEvent event)
です。ここで、APIGatewayV2HTTPEvent
からセキュリティーデータをマッピングする方法に基づいて SecurityIdentity を割り当てます。
quarkus-amazon-lambda-rest
package io.quarkus.amazon.lambda.http;
import java.util.Optional;
import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent;
import io.quarkus.amazon.lambda.http.model.AwsProxyRequest;
import io.quarkus.security.identity.AuthenticationRequestContext;
import io.quarkus.security.identity.IdentityProvider;
import io.quarkus.security.identity.SecurityIdentity;
import io.smallrye.mutiny.Uni;
/**
* Helper interface that removes some boilerplate for creating
* an IdentityProvider that processes APIGatewayV2HTTPEvent
*/
public interface LambdaIdentityProvider extends IdentityProvider<LambdaAuthenticationRequest> {
...
/**
* You must override this method unless you directly override
* IdentityProvider.authenticate
*
* @param event
* @return
*/
default SecurityIdentity authenticate(AwsProxyRequest event) {
throw new IllegalStateException("You must override this method or IdentityProvider.authenticate");
}
}
REST の場合、オーバーライドする重要なメソッドは LambdaIdentityProvider.authenticate(AwsProxyRequest event)
です。ここで、AwsProxyRequest
からセキュリティーデータをマップする方法に基づいて SecurityIdentity を割り当てます。
実装するプロバイダーは CDI Bean である必要があります。次に例を示します。
package org.acme;
import java.security.Principal;
import jakarta.enterprise.context.ApplicationScoped;
import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent;
import io.quarkus.amazon.lambda.http.LambdaIdentityProvider;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.security.runtime.QuarkusPrincipal;
import io.quarkus.security.runtime.QuarkusSecurityIdentity;
@ApplicationScoped
public class CustomSecurityProvider implements LambdaIdentityProvider {
@Override
public SecurityIdentity authenticate(APIGatewayV2HTTPEvent event) {
if (event.getHeaders() == null || !event.getHeaders().containsKey("x-user"))
return null;
Principal principal = new QuarkusPrincipal(event.getHeaders().get("x-user"));
QuarkusSecurityIdentity.Builder builder = QuarkusSecurityIdentity.builder();
builder.setPrincipal(principal);
return builder.build();
}
}
次も同じ例ですが、AWS Gateway REST API を使用しています。
package org.acme;
import java.security.Principal;
import jakarta.enterprise.context.ApplicationScoped;
import io.quarkus.amazon.lambda.http.model.AwsProxyRequest;
import io.quarkus.amazon.lambda.http.LambdaIdentityProvider;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.security.runtime.QuarkusPrincipal;
import io.quarkus.security.runtime.QuarkusSecurityIdentity;
@ApplicationScoped
public class CustomSecurityProvider implements LambdaIdentityProvider {
@Override
public SecurityIdentity authenticate(AwsProxyRequest event) {
if (event.getMultiValueHeaders() == null || !event.getMultiValueHeaders().containsKey("x-user"))
return null;
Principal principal = new QuarkusPrincipal(event.getMultiValueHeaders().getFirst("x-user"));
QuarkusSecurityIdentity.Builder builder = QuarkusSecurityIdentity.builder();
builder.setPrincipal(principal);
return builder.build();
}
}
Quarkus は、この実装を自動的に検出し、前述のデフォルトの実装の代わりに使用する必要があります。
単純な SAM ローカルプリンシパル
sam local
を使用してアプリケーションをテストしている場合は、QUARKUS_AWS_LAMBDA_FORCE_USER_NAME
環境変数を設定することで、アプリケーションの実行時に使用するプリンシパル名をハードコーディングできます。
SnapStart
アプリケーションをLambda SnapStartに最適化するには、 SnapStart構成ドキュメント を参照してください。