IBM Bluemix OpenWhisk 101: Developing a Microservice
IBM Bluemix OpenWhisk is an event-driven compute platform that enables developers to build chains of scalable microservices. It executes application logic in response to events or through direct invocations.
Earlier, we provided an overview of OpenWhisk’s architecture and components. This post shows how to develop a simple JavaScript application and deploy it to OpenWhisk. You will also learn about Docker integration.
Before we start
If you want to follow my code examples, you need to set up a couple of things first:
- IBM Bluemix account
- OpenWhisk early access
- Python and
pip
- OpenWhisk CLI
Because OpenWhisk was only recently announced, it is not publicly available yet. It took a couple of hours for my early access to be approved.
A “Goodbye, sir!” app
Below I show how to write some code and actually run it. While the whole world likes to greet each other using boring “Hello, World” stuff, we are going to do the opposite. A serious application doesn’t have time for such things, so it’s going to state that it’s busy and politely excuse itself.
The source code can be found on GitHub.
I’m going to use JavaScript here, but you can also work with Swift. To start, create the goodbye-sir
folder with goodbye-sir.js
:
function main() { return { payload: "I am pretty busy and can't talk right now. Goodbye, sir!" }; }
The only interesting part of this function is that it returns a JSON object with the payload
key. Once we send this function into the OpenWhisk service, it will become one of the remotely executable actions. As it turns out, OpenWhisk actions are required to return valid JSON. An important thing to note is that OpenWhisk expects every action to be defined in a single file. The file can contain several functions, but at least one of them should be named main
.
Now, we need to use the OpenWhisk CLI to actually deploy our code. In terms of OpenWhisk, the main
function will become an action. I like to automate stuff, so let’s write our deployment code into a separate script—deploy.sh
:
#!/bin/bash wsk action update goodbye-sir goodbye-sir.js
Note that wsk action update
creates a new action called goodbye-sir
if it is missing. Subsequent calls upload updated code.
And finally, let’s make the script executable and kick off the whole process:
$ chmod +x deploy.sh $ ./deploy.sh # => ok: updated action goodbye-sir
If you see something like this, you probably haven’t set up the OpenWhisk credentials:
$ ./deploy.sh # => usage: wsk action update [-h] -u AUTH [--docker] [--copy] [--sequence] # => [--lib LIB] [--shared [{yes,no}]] # => [-a ANNOTATION ANNOTATION] [-p PARAM PARAM] # => [-t TIMEOUT] [-m MEMORY] # => name [artifact] # => wsk action update: error: argument -u/--auth is required
Another common issue is that a fresh Bluemix account doesn’t have any application spaces in the default organization. The error looks similar to this:
$ ./deploy.sh # => error: The supplied authentication is not authorized to access this resource # => (code 323536)
In this case, follow the link and create a new space. Then, you can copy the OpenWhisk CLI configuration code from here. Note that it should have the name of your new space at the end of the command.
Now, we invoke our code with the OpenWhisk CLI:
$ wsk action invoke --blocking goodbye-sir # => ok: invoked goodbye-sir with id abfcdb37efe94b1a8f1807e76385027d # => response: # => { # => "result": { # => "payload": "I am pretty busy and can't talk right now. Goodbye, sir!" # => }, # => "status": "success", # => "success": true # => }
As you might have noticed, we’ve specified the --blocking
parameter. You can call OpenWhisk code asynchronously by simply omitting it. The next command will query OpenWhisk for the action result.
$ wsk action invoke goodbye-sir # => ok: invoked goodbye-sir with id 6cf984e1fbb74f69a54f07eec508f5e3 $ wsk activation result 6cf984e1fbb74f69a54f07eec508f5e3 # => { # => "payload": "I am pretty busy and can't talk right now. Goodbye, sir!" # => }
You can also parametrize your actions. Edit goodbye-sir.js
:
function main(params) { var name = params.name || "sir"; return { payload: "I am pretty busy and can't talk right now. Goodbye, " + name + "!" }; }
When you deploy and run the code, you should see something similar to:
$ ./deploy.sh # => ok: updated action goodbye-sir $ wsk action invoke --blocking goodbye-sir --param name Alex # => ok: invoked goodbye-sir with id 831a1fea3ac74ea4a06a1d05e5dd9822 # => response: # => { # => "result": { # => "payload": "I am pretty busy and can't talk right now. Goodbye, Alex!" # => }, # => "status": "success", # => "success": true # => }
And this basically wraps up our “Goodbye, sir!” application.
Docker included
Let me show you a neat trick.
$ wsk action create --docker example hello-world # => ok: created action example $ wsk action invoke --blocking --result example # => error: { # => … # => "logs": [ # => "2016-03-05T13:48:39.220147731Z stdout: ", # => "2016-03-05T13:48:39.220213883Z stdout: Hello from Docker.", # => "2016-03-05T13:48:39.220227211Z stdout: This message shows that your # => Docker installation appears to be working correctly.", # => … # => ], # => … # => "response": { # => "result": { # => "error": "the action did not produce a valid JSON response" # => }, # => "status": "action developer error", # => "success": false # => }, # => … # => }
As you’ve probably guessed by now, OpenWhisk can run arbitrary Docker images. Currently, support is limited to publicly available images from the Docker Hub.
Note that the execution failed because “the action did not produce a valid JSON response.” Why? OpenWhisk uses STDIN
and STDOUT
to talk to the running container. All incoming parameters come into STDIN
as a string representation of a JSON object. The same is true for STDOUT
—OpenWhisk expects correct JSON to be printed out as the result of an operation.
Anyway, it’s great to see that Docker actions are available. Developers can work with any language or framework they like and still benefit from using OpenWhisk.
Conclusion
OpenWhisk is fairly easy to use. In this post, we have seen how to utilize it to run isolated blocks of code. Note that OpenWhisk does not set any limitations on runtimes, so developers can start building language-agnostic microservices right away.
On the downside, there is an issue with performance. Although OpenWhisk is still experimental, I can’t help but notice how slow Docker actions are. It takes about 10 seconds to execute a “Hello, World” container.
At the same time, it is not true for JavaScript and Swift actions that work much more quickly.
Further reading: