AWS Lambda now supports container images (https://aws.amazon.com/jp/blogs/news/new-for-aws-lambda-container-image-support/).
It is quick and easy to use Base image provided by AWS to create a container image. However, as of January 15, 2021, Java 15 is not available.
The project I'm involved in is currently using Java 15 and ** 11 is a lot of trouble, so make a container image based on 15! ** Said, I thought it would be impossible for me ... I did some research and it worked, so I'll share it.
I will publish the source code as a deliverable immediately. I think it will work.
This is a sample that just returns 200 OK
.
intx24/java-15-lambda-container
Most of the Handler processing is borrowed from sample of awsdocs/aws-lambda-developer-guide. As a change, an unnecessary switch expression was added to confirm Java 15 compatibility.
Based on the Java 11 base image, let's move it first.
Thankfully, there was a related article, so I used it as a reference and created a Java 11-based Lambda image. I tried AWS Lambda container support in Java.
My boss said, "Let's refer to Dockerfile of Java 11 base image", so let's take a look.
You can see that we are ADDing a lot of tar.xz
that we don't understand.
You can unpack the tar.xz
file by doing git lfs pull
. (At first I didn't understand this and thought I was stuck)
Below, I will write what I understood
lambda-entrypoint.sh
ENTRY POINT of Dockerfile.
Apparently / var/runtime/bootstrap
is executed.
If there is no $ {AWS_LAMBDA_RUNTIME_API}
(probably in a local environment), it seems that the emulator can be used to enable local execution.
For the emulator, see Runtime support for Lambda container images ](Https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtimes-images.html#runtimes-api-client), so you can copy this to the container locally. is.
lambda-entrypoint.sh
#!/bin/sh
# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
if [ $# -ne 1 ]; then
echo "entrypoint requires the handler name to be the first argument" 1>&2
exit 142
fi
export _HANDLER="$1"
RUNTIME_ENTRYPOINT=/var/runtime/bootstrap
if [ -z "${AWS_LAMBDA_RUNTIME_API}" ]; then
exec /usr/local/bin/aws-lambda-rie $RUNTIME_ENTRYPOINT
else
exec $RUNTIME_ENTRYPOINT
fi
/var/runtime/bootstrap It was a file I didn't understand. It didn't work when I copied and pasted it into a container & I decided that the contents were too black box, so I will look for an alternative. Apparently you can create bootstrap by following the steps to create a custom runtime?
When I was researching the custom runtime and decided to give up because it was impossible,
I found out that I can execute Lambda function with aws-lambda-java-runtime-interface-client.
It seems that this command should be used as an argument of aws-lambda-rie
.
Dockerfile
I have an image of Amazon Linux 2 with Java 15, so I will base it on this.
Using your own lambda-entrypoint.sh
as ENTRYPOINT, which will be described later,
Pass the Lambda function example.Handler :: handleRequest
.
FROM amazoncorretto:15
# set environment variables
ENV CLASSPATH /var/task/*
WORKDIR /var/task
# copy lambda execution files
COPY aws-lambda-rie /usr/local/bin/aws-lambda-rie
COPY lambda-entrypoint.sh /lambda-entrypoint.sh
# Copy function code
COPY build/libs/java-15-lambda-container-1.0-SNAPSHOT-all.jar /var/task/
# Set the Entrypoint
ENTRYPOINT ["/lambda-entrypoint.sh"]
# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "example.Handler::handleRequest" ]
lambda-entrypoint.sh
Run aws-lambda-java-runtime-interface-client
.
The Lambda function specified in CMD
in the Dockerfile is passed to $ HANDLER_NAME
.
Pass java -cp ./* com.amazonaws.services.lambda.runtime.api.client.AWSLambda example.App :: sayHello
as an argument according to the README of aws-lambda-java-runtime-interface-client
When,
Replace aws-lambda-rie
with/usr/bin/java
because it will interpret java
as the path with bootstrap
and die.
Also, here the classpath has already been specified as an environment variable in the Dockerfile.
#!/bin/sh
if [ $# -ne 1 ]; then
echo "entrypoint requires the handler name to be the first argument" 1>&2
exit 142
fi
HANDLER_NAME=$1
if [ -z "${AWS_LAMBDA_RUNTIME_API}" ]; then
# local environment
exec /usr/local/bin/aws-lambda-rie \
/usr/bin/java \
com.amazonaws.services.lambda.runtime.api.client.AWSLambda "$HANDLER_NAME"
else
exec /usr/bin/java \
com.amazonaws.services.lambda.runtime.api.client.AWSLambda "$HANDLER_NAME"
fi
/var/runtime/bootstrap
It's no longer needed because it's integrated into lambda-entrypoint.sh
.
I had about a year of experience with java itself + I was touching it via intelliJ + I was a person who participated in a prototype project on the way java was recognized to work for the time being when the gradle task was executed.
I didn't even know that it couldn't be executed without passing through the classpath in the first place, and I was exhausted for about half a day with the Class Not Found
error.
For the same reason, I gave up on building a custom runtime in a minute.
run -it
optionAt first I didn't even know the concept of ENTRYPOINT
, so
When I tried to search the container with run -it
to check various files, I was in trouble.
As a result, you can do cat
via docker exec
,
I checked it in the Files tab of IntelliJ docker.
This time, I made a Lambda Container Image based on Java 15, and I was able to get it to work.
I spent about 4 days working on it, including investigation and implementation. It was an experience that I thought there was still much to know.
Recommended Posts