For the longest time, the WebAssembly (Wasm) ecosystem has suffered from a lack of tooling that makes the process of deploying Wasm apps simple, in contrast to the container ecosystem, which provides a myriad of ways to build, deploy, and scale your applications.

In this tutorial, we will explore WebAssembly and how you run your WebAssembly workloads on the planet's most popular container orchestrator using Kwasm.

An Introduction to Wasm and Containers

Before we dive into the tutorial, I want to briefly dive into the concepts of WebAssembly and Containers to help you understand the difference between the two.

WebAssembly (Wasm) Originally conceived to enhance web applications, WebAssembly, often abbreviated as Wasm, is a binary instruction format designed for execution in web browsers. One of the standout features of WebAssembly is that it provides developers secure access to low-level APIs previously unavailable in browser environments.
Containers Containers are lightweight, portable units that package an application along with all its dependencies. They provide a consistent environment for running software across different platforms.

What is the difference between WebAssembly and Containers?

WebAssembly (Wasm) and containers may seem like competing technologies, but they serve different purposes and can complement each other in modern application architectures.

Aspect WebAssembly (Wasm) Containers
Primary Use Compilation target for applications, originally designed for the web. Packaging an application along with its dependencies and configuration into a single, portable unit.
Performance High-performance code running in the browser at near-native speed. Typically, it includes a full operating system, which can make them larger and potentially introduce more security vulnerabilities compared to Wasm modules.
Security Strong security features with sandboxed environment and capability-based security model. Potentially larger attack surface due to the inclusion of a full operating system.
Sandboxing Yes, runs in a sandboxed environment. No inherent sandboxing, relies on container isolation features.
Portability Lightweight, portable, and secure runtime for applications. Portable across different environments, but typically larger and include more dependencies.
Resource Access Capability-based security model restricts access to only explicitly granted resources. Generally, it has broader access to system resources, which can be controlled through container runtime configurations.

Do Wasm apps run as Containers?

Despite these differences, both WebAssembly and containers embrace the Open Container Initiative (OCI) standards for packaging and distribution. This means that while a WebAssembly module and a container image are fundamentally different, they can be packaged and distributed using the same tools and infrastructure.

Later in this post, we'll demonstrate how to run a WebAssembly application on Kubernetes as if it were a container. This is possible because both technologies adhere to OCI standards, allowing for integration into existing container workflows and orchestration platforms.

WebAssembly with Kwasm

Kwasm is an open-source project that enables users to run webassembly workloads on kubernetes, Kwasm takes care of the cumbersome process of bootstrapping nodes and provides custom resource definitions for creating a runtimeclass for running wasm applications.

Prerequisites

To follow along with this portion of the tutorial, you will need the following installed:

Launching a new Cluster

If you haven’t already, you will need to create a new Kubernetes cluster in order to follow along:

Create a 2-node cluster:

civo k3s create --create-firewall --nodes 2 -m --save --switch --wait kwasm

This will launch a 2 node Kubernetes cluster within your Civo account.

Installing Kwasm

Preparing your cluster to run WebAssembly typically involves a lengthy bootstrap process of ssh’ing into your nodes, installing the Wasm Runtime on worker nodes, and updating a few config files here and there thankfully, the Kwasm operator abstracts this process and allows us to install the operator with a single helm command.

Install Kwasm using Helm:

Add the Kwasm repo:

helm repo add kwasm http://kwasm.sh/kwasm-operator/

Install the Kwasm chart:

helm install -n kwasm --create-namespace kwasm-operator kwasm/kwasm-operator

Next, annotate the nodes you would like to run wasm workloads on. For the purpose of this demonstration, we will annotate all nodes in this cluster:

kubectl annotate node --all kwasm.sh/kwasm-node=true

Create a RuntimeClass

Next, we need to set up a RuntimeClass for our WebAssembly workloads. RuntimeClasses in Kubernetes allow us to specify different container runtimes for scheduling pods. This is crucial for running WebAssembly, as it requires a runtime different from that of traditional containers.

For those unfamiliar, a container runtime is the software responsible for running containers. In this case, we're setting up a special runtime that can handle WebAssembly modules instead of traditional container images:

echo '
  apiVersion: node.k8s.io/v1                                           
  kind: RuntimeClass
  metadata:
    name: wasmtime-spin
  handler: spin' | kubectl apply -f -

Creating a WebAssembly deployment

Now that we have our RuntimeClass set up, let's create a deployment for our WebAssembly application. We'll use a simple "hello world" example written in Rust and compiled into WebAssembly.

Here's the command to create our deployment:

echo '
  apiVersion: apps/v1                                                  
  kind: Deployment
  metadata:
    name: wasm-spin
  spec:
    replicas: 1
    selector:
      matchLabels:
        app: wasm-spin
    template:
      metadata:
        labels:
          app: wasm-spin
      spec:
        runtimeClassName: wasmtime-spin
        containers:
        - name: spin-hello
          image: ghcr.io/deislabs/containerd-wasm-shims/examples/spin-rust-hello:latest
          command: ["/"]
' | kubectl apply -f -

Remember, other than the runtimeClassName specification, this looks just like a regular Kubernetes deployment. We're using a pre-built WebAssembly module packaged as an OCI image. This image contains a simple "hello world" application written in Rust and compiled to WebAssembly.

Testing the deployment

To test the deployment, run the following command to port-forward the deployment we just created:

kubectl port-forward deployment/wasm-spin 8000:80

Make a request to the service using curl:

curl localhost:8000/hello

Your output should be similar to:

Hello world from Spin!

Closing thoughts on the WebAssembly ecosystem on Kubernetes

The WebAssembly ecosystem is undoubtedly maturing, but it's important to recognize that it's still in its early stages compared to more established technologies like containers. The journey to seamlessly integrate WebAssembly with Kubernetes has been an evolving one.

A few short years ago, a project called Krustlet aimed to solve the problem of running WebAssembly on Kubernetes using a Rust-based runtime. Krustlet has since been archived.

In this tutorial, we leveraged Kwasm, a more recent solution for running WebAssembly workloads on Kubernetes. However, it's crucial to note that the maintainers currently recommend against using it in production environments due to potential risks to nodes. That said, progress is being made, and some cloud providers are already offering support of which Civo is among the supported providers according to the Kwasm compatibility list.

If you're intrigued by the potential of WebAssembly on Kubernetes and want to dive deeper. Check out this post that explores the benefits in more detail into how WebAssembly can enhance performance in Kubernetes environments.

Looking to dive deeper into wasm on Kubernetes? The spin-operator project provides a more robust feature set and uses Kwasm under the hood. While the tooling for WebAssembly in Kubernetes environments may not yet be as user-friendly or mature as the container ecosystem, the rapid progress and growing interest in this technology are undeniable.