Quarkus REST、Undertow、または Reactive Routes を使用した AWS Lambda
previewWith 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 a single HTTP framework together with the 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.12
-
使用したい場合は、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
はじめに
このガイドでは、Maven アーキタイプを使用してサンプル Java プロジェクトを生成する手順を説明します。その後、プロジェクトの構造について説明しているため、 既存のプロジェクトを 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.31.1
AWS Gateway REST APIを使用する場合は、このスクリプトでプロジェクトを生成します。
mvn archetype:generate \
-DarchetypeGroupId=io.quarkus \
-DarchetypeArtifactId=quarkus-amazon-lambda-rest-archetype \
-DarchetypeVersion=3.31.1
ビルドとデプロイ
プロジェクトのビルド
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 をローカルにインストールする必要があります。
|
|
AWS ドキュメント によると、AL2023
x86-64 バイナリーは、x86-64 アーキテクチャーの x86-64-v2 リビジョン用に構築されています。
Quarkus では、quarkus.native.march のデフォルト値は x86-64-v3 です。
これが原因で、
issues AWS Lambda が古いバージョンのハードウェア
をプロビジョニングする場合
Lambda の互換性を最大限に高めるには、 |
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 に送信することをシミュレートしています。
If you want to hand code raw events for the AWS HTTP API, the AWS Lambda library has the request event type, which is
com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent, and the response event type
of com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPResponse. This corresponds
to the quarkus-amazon-lambda-http extension and the 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 に対応します。
The mock event server is also started for @QuarkusIntegrationTest tests, so it will work
with native binaries too. All this provides similar functionality to the SAM CLI local testing, without the overhead of 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
SAM CLI を使用した AWS Lambda デプロイメントのシミュレート
AWS SAM CLI を使用すると、ラップトップ上で Lambda をシミュレートした環境でローカルに実行することができます。このためには Docker をインストールする必要があります。Maven プロジェクトをビルドした後、以下のコマンドを実行してください
sam local start-api --template target/sam.jvm.yaml
This will start a Docker container that mimics Amazon’s Lambda’s deployment environment. Once the environment is started, you can invoke the example lambda in your browser by going to:
In the console, you’ll see startup messages from the lambda. This particular deployment starts a JVM and loads your lambda as pure Java.
AWS にデプロイする
sam deploy -t target/sam.jvm.yaml -g
Answer all the questions, and your lambda will be deployed, and the necessary hooks to the API Gateway will be set up. If everything deploys successfully, the root URL of your microservice will be output to the console. Something like this:
Key LambdaHttpApi Description URL for application Value https://234asdf234as.execute-api.us-east-1.amazonaws.com/
Value 属性は Lambda のルート URL です。これをブラウザにコピーして、最後に hello を追加します。
Responses for binary types will be automatically encoded with base64. This is different from the behavior using
quarkus:dev, which will return the raw bytes. Amazon’s API has additional restrictions requiring the base64 encoding.
In general, client code will automatically handle this encoding, but in certain custom situations, you should be aware
you may need to manually manage that encoding.
|
ネイティブ実行可能ファイルのデプロイ
ネイティブ実行可能ファイルをデプロイするには、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 のデプロイに必要なものをすべて自動的に生成します。
また、少なくとも生成された Maven アーキタイプ pom.xml では、 quarkus-rest、 quarkus-reactive-routes、 quarkus-undertow
依存関係はすべてオプションです。使用する HTTP フレームワーク (Jakarta REST、Reactive Routes、Servlet) を選択し、
デプロイメントを縮小するには、他の依存関係を削除します。
sam.yaml を調べる
The sam.yaml syntax is beyond the scope of this document. There are a couple of things that must be highlighted just in case you are
going to craft your own custom sam.yaml deployment files.
最初に注意することは、純粋なJavaラムダデプロイメントには特定のハンドラークラスが必要であるということです。 Lambdaハンドラー名は変更しないでください。
Properties:
Handler: io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest
Runtime: java21
このハンドラは、lambdaランタイムと使用しているQuarkus HTTPフレームワーク(Jakarta REST、Servletなど)の間のブリッジとなります。
ネイティブを利用する場合は、ネイティブのGraalVMデプロイメント用に設定する必要のある環境変数があります。 sam.native.yaml を見ると、次のことがわかります。
Environment:
Variables:
DISABLE_SIGNAL_HANDLERS: true
この環境変数は、Quarkus と AWS Lambda カスタムランタイム環境間で互換性が保てない問題について解決します。
最後に、AWS Gateway RESTAPIへのデプロイに固有のことが一つあります。そのAPIは、設定でどのメディアタイプがバイナリーであるかを明示的に指定しない限り、HTTPレスポンスボディはテキストであることを想定しています。より簡単にするために、QuarkusエクステンションはすべてのHTTP応答メッセージのバイナリー(ベース64)エンコーディングを強制します。 sam.yaml ファイルでは、すべてのメディアタイプがバイナリーであると仮定するようにAPI Gatewayを設定する必要があります。
Globals:
Api:
EndpointConfiguration: REGIONAL
BinaryMediaTypes:
- "*/*"
注入可能なAWSコンテキスト変数
Quarkus REST と Jakarta REST を使用している場合、Jakarta REST @Context アノテーションを使用して Jakarta REST リソースクラスに、
CDI @Inject アノテーションを使用して他の場所にさまざまな AWS Context 変数を注入できます。
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 は、Lambda および Quarkus がサポートする HTTP エンドポイントで安全に呼び出すためのさまざまな方法をサポートしています。
これを有効にすると、Quarkus は event json document の関連部分を自動的に解析して、セキュリティーベースのメタデータを検索し、
サーブレットの HttpServletRequest.getUserPrincipal() および Reactive Routes の RouteContext.user() を介して javax.ws.rs.core.SecurityContext を挿入することにより、Quarkus REST で検索できる jakarta.security.Principal
を内部的に登録します。
より多くのセキュリティー情報が必要な場合は、
Principal オブジェクトをクラスに型キャストしてより多くの情報を提供できます。
このセキュリティー機能を有効にするには、これを 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=[^\[\] \ ]+
カスタムセキュリティーの統合
The default support for AWS security only maps the principal name to Quarkus security
APIs and do nothing to map claims, or roles, or permissions. You have full control on
how security metadata in the lambda HTTP event is mapped to Quarkus Security APIs using
implementations of the io.quarkus.amazon.lambda.http.LambdaIdentityProvider
interface. By implementing this interface, you can do things like define role mappings for your principal
or publish additional attributes provided by IAM, or Cognito, or your Custom Lambda security integration.
quarkus-amazon-lambda-httppackage 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-restpackage 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構成ドキュメント を参照してください。