6 Steps To Run Spin Apps on Your Kubernetes Cluster

cover
27 Jun 2024

With open-source SpinKube, you can run serverless WebAssembly workloads (Spin Apps) natively on Kubernetes, leveraging well-known Kubernetes primitives such as Deployments and Pods. Although mastering Kubernetes is hard and time intensive, simplifying the developer and operation experience is always on our minds. That being said, this article provides six simple steps to run and invoke Spin Apps on Kubernetes.

Prerequisites

To follow the instructions of this article, you must have the following in place:

  • Access to a Kubernetes Cluster.
  • The Kubernetes CLI kubectl installed on your machine.
  • The Helm CLI helm installed on your machine.
  • The Spin CLI spin installed on your machine.
  • Language-specific tooling installed on your machine (different installation processes based on your preferred programming language; consult the language guides).

1. Deploy SpinKube

Deploying SpinKube to a Kubernetes cluster is pretty straightforward. The following script deploys SpinKube to the currently active kubect context:

#!/bin/bash

set -euo pipefail
clear
echo "SpinKube Deployment"
echo ""
echo "This script will deploy SpinKube to your current kubectl context"

# Parse arguments
SKIP_CERT_MANAGER=false

while [[ $# -gt 0 ]]; do
    case "$1" in
        --skip-cert-manager)
            SKIP_CERT_MANAGER=true
            echo "Installation of cert-manager will be skipped 💡"
            shift
            ;;
        *)
            echo "Unknown option: $1"
            exit 1
            ;;
    esac
done

# Function to check if an executable is installed and available in PATH
check_executable() {
    local executable="$1"
    if command -v "$executable" >/dev/null 2>&1; then
        echo " - $executable is installed and available in PATH. ✅"
    else
        echo " - $executable is not installed or not available in PATH."
        exit 1
    fi
}

# Function to ask the user whether to proceed or cancel the script
proceed_or_cancel() {
    kubectl config get-contexts
    read -p "This script will modify the current kubectl context!\\n Do you want to proceed? (yes/no): " response
    case "$response" in
    [yY] | [yY][eE][sS])
        echo "Proceeding with the script..."
        ;;
    *)
        echo "Script execution canceled."
        exit 1
        ;;
    esac
}

# Check prerequisites
echo "Checking Prerequisites:"
check_executable "kubectl"
check_executable "helm"

# Prompt user to proceed
proceed_or_cancel

# Install CRDs
echo "Installing CRDs..."
if [ "$SKIP_CERT_MANAGER" = false ]; then
    kubectl apply -f <https://github.com/cert-manager/cert-manager/releases/download/v1.14.3/cert-manager.crds.yaml>
fi
kubectl apply -f <https://github.com/spinkube/spin-operator/releases/download/v0.2.0/spin-operator.crds.yaml>
echo "CRDs Installed ✅"

# Install RuntimeClass and SpinAppExecutor
echo "Installing RuntimeClass and SpinAppExecutor..."
kubectl apply -f <https://github.com/spinkube/spin-operator/releases/download/v0.2.0/spin-operator.runtime-class.yaml>
kubectl apply -f <https://github.com/spinkube/spin-operator/releases/download/v0.2.0/spin-operator.shim-executor.yaml>
echo "RuntimeClass and SpinAppExecutor Installed ✅"

# Add Helm repositories and update repository feeds
echo "Adding Helm Repositories and Updating Feeds..."
if [ "$SKIP_CERT_MANAGER" = false ]; then
    helm repo add --force-update jetstack <https://charts.jetstack.io>
fi
helm repo add --force-update kwasm <http://kwasm.sh/kwasm-operator/>
helm repo add --force-update grafana <https://grafana.github.io/helm-charts>
helm repo update
echo "Helm Repositories Added and Feeds Updated ✅"

# Conditionally install cert-manager
if [ "$SKIP_CERT_MANAGER" = false ]; then
    echo "Installing cert-manager..."
    helm upgrade --install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.14.3
    echo "cert-manager Installed ✅"
else
    echo "Skipping cert-manager installation as per the provided flag."
fi

# Install Kwasm
echo "Installing Kwasm..."
helm upgrade --install kwasm-operator kwasm/kwasm-operator --namespace kwasm --create-namespace --set kwasmOperator.installerImage=ghcr.io/spinkube/containerd-shim-spin/node-installer:v0.14.1
echo "Kwasm Installed ✅"

# Annotating Kubernetes Nodes
echo "Annotating All Kubernetes Nodes..."
kubectl annotate node --all kwasm.sh/kwasm-node=true
echo "All Kubernetes Nodes Annotated ✅"

# Installing Spin Operator
echo "Installing Spin Operator..."
helm upgrade --install spin-operator --namespace spin-operator --create-namespace --version 0.2.0 --wait oci://ghcr.io/spinkube/charts/spin-operator
echo "Spin Operator Installed ✅"

echo "All Done"

SpinKube has only one dependency, which is cert-manager. If you've already deployed cert-manager to your cluster, you can skip its installation by providing the —skip-cert-manager flag upon executing the script above.

2. Create a Spin App

Next, let’s create a simple Spin App for demonstration purposes. The Spin CLI spin has many built-in templates for different programming languages. We’ll use the http-go template as part of this article. (Feel free to use another template if you want to.)

# Create a new Spin App
spin new -t http-go -a hello-spinkube

