RESTEasy Reactive、Undertow、または Reactive Routes を使用した Amazon Lambda
Quarkusでは、AWS Gateway HTTP API または AWS Gateway REST API のいずれかを使用して、お気に入りの Java HTTP フレームワークを Amazon Lambdaとしてデプロイできます。つまり、RESTEasy (JAX-RS)、Undertow (サーブレット)、Reactive Route、Funqy HTTP、その他の Quarkus の HTTP フレームワークで書かれたマイクロサービスを、AWS Lambda としてデプロイすることができます。
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 の課題管理 で受け付けています。 For a full list of possible statuses, check our FAQ entry. |
前提条件
このガイドを完成させるには、以下が必要です:
-
ざっと 30 minutes
-
IDE
-
JDK 11+ がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.8.1+
-
使用したい場合、 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 Amazon 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=2.11.1.Final
AWS Gateway REST APIを使用する場合は、このスクリプトでプロジェクトを生成します。
mvn archetype:generate \
-DarchetypeGroupId=io.quarkus \
-DarchetypeArtifactId=quarkus-amazon-lambda-rest-archetype \
-DarchetypeVersion=2.11.1.Final
ビルドとデプロイ
プロジェクトをビルドします。
quarkus build
./mvnw clean package
これでコードがコンパイルされ、生成されたプロジェクト内に含まれるユニットテストが実行されます。ユニットテストは他のJavaプロジェクトと同じで、Amazonで実行する必要はありません。Quarkus dev モードもこのエクステンションで利用できます。
ネイティブ実行可能ファイルをビルドする場合は、GraalVM が正しくインストールされていることを確認し、ビルドに native
プロパティーを追加してください。
quarkus build --native
./mvnw package -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.
./mvnw package -Dnative -Dquarkus.native.container-build=true
ビルド時に追加生成されるファイル
After you run the build, there are a few extra files generated by the Quarkus lambda extension you are using. These files are in the build directory: target/
for Maven, build/
for Gradle.
-
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 に対応します。
模擬イベントサーバーは、@NativeImageTest
テストおよび @QuarkusIntegrationTest
テストでも起動するため、ネイティブバイナリーでも機能します。これはすべて、Docker のオーバーヘッドなしで、SAM CLI ローカルテストと同様の機能を提供します。
最後に、ポート 8080 またはポート 8081 がコンピューターで使用できない場合は、application.properties を使用して開発モードとテストモードのポートを変更できます。
quarkus.lambda.mock-event-server.dev-port=8082
quarkus.lambda.mock-event-server.test-port=8083
ポート値がゼロの場合、ポートはランダムに割り当てられます。
SAM CLI を使用して Amazon Lambda のデプロイをシミュレートする
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
を追加します。
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 package -Dnative -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 を調べる
There is nothing special about the POM other than the inclusion of the quarkus-amazon-lambda-http
extension (if you are deploying an AWS Gateway HTTP API) or the quarkus-amazon-lambda-rest
extension (if you are deploying an AWS Gateway REST API). These extensions automatically generate everything you might need for your lambda deployment.
また、少なくとも生成された Maven アーキタイプ pom.xml
では、quarkus-resteasy-reactive
、quarkus-reactive-routes
、および quarkus-undertow
の依存関係はすべて任意です。使用する HTTP フレームワーク (JAX-RS、Reactive Routes、Servlet) を選択し、他の依存関係を削除してデプロイメントを縮小します。
sam.yaml を調べる
sam.yaml
の構文はこのドキュメントの範囲を超えています。しかし、カスタムの sam.yaml
デプロイメントファイルを作成しようとしている場合、幾つかの強調しなければならないことがあります。
最初に注意することは、純粋なJavaラムダデプロイメントには特定のハンドラークラスが必要であるということです。 Lambdaハンドラー名は変更しないでください。
Properties:
Handler: io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest
Runtime: java11
このハンドラは、ラムダランタイムと、使用しているQuarkus HTTPフレームワーク(JAX-RS、Servletなど)との間のブリッジとなります。
ネイティブを利用する場合は、ネイティブのGraalVMデプロイメント用に設定する必要のある環境変数があります。 sam.native.yaml
を見ると、次のことがわかります。
Environment:
Variables:
DISABLE_SIGNAL_HANDLERS: true
この環境変数は、QuarkusとAmazon Lambdaカスタムランタイム環境との間のいくつかの非互換性を解決します。
最後に、AWS Gateway RESTAPIへのデプロイに固有のことが一つあります。そのAPIは、設定でどのメディアタイプがバイナリーであるかを明示的に指定しない限り、HTTPレスポンスボディはテキストであることを想定しています。より簡単にするために、QuarkusエクステンションはすべてのHTTP応答メッセージのバイナリー(ベース64)エンコーディングを強制します。 sam.yaml
ファイルでは、すべてのメディアタイプがバイナリーであると仮定するようにAPI Gatewayを設定する必要があります。
Globals:
Api:
EndpointConfiguration: REGIONAL
BinaryMediaTypes:
- "*/*"
注入可能なAWSコンテキスト変数
RESTEasy Reactive と JAX-RS を使用している場合は、JAX-RS @Context
アノテーションを使用するか、CDI @Inject
アノテーションを使用して、さまざまな AWS Context 変数を JAX-RS リソースクラスに挿入できます。
AWS HTTP APIでは、AWS変数 com.amazonaws.services.lambda.runtime.Context
および com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent
を注入することができます。以下はその例です。
import javax.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 javax.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 を使用したトレース
If you are building native images, and want to use AWS X-Ray Tracing with your lambda you will need to include quarkus-amazon-lambda-xray
as a dependency in your pom. The AWS X-Ray library is not fully compatible with GraalVM, so we had to do some integration work to make this work.
セキュリティー統合
API Gateway で HTTP リクエストを呼び出すと、ゲートウェイはその HTTP リクエストを JSON イベントドキュメントに変換し、Quarkus Lambda に転送します。Quarkus Lambda はこの json を解析し、Quarkus がサポートする任意の HTTP フレームワーク (JAX-RS、サーブレット、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 JAX-RS by injecting a javax.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 |
|
|
カスタムセキュリティーの統合
The default support for AWS security only maps the principal name to Quarkus security APIs and does 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-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 javax.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 javax.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 は、この実装を自動的に検出し、前述のデフォルトの実装の代わりに使用する必要があります。