Run WebAssembly on DigitalOcean Kubernetes with SpinKube - In 4 Easy Steps

Thorsten Hans - Mar 27 - - Dev Community

DigitalOcean is a cloud infrastructure provider catering to developers, offering scalable virtual servers, storage solutions, networking services, and managed Kubernetes clusters. It simplifies application deployment, management, and scaling through its intuitive user interface and CLI (doctl), allowing developers to efficiently utilize cloud resources for their projects.

As part of this article, you'll learn how to

  1. Provision a new Kubernetes cluster on DigitalOcean
  2. Deploy SpinKube
  3. Create a simple WebAssembly app using Spin 💫 and Rust 🦀
  4. Deploy the Spin App to Kubernetes using an OCI compliant registry

What is SpinKube

SpinKube is an open-source project that allows you to run WebAssembly workloads on top of Kubernetes. Essentially, SpinKube is a stack that consists of the following components:

In addition to running WebAssembly workloads side-by-side with containers, you can run workloads more efficiently and with higher density. Because of WebAssembly’s portability, you can run the same WebAssembly workload on different architectures to reduce cloud spendings even more by using - for example - arm64 worker nodes 🚀.

At KubeCon EU 2024 - The application for contributing SpinKube to the Cloud Native Computing Foundation (CNCF) has been filed. Click here to watch the keynote recording.

Prerequisites

To follow along this article, the following software must be installed on your local machine:

Although the DigitalOcean account itself is free, you have to provide payment information and you will be charged for allocating resources and renting services - such as the Managed Kubernetes Cluster we will create as part of this article.

1. Provision a Managed Kubernetes Cluster in DigitalOcean

Although the DigitalOcean Control Panel is an easy to grasp UI for provisioning cloud resources, we will use doctl CLI and its doctl kubernetes cluster create command to provision the managed Kubernetes cluster. Execute the following command, to provision a cluster consisting of two worker nodes:

# Variables
location=fra1
node_size=s-4vcpu-8gb-intel

# Create a new Kubernetes cluster
doctl kubernetes cluster create my-spinkube-cluster \
  --region $location \
  --count 2 \  
  --size $node_size \
  --set-current-context \
  --wait
Enter fullscreen mode Exit fullscreen mode

DigitalOcean provides different regions and machine sizes for managed Kubernetes clusters. Use doctl kubernetes options regions to get a list of all regions. By running
doctl kubernetes options sizes you get a list of all supported machine sizes for Kubernetes worker nodes.

Once the managed Kubernetes cluster is provisioned, we can double-check if the corresponding context has been added to kubectl and is marked as the current context:

# List all contexts of kubectlkubectl config get-contexts
CURRENT   NAME                  CLUSTER 
*         my-spinkube-cluster   my-spinkube-cluster 
          k3d-wasm-cluster      k3d-wasm-cluster    

# Set current context to my-spinkube-cluster (if not already)
kubectl config use-context my-spinkube-cluster
Switched to context "my-spinkube-cluster".
Enter fullscreen mode Exit fullscreen mode

2. Install SpinKube

On top of its core components, SpinKube depends on cert-manager. cert-Manager is responsible for provisioning and managing TLS certificates that are used by the admission webhook system of the Spin Operator. Let’s install cert-manager and KWasm using the commands shown here:

# Install cert-manager CRDs
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.crds.yaml

# Add Helm repositories jetstack and KWasm
helm repo add jetstack https://charts.jetstack.io
helm repo add kwasm http://kwasm.sh/kwasm-operator

# Update Helm repositories
helm repo update

# Install cert-manager using Helm
helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.14.4

# Install KWasm operator
helm install \
  kwasm-operator kwasm/kwasm-operator \
  --namespace kwasm \
  --create-namespace \
  --set kwasmOperator.installerImage=ghcr.io/spinkube/containerd-shim-spin/node-installer:v0.13.1
Enter fullscreen mode Exit fullscreen mode

KWasm will install containerd-shim-spin on Kubernetes nodes annotated with kwasm.sh/kwasm-node=true. For the sake of this article, we will annotate all nodes of our cluster using the following command:

# Annotate all Kubernetes nodes with kwasm.sh/kwasm-node=true
kubectl annotate node --all kwasm.sh/kwasm-node=true
Enter fullscreen mode Exit fullscreen mode

Finally, we can install the Spin Operator on the Kubernetes cluster along with necessary Custom Resource Definitions (CRDs), the RuntimeClass and the SpinAppExecutor:

# Install SpinKube CRDs
kubectl apply -f https://github.com/spinkube/spin-operator/releases/download/v0.1.0/spin-operator.crds.yaml

# Install a RuntimeClass for wasmtime-spin-v2
kubectl apply -f https://github.com/spinkube/spin-operator/releases/download/v0.1.0/spin-operator.runtime-class.yaml

# Install the containerd-spin-shim SpinAppExecutor
kubectl apply -f https://github.com/spinkube/spin-operator/releases/download/v0.1.0/spin-operator.shim-executor.yaml

# Install Spin Operator with Helm
helm install spin-operator \
  --namespace spin-operator \
  --create-namespace \
  --version 0.1.0 \
  --wait \
  oci://ghcr.io/spinkube/charts/spin-operator
Enter fullscreen mode Exit fullscreen mode

3. Create a Spin App

Having SpinKube installed on our managed Kubernetes cluster, we can build a simple Spin App for verification purposes. We will use the http-rust template and apply some small changes to the app generated by spin:

# Create a new Spin App
spin new -t http-rust --accept-defaults hello-do-k8s

# Move into the Spin App
cd hello-do-k8s
Enter fullscreen mode Exit fullscreen mode