# Move into the Spin App directory
cd hello-spinkube

In the case of Go(lang), the implementation resists in main.go as shown here:

package main

import (
	"fmt"
	"net/http"

	spinhttp "github.com/fermyon/spin/sdk/go/v2/http"
)

func init() {
	spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/plain")
		fmt.Fprintln(w, "Hello Fermyon!")
	})
}

func main() {}

The generated Spin App and its implementation are more than enough for the sake of this article. However, feel free to modify the implementation if you want to.

3. Distribute the Spin App as an OCI artifact

Spin Apps are distributed as OCI artifacts that could be stored in any OCI-compliant container registry. We’ll use ttl.sh (an anonymous and ephemeral container registry) here. If you prefer using a private registry instead, you must authenticate your Spin CLI using the spin registry logincommand.


Use thespin registry push command in combination with the --build flag to compile your Spin App down to WebAssembly, package it and distribute it as an OCI artifact:

# Build, Package, Push the Spin App
spin registry push --build ttl.sh/hello-spinkube:12h

Building component hello-spinkube with `tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go`
Finished building all Spin components
Pushing app to the Registry..                                                                                                                                                             Pushed with digest sha256:d2e0ccc59e95ca6441017e8cf43ed587bebe9f1a37f66777d6e3c9231a43bd82

Notice the tag (12h) of the OCI artifact, indicating how long the artifact will remain in the ttl.sh registry.

4. Create Kubernetes Deployment Manifests

Having the OCI artifact of the hello-spinkube persisted in ttl.sh, we can move to the fourth step and generate the necessary Kubernetes Deployment Manifests (.yaml).

The spin kube scaffold command offers multiple flags that you can use to alter the Kubernetes Deployment Manifests upon creation. We’ll stick with the defaults for now and store the manifests in a spinapp.yaml file:

# Generate Kubernetes Deployment Manifests
spin kube scaffold --from ttl.sh/hello-spinkube:12h > spinapp.yaml

You can use cat to take a look at what spin kube scaffold generated for us:

# Show generated Kubernetes Deployment Manifests
# cat spinapp.yaml
apiVersion: core.spinoperator.dev/v1alpha1
kind: SpinApp
metadata:
  name: hello-spinkube
spec:
  image: "ttl.sh/hello-spinkube:12h"
  executor: containerd-shim-spin
  replicas: 2

5. Deploy the Spin App

Let’s deploy the Spin App to the Kubernetes cluster. Spin does not make any assumptions about deployment tooling. You can use whatever you’re comfortable with (kubectl, GitOps, Helm Charts…).

Using kubectl apply is the easiest way to deploy the Spin App to the Kubernetes cluster:

# Deploy the Spin App to the Kubernetes cluster
kubectl apply -f spinapp.yaml
spinapp.core.spinoperator.dev/hello-spinkube created

The spin-operator recognizes the new SpinAppcustom resource (CR) being deployed posted to the Kubernetes API server and will provision the necessary Kubernetes primitives.
To retrieve the list of Spin Apps in the default namespace, you can usekubectl get spinapps:

# List all Spin Apps in the default namespace
kubectl get spinapp

NAME             READY   DESIRED   EXECUTOR
hello-spinkube   2       2         containerd-shim-spin

To explore the underlying Kubernetes primitives, you can use the corresponding kubectlcommands shown below:

# List Deployments in the default namespace
kubectl get deployment

NAME             READY   UP-TO-DATE   AVAILABLE   AGE
hello-spinkube   2/2     2            2           26s

# List the Pods in the default namespace
kubectl get pod

NAME                              READY   STATUS    RESTARTS   AGE
hello-spinkube-7f446545db-rvhc5   1/1     Running   0          37s
hello-spinkube-7f446545db-9tt7r   1/1     Running   0          37s

# List the Services in the default namespace
kubectl get svc

NAME             TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
hello-spinkube   ClusterIP   10.43.175.34   <none>        80/TCP    48s

Additionally, you can use kubectl get ep to explore the endpoints assigned to the Kubernetes service (hello-spinkube) of the Spin App.

6. Call the Spin App

The easiest way to invoke the Spin App is by establishing port forwarding to one of the underlying Pods or the Service generated for the Spin App:

# Start port-forwarding
kubectl port-forward svc/hello-spinkube 8080:80

Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80

At this point, you can send HTTP requests to localhost:8080 which will be forwarded to port 80 of the hello-spinkube service in the default namespace of your Kubernetes cluster:

# Send an HTTP request to the Spin App
curl -iX GET <http://localhost:8080>

HTTP/1.1 200 OK
content-type: text/plain
content-length: 15
date: Mon, 03 Jun 2024 12:14:38 GMT

Hello Fermyon!

You can define Ingress routes and route requests from outside of the Kubernetes cluster to your Spin App.

Conclusion

As part of this article, we explored six simple steps (DCDCDC) you can follow to run your Spin Apps on Kubernetes leveraging open-source SpinKube:

  1. Deploy SpinKube
  2. Create a Spin App
  3. Distribute the Spin App as an OCI artifact
  4. Create Kubernetes Deployment Manifests
  5. Deploy the Spin App
  6. Call the Spin App

Once you have a simple Spin App running on Kubernetes, you can explore more advanced capabilities such as using ConfigMaps and Secrets, or leveraging horizontal scaling using HPA or KEDA.