The webcontainers API.
It's Like Function as a Service (FaaS), but with 🐳. Instead of uploading a (NodeJS) function that can be executed by calling a unique URL, you run an executable (one-off) Docker Image, by making a HTTP POST request.
Wasabi ❤️ Kubernetes.
Why not? 😁
You create configurations, where each configuration specifies a Docker Image, that will run to completion each time the Wasabi API is called with the configuration name, where:
- A configuration is a k8s secret.
- The secret contains the Docker Image as a
base64
encoded data property. - The secret name is used as the URL parameter when calling the Wasabi API, to execute the desired Image.
- The Docker Image muste be a one-off executable; it must run to completion.
- To start the process of running the container, a HTTP POST request must be made to the
/jobs/<SECRET_NAME>
endpoint of the Wasabi API. - A JSON payload can be sent to provide Env Vars for the container.
- The entire JSON payload can be passed as an argument to the created container.
- A k8s Job is created inside the cluster on which Wasabi is installed, everytime the Wasabi API is called.
- The container runs to completion, inside the Pod that was created by the Job.
The Wasabi API is a simple Go HTTP API that can be installed on any k8s cluster using Helm.
First install the Wasabi API on your k8s cluster in a separate namespace by running:
> helm install ./charts/wasabi --namespace webcontainers --set wasabi.namespace=webcontainers --name wasabi
It's recommended to install Wasabi in it's own namespace.
This will create a service of type LoadBalancer
:
> kub get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
wasabi LoadBalancer 10.63.241.41 34.196.174.132 80:31878/TCP 18m
Use the external IP and send a GET request to the /health
endpoint, to see if Wasabi can receive
traffic:
> curl http://34.196.174.132/health
{"message":"OK"}
Creating a config, means creating a k8s secret, which must contain bas64
encoded data values. We
have to provide the Docker Image we wish to execute, for example danillouz/docker-say. This is a simple NodeJS script that prints passed
arguments to STDOUT, just for demo purposes. The source code can be viewed here.
Encode the Docker Image name:
> echo -n "danillouz/docker-say" | base64
ZGFuaWxsb3V6L2RvY2tlci1zYXk=
Create the k8s manifest file:
> cat <<EOF > docker-say.yaml
apiVersion: v1
kind: Secret
metadata:
name: webcontainers-config-docker-say
namespace: webcontainers
type: Opaque
data:
image: ZGFuaWxsb3V6L2RvY2tlci1zYXk=
EOF
Create the k8s secret:
> kubectl create -f docker-say.yaml -n webcontainers
secret "webcontainers-config-docker-say" created
Now we can use the secret name webcontainers-config-docker-say
, to execute the configured Docker
Image by sending a HTTP POST request to the jobs/webcontainers-config-docker-say
endpoint of the
Wasabi API:
curl -H "Content-Type: application/json" -X POST -d '{"data":"Hello World!"}' http://34.196.174.132/jobs/webcontainers-config-docker-say
{"UID":"990ce026-3cfb-11e8-bf98-42010a840020","message":"OK","name":"webcontainer-1523391193"}
This created Job webcontainer-1523391193
and if we inspect the logs of the container that was run,
we'll see:
> kubectl logs jobs/webcontainer-1523391193 -n webcontainers
say.js args: [ '{"config":{"image":"danillouz/docker-say"},"payload":{"data":"Hello World!"}}' ]
This single JSON string contains a config
property, which contains all data
properties specified
in the corresponding secret. And a payload
property, which contains the entire JSON payload that
was sent when making the HTTP POST request. The NodeJS script can print it by accessing process.argv
.
Some executable Docker Images don't accept arguments but are configured purely by passing Env Vars, for example slack-notify. We can still execute Images like this with Wasabi by sending the following JSON payload:
{
"containerEnvVars": [
{
"name": "SLACK_WEBHOOK",
"value": "https://hooks.slack.com/services/xxxxxxxxx/xxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxx"
},
{ "name": "SLACK_TITLE", "value": "Wasabi is alive!" },
{ "name": "SLACK_MESSAGE", "value": "Hello World!" },
{ "name": "SLACK_COLOR", "value": "#d1f1a9" }
],
"passContainerArg": false
}
By providing the property passContainerArg
with value false
, the JSON payload is NOT passed as
an argument.
At this stage Wasabi is just a proof of concept and I advise not to run it in production, yet. But it's awesome for experimentation and executing webhook.