AWS Lambda
quarkus-amazon-lambda
扩展允许你使用Quarkus来构建你的AWS Lambdas。你的Lambdas可以根据需要使用来自CDI或Spring的注入注解,以及其他Quarkus设施。
Quarkus lambdas可以使用Amazon Java Runtime进行部署,如果你想要更小的内存占用和更快的冷启动启动时间,你也可以构建一个本地可执行文件,并使用Amazon的Custom Runtime。
Quarkus’s integration with lambdas also supports Quarkus’s Live Coding development cycle. You can bring up your Quarkus lambda project in dev or test mode and code on your project live.
先决条件
完成这个指南,你需要:
-
大概30 minutes
-
编辑器
-
JDK 17+ installed with
JAVA_HOME
configured appropriately -
Apache Maven 3.9.9
-
如果你愿意的话,还可以选择使用Quarkus CLI
-
如果你想构建原生可执行程序,可以选择安装Mandrel或者GraalVM,并正确配置(或者使用Docker在容器中进行构建)
-
AWS SAM CLI ,用于本地测试
对于Gradle项目,请 见下文 ,或进一步参考 Gradle设置页面中 的指南。 |
创建Maven部署项目
使用我们的Maven Archetype创建Quarkus AWS Lambda maven项目。
mvn archetype:generate \
-DarchetypeGroupId=io.quarkus \
-DarchetypeArtifactId=quarkus-amazon-lambda-archetype \
-DarchetypeVersion=3.16.3
如果你喜欢使用Gradle,你可以通过 code.quarkus.io 将 Copy the build.gradle, gradle.properties and settings.gradle into the above-generated Maven archetype project, to follow along with this guide. Execute: gradle wrapper to set up the gradle wrapper (recommended). For full Gradle details, see the Gradle build section below. |
选择你的Lambda
The quarkus-amazon-lambda
extension scans your project for a class that directly implements the Amazon RequestHandler<?, ?>
or RequestStreamHandler
interface.
It must find a class in your project that implements this interface, or it will throw a build time failure.
If it finds more than one handler class, a build time exception will also be thrown.
但有时,你可能会有几个共享代码的相关lambdas,创建多个maven模块是你不想做的开销。 quarkus-amazon-lambda
扩展允许你在一个项目中捆绑多个lambdas,并使用配置或环境变量来选择你要部署的处理程序。
生成的项目里有三个lambdas。两个实现 RequestHandler<?, ?>
接口,一个实现 RequestStreamHandler
接口。使用了一个,另两个未使用。如果你打开 src/main/resources/application.properties
,你会看到:
quarkus.lambda.handler=test
quarkus.lambda.handler
属性告诉Quarkus要部署哪个lambda处理程序。这也可以用一个环境变量来覆盖。
如果你查看项目中生成的三个处理程序类,你会发现它们的 @Named
不同。
@Named("test")
public class TestLambda implements RequestHandler<InputObject, OutputObject> {
}
@Named("unused")
public class UnusedLambda implements RequestHandler<InputObject, OutputObject> {
}
@Named("stream")
public class StreamLambda implements RequestStreamHandler {
}
处理程序类的CDI名称必须与 quarkus.lambda.handler
属性中指定的值匹配。
部署到AWS Lambda Java Runtime
有几个步骤可以让lambda在AWS上运行。生成的maven项目包含一个有用的脚本,来为纯Java和原生部署创建、更新、删除和调用你的lambda。
创建一个执行角色
查看使用AWS CLI部署lambda的 入门指南 。具体来说,确保你已经创建了一个 Execution Role
。你需要在你的配置文件或控制台窗口中定义一个 LAMBDA_ROLE_ARN
环境变量,或者,你可以编辑由构建生成的 manage.sh
脚本,并把角色值直接放在那里:
LAMBDA_ROLE_ARN="arn:aws:iam::1234567890:role/lambda-role"
构建额外生成的文件
After you run the build, there are a few extra files generated by the quarkus-amazon-lambda
extension. These files
are in the build directory: target/
for maven, build/
for gradle.
-
function.zip
- lambda deployment file -
manage.sh
- wrapper around aws lambda cli calls -
bootstrap-example.sh
- 用于原生部署的bootstrap脚本示例 -
sam.jvm.yaml
- (可选)用于sam cli和本地测试 -
sam.native.yaml
- (可选),用于Sam cli和原生本地测试
创建函数
target/manage.sh
脚本是用于使用AWS Lambda Java runtime管理你的lambda。此脚本只是为了方便你而提供。如果你想了解执行哪些aws命令来创建、删除和更新你的lambdas,请检查 manage.sh
脚本的输出。
manage.sh
支持四种操作: create
, delete
, update
和 invoke
。
To verify your setup, that you have the AWS CLI installed, executed aws configure for the AWS access keys,
and set up the LAMBDA_ROLE_ARN environment variable (as described above), please execute manage.sh without any parameters.
A usage statement will be printed to guide you accordingly.
|
如果使用Gradle, manage.sh 中二进制文件的路径必须从 target 变为 build
|
要查看 用法
声明,并验证AWS配置:
sh target/manage.sh
你可以使用以下命令 创建
你的函数:
sh target/manage.sh create
或者如果在这个shell中没有定义 LAMBDA_ROLE_ARN
:
LAMBDA_ROLE_ARN="arn:aws:iam::1234567890:role/lambda-role" sh target/manage.sh create
不要改变处理程序的开关。这必须被硬编码为 io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest 。这个处理程序引导Quarkus,并包装你实际的处理程序,以便可以执行注入。
|
如果在创建函数时有任何问题,你必须在重新运行 create
命令之前使用 delete
函数来删除它。
sh target/manage.sh delete
命令也可以是叠加的:
sh target/manage.sh delete create
调用Lambda
使用 invoke
命令来调用你的函数。
sh target/manage.sh invoke
这个lambda示例接受通过 --payload
开关传入的输入,该开关指向项目根目录中的一个json文件。
这个lambda也可以使用SAM CLI在本地调用,如:
sam local invoke --template target/sam.jvm.yaml --event payload.json
如果你正在使用原生镜像构建,只需将模板名称替换为原生版本:
sam local invoke --template target/sam.native.yaml --event payload.json
部署到AWS Lambda Custom (native) Runtime
If you want a lower memory footprint and faster initialization times for your lambda, you can compile your Java
code to a native executable. Just make sure to rebuild your project with the -Dnative
switch.
对于Linux主机,执行。
quarkus build --native
./mvnw install -Dnative
./gradlew build -Dquarkus.native.enabled=true
如果你在非Linux系统上构建,你还需要传入一个属性,指示Quarkus使用docker构建,因为Amazon Lambda需要linux二进制文件。你可以通过向你的构建传递这个属性来实现: -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 install -Dnative -DskipTests -Dquarkus.native.container-build=true
./gradlew build -Dquarkus.native.enabled=true -Dquarkus.native.container-build=true
这两个命令中的任何一个都会编译并创建一个本地可执行镜像。它还会生成一个zip文件 target/function.zip
。这个zip文件包含已重命名为 bootstrap
的本地可执行镜像,。这是AWS Lambda Custom (Provided) Runtime的要求。
这里的说明与上面的完全一样,但有一个变化:你需要将 native
作为第一个参数添加到 manage.sh
脚本中:
sh target/manage.sh native create
如上所述,命令可以叠加。如果你想使用原生镜像构建,唯一的要求是 native
是第一个参数。该脚本将处理管理你原生镜像功能部署所需的其余细节。
如果你想了解执行了哪些aws命令来创建、删除和更新lambdas,请检查 manage.sh
脚本的输出。
对于原生的创建命令,需要注意的一点是, aws lambda create-function
调用必须设置一个特定的环境变量:
--environment 'Variables={DISABLE_SIGNAL_HANDLERS=true}'
检查POM和Gradle构建
除了将 quarkus-amazon-lambda
扩展作为一个依赖项之外,POM并没有什么特别之处。该扩展会为你的lambda部署自动生成可能需要的一切。
In previous versions of this extension, you had to set up your pom or gradle to zip up your executable for native deployments, but this is not the case anymore. |
Gradle build
Similarly, for Gradle projects, you also just have to add the quarkus-amazon-lambda
dependency. The extension automatically generates everything you might need
for your lambda deployment.
Gradle依赖项示例:
dependencies {
implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")
implementation 'io.quarkus:quarkus-resteasy'
implementation 'io.quarkus:quarkus-amazon-lambda'
testImplementation 'io.quarkus:quarkus-junit5'
testImplementation 'io.rest-assured:rest-assured'
}
实时编码和单元/集成测试
To mirror the AWS Lambda environment as closely as possible in a dev environment, the Quarkus AWS Lambda extension boots up a mock AWS Lambda event server in Quarkus Dev and Test mode. This mock event server simulates a true AWS Lambda environment.
当在Quarkus开发模式下运行时,你可以对 <a href="http://localhost:8080" class="bare">http://localhost:8080</a>
执行HTTP POST来向它提供事件。模拟事件服务器将接收事件,你的lambda将被调用。你可以在你的lambda上执行实时编码,所做的修改将自动被重新编译,并在下次调用时可用。下面是一个例子:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
$ curl -d "{\"name\":\"John\"}" -X POST http://localhost:8080
For your unit tests, you can also invoke on the mock event server using any HTTP client you want. Here’s an example using rest-assured. Quarkus starts up a separate Mock Event server under port 8081. The default port for Rest Assured is automatically set to 8081 by Quarkus, so you can invoke on this endpoint.
import org.junit.jupiter.api.Test;
import io.quarkus.test.junit.QuarkusTest;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.containsString;
@QuarkusTest
public class LambdaHandlerTest {
@Test
public void testSimpleLambdaSuccess() throws Exception {
Person in = new Person();
in.setName("Stu");
given()
.contentType("application/json")
.accept("application/json")
.body(in)
.when()
.post()
.then()
.statusCode(200)
.body(containsString("Hello Stu"));
}
}
The mock event server is also started for @QuarkusIntegrationTest
tests so 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
端口值为零将导致随机分配端口。
To turn off the mock event server:
quarkus.lambda.mock-event-server.enabled=false
使用SAM CLI进行测试
如果你不想使用模拟事件服务器,你可以用SAM CLI测试lambdas。
AWS SAM CLI 允许你在模拟的Lambda环境中在你的笔记本电脑上本地运行lambdas。这需要安装 docker 。如果你选择使用它,这是一个可选的方法。否则,Quarkus的JUnit集成应该足以满足你的大部分需求。
已经为JVM和原生执行模式生成了一个启动模板。
运行下面的SAM CLI命令来本地测试你的lambda函数,传递合适的SAM 模板
。 event
参数使用任何JSON文件,在本例中是样本 payload.json
。
如果使用Gradle,YAML模板中的二进制文件的路径必须从 target 改为 build
|
sam local invoke --template target/sam.jvm.yaml --event payload.json
本地镜像也可以使用 sam.native.yaml
模板进行本地测试:
sam local invoke --template target/sam.native.yaml --event payload.json
修改 function.zip
There are times when you may have to add some additions to the function.zip
lambda deployment that is generated
by the build. To do this, create a zip.jvm
or zip.native
directory within src/main
.
Create zip.jvm/
if you are doing a pure Java lambda. zip.native/
if you are doing a native deployment.
你在zip目录下创建的任何文件和目录都将包括在 function.zip
内
自定义 bootstrap
脚本
有些时候你可能想在lambda调用你的原生quarkus lambda部署时设置特定的系统属性或其他参数。如果你在 zip.native
中包含一个 bootstrap
脚本文件,quarkus 扩展会在 function.zip
中自动将可执行文件重命名为 runner
,并将 bootstrap
脚本的 unix 模式设置为可执行。
如果你包含一个自定义的 bootstrap 脚本,那么本地可执行文件必须被引用为 runner 。
|
扩展会在 target/bootstrap-example.sh
中生成了一个示例脚本。
使用AWS XRay和GraalVM进行追踪
如果你正在构建原生镜像,并希望在你的lambda中使用 AWS X-Ray 追踪 ,你将需要在你的pom中把 quarkus-amazon-lambda-xray
作为一个依赖项来包括。AWS X-Ray库与GraalVM并不完全兼容,因此我们不得不做一些整合工作来使其可以正常工作。
此外,记得在 manage.sh
, cmd_create()
函数中启用AWS X-Ray追踪参数。这也可以在AWS管理控制台中进行设置。
--tracing-config Mode=Active
对于Sam模板文件,将以下内容添加到YAML函数属性中。
Tracing: Active
AWS X-Ray确实在你的发行版中添加了许多类,确保你至少使用了256MB的AWS Lambda内存大小。这在 manage.sh
cmd_create()
中进行了明确的设置。虽然原生镜像可能总是使用较低的内存设置,但建议保持设置不变,特别是为了帮助比较性能。
使用HTTPS或SSL/TLS
If your code makes HTTPS calls (e.g. to a microservice, to an AWS service), you will need to add configuration to the native image, as GraalVM will only include the dependencies when explicitly declared. Quarkus, by default enables this functionality on extensions that implicitly require it. For further information, please consult the Quarkus SSL guide
打开src/main/resources/application.properties,并在你的原生镜像中添加以下行来启用SSL。
quarkus.ssl.native=true
使用AWS Java SDK v2
Quarkus现在有DynamoDB、S3、SNS和SQS的扩展(还会有更多)。请查看 那些指南 关于如何使用带有Quarkus的各种AWS服务 ,而不是像下面这样手动连接。 |
通过最小的整合,可以利用AWS Java SDK v2,其可以用来调用诸如SQS、SNS、S3和DynamoDB等服务。
For native image, however, the URL Connection client must be preferred over the Apache HTTP Client when using synchronous mode, due to issues in the GraalVM compilation (at present).
把 quarkus-jaxb
作为一个依赖项添加到你的Maven pom.xml
,或Gradle build.gradle
文件中。
You must also force your AWS service client for SQS, SNS, S3 et al., to use the URL Connection client, which connects to AWS services over HTTPS, hence the inclusion of the SSL enabled property, as described in the 使用HTTPS或SSL/TLS section above.
// select the appropriate client, in this case SQS, and
// insert your region, instead of XXXX, which also improves startup time over the default client
client = SqsClient.builder().region(Region.XXXX).httpClient(software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient.builder().build()).build();
对于Maven,在你的 pom.xml
中添加以下内容。
<properties>
<aws.sdk2.version>2.10.69</aws.sdk2.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>${aws.sdk2.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>url-connection-client</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>apache-client</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<!-- sqs/sns/s3 etc -->
<artifactId>sqs</artifactId>
<exclusions>
<!-- exclude the apache-client and netty client -->
<exclusion>
<groupId>software.amazon.awssdk</groupId>
<artifactId>apache-client</artifactId>
</exclusion>
<exclusion>
<groupId>software.amazon.awssdk</groupId>
<artifactId>netty-nio-client</artifactId>
</exclusion>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>commons-logging-jboss-logging</artifactId>
</dependency>
</dependencies>
如果你看到 java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty 或类似的SSL错误,由于GraalVM的当前状态,有一些额外的工作来捆绑 function.zip ,如下所示。更多信息,请参见 Quarkus Native SSL指南 。
|
对客户端SSL的额外要求
本地可执行文件需要一些额外的步骤来启用S3和其他AWS库需要的客户端SSL。
-
一个自定义的
bootstrap
脚本 -
必须将
libsunec.so
添加到function.zip
中 -
必须将
cacerts
添加到function.zip
中
To do this, first create a directory src/main/zip.native/
with your build. Next create a shell script file called bootstrap
within src/main/zip.native/
, like below. An example is created automatically in your build folder (target or build), called bootstrap-example.sh
#!/usr/bin/env bash
./runner -Djava.library.path=./ -Djavax.net.ssl.trustStore=./cacerts
如果你的 cacerts
文件有密码保护,则额外设置 -Djavax.net.ssl.trustStorePassword=changeit
。
接下来,你必须将GraalVM发行版中的一些文件复制到 src/main/zip.native/
。
GraalVM versions can have different paths for these files whether you are using the Java 8 or 11 version. Adjust accordingly. |
cp $GRAALVM_HOME/lib/libsunec.so $PROJECT_DIR/src/main/zip.native/
cp $GRAALVM_HOME/lib/security/cacerts $PROJECT_DIR/src/main/zip.native/
现在,当你运行原生构建时,所有这些文件都将包含在 function.zip
中
如果你使用Docker镜像来进行构建,那么你必须从这个镜像中提取这些文件。 |
要提取所需的ssl,你必须在后台启动一个Docker容器,并附加到该容器来复制工件。
首先,让我们启动GraalVM容器,注意容器ID的输出。
docker run -it -d --entrypoint bash quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21
# This will output a container id, like 6304eea6179522aff69acb38eca90bedfd4b970a5475aa37ccda3585bc2abdde
# Note this value as we will need it for the commands below
First, libsunec.so
, the C library used for the SSL implementation:
docker cp {container-id-from-above}:/opt/graalvm/lib/libsunec.so src/main/zip.native/
Second, cacerts
, the certificate store. You may need to periodically obtain an updated copy, also.
docker cp {container-id-from-above}:/opt/graalvm/lib/security/cacerts src/main/zip.native/
你的最终档案将看起来像这样:
jar tvf target/function.zip
bootstrap
runner
cacerts
libsunec.so
使用容器镜像部署到AWS Lambda
AWS Lambda supports creating your lambdas by referencing container images rather than uploading ZIP files. This can have some benefits such as bypassing the size limit of the uploaded ZIP files. You can define lambda functions for both native builds and regular JVM builds.
JVM container image
对于正常的JVM发行版,你需要在官方AWS Java基础镜像之上建立你的镜像。下面是一个Dockerfile的例子,它将从你的Quarkus Lambda项目创建一个容器镜像。它假设已经执行了 mvn package
,并且二进制文件已存在于 target/
目录中:
FROM public.ecr.aws/lambda/java:11
ADD target/my-service-0.0.1-SNAPSHOT-runner.jar /var/task/lib/my-service.jar
ADD target/lib/ /var/task/lib/
CMD ["io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest"]
本地可执行的容器镜像
To create a lambda container image that uses the native executable we’ll need to do things a little differently. In this case, we won’t need to use the java:11
base image from AWS, but instead we’ll use a special image that assumes that the runtime environment for the lambda is provided. The example below creates such a container. It assumes that a Maven build has been executed (such as mvn package -Dnative=true
) and has generated the native binary into the target/
directory. The binary needs to be named bootstrap
and be placed in /var/runtime/
:
FROM public.ecr.aws/lambda/provided
ADD target/my-service-0.0.1-SNAPSHOT-runner /var/runtime/bootstrap
RUN chmod ugo+x /var/runtime/bootstrap
CMD ["io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest"]
部署一个容器镜像lambda
下面,你可以看到如何使用 docker
和 aws
命令行工具将上面创建的容器镜像构建并部署到AWS中。这些指令对原生和jvm容器镜像都适用,并假设已经登录到 aws
命令行工具。
构建Docker镜像
# Assuming we are located in the root directory of the project and created a Dockerfile there
docker build .
[output omitted]
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:[SOME SHA] 0.0s
使用你的ECR注册信息给镜像打上标签
docker tag [SOME SHA] [YOUR AWS ACCOUNT ID].dkr.ecr.[YOUR AWS ACCOUNT REGION].amazonaws.com/my/test/quarkus-lambda:v1
将Docker记录到你的ECR注册表中,并将Docker镜像推送到其中
aws ecr get-login-password --region region | docker login --username AWS --password-stdin [YOUR AWS ACCOUNT ID].dkr.ecr.[YOUR AWS ACCOUNT REGION].amazonaws.com
docker push [YOUR AWS ACCOUNT ID].dkr.ecr.[YOUR AWS ACCOUNT REGION].amazonaws.com/my/test/quarkus-lambda:v1
用AWS CLI工具创建AWS lambda函数
请确保你引用了之前上传的镜像(假设存在一个可以用来运行lambda的角色)。请注意,对于JVM lambda函数来说,默认的 128Mb
内存限制不足以运行该函数,这不是不可能的。在这种情况下,你可以在创建函数时通过向 aws lambda create-function
命令提供 --memory-size 256
参数来增加内存限制。你也可以在创建函数后,在AWS控制台中调整该函数。
aws lambda create-function --function-name my-test-quarkus-lambda-function --package-type Image --code ImageUri=[YOUR AWS ACCOUNT ID].dkr.ecr.[YOUR AWS ACCOUNT REGION].amazonaws.com/my/test/quarkus-lambda:v1 --role arn:aws:iam::[YOUR AWS ACCOUNT ID]:role/[SOME ROLE]
现在你可以使用AWS控制台来查看和测试你的新lambda函数。
亚马逊Alexa整合
要使用带有Quarkus原生的Alexa ,你需要使用托管在Quarkiverse Hub上的Quarkus Amazon Alexa扩展
<dependency>
<groupId>io.quarkiverse.alexa</groupId>
<artifactId>quarkus-amazon-alexa</artifactId>
<version>${quarkus-amazon-alexa.version}</version> (1)
</dependency>
1 | Define the latest version of the extension in your POM file. |
Create your Alexa handler, as normal, by sub-classing the abstract com.amazon.ask.SkillStreamHandler
, and add your request handler implementation.
That’s all there is to it!
SnapStart
To optimize your application for Lambda SnapStart, check the SnapStart Configuration Documentation.