Deploy Atmo Wasm microservices to K3S on Civo.com thanks to Knative
Some weeks ago, I wrote a blog post explaining how to deploy an Atmo wasm microservice (from Suborbital) on a Civo Kubernetes cluster.
The post is here: #88
Today, this new post is very similar to the previous one, but we will greatly improve the developer experience with Knative Serving.
If I wanted to quickly define Knative, I would say it's a way to easily turn a Kube cluster into a single-tenant PaaS (or CaaS).
Once the setup is finished, you will deploy an Atmo service like that (without any yaml file):
kn service create atmo-service \
--namespace demo \
--env ATMO_HTTP_PORT="8080" \
--image registry.gitlab.com/k33g_org/discovering-atmo/deploy-atmo-on-civo-with-knative/atmo-services:0.0.0 \
--force
All the source codes are on a "gitpodified" project: https://gitlab.com/k33g_org/discovering-atmo/deploy-atmo-on-civo-with-knative
Requirements
To create a K3s cluster on Civo you'll need the Civo CLI:
brew tap civo/tools
brew install civo
🖐 You need to be registered on Civo and get a "Civo Key" to be able to use the Civo CLI
To connect to the cluster you'll need the kubectl
CLI, and, it's not mandatory, but K9s is a very convenient tool to manage a Kubernetes cluster:
brew install derailed/k9s/k9s
brew install kubernetes-cli
To create and build Atmo services, you'll need the subo
CLI:
brew tap suborbital/subo
brew install subo
And, at the end to deploy with Knative, you'll the kn
CLI:
brew install kn
Create a K3s cluster on Civo
We're going to create a large cluster and store the KUBECONFIG file to ./config/k3s.yaml
I tested the Knative install on a smaller cluster (it works)
CLUSTER_NAME="bigpanda"
CLUSTER_SIZE="g3.k3s.large"
CLUSTER_NODES=1
CLUSTER_REGION=NYC1
export KUBECONFIG=$PWD/config/k3s.yaml
Store the value of the Civo key in a variable (CIVO_API_KEY
) and run the below command to create a K3s cluster in less than a minute:
civo apikey add civo-key ${CIVO_API_KEY}
civo apikey current civo-key
civo kubernetes create ${CLUSTER_NAME} --size=${CLUSTER_SIZE} --nodes=${CLUSTER_NODES} --region=${CLUSTER_REGION} --remove-applications Traefik --wait
🖐 ️ Knative provides its own network layer, so we need to install K3S without Traefik
Get the KUBECONFIG file and save it `./config/k3s.yaml``
civo --region=${CLUSTER_REGION} kubernetes config ${CLUSTER_NAME} > ./config/k3s.yaml
Check if you can connect
Type the below commands:
export KUBECONFIG=$PWD/config/k3s.yaml
kubectl get pods --all-namespaces
You should get something like that:
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-854c77959c-579kv 1/1 Running 0 14m
kube-system local-path-provisioner-7c458769fb-72frb 1/1 Running 0 14m
kube-system civo-csi-controller-0 3/3 Running 0 14m
kube-system metrics-server-86cbb8457f-lfljp 1/1 Running 0 14m
kube-system civo-csi-node-mnlrx 2/2 Running 0 14m
Or, even better, use K9s:
export KUBECONFIG=$PWD/config/k3s.yaml
k9s --all-namespaces
And now, we are going to install Knative.
Install Knative
The install of Knative is pretty straightforward:
export KUBECONFIG=$PWD/config/k3s.yaml
export KNATIVE_VERSION="1.1.0"
# Install the Custom Resource Definitions (aka CRDs):
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v${KNATIVE_VERSION}/serving-crds.yaml
# Install the core components of Serving:
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v${KNATIVE_VERSION}/serving-core.yaml
kubectl apply -f https://github.com/knative/net-kourier/releases/download/knative-v${KNATIVE_VERSION}/kourier.yaml
kubectl patch configmap/config-network \
--namespace knative-serving \
--type merge \
--patch '{"data":{"ingress-class":"kourier.ingress.networking.knative.dev"}}'
# ====== wait ... ======
kubectl wait --for=condition=Ready pod -l app=svclb-kourier -n kourier-system
kubectl wait --for=condition=available deployment/activator -n knative-serving
kubectl wait --for=condition=available deployment/autoscaler -n knative-serving
kubectl wait --for=condition=available deployment/controller -n knative-serving
kubectl wait --for=condition=available deployment/webhook -n knative-serving
# ======================
# Check
# Fetch the External IP address or CNAME
kubectl --namespace kourier-system get service kourier
# Configure DNS (Magic DNS sslip.io)
# Knative provides a Kubernetes Job called default-domain that configures Knative Serving to use sslip.io as the default DNS suffix.
# sslip.io is a DNS (Domain Name System) service that, when queried with a hostname with an embedded IP address, returns that IP address.
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v${KNATIVE_VERSION}/serving-default-domain.yaml
# ====== wait ... ======
kubectl wait --for=condition=complete job/default-domain -n knative-serving
# ======================
kubectl get pods --namespace knative-serving
# Knative also supports the use of the Kubernetes Horizontal Pod Autoscaler (HPA) for driving autoscaling decisions.
# The following command will install the components needed to support HPA-class autoscaling:
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v${KNATIVE_VERSION}/serving-hpa.yaml
Create and test an Atmo project and add a wasm service
Creating an Atmo project is very easy:
subo create project services
cd services
rm -rf .git # we're already in a git repository
A demo "helloworld" service (aka "runnable") is automatically generated in the project. The code is simple, and it's enough for our experiments:
👀 /services/helloworld/src/lib.rs
use suborbital::runnable::*;
struct HelloWorld{}
impl Runnable for HelloWorld {
fn run(&self, input: Vec<u8>) -> Result<Vec<u8>, RunErr> {
let in_string = String::from_utf8(input).unwrap();
Ok(String::from(format!("hello {}", in_string)).as_bytes().to_vec())
}
}
🖐 the route to reach the service is defined in/services/Directive.yaml
handlers:
- type: request
resource: /hello
method: POST
steps:
- fn: helloworld
Build and serve (in developer mode)
Build and package the wasm service:
subo build .
This will produce a bundle named
runnables.wasm.zip
that contains your wasm services (just only one in your case)
Run a development Atmo server to serve the bundle:
subo dev
Call the service:
curl localhost:8080/hello -d 'Jane'
Embed the bundle in a container (and push to a container registry)
You can find a Dockerfile in the services
directory. So, we're going to use it to embed the "runnables" bundle in a container.
🖐 I'm using the GitLab container registry, but it works with any registry of course.
Login to the registry
docker login registry.gitlab.com -u k33g -p ${GITLAB_TOKEN_ADMIN}
# k33g is my GitLab handle
# I use a personal web token to authenticate to the registry
# and I store it in `GITLAB_TOKEN_ADMIN`
Build and push the image
cd services
docker build -t atmo-services . # (🖐 don't forget the `.` at the end of the command)
docker tag atmo-services registry.gitlab.com/k33g_org/discovering-atmo/deploy-atmo-on-civo-with-knative/atmo-services:0.0.0
docker push registry.gitlab.com/k33g_org/discovering-atmo/deploy-atmo-on-civo-with-knative/atmo-services:0.0.0
You can read more about the GitLab container registry here: https://docs.gitlab.com/ee/user/packages/container_registry/
Serve it with Docker
Now, we can serve the service with Docker:
docker run -e ATMO_HTTP_PORT=8080 -p 8080:8080 registry.gitlab.com/k33g_org/discovering-atmo/deploy-atmo-on-civo-with-knative/atmo-services:0.0.0
And we can test again the service:
curl localhost:8080/hello -d 'Jane'
And if we can run Atmo services in Docker, we can deploy Atmo services on Kubernetes, and then on Civo
Deploy the "helloworld" service on Civo with Knative
export KUBECONFIG=$PWD/config/k3s.yaml
export KUBE_NAMESPACE="demo"
kubectl create namespace ${KUBE_NAMESPACE} --dry-run=client -o yaml | kubectl apply -f -
kn service create atmo-service \
--namespace demo \
--env ATMO_HTTP_PORT="8080" \
--image registry.gitlab.com/k33g_org/discovering-atmo/deploy-atmo-on-civo-with-knative/atmo-services:0.0.0 \
--force
You should get an output like below:
Creating service 'atmo-service' in namespace 'demo':
0.086s The Route is still working to reflect the latest desired specification.
0.102s Configuration "atmo-service" is waiting for a Revision to become ready.
12.789s ...
12.912s Ingress has not yet been reconciled.
13.082s Waiting for load balancer to be ready
13.145s Ready to serve.
Service 'atmo-service' created to latest revision 'atmo-service-00001' is available at URL:
http://atmo-service.demo.212.2.244.140.sslip.io
You can see the new pod created to serve the Atmo microservice
And we can test again the service:
curl http://atmo-service.demo.212.2.244.140.sslip.io/hello -d 'Jane'
Some useful commands
If you want to delete your cluster:
CLUSTER_NAME="bigpanda"
CLUSTER_REGION=NYC1
export KUBECONFIG=$PWD/config/k3s.yaml
civo apikey add civo-key ${CIVO_API_KEY}
civo apikey current civo-key
civo kubernetes remove ${CLUSTER_NAME} --region=${CLUSTER_REGION} --yes
That's all folks
The next blog post will be about the same topic but with Knative.
👋
- If you loved this "post" (or not), don't forget to use the emojis reactions
- Don't hesitate to add comments and/or ask questions
- You can subscribe to the Rss feed