Knative was recently accepted as a CNCF incubation project and there are so many exciting things about it(!), one of them being the evolution and adoption of its components. Through this blog, we will be looking at one such component - func
plugin; creating a function in go
runtime in our local machine and then pushing it to a github repository and finally initiating its deployment. Once the function is deployed, we will be provided with a URL through which we can access it.
The expectation out of this is for a developer to get as comfortable as possible with writing functions, and essentially reach a point where they no longer have to worry about low level k8s resources (and ops in general). This would also mean that to run our code in a k8s cluster, we'd essentially need to run just one command.
Prerequisites
A v1.21 k8s cluster or later versions. It is possible to run this blog on older versions, but for that, knative and tekton versions will have to be installed accordingly - check their releases for more details.
Warning: This blog requires administrator privileges on the k8s cluster, and the function builds happen on privileged containers.
Installing Knative and Tekton
-
Knative has many components but for this blog we will stick to serving which is the only component required to run functions and enable features like deployment, scaling, ingress configuration - basically the component that helps with the lifecycle of a function.
For installation refer to installing Serving documentation or run the following commands:- kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.3.0/serving-crds.yaml
- kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.3.0/serving-core.yaml
- kubectl apply -f https://github.com/knative/net-kourier/releases/download/knative-v1.3.0/kourier.yaml
- kubectl patch configmap/config-network \ --namespace knative-serving \ --type merge \ --patch '{"data":{"ingress.class":"kourier.ingress.networking.knative.dev"}}'
For this blog we are going to use slightly changed
func
cli (which usually can be installed from kn-pluging-func releases), so we can clone the forked repo and runmake install
to generate the binary, and use it like any other cli. This change is still a WIP in upstream, to know more about it, you can follow this issue and this pr.-
To install Tekton Pipelines refer to Tekton Pipelines documentation or run the following command:
kubectl apply -f https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
Check that both Knative Serving and Tekton Pipelines components are running by checking the status of pods in knative-serving
and tekton-pipelines
namespaces.
Writing the function
Following command will create a local repository kcd-chennai
containing the function signature in handle.go
and the function configuration parameters in func.yaml
.
$ func create -l go kcd-chennai
Created go Function in /home/shashank/kcd-chennai
$ ls
func.yaml go.mod handle.go handle_test.go README.md
Now we can edit the handle.go
file to add our custom code like this:
package function
import (
"context"
"fmt"
"net/http"
)
// Handle an HTTP Request.
func Handle(ctx context.Context, res http.ResponseWriter, req *http.Request) {
fmt.Println("received a request")
fmt.Fprintln(res, "Hello from KCD Chennai! Read more about us here: https://community.cncf.io/events/details/cncf-kcd-chennai-presents-kubernetes-community-days-chennai-2022/") // Send KCD community link back to the client
}
Info: Notice that we didnt have to write most of the go code that we'd usually write, we simply just added our business logic.
This is an http
template, but there is also a cloudevent
template which is more relevant in the world of FaaS and serverless, but out of scope for this blog.
Building the function
Now we will see, how we can build this code and deploy it on kubernetes and successfully invoke it using a URL.
Building locally
We can run the following command:
$ func deploy
Our function is now available on the URL http://kcd-chennai.default.example.com
.
To access this URL from outside the k8s cluster, ideally we'd need the kourier
service in kourier-system
namespace to have a discoverable ExternalIP
, but for this blog we can try to hit the function URL from within the cluster using the following two commands:
export ingressIP=$(kubectl get svc kourier --namespace kourier-system -ojsonpath='{.spec.clusterIP}')
curl $ingressIP -H "Host: kcd-chennai.default.example.com"
Info: It's possible to configure the hostname of this function to a custom value, or to even explicitly make the function private to our cluster and then access it using http://kcd-chennai.default.svc.cluster.local
(we can also use this URL to access the function instead of the above 2 commands).
Building from github repo on-cluster
To do this, we'd need to push our local code to a github repo and create some tekton resources as describe below, tekton being a cloud native CICD system, will use those resources to run a pipeline for things like - cloning our code, building it and then deploying the function.
Warning: We'd need persistent volume claims to be working in our cluster, since the code will be cloned in there.
For tekton to do its job, we need to run following commands:
- kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/master/task/git-clone/0.4/git-clone.yaml
- kubectl apply -f https://raw.githubusercontent.com/Shashankft9/kn-plugin-func/pipeline-configure/pipelines/resources/tekton/task/buildpacks/0.3/buildpacks.yaml
- kubectl apply -f https://raw.githubusercontent.com/knative-sandbox/kn-plugin-func/main/pipelines/resources/tekton/task/func-deploy/0.1/func-deploy.yaml
Next we have to change the configuration in func.yaml
to look like this:
build: git
git:
url: https://github.com/Shashankft9/kcd-chennai.git
revision: master
Now, run the following command:
$ func deploy
Follow the video below to see the progress of build and how we can access the function.
Inherent function features from Knative
Since the functions are running with knative serving layer, it leverages some of the features listed below (non exhaustive):
- autoscaling on the basis of rps/concurrency.
- automatic ingress configuration, we use tls as well.
- scale to and from zero by default.
- can work with many languages like go, python, rust, node, springboot etc.
- readiness and liveness probes configured automatically for functions.
- no need to create service, deployment and other resource files with labels, ports etc.
- easier and faster deployments.
- prometheus metrics like
http_requests_total
with respective response codes are exposed. - traffic splitting, progressive rollouts available.
- ability to handle unstable windows, sudden traffic bursts with scaling configuration.
- easy integration with an event driven architecture because of the usage of cloudevents.
- more secure deployments because function builds use Buildpacks under the hood.
- by using Tekton for builds, we can easily configure the pipeline to add features like cloning code from a private github repo and more.
Things you can explore as a follow-up to get more comfortable with what we did in this blog:
- https://github.com/knative-sandbox/kn-plugin-func
- https://knative.dev/docs/serving/
- https://www.youtube.com/c/KnativeProject/videos
- https://slack.knative.dev/
- https://github.com/cloudevents/sdk-go
If you got stuck at any of the steps in the blog or want to report any issues, feel free to ping me (Shashank Sharma) in the knative slack - thanks!