When Ruby support on AWS Lambda was announced yesterday, I was so excited about it that I had to try it right away. We've been using Lambda for a while at Honeybadger, and I have longed to be able to write our functions in Ruby. Having played with the new Ruby support for a few hours, I'm feeling confident we'll be spending less time with Node, Go, and Python. :)

A Quick Example

While the announcement blog post from AWS can get you up and running quickly with Ruby on Lambda, I wanted to share an example of using the Serverless framework to deploy your Ruby Lambda functions. I've found this framework does a fantastic job of removing the annoyances of working with Lambda functions. While the current version (1.34) of the framework doesn't have template generation for Ruby projects ready yet (the PR was merged a couple of hours before I started writing this post), it's easy enough to get started without the generator. Here's a sample config file that I'll use for this walkthrough, which is a simple function that will populate a DynamoDB table with metadata associated with S3 objects when they are created:

400: Invalid request

The resources section of the config results in the automatic creation of a new DynamoDB table, and the name of the newly-created table is placed in the an environment variable for the function, which we'll use in the code. The function is triggered by SNS events for an SNS topic that has been created previously. That SNS topic receives notifications for PUT events on the S3 bucket, which is also created manually.

Here's the Ruby code:

400: Invalid request

The code doesn't do a whole lot -- it receives SNS events, loops through the records in the SNS messages to get the info about each S3 object that was created, performs a HEAD request on the S3 object to get the metadata, then puts (creates or updates) an item in the DynamoDB table. The only dependency for this function is the AWS SDK, which is included in the runtime environment, so you don't even need to worry about a Gemfile. However, if you do have some additional gems you'd like to bring to the party, that's easy to do. Let's take a look at how to include additional gems as we talk about deployment.

Deployment

Getting this code in its current state to Lambda is super easy, thanks to the Serverless framework:

sls deploy

That one command will do all of this for you:

  • Zip up your code
  • Put the zip file onto S3
  • Deploy the function in Lambda (complete with environment variables and SNS trigger)
  • Create the IAM policies
  • Create the DynamoDB table

Once the deployment is done, creating objects in the bucket will result in new items being added to the DynamoDB table.

Dependencies

Getting additional gem dependencies included with your deployed function is easy -- all you need to do is create a Gemfile, add the gems you want to bundle, and run bundle install --path vendor/bundle. This will bundle the gems into the local directory, and they will get zipped up and added to the function with the rest of your code.

The one caveat for dependencies, though, are gems that require some sort of C extension to be compiled. Since your development environment is most likely different from the Lambda runtime environment, you need to do a little more work to get those extensions compiled to work in AWS. Thanks to the lambci Docker images, though, and the Docker image I created on top of their Ruby image, it's not hard to do. To bundle your dependencies and have them compiled properly, just run my docker image, like so:

docker run --rm -v $(pwd):/var/task stympy/lambda-ruby2.5

All it does is run the bundler command line mentioned above, so the gems will be placed in vendor/bundle, ready to be included in the ZIP file that is created by the deploy command.

With that, you're off to the races. I hope you'll enjoy running Ruby in Lambda as much I have. :)