SpinKube - the first look at WebAssembly/WASI application (SpinApp) on Kubernetes
Tags: webassembly, serverless, tutorial, kubernetes
Published: 2024-03-27 11:51:55

# Introduction

Just a couple of days ago, Fermyon Labs published SpinKube, and it gained traction a lot in the community. And Fermyon guys promised to bring WebAssembly/WASI (Spin) to the Kubernetes ecosystem, and they did it. If you don't read about the really exciting news then please read it at [Introducing SpinKube and Fermyon Platform for Kubernetes](https://www.fermyon.com/blog/introducing-spinkube-fermyon-platform-for-k8s). And then they are also generous enough to submit it to CNCF as [a sandbox project](https://github.com/cncf/sandbox/issues/90).

If you have followed me for enough long, then you will know I supported WebAssembly/WASI when it released [the standard](https://github.com/WebAssembly/WASI), check [this repository](https://github.com/thangchung/webassembly-tour) for my work. And so not long after, I also experimented with Spin by Fermyon and ran it on Kubernetes with sidecar mode enabled (Dapr). And around last year, I felt the future of serverless computing was near after made Spin run on Kubernetes with Dapr enabled run very smoothly.

I came to the meet-up in Vietnam (Ho Chi Minh city) to talk about it in November last year. My topic was [`WebAssembly/WASI, Docker container, Dapr, and Kubernetes better together`](https://community.cncf.io/events/details/cncf-cloud-native-ho-chi-minh-presents-november-2023-meetup/), and look at what I thought about how WebAssembly/WASI fits with Kubernetes and Dapr in the picture below.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ffdj7x8mz3rnbqxtqv69.png)

Some tweets by the WebAssembly/WASI community after that:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dn5ox1gcq2n1ld4lg8ak.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hefgpx9qmv763tiwa2zq.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rqlg11twpnmopjdrwp3a.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4em5ehqc2mj5bww22ath.png)

## SpinKube architecture

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5kaly8wzfmxapbhsz1bf.png)

That is cool, right? We can publish the `wasm` file using the `OCI` standard, we don't need to put it into `Dockerfile` anymore, and from that, we can scaffold the `wasm` and deploy it into Kubernetes. It makes developer experience handy. And I think the guys at Fermyon, Microsoft Deis Labs, Liquid Reply and SUSE Corporation did excellent work here. Developers who developed the Wasm/Wasi application will benefit from that. Kudos to you, guys ❤️