Modify the implementation ( in ./src/lib.rs) to match the following:

use spin_sdk::http::{IntoResponse, Request, Response};
use spin_sdk::http_component;

#[http_component]
fn handle_hello_do_k8s(req: Request) -> anyhow::Result<impl IntoResponse> {
    println!("Handling request to {:?}", req.header("spin-full-url"));

    Ok(Response::builder()
        .status(200)
        .header("content-type", "text/plain")
        .body("Hello from DigitalOcean Kubernetes")
        .build())
}
Enter fullscreen mode Exit fullscreen mode

With this, we expect our Spin App to respond on incoming HTTP requests with an HTTP 200, a Content-Type of text/plain and payload of Hello from DigitalOcean Kubernetes.

Compile the Spin App using spin build and use spin up to test your app locally:

# Build the Spin App
spin build

## snip 
Finished release [optimized] target(s) in 8.92s
Finished building all Spin components

# Run the Spin App locally
spin up
Logging component stdio to ".spin/logs/"Serving http://127.0.0.1:3000
Available Routes:
  hello-do-k8s: http://127.0.0.1:3000 (wildcard)

# You can terminate the app at any time using CTRL+C
Enter fullscreen mode Exit fullscreen mode

You can test the Spin App locally, by sending an HTTP request to localhost:3000 from within a new terminal instance:

# Send a HTTP GET request to localhost:3000
curl -iX GET http://localhost:3000
HTTP/1.1 200 OK
content-type: text/plain
transfer-encoding: chunked
date: Tue, 25 Mar 2024 11:40:28 GMT
Hello from DigitalOcean Kubernetes
Enter fullscreen mode Exit fullscreen mode

4. Deploy the Spin App via OCI compliant registry

Spin Apps are distributed as OCI artifacts, meaning we can choose from many different container registries for distributing them. For demonstration purposes, we will use ttl.sh (an anonymous and ephemeral Docker image registry) in which the tag of an artifact defines how long the artifact remains available (TTL).

We use the spin registry push command for distributing Spin Apps through OCI compliant registries.

You can also use the spin registry login command to authenticate with private registries and distribute your Spin Apps without making them publicly available at all.

# Build the Spin App
spin build

# Publish the Spin App as OCI artifact
# The artifact will remain accessible for 24 hours (24h tag)
spin registry push ttl.sh/hello-do-k8s:24h
Pushing app to the
Pushed with digest sha256:b77b7cd7644be0b32613cbec1be5049eb96c7e536377165c4c08e1467c4087b2
Enter fullscreen mode Exit fullscreen mode

Finally, we deploy our Spin App to the managed Kubernetes cluster running on DigitalOcean. Again, we use the spin CLI to scaffold the necessary Kubernetes manifests and pipe them to kubectl which will interact with Kubernetes API server and provision the SpinApp custom resource (CR):

# Deploy Spin App to Kubernetes
spin kube scaffold -f ttl.sh/hello-do-k8s:24h | kubectl apply -f -
spinapp.core.spinoperator.dev/hello-do-k8s created
Enter fullscreen mode Exit fullscreen mode

Let’s use kubectl and take a closer look at what we got:

# List Spin Apps in the default namespacekubectl get spinapps
NAME            READY   DESIRED   EXECUTOR
hello-do-k8s    2       2         containerd-shim-spin

# List Deploymentskubectl get deployments
NAME            READY   UP-TO-DATE   AVAILABLE   AGE
hello-do-k8s    2/2     2            2           103s

# List Pods (wide)kubectl get pods -o wide
NAME                             READY   STATUS     AGE     IP
hello-do-k8s-665676d5bd-9gt4s    1/1     Running    2m14s   10.42.0.7
hello-do-k8s-665676d5bd-5fjpm    1/1     Running    2m14s   10.42.1.7

# List all Services and their Endpointskubectl get services,endpoints
NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/hello-do-k8s    ClusterIP   10.43.75.164    <none>        80/TCP    4m7s

NAME                      ENDPOINTS                   AGE
endpoints/hello-do-k8s    10.42.0.7:80,10.42.1.7:80   4m7s
Enter fullscreen mode Exit fullscreen mode

As you can see, the Spin Operator takes care of all the Kubernetes primitives for running and accessing our Spin App.

Testing the Spin App running on Kubernetes

Let’s configure port forwarding, to access the Spin App using curl from our local machine:

# Start port-forwarding
kubectl port-forward services/hello-do-k8s 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Enter fullscreen mode Exit fullscreen mode

Use a new terminal instance and send an HTTP request to locahost:8080 and verify receiving the expected response back:

# Send an HTTP GET request to localhost:8080
curl -iX GET http://localhost:8080
HTTP/1.1 200 OK
content-type: text/plain
transfer-encoding: chunked
date: Tue, 25 Mar 2024 11:48:21 GMT
Hello from DigitalOcean Kubernetes
Enter fullscreen mode Exit fullscreen mode

Cleaning up the Cloud Resources

To remove the cloud infrastructure we created as part of this article from your DigitalOcean account, run the following command:

# Delete the DigitalOcean managed Kubernetes cluster
doctl kubernetes cluster delete my-spinkube-cluster --force
Enter fullscreen mode Exit fullscreen mode

Conclusion

By deploying SpinKube onto your managed Kubernetes clusters in DigitalOcean, you can run WebAssembly workloads right beside containers. As WebAssembly apps are way smaller than containers, you can run more workloads on the same amount of compute resources. As you’ve learned in this article, the deployment of SpinKube is simple and requires only a couple of steps.

If you want to learn more about SpinKube, consider reading the SpinKube documentation. There are plenty of tutorials, like, for example:

. . . . . . . . . . . . . . . . . .