[JAVA] Build AWS Lambda with Quarkus

Quarkus (https://quarkus.io/) is SUPERSONIC SUBATOMIC JAVA,

A Kubernetes Native Java stack tailored for OpenJDK HotSpot and GraalVM, crafted from the best of breed Java libraries and standards.

That's right. I don't know what you're saying, but it looks great.

Since it is possible to convert Java programs natively with the function of GraalVM, it can be combined with AWS Lambda's custom runtime to solve the ** cold start problem ** which is a characteristic of Java applications running on AWS Lambda. You can expect it.

I tried it according to the procedure on the official website, so I will leave the procedure.

QUARKUS - BUILDING A NATIVE EXECUTABLE https://quarkus.io/guides/building-native-image

QUARKUS - AMAZON LAMBDA https://quarkus.io/guides/amazon-lambda

environment

Deployment procedure of template application

Step 1. Amazon Linux 2 Preferences

Amazon Linux 2 of Docker image can not tar or ʻunzip`, so I will put various things. I don't know if I need all of them, but I've put in this much.

yum install -y sudo shadow-utils procps tar.x86_64 gzip xz unzip witch git python3 tree

Step 2. Install GraalVM

Download it from the official website and expand it.

curl -s -L -o /tmp/graalvm-ce-java11-linux-amd64-20.1.0.tar.gz https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-20.1.0/graalvm-ce-java11-linux-amd64-20.1.0.tar.gz
tar zxf /tmp/graalvm-ce-java11-linux-amd64-20.1.0.tar.gz -C /opt/
ln -s /opt/graalvm-ce-java11-20.1.0 /opt/graalvm

Set JAVA_HOME to GraalVM and use GraalVM to build with Maven.

export GRAALVM_HOME=/opt/graalvm
export JAVA_HOME=$GRAALVM_HOME
export PATH=$GRAALVM_HOME/bin:$PATH

Finally, install the native-image required for Native build. The command is gu (GraalVM Updater).

gu install native-image

Step 3. Install Maven

Maven used to build Quarkus requires version 3.6.2 or higher. The version that can be installed with yum was old, so I downloaded it from the official website and installed it.

curl -s -L -o /tmp/apache-maven-3.6.3-bin.tar.gz https://downloads.apache.org/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz
tar zxf /tmp/apache-maven-3.6.3-bin.tar.gz -C /opt/
ln -s /opt/apache-maven-3.6.3 /opt/apache-maven

maven version check

bash-4.2# mvn --version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: /opt/apache-maven
Java version: 11.0.7, vendor: GraalVM Community, runtime: /opt/graalvm-ce-java11-20.1.0
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "4.19.76-linuxkit", arch: "amd64", family: "unix"
bash-4.2# 

You can see that it is running on the GraalVM JVM.

Step 4. Create a project with Maven

Create a project with the mvn command.

mvn archetype:generate \
    -DarchetypeGroupId=io.quarkus \
    -DarchetypeArtifactId=quarkus-amazon-lambda-archetype \
    -DarchetypeVersion=1.6.0.Final

After a while, you will be asked a question at the prompt, so answer it. The part enclosed in [] is the user input.

Define value for property 'groupId': [myGroup]
Define value for property 'artifactId': [myArtifact]
Define value for property 'version' 1.0-SNAPSHOT: : []
Define value for property 'package' myGroup: : [example]
Confirm properties configuration:
groupId: myGroup
artifactId: myArtifact
version: 1.0-SNAPSHOT
package: example
 Y: : [Y]

A directory is created with ʻartifactId` (myArtifact) and a project is generated.

The project structure immediately after creation looks like this.

bash-4.2# tree myArtifact/
myArtifact/
├── build.gradle
├── gradle.properties
├── payload.json
├── pom.xml
├── settings.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── example
    │   │       ├── InputObject.java
    │   │       ├── OutputObject.java
    │   │       ├── ProcessingService.java
    │   │       ├── StreamLambda.java
    │   │       ├── TestLambda.java
    │   │       └── UnusedLambda.java
    │   └── resources
    │       └── application.properties
    └── test
        ├── java
        │   └── example
        │       └── LambdaHandlerTest.java
        └── resources
            └── application.properties

9 directories, 14 files
bash-4.2# 

Step 5. Setting the Handler to start with Lambda

The Handler called by Lambda is set in quarkus.lambda.handler of resources / application.properties.

resources/application.properties


quarkus.lambda.handler=test

With the above settings, the class named test below will be called.

java:main/java/example.TestLambda.java


@Named("test")
public class TestLambda implements RequestHandler<InputObject, OutputObject> {
}

The rest is coded like normal Lambda. In addition to test, stream is also available as a template.

Step 6. Create a deployment package

Let's create a deployment package with the template as it is.

mvn clean package -Pnative

It takes a lot of time. I think it will take more than 10 minutes.

Step 7. Check the contents of the deployment package

When the deploy package is successfully created, target / function.zip will be generated. When I tried to expand it, the contents were only bootstrap.

bash-4.2# unzip function.zip 
Archive:  function.zip
  inflating: bootstrap               
bash-4.2# 

Step 8. Deploy Lambda

A file called manage.sh is also generated in the target directory, and it seems that you can deploy it on AWS from here. I was new to the custom runtime, so I tried it from the management console. After creating the deployment package, a file called target / sam.native.yaml is also generated, so I referred to this for the handler name and environment variables.

Create a new function. Let's say the runtime is providing your own bootstrap.

ap-northeast-1.console.aws.amazon.com_lambda_home_region=ap-northeast-1(Laptop with MDPI screen).png

After creating the function, upload the program. Select Upload .zip file from Action in the function code and select function.zip.

ap-northeast-1.console.aws.amazon.com_lambda_home_region=ap-northeast-1(Laptop with MDPI screen) (1).png

The handler will be not.used.in.provided.runtime.

ap-northeast-1.console.aws.amazon.com_lambda_home_region=ap-northeast-1(Laptop with MDPI screen) (2).png

Add DISABLE_SIGNAL_HANDLERS to the environment variable and set the value to true.

ap-northeast-1.console.aws.amazon.com_lambda_home_region=ap-northeast-1(Laptop with MDPI screen) (4).png

The setting is completed up to this point. Let's test run with the following JSON as input.

ap-northeast-1.console.aws.amazon.com_lambda_home_region=ap-northeast-1(Laptop with MDPI screen) (3).png

{
  "name": "Bill",
  "greeting": "hello"
}

Here is the operation result. It worked safely.

ap-northeast-1.console.aws.amazon.com_lambda_home_region=ap-northeast-1(Laptop with MDPI screen) (5).png

Added AWS SDK

To use the AWS SDK, you need to make some settings, not just add it to pom.xml.

Step 1. Enable SSL communication

Add the following to resources / application.properties. (Although it seems that the official document says that it is valid by default)

resources/application.properties


quarkus.ssl.native=true

Step 2. Add dependency

First of all, quarkus-jaxb is required, so add this.

pom.xml


<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-jaxb</artifactId>
</dependency>

Next, I will add the AWS SDK library.

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

When translated `However, for native images, due to GraalVM compilation issues (currently), URL-connected clients should be prioritized over Apache HTTP clients when using synchronous mode. `` That's right. Therefore, the description is as follows.

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>
          <artifactId>s3</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>
          <version>1.0.0.Final</version>
      </dependency>
  </dependencies>

Step 3. Write Java code

In this example of accessing S3, when creating S3Client, explicitly specify ʻUrlConnectionHttpClient in httpClient`.

S3Client s3 = S3Client.builder()
  .region(Region.AP_NORTHEAST_1)
 
 .httpClient(software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient.builder().build())
  .build();

Step 4. Settings required for SSL communication

The deployment package must include the following for SSL communication:

First, create the src / main / zip.native / directory and create the bootstrap.

zip.native/bootstrap


#!/usr/bin/env bash

./runner -Djava.library.path=./ -Djavax.net.ssl.trustStore=./cacerts

Then copy libsunec.so and cacerts. These two files are included in GraalVM.

cp $GRAALVM_HOME/lib/libsunec.so $PROJECT_DIR/src/main/zip.native/
cp $GRAALVM_HOME/lib/security/cacerts $PROJECT_DIR/src/main/zip.native/

Step 5. Create a deployment package

The procedure for creating a deployment package remains the same.

mvn clean package -Pnative

Step 6. Check the contents of the deployment package

If you create a deployment package with SSL enabled, the contents of target / function.zip will change.

bash-4.2# unzip function.zip 
Archive:  function.zip
  inflating: bootstrap               
  inflating: cacerts                 
  inflating: libsunec.so             
  inflating: runner                  
bash-4.2# 

You can see that it contains the pre-prepared bootstrap``cacerts``libsunec.so.

At the end

Check here for verification results that cold start faster than the normal Java runtime.

Quarkus saves Java Lambda! ?? https://qiita.com/moritalous/items/4de31a66edac728ba088

Recommended Posts

Build AWS Lambda with Quarkus
AWS Lambda with Java starting now Part 1
Build an environment with Docker on AWS
Getting Started with Micronaut 2.x ~ Native Build and Deploy to AWS Lambda ~
How to use Java framework with AWS Lambda! ??
AWS Lambda timezone change
Quarkus saves Java Lambda! ??
Build Doma1 with Ant
Build Growai with Centos7
Build Java with Wercker
Build bazel with alpine
How to deploy Java to AWS Lambda with Serverless Framework
Build WordPress environment with Docker (Local) and AWS (Production)
Use Lambda Layers with Java
Notify Slack of AWS bills daily with Lambda for Ruby
Build GitLab / Mattermost with DockerForWindows
Build softether VPN with Centos7.
Regularly post imaged tweets on Twitter with AWS Lambda + Java
Create a SlackBot with AWS lambda & API Gateway in Java
Memoization recursion with lambda expression
Build docker environment with WSL
Build Ubuntu 18.04.5 with dual boot
[Introduction] Build MVC with Scala
Build DynamoDB local with Docker
With [AWS] CodeStar, you can build a Spring (Java) project running on Lambda in just 3 minutes! !!
Install gem in Serverless Framework and AWS Lambda with Ruby environment
Try AWS Lambda Runtime Interface Emulator with Docker Desktop for M1
Try running SlackBot made with Ruby x Sinatra on AWS Lambda
Create a periodical program with Ruby x AWS Lambda x CloudWatch Events
Until INSERT S3 object into EC2 DB with Lambda @ java: AWS
Java lambda expressions learned with Comparator
Build Couchbase local environment with Docker
Build a Java project with Gradle
Build a Node.js environment with Docker
Build a Tomcat 8.5 environment with Pleiades 4.8
Build PlantUML environment with VSCode + Docker
Build environment with vue.js + rails + docker
Is Java on AWS Lambda slow?
Hello World on AWS Lambda + Java
Build Rails environment with Docker Compose
Build a web application with Javalin
Java build with mac vs code
Build jooby development environment with Eclipse
Getting started with Java lambda expressions
Build docker + laravel environment with laradock
Run C binaries on AWS Lambda
Build WebRTC Janus with Docker container
Spring Boot gradle build with Docker
Things to keep in mind when using Apache PDFBox® with AWS Lambda
I tried to visualize the access of Lambda → Athena with AWS X-Ray
Build a development environment on AWS EC2 with CentOS7 + Nginx + pm2 + Nuxt.js