DEV Community

Thang Chung
Thang Chung

Posted on

SpinKube - the first look at WebAssembly/WASI application (SpinApp) on Kubernetes

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. And then they are also generous enough to submit it to CNCF as a sandbox project.

If you have followed me for enough long, then you will know I supported WebAssembly/WASI when it released the standard, check this repository 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, and look at what I thought about how WebAssembly/WASI fits with Kubernetes and Dapr in the picture below.

Image description

Some tweets by the WebAssembly/WASI community after that:

Image description

Image description

Image description

Image description

SpinKube architecture

Image description

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 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 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/).

> 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
Enter fullscreen mode Exit fullscreen mode

Install spin-operator into your Kubernetes cluster:

> 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

Let's install Dapr into this cluster as well.

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

Install Redis, because it needs for Dapr:

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

Then, install Dapr components into the cluster:

> 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
Enter fullscreen mode Exit fullscreen mode
> kubectl get components
NAME         AGE
pubsub       3d4h
statestore   3d4h
Enter fullscreen mode Exit fullscreen mode

Setup and deploy SpinApp (Sidecar with Dapr)

My setup project to start as below:

Image description

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

Now, let's deploy it.

> cd product-api
> spin registry push --build ttl.sh/coffeeshop-product-api-spin:24h
Enter fullscreen mode Exit fullscreen mode

Deploy product-api (SpinApp):

> 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
Enter fullscreen mode Exit fullscreen mode
> 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
Enter fullscreen mode Exit fullscreen mode

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.

> 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
Enter fullscreen mode Exit fullscreen mode

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
  }
]
Enter fullscreen mode Exit fullscreen mode

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 ❤️

Top comments (4)

Collapse
 
sohan26 profile image
Sohan

Loved the post Thang! Excellent stuff.

Collapse
 
thangchung profile image
Thang Chung

thanks, Sohan. How is your FOSSASIA presentation in Hanoi?

Collapse
 
sohan26 profile image
Sohan

I unfortunately had to decline the talk as I couldn't make it :(
Maybe next year 🤞

Collapse
 
tjhunkin profile image
tjhunkin

What issues does this stack resolve?