Just a few days ago, when watching the news from [CNCF 2024 organized in France](https://www.cncf.io/blog/2024/03/21/kubecon-cloudnativecon-europe-2024-day-three-the-power-of-sustainable-computing/) just happened, and saw how SpinKube was announced in this event, I stumbled into their repository and read the docs as well as did some experiments and recognized that that is super easy to get started with SpinApp on Kubernetes 👀

Curiosity, I came to their document to find out if there is any way to run SpinApp with Daprizing App. I found the issue on GitHub at https://github.com/spinkube/documentation/pull/29, struggled to deep dive into the `spin-operator` code a bit, and found out the way to run `Dapr` on SpinApp, check it out https://github.com/spinkube/spin-operator/issues/192.

So I started to update my example code: [the `coffeeshop`](https://developer.fermyon.com/hub/preview/sample_coffee_shop_in_dapr_k3d) into the Component Model (blog it later), and try to run it on `SpinKube`.

This leads me to sum up what I did in this post below. Now let's get started...

## Setup SpinKube and Dapr

Setup `SpinKube` as the guidance below (or you can go to https://www.spinkube.dev/docs/spin-operator/quickstart/).

```sh
> kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.3/cert-manager.yaml
> kubectl apply -f https://github.com/spinkube/spin-operator/releases/download/v0.1.0/spin-operator.runtime-class.yaml
> kubectl apply -f https://github.com/spinkube/spin-operator/releases/download/v0.1.0/spin-operator.crds.yaml
> kubectl apply -f https://github.com/spinkube/spin-operator/releases/download/v0.1.0/spin-operator.shim-executor.yaml
```

Install `spin-operator` into your Kubernetes cluster:

```sh
> helm install spin-operator \
  --namespace spin-operator \
  --create-namespace \
  --version 0.1.0 \
  --wait \
  oci://ghcr.io/spinkube/charts/spin-operator
```

Let's install `Dapr` into this cluster as well.

```sh
# for dev local only otherwise you need to install the Dapr operator on your dev, staging or production environment
> dapr init -k
```

Install `Redis`, because it needs for Dapr:

```sh
> helm install my-redis oci://registry-1.docker.io/bitnamicharts/redis --set architecture=standalone --set global.redis.password=P@ssw0rd
```

Then, install Dapr components into the cluster:

```sh
> kubectl apply -f - <<EOF
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.redis
  version: v1
  metadata:
  - name: redisHost
    value: my-redis-master:6379
  - name: redisPassword
    value: "P@ssw0rd"
  - name: actorStateStore
    value: "true"
---
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: pubsub
  namespace: default
spec:
  type: pubsub.redis
  version: v1
  metadata:
  - name: redisHost
    value: my-redis-master:6379
  - name: redisPassword
    value: "P@ssw0rd"
EOF
```

```sh
> kubectl get components
NAME         AGE
pubsub       3d4h
statestore   3d4h
```

## Setup and deploy SpinApp (Sidecar with Dapr)

My setup project to start as below:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zmcv5fqhxyuyoji3yejz.png)

I write down the `product-api` with Rust on WebAssembly `Component Model` using Spin to run.

Now, let's deploy it.

```sh
> cd product-api
> spin registry push --build ttl.sh/coffeeshop-product-api-spin:24h
```

Deploy `product-api` (SpinApp):

```sh
> kubectl apply -f - <<EOF
apiVersion: core.spinoperator.dev/v1alpha1
kind: SpinApp
metadata:
  name: product-app
spec:
  image: "ttl.sh/coffeeshop-product-api-spin:24h"
  executor: containerd-shim-spin
  replicas: 1
  podAnnotations:
    dapr.io/enabled: "true"
    dapr.io/app-id: "product-app"
    dapr.io/app-port: "80"
    dapr.io/enable-api-logging: "true"
EOF
```

```sh
> kubectl get po
NAME                           READY   STATUS    RESTARTS      AGE
product-app-54d4798948-z55dz   2/2     Running   7 (55m ago)   3d4h
my-redis-master-0              1/1     Running   2 (55m ago)   3d4h
```

Look at the `2/2` in READY column above. It has 2 containers run on this deployment (one for `product-api`, and another one is daprd). It's very easy and cool, right?

Now make an ingress for it, then we are ready to go.

```sh
> kubectl apply -f - <<EOF
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: strip-prefix
spec:
  stripPrefix:
    forceSlash: false
    prefixes:
    - /p
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: wasm-ingress
  annotations:
    ingress.kubernetes.io/ssl-redirect: "false"
    traefik.ingress.kubernetes.io/router.middlewares: default-strip-prefix@kubernetescrd
spec:
  ingressClassName: traefik
  rules:
  - http:
      paths:
      - path: /p
        pathType: Prefix
        backend:
          service:
            name: product-app
            port:
              number: 80
EOF
```

Final step, we `curl` it:

```
> curl http://localhost:8081/p/v1-get-item-types | jq .
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   898    0   898    0     0   211k      0 --:--:-- --:--:-- --:--:--  292k
[
  {
    "image": "img/CAPPUCCINO.png",
    "item_type": "Cappuccino",
    "name": "CAPPUCCINO",
    "price": 4.5
  },
  {
    "image": "img/COFFEE_BLACK.png",
    "item_type": "CoffeeBlack",
    "name": "COFFEE_BLACK",
    "price": 3
  },
  {
    "image": "img/COFFEE_WITH_ROOM.png",
    "item_type": "CoffeeWithRoom",
    "name": "COFFEE_WITH_ROOM",
    "price": 3
  },
  {
    "image": "img/ESPRESSO.png",
    "item_type": "Espresso",
    "name": "ESPRESSO",
    "price": 3.5
  },
  {
    "image": "img/ESPRESSO_DOUBLE.png",
    "item_type": "EspressoDouble",
    "name": "ESPRESSO_DOUBLE",
    "price": 4.5
  },
  {
    "image": "img/LATTE.png",
    "item_type": "Latte",
    "name": "LATTE",
    "price": 4.5
  },
  {
    "image": "img/CAKEPOP.png",
    "item_type": "Cakepop",
    "name": "CAKEPOP",
    "price": 2.5
  },
  {
    "image": "img/CROISSANT.png",
    "item_type": "Croissant",
    "name": "CROISSANT",
    "price": 3.25
  },
  {
    "image": "img/MUFFIN.png",
    "item_type": "Muffin",
    "name": "MUFFIN",
    "price": 3
  },
  {
    "image": "img/CROISSANT_CHOCOLATE.png",
    "item_type": "CroissantChocolate",
    "name": "CROISSANT_CHOCOLATE",
    "price": 3.5
  }
]
```

# Conclusion

Now, we can use `SpinKube` to run any `SpinApp` workload, and it will be more effective because we can run some kind of polyglot workload in one Kubernetes cluster (container apps in the CNCF ecosystem and WASM/WASI together). I will continue to work on the `coffeeshop` to explore more possibilities in the next couple of weeks. There is far more than this awaiting you in my next posts.

Thanks for your reading ❤️