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.yamlkubectl 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.
- 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.)
:)