Deploying Scala Jar Files into AWS Lambda

This blog post is a text-representation of a lightning talk I gave at a monthly meeting at work. A video recording is available here.

AWS Lambda is a product that allows you to upload code, configure a "trigger" for that code, and run the code in Amazon's infrastructure and be billed in 100ms increments for the compute resources.  Read more about it here.  In this article let's take a look at how we can put some Scala code into AWS.

Set Up Environment

  • Log into the AWS Management Console and navigate to IAM.  
  • Click Users->Find your username, and click on it (or create one)
  • Click Security Credentials
  • Click Create Access Key
  • Note the Access Key and Secret Access Key, you'll need these.
While you're here, let's make an execution role
  • Click Roles
  • Click Create New Roles
  • I'm going to use the name "lambda_basic_execution"
  • Choose "select" next to AWS Lambda
  • Create a policy
  • Find and select AWSLambdaBasicExecutionRole
Ok, that's enough AWS stuff.  Let's get your laptop environment going...


Install the AWS Command Line Interface.  On a mac with brew, I just typed "brew install awscli".

Now configure the aws command line interface:

$ aws configure AWS Access Key ID [****************PEPQ]: AWS Secret Access Key [****************+iVg]: Default region name [us-west-1]: Default output format [json]:

The Code

Next, we need to create a project.  Create a normal maven project, but be sure to include the following two additions.  A plugin:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.3</version> <configuration> <createDependencyReducedPom>false</createDependencyReducedPom> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> </execution> </executions> </plugin>
And a dependency:
<dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> <version>1.0.0</version> </dependency>

There is a poorly documented requirement that you return a specially formatted Java Map that represents the HTTP response.  This is necessary because not all AWS Lambda functions speak HTTP, but this one does.  The Map will be serialized using Jackson by Amazon, so we need to return Java POJO's and Java Collections.  Since we're writing Scala, things get a little ugly.  Here's a little helper package object to make that easier:
package object gatewayResponse { def response(response:ResponseCode, body:String, headers:Map[String, String] = Map()):java.util.Map[String, Object] = { mapAsJavaMap(Map( "statusCode"-> response.boxed(), "headers"-> mapAsJavaMap(headers).asInstanceOf[java.util.Map[java.lang.String, java.lang.String]], "body"-> body) ).asInstanceOf[java.util.Map[java.lang.String, java.lang.Object]] } case class ResponseCode(value:Int){ def boxed() = { Int.box(value) } } val OK = ResponseCode(200) }

And, finally, our lambda function:
package com.cj.demo import com.amazonaws.services.lambda.runtime.Context; import com.cj.lambda.gatewayResponse._ class Demo{ def myHandler(context:Context ) = { response(OK, "David Says Hello") } }

Deployment

To build the code, just run:
mvn package

And to deploy the lambda to AWS the first time, just run the following:
$ aws lambda create-function \ > --function-name scala-lambda-demo \ > --runtime java8 \ > --role arn:aws:iam::727586729164:role/lambda_basic_execution \ > --handler com.cj.demo.Demo::myHandler \ > --zip-file fileb://target/scala-lambda-demo-1.0.jar

Handler

One last one-time step we need to follow is to create an HTTP endpoint and link it to AWS.  Do the following:

  • Navigate once again to the AWS Console.
  • Navigate to the API Gateway
  • Click "Create API"
  • Give the API a name and click "Create API"
  • Create a resource, and then inside that resource create a method of type "get".
  • Integration type=lambda, use lambda proxy integration, make the region match your previously chosen region, and the lambda function you deployed should auto-complete when you start typing its name.  If it doesn't auto-complete, it means that you've either chosen the wrong region or that something went wrong when you used the aws command to create the function.


Now, click actions->deploy API and you should see the following screen:

On the next screen, you will be given your HTTP endpoint:

Just take that "Invoke URL" and append the resource name to the end, and it should take you to the results of running your Scala code.  If you see {"message":"Missing Authentication Token"}, don't forget to add the name of your resource to the URL!

That's it. You've create an HTTP endpoint and linked a "hello world" jar in AWS Lambda and written in Scala to that HTTP endpoint. If you want to add features to your application, just repeat the mvn package step and the use the following code to update the lambda function:
aws lambda update-function-code \ --function-name scala-lambda-demo \ --zip-file fileb://target/scala-lambda-demo-1.0.jar


Happy Coding!


1 comment