Lambda function using Docker Container Image -Part 1 How to develop and deploy Lambda function with Java (21) runtime

Vadym Kazulkin - Aug 26 - - Dev Community

Introduction

In this article I'd like explore how to develop and deploy Lambda function using Docker Container Image and Java runtime. In this article I'll use Java 21 Corretto runtime. But as with container images we can use even the recent Java runtimes like Java 22 and not only long-term support (LTS) Java runtimes like Java 17 and 21. It's easy to update this example when newer Java version will be released (like Java version 23 in September 2024).

How to develop and deploy Lambda function using AWS Lambda base Docker container image and Java (21) runtime

The AWS base images are preloaded with a language runtime, a runtime interface client to manage the interaction between Lambda and your function code, and a runtime interface emulator for local testing. For using an AWS base image for Java please refer to this article.

For the sake of explanation, we'll use our sample application and use Java 21 runtime for our Lambda functions.

Image description

In this application we'll create and retrieve products and use DynamoDB as the NoSQL database. You can find the DynamoProductDao.java implementation here. We also put Amazon API Gateway in front of it as defined in the AWS SAM template. In this template we also defined 2 Lambda functions : PutProductWithPureJava21DockerImage and GetProductByIdWithPureJava21DockerImage.

We need to define Dockerfile where we use AWS Lambda base Docker image public.ecr.aws/lambda/java:21 for Java 21 from the public Amazon ECR repository . Here you can find all Lambda base Docker images for Java provided by AWS.

In the Docker image we also copy function code and runtime dependencies from Maven layout to the runtime environment variable LAMBDA_TASK_ROOT – The path to your Lambda function code.



COPY target/classes ${LAMBDA_TASK_ROOT}
COPY target/dependency/* ${LAMBDA_TASK_ROOT}/lib/


Enter fullscreen mode Exit fullscreen mode

Everything else is defined in the Lambda Function declaration in the AWS SAM template.



 GetProductByIdFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: GetProductByIdWithPureJava21DockerImage
      PackageType: Image
      ImageUri: !Sub ${AWS::AccountId}.dkr.ecr.eu-central-1.amazonaws.com/aws-pure-lambda-java21-custom-docker-image:v1
      ImageConfig:
        Command: ["software.amazonaws.example.product.handler.GetProductByIdHandler::handleRequest"]


Enter fullscreen mode Exit fullscreen mode

The important pieces are:

  • PackageType: Image which says that we package our Lambda function as Docker container image
  • ImageUri: !Sub ${AWS::AccountId}.dkr.ecr.eu-central-1.amazonaws.com/aws-pure-lambda-java21-custom-docker-image:v1 which references the path of the Docker container Image in the Amazon Elastic Container Registry. More about how to push image there later. -ImageConfig: Command: ["software.amazonaws.example.product.handler.GetProductByIdHandler::handleRequest"] is overridden CMD to our GetProductById handler. As this CMD is unique for each Lambda function it's better to declare it on the level of the Lambda function as in the Docker file.

Now let's answer the question how to create and push Docker container image to the ECR repository. You can do it automatically then you execute sam build and sam deploy command chain or in case you build and package the application outside of the SAM and only deploy with SAM you need to di it manually like this



Build Docker image with name aws-pure-lambda-java21-custom-docker-image:

docker build --no-cache -t aws-pure-lambda-java21-custom-docker-image:v1 .

Login to Amazon ECR in case the credentials are expired:

aws ecr get-login-password --region eu-central-1 | docker login --username AWS --password-stdin {aws_account_id}.dkr.ecr.eu-central-1.amazonaws.com  

Create Amazon ECR repository if not exists with the name aws-pure-lambda-java21-custom-docker-image:

aws ecr create-repository --repository-name aws-pure-lambda-java21-custom-docker-image --image-scanning-configuration scanOnPush=true --region eu-central-1  

Tag our local Docker image and  link it with Amazon ECR repository. Exactly this image is referenced as ImageUri in the Lambda function:

docker tag aws-pure-lambda-java21-custom-docker-image:v1 {aws_account_id}.dkr.ecr.eu-central-1.amazonaws.com/aws-pure-lambda-java21-custom-docker-image:v1

Push the Docker image to the Amazon ECR repository:
docker push {aws_account_id}.dkr.ecr.eu-central-1.amazonaws.com/aws-pure-lambda-java21-custom-docker-image:v1 


Enter fullscreen mode Exit fullscreen mode

When deploying the function with sam deploy -g we need to additionally provide the Amazon ECR URL using the sam parameter option --image-repository, see sam deploy parameters.

How to develop and deploy Lambda function using AWS OS-only base image and Java (21) runtime

AWS OS-only base images contain an Amazon Linux distribution and the runtime interface emulator. These images are commonly used to create container images for compiled languages, such as Java and Rust, and for a language or language version that Lambda doesn't provide a base image for, such as Node.js 19. We can also use OS-only base images to implement a custom runtime.

If we'd like to use an AWS OS-only base image for Java, we must include the runtime interface client in our image or dependency management. The runtime interface client extends the Lambda runtime API, which manages the interaction between Lambda and your function code.

I provided sample application also for this use case.

I included AWS Lambda Java Runtime Interface Client in the pom.xml like this:



<dependency>
   <groupId>com.amazonaws</groupId>
   <artifactId>aws-lambda-java-runtime-interface-client</artifactId>
</dependency>


Enter fullscreen mode Exit fullscreen mode

Also Dockerfile looks a bit different in this case.
We used amazoncorretto:21-alpine as base Docker image and had to define entry point explicitly pointing to the entry class AWSLambda from AWS Lambda Java Runtime Interface Client.



ENTRYPOINT [ "/usr/bin/java", "-cp", "./*", "com.amazonaws.services.lambda.runtime.api.client.AWSLambda" ]


Enter fullscreen mode Exit fullscreen mode

All other things: SAM template, source code and application and docker image building and deployment steps remain the same as in AWS Lambda base Docker container image example.

Conclusion

In this article we explored how to develop and deploy Lambda function using Docker Container Image and Java runtime. We considered 2 use cases:

  • AWS Lambda base Docker container image
  • AWS OS-only base image

In the next article we'll measure cold and warms start times of the Lambda function using AWS Lambda base Docker container image.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .