Ruby goes Serverless! - Apache OpenWhisk adds native support for Ruby

Ruby has finally become a first class citizen in the Serverless world now that Apache OpenWhisk has added native support for the Ruby Runtime. Thanks to the awesome work from Kei Sawada and the OpenWhisk community, Ruby has been added to the list of languages that are natively supported via OpenWhisk. The current list includes PHP, Go, Swift, Java, JavaScript, Python and Docker Containers.

If you combine Ruby with Serverless’ not having to care about scaling and managing infrastructure you have a combination that is truly optimized for developer happiness.

Running locally

There are many ways you can install Apache OpenWhisk locally or on your cluster. These instructions are for Docker Compose, but there are other options including Kubernetes, OpenShift, Mesos, Vagrant and more.

Installing via Docker Compose (Mac, Linux & Windows)

My favorite way to install OpenWhisk is via Docker Compose. In my opinion it’s the easiest since the other installations are geared more towards actual production environments and are therefore a little more involved. These following instructions are based on documentation from openwhisk.org.

Make sure you have Git, Docker & Docker Compose installed and then run the following commands.

$ git clone https://github.com/apache/incubator-openwhisk-devtools.git
Cloning into 'incubator-openwhisk-devtools'...
remote: Counting objects: 611, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 611 (delta 11), reused 17 (delta 9), pack-reused 589
Receiving objects: 100% (611/611), 266.75 KiB | 0 bytes/s, done.
Resolving deltas: 100% (232/232), done.
Checking connectivity... done.
$ cd incubator-openwhisk-devtools/docker-compose
$ make quick-start
Downloading source tar ball....
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 134 0 134 0 0 699 0 --:--:-- --:--:-- --:--:-- 701
100 10.4M 0 10.4M 0 0 4273k 0 --:--:-- 0:00:02 --:--:-- 5930k
Unpacking tarball.
...

You should get a response like this if your OpenWhisk environment is running well:

ok: whisk auth set. Run 'wsk property get --auth' to see the new value.
ok: whisk API host set to 192.168.143.239
ok: whisk namespace set to guest
waiting for the Whisk invoker to come up ...
creating the hello.js function ...
invoking the hello-world function ...
adding the function to whisk ...
ok: created action hello
invoking the function ...
invocation result: { "payload": "Hello, World!" }
{ "payload": "Hello, World!" }
creating an API from the hello function ...
ok: updated action hello
invoking: http://192.168.143.239:9090/api/.../hello/world
"payload": "Hello, World!"
ok: APIs
Action Verb API Name URL
/guest/hello get /hello http://192.168.143.239:9090/api/.../hello/world
deleting the function ...
ok: deleted action hello
To invoke the function again use: make hello-world
To stop openwhisk use: make destroy

Now that we have that running let’s install the wsk command line interface (cli) so we can start interacting with our OpenWhisk instance.

The easiest way to do that for Mac or Linux is via Homebrew (Mac) or Linuxbrew (Linux). Make sure you have one of these on your system and the we can use brew to install the cli:

$ brew install wsk
==> Installing wsk
==> Downloading https://homebrew.bintray.com/bottles/wsk-0.9.0-incubating.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring wsk-0.9.0-incubating.high_sierra.bottle.tar.gz
🍺 /usr/local/Cellar/wsk/0.9.0-incubating: 6 files, 11.4MB

If you are on Windows or you don’t want to use brew you can also download a binary release of wsk.

Great, now you have the wsk cli and have OpenWhisk running locally let’s start building some Serverless Ruby actions!

Let’s move to a different directory just to keep things clean.

$ mkdir ~/my-serverless-actions
$ cd ~/my-serverless-actions

Now we can create our action, let’s name the file hello_world.rb:

All you need to get your Serverless Ruby action working is a method named main that accepts one argument (that argument is going to be useful later when we start sending different parameters to the serverless action). And a hash as a response. This hash will get converted to JSON and will be the output of your action.

Before we can push the action to OpenWhisk we need to get authentication and the API host pointing to the right place. Thanks to our make quick-start command we now have a .wskprops file in our home directory which has all authentication keys and other properties needed to get our wsk command pointing in the right direction.

Now it’s time to push our Ruby action to our OpenWhisk instance:

$ wsk action create hello-world hello_world.rb -i
ok: created action hello-world

The -i flag we are using on all our wsk commands is necessary because Docker Compose creates self signed certificates. In production environments you won’t have to use the -i or --insecure flags because you’ll have a validate certificate in place.

Let’s try out our newly minted Ruby action by invoking it via the cli. There are different ways of invoking an action, the default is fire and forget. It’ll be invoked asynchronously and you be able to come back for the response later. But in our case let’s use the --result flag to make sure we get the response now.

$ wsk action invoke hello-world -i --result
{
"hello": "world"
}

We now have our very own Serverless action running natively in Ruby!

Let’s update our hello_world.rb to accept some parameters.

In this case we are looking for a name parameter and replacing it with “stranger” if we don’t pass anything in.

Now let’s push our updated action to OpenWhisk.

$ wsk action update hello-world hello_world.rb -i
ok: updated action hello-world

And let’s try running it the same way we did before:

$ wsk action invoke hello-world -i --result
{
"greeting": "Hello stranger!"
}

And now let’s pass in a name parameter:

$ wsk action invoke hello-world -i --result --param name World
{
"greeting": "Hello world!"
}

Awesome! That all you need to know to start creating some awesome serverless actions in Ruby! Checkout below for some bonus material on how to work with Ruby Gems.

Bonus material

Your actions don’t have to just be single files, you can also package multiple files into one action.

Using Gems

By default the OpenWhisk Ruby runtime comes with a number of Gems installed including jwt, mechanize and ActiveSupport.

You’d use those as you would in any other Ruby project: require "jwt".

To demonstrate the use of Ruby Gems in our project let’s create a Lorem Ipsum generator.

Before we start on this, let’s check to make sure you are running a Ruby version over 2.5.0 but below 2.6.0 (same major version as the OpenWhisk’s ruby:2.5 runtime).

$ ruby -v
ruby 2.5.1

If you aren’t getting 2.5.x use rbenv or rvm to switch to that version of ruby before you continue.

To start let’s create a new folder named lipsum.

$ mkdir lipsum
$ cd lipsum

First create a Gemfile to and specify which gems we want to use.

If you don’t have bundler installed you should do that now. Then run bundle with the standalone flag.

$ bundle install --standalone
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies...
Fetching betterlorem 0.1.2
Installing betterlorem 0.1.2
Using bundler 1.16.4
Bundle complete! 1 Gemfile dependency, 2 gems now installed.
Bundled gems are installed into `./bundle`

Then you need to create your action in main.rb. Since you’re going to be uploading multiple files this time OpenWhisk will expect the action’s main method to be in main.rb.

Now let’s grab all of the Ruby files we want as part of our action and let’s zip them up for easy distribution.

$ zip -r lipsum_generator.zip main.rb bundle
adding: main.rb (deflated 20%)
adding: bundle/ (stored 0%)
adding: bundle/bundler/ (stored 0%)
adding: bundle/bundler/setup.rb (deflated 32%)
adding: bundle/ruby/ (stored 0%)
...
adding: bundle/ruby/2.4.0/gems/betterlorem-0.1.2/doc/created.rid (deflated 61%)

If you get an error that your Ruby version is not correct please make sure you switch to ruby version 2.5.x.

Now we have that we can create our action in OpenWhisk, let’s name it lipsum.

$ wsk action create lipsum -i --kind=ruby:2.5 lipsum_generator.zip
ok: created action lipsum

Let’s now see if it works by invoking our action.

$ wsk action invoke lipsum -i --result --param length 20
{
"lipsum": "Pharisæorum, et sadducæorum, venientes ad baptismum suum, dixit eis Progenies viperarum, quis demonstravit vobis fugere a ventura ira? Facite ergo."
}

Nice! The only thing that had been missing from our APIs has been a Lorem Ipsum text generator and you totally fixed that, well done! Up next: Create an API to RickRoll people!? If you do I’m never gonna give you up!

Conclusion

You’ve been able to setup OpenWhisk locally. And you’ve been able to create a Serverless action in Ruby (my favorite programming language!). You didn’t need to use Docker or any bash injection hacks, because Apache OpenWhisk supports Ruby natively. You’ve been able to use gems to pull in dependencies and you’ve been able to distribute multiple files. Yay!

Personally I think Ruby is probably one of the best languages for Serverless, it’s compact, elegant and easy to read. If you combine Ruby with Serverless’ not having to care about scaling and managing infrastructure you have a combination that is truly optimized for developer happiness.

I’m looking forward to hearing what your experiences are with Ruby and OpenWhisk. Please let me know if you bump into anything confusing as we’ve recently relaunched the OpenWhisk website and are making sure OpenWhisk is as easy to use as possible.