Building functions with Knative and Tekton
Tags: knative, functions, tekton, faas
Published: 2022-04-18 05:39:57

Knative was recently [accepted as a CNCF incubation project](https://www.cncf.io/blog/2022/03/02/knative-accepted-as-a-cncf-incubating-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](https://knative.dev/docs/install/yaml-install/serving/install-serving-with-yaml/) 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](https://https://github.com/knative-sandbox/kn-plugin-func/releases)), so we can clone the [forked repo](https://github.com/Shashankft9/kn-plugin-func/tree/pipeline-configure) and run `make 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](https://github.com/knative-sandbox/kn-plugin-func/issues/827) and this [pr](https://github.com/knative-sandbox/kn-plugin-func/pull/830).

- To install Tekton Pipelines refer to [Tekton Pipelines documentation](https://github.com/tektoncd/pipeline/blob/main/docs/install.md) 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`](https://cloudevents.io/) 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
```
![](https://i.imgur.com/Lr6XUKw.png)


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

![](https://i.imgur.com/wL56Kvo.png)

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.
[![function build using tekton](http://img.youtube.com/vi/qt90X0xTfFc/0.jpg)](http://www.youtube.com/watch?v=qt90X0xTfFc)

## 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](https://buildpacks.io/) 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!