Image Credit https://unsplash.com/photos/klWUhr-wPJ8

Installing Knative on local Kubernetes cluster.

Sai Kiran

--

To build any cloud native application, Serverless has become the go-to strategy. These days, it’s easy to visualize the entire application stack broken down into functions, and deployed using a Serverless framework.

This modern stack becomes indispensable when one factors in modern application requirements like scaling, agility, fault tolerant, zero downtime etc. But the development workflow becomes a bit more complicated with distributed development teams. CICD Pipelines needs to build application images keep them in sync, manage releases etc and it becomes difficult for developer to replicate the entire application to test and simulate.

Knative: Server-less Framework

There are many popular serverless frameworks in the market for eg. Openfaas, Kubeless, OpenWhisk, Serverless etc. Knative as its name suggests is native to Kubernetes and provides vast set of features for event based function execution and plain old request based execution. With this both Orchestration and Choreography deployment methods for micro-services can be easily implemented.

Installing Knative on local k8s

Knative can be installed using any network layer in between. Knative team recommends Kourier, as it was built ground up to support Knative and its features.

If Istio fits your requirements, then Knative can be integrated along with Istio as networking layer and you can use all Istio features for Knative functions as if they are regular k8s resources.

Lets see both deployment methods (Knative with Kourier and with Istio). To begin with, lets install microk8s, which would be our local k8s.

sudo snap install microk8s --classic

Knative with Kourier

Enable microk8s addons for storage, helm, dns etc

microk8s enable helm3 ingress dashboard dns storage

Add alias for helm and kubectl,

sudo snap alias microk8s.kubectl kubectl
sudo snap alias microk8s.helm3 helm

Install Knative Serving.

(you can also follow the instructions on Knative site on installation here)

# Serving components 
kubectl apply -f https://github.com/knative/serving/releases/download/v0.25.0/serving-crds.yaml
kubectl apply -f https://github.com/knative/serving/releases/download/v0.25.0/serving-core.yaml

Networking layer with Kourier

kubectl apply -f https://github.com/knative/net-kourier/releases/download/v0.25.0/kourier.yamlkubectl patch configmap/config-network \--namespace knative-serving \--type merge \--patch '{"data":{"ingress.class":"kourier.ingress.networking.knative.dev"}}'

Since our local K8s deployment would not have a valid DNS and is not exposed over internet, we can configure it to use Magic DNS. It configures Knative to use sslip.io as default DNS suffix. This way your functions can be accessible locally with a sslip.io DNS assigned to it.

kubectl apply -f https://github.com/knative/serving/releases/download/v0.25.0/serving-default-domain.yaml

Knative with Istio

To use Istio, Knative recommends to use default Istio installation which comes with the K8s distribution or else follow instructions here.

In our case, Microk8s provides Istio as an addon, so lets enable all requisite addons in Microk8s

microk8s enable helm3 istio dashboard metallb dns storage

Adding alias for kubectl and helm

sudo snap alias microk8s.kubectl kubectl
sudo snap alias microk8s.helm3 helm

Installing Knative serving components

kubectl apply -f https://github.com/knative/serving/releases/download/v0.24.0/serving-crds.yamlkubectl apply -f https://github.com/knative/serving/releases/download/v0.24.0/serving-core.yaml

Installing Knative Istio controller

kubectl apply -f https://github.com/knative/net-istio/releases/download/v0.24.0/net-istio.yaml

Enabling default DNS(Magic DNS)

kubectl apply -f https://github.com/knative/serving/releases/download/v0.24.0/serving-default-domain.yaml

Build and Deploy Knative function

Lets build a small python function and deploy it as a Knative function. To begin with, lets create a python file app.py as follows,

import os
from flask import Flask

app = Flask(__name__)


@app.route('/')
def hello_world():
target = os.environ.get('TARGET', 'World')
return 'Hello {}!\n'.format(target)


if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))

Now to deploy this application, lets prepare a Knative deployment file as below.

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: hello-knative
namespace: default
spec:
template:
spec:
containers:
- image: dev.local/hello-knative:local
env:
- name: TARGET
value: "Knative !!"
imagePullPolicy: Never

Since this would be a Knative function, we do not need to define a deployment and service for this function, but a custom Knative resource. This resource will create the deployment and service and scale it up and down as per the traffic and would take care of exposing this function over the DNS when configured.

Accessing functions

To check the functions deployed, use this command

kubectl get ksvc 

You would see the list of functions deployed,

NAME                     URL                                                       LATESTCREATED                  LATESTREADY              READY     REASONhello-private-function   http://hello-private-function.default.svc.cluster.local   hello-private-function-00001                            Unknown   RevisionMissinghello-knative            http://hello-knative.default.192.168.1.40.sslip.io        hello-knative-00001            hello-knative-00001      Truehello-world              http://hello-world.default.192.168.1.40.sslip.io          hello-world-00001              hello-world-00001        Unknown   Uninitializedhello-kubernetes         http://hello-kubernetes.default.192.168.1.40.sslip.io     hello-kubernetes-00001         hello-kubernetes-00001   Unknown   IngressNotConfigured

Now to access the functions, if DNS is configured, we can directly access the url as shown in the list above. If not, then we need to get the cluster IP/ external IP of the networking component installed with Knative (kourier/istio)

For Kourier use below command to get Cluster IP/External IP.

kubectl --namespace kourier-system get service kourier

For Istio use below command to get Cluster IP/External IP.

kubectl get svc $INGRESS_GATEWAY --namespace istio-system --output jsonpath="{.status.loadBalancer.ingress[*]['ip']}"

To access the function from local machine we have 2 options.

  1. Setting hostname and IP in ‘/etc/hosts’ as below.
    Set the cluster IP and host name in etc hosts file, so that we can access the function directly.
#/etc/hosts file 
CLUSTER_IP HOSTNAME
192.168.43.12 http://hello-private-function.default.svc.cluster.local

2. Setting “Host” header when calling the function

curl -H "http://helloworld-python.default.1.2.3.4.sslip.io" http://CLUSTER_IP

Conclusion

For full example and code, please visit the github repo. Makefile inside the repo automates the build and deploy stages of the functions. And the repo contains other examples on how to use Knative functions (private function/public functions etc.)

:)

--

--