Deploy Wasm functions on Kubernetes with Simplism.

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:

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 the DNS 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:

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