# GraalVM with AWS Lambda - Starter

> **TL;DR**
> Deploy a GraalVM Native Image as a custom runtime on AWS Lambda. Build locally with Docker (AL2 + GraalVM), package as a ZIP with a `bootstrap` script, upload via the console, and test end-to-end.

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 will not 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:

```SHELL
.
├── bootstrap
├── function.sh
```

### Example Bootstrap

`bootstrap`

```SHELL
#!/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`

```SHELL
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:

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

Configure the Lambda as follows:

![Lambda configuration settings](img/lambda-1.png)

Set the correct entry point after creation:

![Lambda entry point configuration](img/lambda-2.png)

Finally, upload your zip file:

![Lambda zip file upload](img/lambda-3.png)

Congratulations, you are now running a custom runtime!

> **Tip:**
> References
>
>
>
> You can further separate the custom runtime from the Lambda by using Lambda layers. This is not covered in this article, but you can learn more about it in the [AWS Lambda Runtimes documentation](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html).

## 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`

```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:

```SHELL
docker build -t al2graalvm:1.0 .
```

> **Tip:**
> References
>
>
>
> This Dockerfile is a modified version of Mark Sailes's [al2-graalvm Dockerfile](https://github.com/marksailes/al2-graalvm/blob/main/x86/java17/Dockerfile).

### Run the Build on AL2

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

With Gradle:

```SHELL
gradle nativeCompile
```

Gradle will output the native image executable to:

```SHELL
/build/native/nativeCompile/
```

### Package Native Image

```SHELL
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:

```SHELL
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:

![Lambda function test result](img/lambda-4.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.

