In this PoC, we show how you can use OpenFaaS and Krustlet to run WebAssembly functions on any Kubernetes cluster.
Why does it matter? Well, I'll be talking about this as part of the Serverless Practitioners Summit EU 2020.
See you online!
Talk is now available online! https://www.youtube.com/watch?v=so2dGQGKTpE
Demo code and setup instructions based on https://github.com/deislabs/krustlet
curl -sLS https://dl.get-arkade.dev | sudo sh
arkade get kind
- OpenFaaS CLI
arkade get faas-cli
- kubectl
arkade get kubectl
- Krustlet v0.3.0
Download the binary from https://github.com/deislabs/krustlet/releases/tag/v0.3.0
kind create cluster
Since this is an experiment, I'm using kind as my cluster. This allows me to create and tear down my cluster almost instantly, in case somethign goes wrong. That being said, this works on any Kubernetes cluster. Take a look at Krustlet's installation instructions for more info on this.
Krustlet is a tool to run WebAssembly workloads natively on Kubernetes. Krustlet acts like a node in your Kubernetes cluster. When a user schedules a Pod with certain node tolerations, the Kubernetes API will schedule that workload to a Krustlet node, which will then fetch the module and run it. We'll be using Krustlet to run our WebAssembly functions.
Open a second terminal window, and activate your kind's cluster Kubeconfig context.
kubectl cluster-info --context kind-kind
Clone the repository:
git clone https://github.com/rberrelleza/openfaas-plus-webassembly
Now, run the bootstrap script. This will create all the configurations and keys needed to be able to register the Krustlet node with your cluster.
./hacks/bootstrap-krustlet.sh
Set your context to the Krustlet's KUBECONFIG.
export KUBECONFIG=$HOME/.krustlet/config/kubeconfig
Get your local machine's IP (in my case, en0):
ifconfig en0
And finally, start the Krustlet.
krustlet-wascc --node-ip $IP --cert-file=$HOME/.krustlet/config/krustlet.crt --private-key-file=$HOME/.krustlet/config/krustlet.key --bootstrap-file=$HOME/.krustlet/config/bootstrap.conf --hostname krustlet
We are using
krustlet-wascc
so we can leverage its networking capabilities. At the time of writing,krustlet-wasi
was not able to open a network socket.
When the Krustlet starts, it won't long anything. It is waiting for a certificate to be approved on your cluster. To do it, go back to the terminal where you started the cluster, and run:
kubectl certificate approve krustlet-tls
Once the certificate is approved, you'll see the Krustlet start logging.
For this experiment, we are going to install OpenFaaS using arkade. We are going to set it without authentication to keep things simple. Don't do this in prod ;)
arkade install openfaas --clusterrole --basic-auth=false --operator --pull-policy Always --set serviceType=ClusterIP --wait
kubectl port-forward -n openfaas svc/gateway 8000:8080
With Krustlet, we now have a cluster that can handle both container as well as WebAssembly-based workloads. In order to tell OpenFaaS to schedule our WebAssembly functions in the Krustlet, we are going to use OpenFaaS' brand new profiles feature.
A profile is way of injecting platform-specific information to OpenFaaS functions. In this, case are going to use it to set the tolerations and taints required to ensure that the function runs in the Krustlet, instead of on a regular node.
kind: Profile
apiVersion: openfaas.com/v1
metadata:
name: wascc
namespace: openfaas
spec:
tolerations:
- key: "node.kubernetes.io/network-unavailable"
operator: "Exists"
effect: "NoSchedule"
- key: "krustlet/arch"
operator: "Equal"
value: "wasm32-wascc"
effect: "NoExecute"
Create the profile by running the command below:
kubectl apply -f hacks/profile-wascc.yaml
To create the function, we are going to use the following OpenFaaS manifest:
provider:
name: openfaas
gateway: http://127.0.0.1:8000
functions:
hello-world:
image: webassembly.azurecr.io/greet-wascc:v0.5
annotations:
com.openfaas.profile: wascc
com.openfaas.health.http.path: "/"
There's two big things to notice here:
- The
image
field points to a registry, just as if it was a container! WebAssemblies can be pushed and pulled from an OCI-compliant registry, allowing us to reuse a well-known distribution channel. For this case, we are using one of the pre-existing demos. - The
com.openfaas.profile: wascc
annotation. This is what tells OpenFaaS to attach the profile we created earlier to our function, which in turn tells Kubernetes to place the workload in the Krustlet (composition ftw!)
We run the usual deploy
command to deploy our function:
faas-cli deploy -f hello/hello.yaml
After a few seconds, the function will be up and running. You can check its state by running:
kubectl get pods -n=openfaas-fn
Finally, let's call the function, to see everything working end to end.
curl http://localhost:8080
$ Hello, world!
We are using
curl
instead offaas-cli invoke
due to current limitations in Kruslet's networking implementation. This should be fixed in the near future.
This PoC shows how it is possible to use Krustlet, Kubernetes and OpenFaaS to to build and deploy WebAssembly-based functions.
Writing functions using WebAssembly brings us several benefits, such as:
- The resulting binary is smaller, which results in faster pulls, and faster cold starts.
- We can write functions on any language, and compile them to a common target/platform, without the need of containers.
- WebAssembly's sandbox promises better safety and isolation features.
- The same binary will work on any platform, from clusters to smaller edge devices.
- waSCC Actor SDK gives us some of the abstractions that OpenFaaS' watchdog, specially around not having to deal with HTTP listener mechanics.
On the other side, OpenFaaS lets us build, publish, deploy and scale up/down our WebAssembly functions just as easily as container-based ones. And we can do it on any Kubernetes cluster.
It is true that there are certain limitations, but it's only a matter of time before they get implemented/fixed. As the Krustlet project matures, a lot of the challenges I encountered will simple go away:
- Functions have a fixed port, and are only available through the Node IP, since Krustlet hasn't implemented the full Kubernetes network stack
- I have to use a public repository, since Krustlet doesn't support pull secrets
- Building the wascc sample requires complex key management and signing binaries. And there's no way (today) for Krustlet to handle this dynamically.
- Building wascc assemblies and getting them to run in Krustlet is still a bit of a dark art
Interested in collaborating on this experiment? Let's talk!.