OpenWhisk recently announced the following changes to Docker-based Actions.
Developers can now deploy runtime files to the Action environment prior to invocation.
This makes it much easier to support (almost) any programming language in OpenWhisk. Awesome!
Let’s start by explaining how this new feature works…
Docker Actions
Docker Actions in OpenWhisk are built from the following repository using the python:2.7.12-alpine base image. This image is available on Docker Hub as openwhisk/dockerskeletion
.
The image includes a Python application which implements the HTTP API used to handle platform requests, e.g. invoke the action with these parameters.
This service executes a file (/action/exec
) for each invocation. Replacing this file allows us to control the runtime environment.
Request parameters are passed, using a JSON string, as the first command-line argument. Response values are interpreted as JSON written to stdout
.
Developers can now include a zip file when creating Docker-based Actions. This archive will be extracted into the /action
directory prior to invocations. If the archive contains a file named exec
this will replace the exectuable file called by the invocation handler.
Testing It Out
Using the wsk
command-line, developers can create Actions using this Docker image.
If the archive file is missing, the /action/exec
path contains the the following stub file.
$ wsk action create skeleton --docker openwhisk/dockerskeleton
ok: created action skeleton
$ wsk action invoke skeleton --blocking --result
{ "error": "This is a stub action. Replace it with custom logic." }
Let’s update this stub file to return a custom greeting.
$ cat exec
#!/bin/bash
echo "{ \"hello\": \"ran without a docker pull!\" }"
$ ./exec
{ "hello": "ran without a docker pull!" }
$ zip exec.zip exec
adding: exec (stored 0%)
$ wsk action create custom_docker_action exec.zip --docker
ok: created action custom_docker_action
$ wsk action invoke custom_docker_action --blocking --result
{ "hello": "ran without a docker pull!" }
The archive file could include a static binary, or even a complete runtime, to replace the exec
stub.
All files in the archive file will be available under the /action
directory.
Running Locally
The openwhisk/dockerskeleton
image exposes a Python-based HTTP server on port 8080.
Pulling the openwhisk/dockerskeleton
image from Docker Hub allows us to run it locally for development.
$ docker pull openwhisk/dockerskeleton
$ docker run -it -p 8080:8080 openwhisk/dockerskeleton
The platform uses the following HTTP endpoints to initialise and invoke Actions.
POST /init
-> Set up Action source from JSON payload.POST /run
-> Invoke Action
Initialising The Environment
Before invoking Actions using this image, we need to deploy and unpack the archive file into the /action
directory.
Reviewing the Python source code, the platform triggers this by sending a HTTP POST with the following JSON to /init
endpoint.
{
"value": {
"binary": true,
"code": "..."
}
}
code
contains the archive file as a base64 encoded string.
Let’s try this out using the action archive we created above.
$ base64 exec.zip | echo "\"$(cat)\"" | jq '{value: {binary: true, code: .}}' > init.json
$ cat init.json
{
"value": {
"binary": true,
"code": "UEsDBAoAAAAAAOlqMEr1+JNAQQAAAEEAAAAEABwAZXhlY1VUCQADRcl8WFDJfFh1eAsAAQT1AQAABBQAAAAjIS9iaW4vYmFzaAplY2hvICJ7IFwiaGVsbG9cIjogXCJyYW4gd2l0aG91dCBhIGRvY2tlciBwdWxsIVwiIH0iClBLAQIeAwoAAAAAAOlqMEr1+JNAQQAAAEEAAAAEABgAAAAAAAEAAADtgQAAAABleGVjVVQFAANFyXxYdXgLAAEE9QEAAAQUAAAAUEsFBgAAAAABAAEASgAAAH8AAAAAAA=="
}
}
Now we can issue the HTTP request to push this archive into the container.
$ http post localhost:8080/init < init.json
HTTP/1.1 200 OK
Content-Length: 2
Content-Type: text/html; charset=utf-8
Date: Mon, 16 Jan 2017 14:11:04 GMT
OK
Accessing the container filesystem allows us to verify the archive has been extracted correctly.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b37a7dc1cab1 openwhisk/dockerskeleton "/bin/bash -c 'cd ..." About an hour ago Up About an hour 0.0.0.0:8080->8080/tcp relaxed_davinci
$ docker exec -it b37a7dc1cab1 /bin/sh
/ # cd /action
/action # ls
exec
/action # cat exec
#!/bin/bash
echo "{ \"hello\": \"ran without a docker pull!\" }"
Invocation Requests
Action invocations are triggered by sending a HTTP POST to the /run
endpoint.
This endpoint expects the following JSON body.
{
"value": {
"foo": "bar"
}
}
The inner object parameters under the value
property are passed, as a JSON string, to the executable as the first command-line argument.
Sending this request to our container will trigger the shell script from our archive and return the JSON response.
$ echo "{}" | jq '{value: .}' | http post localhost:8080/run
HTTP/1.1 200 OK
Content-Length: 44
Content-Type: application/json
Date: Mon, 16 Jan 2017 14:17:15 GMT
{
"hello": "ran without a docker pull!"
}
Conclusion
Recent updates to Docker-based Actions in OpenWhisk make it much easier to customise the runtime environment.
Being able to deploy arbitrary files into the runtime container, prior to invocation, simplifies the process of supporting new runtimes.
Hopefully this blog post has shown you how to get started with this feature.
Over the next few weeks, we’re going to show you how to use this approach to run lots of new programming languages on the platform. Stay tuned for updates…