Iqbal´s DLQ Help

GraalVM with AWS Lambda - Starter

The goal of this guide is to demonstrate how Java can compete effectively in the serverless space. We will build a native executable using GraalVM's Native Image technology and deploy it on AWS Lambda.

Custom Lambda Runtimes

AWS Lambda supports managed runtimes for most prominent programming languages, including Java. However, in this tutorial, we won't use the AWS runtime for Java because the objective is to avoid running on the JVM.

AWS Lambda allows you to run custom runtimes directly on Amazon Linux 2 (AL2). This enables us to run a native binary, provided it is built for AL2 and the x86_64/arm64 architecture.

To deploy a custom runtime, you need to provide a zip archive containing a bootstrap file and the function being invoked:

. ├── bootstrap ├── function.sh

Example Bootstrap

bootstrap

#!/bin/sh set -euo pipefail # Initialization - load function handler source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh" # Processing while true do HEADERS="$(mktemp)" # Get an event. The HTTP request will block until one is received EVENT_DATA=$(curl -sS -LD "$HEADERS" "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next") # Extract request ID by scraping response headers received above REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2) # Run the handler function from the script RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA") # Send the response curl "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE" done

Example Function

function.sh

function handler () { EVENT_DATA=$1 echo "$EVENT_DATA" 1>&2 RESPONSE="Echoing request: '$EVENT_DATA'" echo $RESPONSE }

Package and Grant Permissions

Grant the necessary permissions and zip the files together:

chmod 755 function.sh bootstrap zip lambda.zip bootstrap function.sh

Configure the Lambda as follows:

img.png

Set the correct entry point after creation:

img.png

Finally, upload your zip file:

img.png

Congratulations, you are now running a custom runtime!

Compile GraalVM Native Image

Prepare Build Environment

GraalVM does not currently support cross-platform compilation. Therefore, you need to build your native executable on the same platform where the Lambda function will run, i.e., AL2 amd64.

We will use a custom AL2 Docker image that includes GraalVM for JDK 17, along with Gradle and Maven build tools.

Dockerfile

FROM amazonlinux:2 RUN yum -y update \ && yum install -y tar unzip gzip bzip2-devel ed gcc gcc-c++ gcc-gfortran \ less libcurl-devel openssl openssl-devel readline-devel xz-devel \ zlib-devel glibc-static libcxx libcxx-devel llvm-toolset-7 zlib-static \ && rm -rf /var/cache/yum ENV GRAAL_VERSION 22.3.0 ENV GRAAL_FOLDERNAME graalvm-ce-java17-${GRAAL_VERSION} ENV GRAAL_FILENAME graalvm-ce-java17-linux-amd64-${GRAAL_VERSION}.tar.gz RUN curl -4 -L https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-${GRAAL_VERSION}/${GRAAL_FILENAME} | tar -xvz RUN mv $GRAAL_FOLDERNAME /usr/lib/graalvm RUN rm -rf $GRAAL_FOLDERNAME # Graal Maven plugin requires Maven 3.3.x ENV MVN_VERSION 3.8.8 ENV MVN_FOLDERNAME apache-maven-${MVN_VERSION} ENV MVN_FILENAME apache-maven-${MVN_VERSION}-bin.tar.gz RUN curl -4 -L https://mirrors.ukfast.co.uk/sites/ftp.apache.org/maven/maven-3/${MVN_VERSION}/binaries/${MVN_FILENAME} | tar -xvz RUN mv $MVN_FOLDERNAME /usr/lib/maven RUN rm -rf $MVN_FOLDERNAME # Gradle ENV GRADLE_VERSION 7.4.1 ENV GRADLE_FOLDERNAME gradle-${GRADLE_VERSION} ENV GRADLE_FILENAME gradle-${GRADLE_VERSION}-bin.zip RUN curl -LO https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip RUN unzip gradle-${GRADLE_VERSION}-bin.zip RUN mv $GRADLE_FOLDERNAME /usr/lib/gradle RUN rm -rf $GRADLE_FOLDERNAME VOLUME /project WORKDIR /project RUN /usr/lib/graalvm/bin/gu install native-image RUN ln -s /usr/lib/graalvm/bin/native-image /usr/bin/native-image RUN ln -s /usr/lib/maven/bin/mvn /usr/bin/mvn RUN ln -s /usr/lib/gradle/bin/gradle /usr/bin/gradle ENV JAVA_HOME /usr/lib/graalvm ENTRYPOINT ["sh"]

Build the Docker image to serve as your build environment:

docker build -t al2graalvm:1.0 .

Run the Build on AL2

Use the AL2 GraalVM Docker image to compile a native image binary:

With Gradle:

gradle nativeCompile

Gradle will output the native image executable to:

/build/native/nativeCompile/

Package Native Image

cp ./build/native/nativeCompile/my-binary my-binary chmod 755 function.sh bootstrap my-binary zip lambda.zip bootstrap function.sh my-binary

Modify function.sh to call the binary:

function handler () { EVENT_DATA=$1 echo "$EVENT_DATA" 1>&2 RESPONSE="Echoing request: '$EVENT_DATA'" ./my-binary echo $RESPONSE }

Re-upload the newly created zip file and test your function:

img.png

This is IntelliJ's Java "Hello World" example.

Summary

This guide provides a quick setup for deploying GraalVM on AWS Lambda. In the next article, we will cover a real-world example using a Spring Boot application in future articles.

Last modified: 02 May 2025