AWS Lambda function in Scala with Container Image
In this post I will describe how you can write your code in Scala, package it in a Docker Container, and run it serverless with AWS Lambda.
Scala project
First, create a Scala project using sbt as the build tool. I have created a github repository with the final result at the end of this post.
Creating a fat Jar
AWS Lambda does not offer a runtime for Scala. However, I can create a fat Jar and run it with the Java runtime. This works, because a fat Jar is built with all the project dependencies packaged in one big Jar, including all Scala libraries.
I install the sbt-assembly
plugin by adding the following line to project/plugins.sbt
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.0.0")
and in build.sbt
I set the filename of the fat Jar to something that does not depend on the project name or scala version
assembly / assemblyOutputPath := file("target/function.jar")
Now, I can create the fat jar by simply typing
sbt assembly
but there is no need to do this step manually, because I will do this within docker
Creating the handler
For the handler, I adapt the Java code from this example and I get the following.
package com.example
import java.util.{ Map => JavaMap }
import com.amazonaws.lambda.thirdparty.com.google.gson.GsonBuilder
import com.amazonaws.services.lambda.runtime.{Context, RequestHandler}
class LambdaHandler() extends RequestHandler[JavaMap[String, String], String] {
val gson = new GsonBuilder().setPrettyPrinting.create
override def handleRequest(event: JavaMap[String, String], context: Context): String = {
val logger = context.getLogger
logger.log(s"ENVIRONMENT VARIABLES: ${gson.toJson(System.getenv)}\n")
logger.log(s"CONTEXT: ${gson.toJson(context)}\n")
logger.log(s"EVENT: ${gson.toJson(event)}\n")
"Hello from Scala!"
}
}
Create the Dockerfile
I add a Dockerfile
at the root of the project
FROM mozilla/sbt as builder
COPY . /lambda/src/
WORKDIR /lambda/src/
RUN sbt assembly
FROM public.ecr.aws/lambda/java:11
COPY --from=builder /lambda/src/target/function.jar ${LAMBDA_TASK_ROOT}/lib/
CMD ["com.example.LambdaHandler::handleRequest"]
What I am doing here is the following
- I am using a multi-stage build to build the fat Jar with sbt
- Then I start using the official AWS docker image for lambda in Java
- I copy over the fat Jar only
- You will need to modify the
CMD
to the full name of your class and method
Build and publish the image
In what follows, you need to replace <aws-region>
and <aws-account-number>
with your selected region and your account number. I am assuming that you have installed the AWS CLI and have configured your credentials.
I create a repository in Amazon ECR called lambda-scala-example
aws ecr create-repository --region <aws-region> --repository-name lambda-scala-example
I login to ECR with docker
aws ecr get-login-password --region <aws-region> | docker login --username AWS --password-stdin <aws-account-number>.dkr.ecr.<aws-region>.amazonaws.com
I build and tag the image
docker build . -t <aws-account-number>.dkr.ecr.<aws-region>.amazonaws.com/lambda-scala-example
Finally, I push the image
docker push <aws-account-number>.dkr.ecr.<aws-region>.amazonaws.com/lambda-scala-example
I can now see the image in ECR in the AWS Management Console (in the selected region)
The lambda function
I navigate to AWS Lambda in the same AWS region and I create a new function
I select the option Container image
and enter a name. Then, I press Browse images
to locate the container image URI
I select the image, leave all optional settings as they are and I create the function
Test the function
I test the function and it runs successfully
Conclusion
If Scala is your preferred language, you can easily use it with Lambda functions to create serverless microservices.
The complete example project can be found in this github repository.