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)

ecr.png

The lambda function

I navigate to AWS Lambda in the same AWS region and I create a new function

2-create-lambda.png

I select the option Container image and enter a name. Then, I press Browse images to locate the container image URI

3.select-container-image.png

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

4-test-function.png

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.