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.
To follow the instructions of this article, you must have the following in place:
kubectl
installed on your machine.helm
installed on your machine.spin
installed on your machine.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.
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.
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 login
command.
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.
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
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 SpinApp
custom 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 kubectl
commands 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.
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.
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:
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.