Deploy Wasm functions on Kubernetes with Simplism.
Part 1: How to serve Extism plug-ins on Civo with Simplism
With these blog posts (part 1 & part 2), I will explain how to start a kind of FaaS on Kubernetes. But let's start with a set of Q&A:
What is a FaaS? A FaaS (Function as a Service) platform is a service that enables the deployment and execution of functions without having to worry about the underlying infrastructure.
What is Simplism? Simplism is a highly lightweight application server that can serve WebAssembly plugins via HTTP as microservices (or even nanoservices) in a speedy and straightforward manner. Simplism is cloud provider-agnostic, allowing you to deploy it anywhere, from a simple Raspberry Pi Zero to Kubernetes. Simplism is developed with the Go SDK of Extism, and the WebAssembly plugins are developed with the various PDKs of Extism (you can develop the plugins in various languages).
The primary use cases of Simplism are
Functions
Webhooks
Nanoservices
What is Extism? Extism is a lightweight, open-source framework for building extensible applications using WebAssembly (WASM). It provides a simple and secure API for loading, executing, and interacting with WASM modules. This makes it easy to create applications that can be extended with custom code from different programming languages.
What is WebAssembly (well-known as WASM)? WebAssembly is a low-level, portable bytecode that can achieve near-native performance on the web. You can create WebAssembly programs using various languages, including Rust, Go, Zig, etc. The significant advantages of WebAssembly include efficiency, speed, security, and, of course, its multilingual nature.
What is Civo?Civo is a cloud infrastructure provider specialising in managed Kubernetes and edge computing. A significant advantage of Civo is that it is effortless to use. The user experience is excellent.
Prerequisites
Kubernetes
You need a Kubernetes Cluster. I use Civo, a Kubernetes as a Service provider. Of course, you can use any Kubernetes provider or do it locally (I will do another blog post on this topic). I wrote two blog posts that can help you if you are a beginner with Kube:
You need some tools:
K9S (it's not mandatory, but it's beneficial to manage a Kubernetes cluster): https://k9scli.io/
envsubst
to substitute the values of environment variables in a file. The install process is described here: https://command-not-found.com/envsubst
Before going further, create a project directory with this structure:
.
├── config
├── manifests
├── tmp
└── wasm-files
Once you get a Kubernetes running cluster, get the config file of this server and copy it into the config directory. Then, define the value of KUBECONFIG
and you can connect to it with the following command:
# in your project directory
export KUBECONFIG=$PWD/config/k3s.yaml
kubectl get pods --all-namespaces
You should get something like this:
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system civo-ccm-db67548d-cnxkk 1/1 Running 0 3m43s
kube-system civo-csi-node-xnc4f 2/2 Running 0 3m33s
kube-system coredns-59b4f5bbd5-lxbkw 1/1 Running 0 3m43s
kube-system metrics-server-7b67f64457-xmngb 1/1 Running 0 3m43s
kube-system civo-csi-controller-0 4/4 Running 0 3m44s
default install-traefik2-nodeport-wo-6vkwl 0/1 Completed 0 3m19s
kube-system traefik-f2hpb 1/1 Running 0 3m12s
If you installed K9S, you can use it with the following command:
export KUBECONFIG=$PWD/config/k3s.yaml
k9s --all-namespaces
✋ Important: For the rest, create a .env
vile with this content:
KUBECONFIG=$PWD/config/k3s.yaml
KUBE_NAMESPACE="simplism-faas"
DNS="ffb140f9-7479-4308-9763-9f70628794b1.k8s.civo.com"
IMAGE_NAME="k33g/simplism"
IMAGE_TAG="0.1.3"
DNS
is the domain name of your K8S cluster (remember, I'm using Civo.com, you will find this value into the dashbord of your cluster on Civo), set theDNS
variable with the information of your own cluster.
KUBE_NAMESPACE
is the namespace we will use in our cluster.
And now, we can start to play! 🎉
Deploy a Wasm Simplism function in a pod
wasm function == simplism plug-in
First, we need a manifest file:
wget https://github.com/bots-garden/simplism/releases/download/v0.1.3/deploy-wasm-from-remote.yaml -O ./manifets/deploy-wasm-from-remote.yaml
Deploy functions from remote files
Simplism can download and execute Wasm plug-ins from remote URLs.
There are some Simplism plug-ins available here for demonstration purposes:
https://github.com/simplism-registry/small-cow/releases/tag/v0.0.0
https://github.com/simplism-registry/small-ant/releases/tag/v0.0.0
Deploy a first function.
set -o allexport; source .env; set +o allexport
export SERVICE_NAME="small-cow"
export WASM_FILE="small-cow.wasm"
export WASM_URL="https://github.com/simplism-registry/small-cow/releases/download/v0.0.0/small-cow.wasm"
export FUNCTION_NAME="handle"
rm -f tmp/deploy.${SERVICE_NAME}.yaml
envsubst < manifests/deploy-wasm-from-remote.yaml > tmp/deploy.${SERVICE_NAME}.yaml
# Create namespace (if needed)
kubectl create namespace ${KUBE_NAMESPACE} --dry-run=client -o yaml | kubectl apply -f -
# Deploy
kubectl apply -f tmp/deploy.${SERVICE_NAME}.yaml -n ${KUBE_NAMESPACE}
You should see:
service/small-cow created
deployment.apps/small-cow created
ingress.networking.k8s.io/small-cow created
If you launch K9S (k9s --all-namespaces
), you should get this with the deployed pod (small-cow-5c85dc47ff-tsm7k
):
You can get the ingress of the service with this command:
kubectl describe ingress ${SERVICE_NAME} -n ${KUBE_NAMESPACE}
You should get something like this: simplism-faas.small-cow.d730feb5-db34-4e86-8e93-fb848e7c5808.k8s.civo.com
(the URL is composed with ${KUBE_NAMESPACE}.${SERVICE_NAME}.${DNS}
).
Now you can call the function:
curl http://${KUBE_NAMESPACE}.${SERVICE_NAME}.${DNS} -d '👋 Hello World 🌍'
# you should get:
^__^
(oo)\_______
(__)\ )\/\
||----w |
|| ||
👋 Hello World 🌍
Let's try another function
set -o allexport; source .env; set +o allexport
export SERVICE_NAME="small-ant"
export WASM_FILE="small_ant.wasm"
export WASM_URL="https://github.com/simplism-registry/small-ant/releases/download/v0.0.0/small_ant.wasm"
export FUNCTION_NAME="handle"
rm -f tmp/deploy.${SERVICE_NAME}.yaml
envsubst < manifests/deploy-wasm-from-remote.yaml > tmp/deploy.${SERVICE_NAME}.yaml
# Create namespace (if needed)
kubectl create namespace ${KUBE_NAMESPACE} --dry-run=client -o yaml | kubectl apply -f -
# Deploy
kubectl apply -f tmp/deploy.${SERVICE_NAME}.yaml -n ${KUBE_NAMESPACE}
You can get the ingress of the service with this command:
kubectl describe ingress ${SERVICE_NAME} -n ${KUBE_NAMESPACE}
Now you can call the function:
curl http://${KUBE_NAMESPACE}.${SERVICE_NAME}.${DNS} -d '✋ Hey people 🤗'
# you should get:
/\/\
\_\ _..._
(" )(_..._)
^^ // \\
✋ Hey people 🤗
When you are developing the WebAssembly plug-ins, you will want to be able to publish them directly from your workstation to the K8S cluster. Let's see how we can achieve this.
Deploy functions from local files.
In this case, we must create a Kubernetes volume to store the Wasm files. Then, we will copy the wasm files on this volume and deploy the Wasm function from the volume.
First, add this line to the .env
file:
VOLUME_SIZE="10Mi"
First, we need these manifest files:
wget https://github.com/bots-garden/simplism/releases/download/v0.1.3/wasm-files-volume.yaml -O ./manifets/wasm-files-volume.yaml
wget https://github.com/bots-garden/simplism/releases/download/v0.1.3/deploy-wasm-from-volume.yaml -O ./manifets/deploy-wasm-from-volume.yaml
We need some wasm files; let's download them from GitHub:
wget https://raw.githubusercontent.com/bots-garden/simplism/main/k8s/wasm-files/hello-world.wasm -O ./wasm-files/hello-world.wasm
wget https://raw.githubusercontent.com/bots-garden/simplism/main/k8s/wasm-files/small-cow.wasm -O ./wasm-files/small-cow.wasm
wget https://raw.githubusercontent.com/bots-garden/simplism/main/k8s/wasm-files/small_ant.wasm -O ./wasm-files/small_ant.wasm
Then, to create the volume:
set -o allexport; source .env; set +o allexport
rm -f tmp/create.wasm.files.volume.yaml
envsubst < manifests/wasm-files-volume.yaml > tmp/create.wasm.files.volume.yaml
# Create namespace (if needed)
kubectl create namespace ${KUBE_NAMESPACE} --dry-run=client -o yaml | kubectl apply -f -
# Deploy
kubectl apply -f tmp/create.wasm.files.volume.yaml -n ${KUBE_NAMESPACE}
You should see:
persistentvolume/pv-wasm-files-volume created
persistentvolumeclaim/pv-claim-wasm-files created
pod/wasm-files-store created
Now, we can copy a local WASM file to the storage volume:
kubectl cp ./wasm-files/hello-world.wasm ${KUBE_NAMESPACE}/wasm-files-store:wasm-files/hello-world.wasm
Let's check if the WASM file is in the storage volume:
kubectl exec -n ${KUBE_NAMESPACE} -it wasm-files-store -- /bin/sh
# Then at the prompt type:
ls wasm-files
# you should see: hello-world.wasm
Now it's time to deploy the Wasm function from the volume:
set -o allexport; source .env; set +o allexport
export SERVICE_NAME="hello-world"
export WASM_FILE="hello-world.wasm"
export FUNCTION_NAME="handle"
rm -f tmp/deploy.${SERVICE_NAME}.yaml
envsubst < manifests/deploy-wasm-from-volume.yaml > tmp/deploy.${SERVICE_NAME}.yaml
# Create namespace (if needed)
kubectl create namespace ${KUBE_NAMESPACE} --dry-run=client -o yaml | kubectl apply -f -
# Deploy
kubectl apply -f tmp/deploy.${SERVICE_NAME}.yaml -n ${KUBE_NAMESPACE}
You can get the ingress of the service with this command:
kubectl describe ingress ${SERVICE_NAME} -n ${KUBE_NAMESPACE}
Now you can call the function:
curl http://${KUBE_NAMESPACE}.${SERVICE_NAME}.${DNS} -d 'Bob Morane'
# you should get:
🤗 Hello Bob Morane
If you want to get the list of the deployed Wasm functions, you can use this command:
kubectl get ingress -l component=simplism-function --namespace simplism-faas
NAME CLASS HOSTS ADDRESS PORTS AGE
small-cow <none> simplism-faas.small-cow.d730feb5-db34-4e86-8e93-fb848e7c5808.k8s.civo.com d730feb5-db34-4e86-8e93-fb848e7c5808.k8s.civo.com 80 23m
small-ant <none> simplism-faas.small-ant.d730feb5-db34-4e86-8e93-fb848e7c5808.k8s.civo.com d730feb5-db34-4e86-8e93-fb848e7c5808.k8s.civo.com 80 17m
hello-world <none> simplism-faas.hello-world.d730feb5-db34-4e86-8e93-fb848e7c5808.k8s.civo.com d730feb5-db34-4e86-8e93-fb848e7c5808.k8s.civo.com 80 9m53s
Well, you see, that is not really complicated. Now, it would be nice if we could push the wasm files to a kind of registry and then deploy them to K8S from this registry.
It turns out that Simplism has a "registry" mode. That is to say that it can be used as a file server (it's basic but convenient). In Part 2 of this article, we will see how to deploy a “Simplism Registry” on Kubernetes and how to use it (you are not obliged to deploy the registry into Kubernetes; you can absolutely host it everywhere).
Stay tuned until the next episode. 👋
Btw, I created a project with all the files of this tutorial, and I "gitpodified" the project, so you don't need to install anything: https://github.com/bots-garden/simplism-on-k8s