The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

AWS Lambda with Quarkus REST, Undertow, or Reactive Routes

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.6

  • 使用したい場合は、 Quarkus CLI

  • ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること

  • An Amazon AWS account

  • AWS SAM CLI

はじめに

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.9.4

AWS Gateway REST APIを使用する場合は、このスクリプトでプロジェクトを生成します。

mvn archetype:generate \
       -DarchetypeGroupId=io.quarkus \
       -DarchetypeArtifactId=quarkus-amazon-lambda-rest-archetype \
       -DarchetypeVersion=3.9.4

ビルドとデプロイ

プロジェクトのビルド

コマンドラインインタフェース
quarkus build
Maven
./mvnw install

これでコードがコンパイルされ、生成されたプロジェクト内に含まれるユニットテストが実行されます。ユニットテストは他のJavaプロジェクトと同じで、Amazonで実行する必要はありません。Quarkus dev モードもこのエクステンションで利用できます。

ネイティブ実行可能ファイルをビルドする場合は、GraalVM が正しくインストールされていることを確認し、ビルドに native プロパティーを追加してください。

コマンドラインインタフェース
quarkus build --native
Maven
./mvnw install -Dnative
Linux 以外のシステムでビルドしている場合は、Amazon Lambda が Linux バイナリーを必要とするため、Docker ビルドを使用するように Quarkus に指示するプロパティーも渡す必要があります。これを行うには、ビルドコマンドに -Dquarkus.native.container-build=true を渡します。ただし、これには Docker をローカルにインストールする必要があります。
コマンドラインインタフェース
quarkus build --native --no-tests -Dquarkus.native.container-build=true
# The --no-tests flag is required only on Windows and macOS.
Maven
./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.
Maven
./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.Contextio.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

マッピング方法は次のとおりです。

Table 1. HTTP quarkus-amazon-lambda-http
認証タイプ プリンシパルクラス プリンシパル名の Json パス

Cognito JWT

io.quarkus.amazon.lambda.http.CognitoPrincipal

requestContext.authorizer.jwt.claims.cognito:username

IAM

io.quarkus.amazon.lambda.http.IAMPrincipal

requestContext.authorizer.iam.userId

カスタム Lambda

io.quarkus.amazon.lambda.http.CustomPrincipal

requestContext.authorizer.lambda.principalId

Table 2. REST quarkus-amazon-lambda-rest
認証タイプ プリンシパルクラス プリンシパル名の Json パス

Cognito

io.quarkus.amazon.lambda.http.CognitoPrincipal

requestContext.authorizer.claims.cognito:username

IAM

io.quarkus.amazon.lambda.http.IAMPrincipal

requestContext.identity.user

カスタム Lambda

io.quarkus.amazon.lambda.http.CustomPrincipal

requestContext.authorizer.principalId

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セキュリティ統合によって提供される追加属性を公開したりすることができます。

HTTP 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 を割り当てます。

REST 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構成ドキュメント を参照してください。

関連コンテンツ