Choosing a packaging mechanism for Java based AWS Lambda functions

Packaging Java based AWS Lambda functions
PackagingApi architecture

1. Managed Runtime — Zip

task buildZip(type: Zip) {
from compileJava
from processResources
into('lib') {
from configurations.runtimeClasspath
}
}
build.dependsOn buildZip
  • We are using the managed AWS runtime environment
  • Simple tooling support available for both Gradle & Maven
  • We can avoid classpath and merging conflicts (see uber-jar)
  • We are limited to a total of 250MB in package size (unzipped)
  • Popular frameworks are usually providing an uber-jar (Micronaut, Spring) so we would have to add an additional build step to create the zip file

2. Managed Runtime — uber-jar

shadowJar {
archiveBaseName.set('lambda-uber')
transform(Log4j2PluginsCacheFileTransformer)
}
build.dependsOn shadowJar
  • We are using the managed AWS runtime environment
  • We are using a familiar packaging mechanisms that is used in popular frameworks with advanced tooling support (See Maven Shade plugin, Gradle ShadowJar)
  • We can use additional optimisation techniques such as minimizeJar to further reduce the package size.
  • Depending on the applications libraries we have to identify a merging strategy (See Log4j Transformer)
  • We are limited to a total of 250MB in package size (unzipped)

3. Container Runtime — Base Image

FROM --platform=linux/amd64 amazonlinux:2 as builderRUN yum -y update
RUN yum install -y java-11-amazon-corretto
ENV JAVA_HOME="/usr/lib/jvm/java-11-amazon-corretto.x86_64"
COPY ApiHandlers .
RUN ./gradlew clean build
FROM public.ecr.aws/lambda/java:11COPY --from=builder build/libs/lambda-uber-all.jar ${LAMBDA_TASK_ROOT}/lib/CMD [ "com.ilmlf.delivery.api.handlers.CreateSlots::handleRequest"]
  • We can use familiar container tooling support. Apart from AWS CDK we can also use the docker-cli directly to work with the images
  • AWS Lambda base images have a good caching strategy (Source)
  • We can create functions up to 10GB of package size vs. 250 MB on the zip / uber-jar approach.
  • We can still use the environment tools options to modify the runtime (JAVA_TOOLS_OPTIONS)
  • We do not need an additional Runtime interface client (See Custom Runtime)
  • We rely on a pre-defined image which gives us less flexibility on the environment configuration compared to a custom runtime
  • We can’t use Lambda Layers directly in the standard configuration but would need to work with them in the container image itself (See Working with Lambda layers and extensions in container images)
  • In contrast to the managed runtimes, we are now also billed for the initialisation time of the function which increases cost. In addition, charges for storing the image in Amazon ECR apply.

4. Container Runtime — Custom Image

implementation group: 'com.amazonaws', name: 'aws-lambda-java-runtime-interface-client', version: '2.1.0'
FROM openjdk:11-jdk-slim as builderCOPY ApiHandlers .
RUN ./gradlew clean build
FROM openjdk:11-jre-slimCOPY --from=builder build/libs/lambda-uber-all.jar .ENTRYPOINT [ "java", "-cp", "./*", "-XX:TieredStopAtLevel=1", "com.amazonaws.services.lambda.runtime.api.client.AWSLambda" ]
CMD [ "com.ilmlf.delivery.api.handlers.CreateSlots::handleRequest" ]
  • We can use our own internal base images which allows us to stick to corporate guidelines or compliance requirements
  • Depending on the image we are using, we might have a smaller container image size (For example: 103 MB with the openjdk:11-jre-slim image vs. 198 MB for the AWS Lambda base image — including the application)
  • We have great flexibility on configuring the environment
  • We can use the latest Java language versions that the managed runtime or base container images do not provide yet or apply our own patching cycle
  • We need to add the Custom Runtime Interface Client as an additional dependency and configure the entrypoint accordingly
  • We have to take care of patching and securing the custom image
  • Similar to the base image we can’t use the AWS Lambda Layers via configuration
  • We are also billed for the init duration of the function
  • AWS Lambda base images might have a better caching strategy (Source)

5. Custom Runtime

FROM --platform=linux/amd64 amazonlinux:2RUN yum -y update
RUN yum install -y java-11-amazon-corretto zip
ENV JAVA_HOME="/usr/lib/jvm/java-11-amazon-corretto.x86_64"
COPY ApiHandlers .
RUN ./gradlew clean build --no-daemon
RUN jdeps -q --ignore-missing-deps --multi-release 11 --print-module-deps \
build/libs/lambda-uber-all.jar > jre-deps.info
RUN jlink --verbose --compress 2 --strip-debug --no-header-files --no-man-pages --output /jre11-slim \
--add-modules $(cat jre-deps.info)
RUN /jre11-slim/bin/java -Xshare:dump# Package everything together into a custom runtime archive
WORKDIR /
RUN cp /resources/bootstrap bootstrap
RUN chmod 755 bootstrap
RUN cp /build/libs/lambda-uber-all.jar function.jar
RUN zip -r runtime.zip bootstrap function.jar /jre11-slim
#!/bin/sh
$LAMBDA_TASK_ROOT/jre11-slim/bin/java \
--add-opens java.base/java.util=ALL-UNNAMED \
-XX:+TieredCompilation \
-XX:TieredStopAtLevel=1 \
-XX:+UseSerialGC \
-cp function.jar com.amazonaws.services.lambda.runtime.api.client.AWSLambda "$_HANDLER"
  • We can use the latest Java versions that the managed runtime or base container images do not provide yet or apply our own patching cycle
  • We have great flexibility on configuring the environment
  • We can use the the zip file as deployment package and do not need a container registry
  • We can use the standard Lambda Layers configuration
  • We can distribute the custom runtime as a layer on its own
  • We are restricted by the 250 MB unzipped maximum package size limit
  • We have to take care of the bundling process on our own (This can be automated)
  • We need additional configuration files such as bootstrap.sh and the runtime interface client
  • We have to take care of patching and securing the custom runtime
  • We are also billed for the initialisation duration

Considerations & Outlook

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store