DEV Community

José Castillo Lema
José Castillo Lema

Posted on • Originally published at josecastillolema.github.io on

DevConf.CZ 2023 - Writing a K8s Operator for Knative Functions

About

DevConf.CZ is an annual, free, Red Hat sponsored community conference for developers, admins, DevOps engineers, testers, documentation writers and other contributors to open source technologies. At DevConf.CZ, FLOSS communities sync, share, and hack on upstream projects together.

DevConf.cz Mini is a bi-annual, highly focused, local, in-person version of DevConf.cz. It offers an opportunity to return to our conference themes in a smaller setting and establishes a platform for the Czech Republic and EMEA based community to sync, share and hack on upstream projects together.

There is no admission or ticket charge for DevConf.CZ events. However, you are required to complete a free registration. Watch this site for updates about registration.

We are committed to fostering an open and welcoming environment at our conference. We set expectations for inclusive behavior through our code of conduct and media policies, and are prepared to enforce these.

Schedule

Writing a K8s Operator for Knative Functions

Serverless and Function as a Service (FaaS) are getting more and more attention from customers and developers as a way to develop, run and manage applications functionality without the burden of infrastructure related knowledge. All big cloud providers offer them already, e.g., AWS Lambda, Google Cloud Functions or Microsoft Azure Functions. One of the most relevant upstream projects for serverless is Knative, which recently added support for functions (create, build, and deploy) on top of K8s clusters.

This workshop will introduce you to the the PHYSICS European project and its FaaS model, as well as to building Kubernetes operators. You will implement a K8s Operator, using the operatorsdk framework, to provide the functionality of the Knative CLI. This will allow easier creation, build and deployment of functions with Knative just by creating Kubernetes (CR) objects, and will help you learn the internals about how K8s Operators work in a real life example.

Friday June 16, 2023 • 2:15pm - 3:35pm CEST

Speakers

Links

Workshop

physics-devconf

This repository provides an easy way to deploy a KinD cluster with Knative (using this script) on top of a Fedora 37 VM.

It also provides a couple of sample scripts to deploy a Knative service and a function.

Index

Goals

  • Get familiar on how to create/test Knative functions
  • Get familiar with the operator SDK

Deploy the environment (VM)

The VM requires 4 vCPUs and 6GB of memory. It takes approximately 10 minutes to come up:

$ vagrant up
Bringing machine 'default' up with 'libvirt' provider...
==> default: Checking if box 'fedora/37-cloud-base' version '37.20221105.0' is up to date...
==> default: Creating image (snapshot of base box volume).
==> default: Creating domain with the following settings...
...
    default: configmap/config-br-defaults configured
    default: ⑦ Dapr
    default: ./allocate.sh: line 251: dapr: command not found
    default: popd
    default: ~/go/src/github.com/knative/func
    default:
    default: cat <<EOF | sudo tee /etc/docker/daemon.json
    default: {"insecure-registries": ["localhost:50000"]}
    default: EOF
    default: {"insecure-registries": ["localhost:50000"]}

Enter fullscreen mode Exit fullscreen mode

The provision script installs:

  • Docker
  • Golang
  • Pip
  • Git
  • Curl
  • Wget
  • Cosign
  • Kubectl
  • Kn - the Knative client
  • Func - Knative functions
  • Kind
  • Operator-sdk

Access the environment

  1. Login into the virtual machine just created:
 $ vagrant ssh

Enter fullscreen mode Exit fullscreen mode
  1. Check if all the pods are running:
 $ kubectl get pods -A
 NAMESPACE NAME READY STATUS RESTARTS AGE
 contour-external contour-56cfd44877-gmzdd 1/1 Running 0 2m30s
 contour-external contour-56cfd44877-wj844 1/1 Running 0 2m30s
 contour-external contour-certgen-v1.22.0-qbbx6 0/1 Completed 0 2m30s
 contour-external envoy-4j2vr 2/2 Running 0 2m30s
 contour-internal contour-865fdc98f9-48vv9 1/1 Running 0 2m29s
 contour-internal contour-865fdc98f9-l22kw 1/1 Running 0 2m29s
 contour-internal contour-certgen-v1.22.0-5t52p 0/1 Completed 0 2m30s
 contour-internal envoy-vlxrb 2/2 Running 0 2m29s
 knative-eventing eventing-controller-64b4b79c45-bxk6f 1/1 Running 0 4m5s
 knative-eventing eventing-webhook-86f7dd95db-phc9x 1/1 Running 0 4m5s
 knative-eventing imc-controller-769d8b7f66-hx2lj 1/1 Running 0 3m33s
 knative-eventing imc-dispatcher-55979cf74b-8n2w9 1/1 Running 0 3m33s
 knative-eventing mt-broker-controller-f97f8747-r7nnr 1/1 Running 0 3m21s
 knative-eventing mt-broker-filter-77c75d69fb-j4972 1/1 Running 0 3m21s
 knative-eventing mt-broker-ingress-d96f6d8b5-g4ng6 1/1 Running 0 3m21s
 knative-serving activator-75777fd57c-hwsth 1/1 Running 0 4m49s
 knative-serving autoscaler-57d647d6ff-cs2bx 1/1 Running 0 4m49s
 knative-serving controller-677995dc7b-9tbmj 1/1 Running 0 4m48s
 knative-serving domain-mapping-5676fb7bcf-92xmf 1/1 Running 0 4m48s
 knative-serving domainmapping-webhook-fcbd7dff4-5v26r 1/1 Running 0 4m48s
 knative-serving net-contour-controller-847758c4bf-kltdx 1/1 Running 0 2m
 knative-serving webhook-544b958c69-h7vmz 1/1 Running 0 4m48s
 kube-system coredns-6d4b75cb6d-btqsp 1/1 Running 0 5m16s
 kube-system coredns-6d4b75cb6d-shbkf 1/1 Running 0 5m16s
 kube-system etcd-func-control-plane 1/1 Running 0 5m35s
 kube-system kindnet-mr2xx 1/1 Running 0 5m16s
 kube-system kube-apiserver-func-control-plane 1/1 Running 0 5m30s
 kube-system kube-controller-manager-func-control-plane 1/1 Running 0 5m30s
 kube-system kube-proxy-vpb8z 1/1 Running 0 5m16s
 kube-system kube-scheduler-func-control-plane 1/1 Running 0 5m32s
 local-path-storage local-path-provisioner-6b84c5c67f-575j5 1/1 Running 0 5m16s
 metallb-system controller-6c58495cbb-j52ls 1/1 Running 0 3m3s
 metallb-system speaker-v5hd2 1/1 Running 0 3m3s

Enter fullscreen mode Exit fullscreen mode
  1. Check if the local registry is running:
 $ docker ps
 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
 59be051ba43c registry:2 "/entrypoint.sh /etc…" 2 minutes ago Up 2 minutes 127.0.0.1:50000->5000/tcp func-registry
 caf78811a6a9 kindest/node:v1.24.6 "/usr/local/bin/entr…" 4 minutes ago Up 4 minutes 127.0.0.1:39609->6443/tcp, 127.0.0.1:80->30080/tcp, 127.0.0.1:443->30443/tcp func-control-plane

Enter fullscreen mode Exit fullscreen mode

Create a new (python) function and invoke it

  1. Create the Knative function:
 $ func create -l python test-hw
 Created python function in /home/vagrant/test-hw

Enter fullscreen mode Exit fullscreen mode
  1. Take a look around and change the func.py code as follows:
 $ cd test-hw

 $ ls
 app.sh func.py func.yaml Procfile README.md requirements.txt test_func.py

 $ cat func.py


 from parliament import Context
 from flask import Request
 import json

 # parse request body, json data or URL query parameters
 def payload_print(req: Request) -> str:
     if req.method == "GET":
         return "DevConf.cz 2023!"

 def main(context: Context):
     """
     Function template
     The context parameter contains the Flask request object and any
     CloudEvent received with the request.
     """

     # Add your business logic here
     print("Received request")

     if 'request' in context.keys():
         return payload_print(context.request), 200
     else:
         print("Empty request", flush=True)
         return "{}", 200

Enter fullscreen mode Exit fullscreen mode
  1. Build (and push) the function to the internal registry:
 $ export FUNC_REGISTRY=localhost:50000/kn-user
 $ func build --push
 🙌 Function image built: localhost:50000/kn-user/test-hw:latest
 🕕 Pushing function image to the registry "localhost:50000" using the "" user credentials

Enter fullscreen mode Exit fullscreen mode
  1. Check that the image has been correctly pushed into the internal registry:
 $ curl localhost:50000/v2/_catalog
 {"repositories":["kn-user/test-hw"]}

Enter fullscreen mode Exit fullscreen mode
  1. Deploy the function to the kind cluster:
 $ func deploy --build=false --push=false
 ✅ Function deployed in namespace "default" and exposed at URL:
      http://test-hw.default.127.0.0.1.sslip.io

Enter fullscreen mode Exit fullscreen mode
  1. Check that the function has been correctly deployed. A new Knative service (ksvc) object is created, which triggers the Knative controllers to create the other k8s objects (deployment and route). After approximately one minute minute the deployment is scaled down to 0 replicas if not used to spare resources:
 $ kubectl get ksvc
 NAME URL LATESTCREATED LATESTREADY READY REASON
 test-hw http://test-hw.default.127.0.0.1.sslip.io test-hw-00001 test-hw-00001 True

 $ kubectl get deploy
 NAME READY UP-TO-DATE AVAILABLE AGE
 test-hw-00001-deployment 0/0 0 0 104s

 $ kubectl get route
 NAME URL READY REASON
 test-hw http://test-hw.default.127.0.0.1.sslip.io True

 $ kubectl get pods
 (empty if more than a minute has passed)

Enter fullscreen mode Exit fullscreen mode
  1. Invoke the function:
$ curl http://test-hw.default.127.0.0.1.sslip.io
DevConf.cz 2023!

Enter fullscreen mode Exit fullscreen mode
  1. Check that the deployment has been scaled up:
$ kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
test-hw-00001-deployment 1/1 1 1 3s

Enter fullscreen mode Exit fullscreen mode

Fork the base operator github repository and deploy locally

  1. Fork this github repository into your github account: https://github.com/luis5tb/devconf-knative-operator

  2. Clone your fork locally inside the VM (change YOUR_USER by yours):

$ git clone https://github.com/YOUR_USER/devconf-knative-operator.git

Enter fullscreen mode Exit fullscreen mode

In case you want to start an operator from scratch do the next instead (change YOUR_USER by yours):

$ mkdir devconf-knative-operator
$ cd devconf-knative-operator

# Create base operator
$ operator-sdk init --domain example.com --repo github.com/YOUR_USER/devconf-knative-operator

# Add API
$ operator-sdk create api --group knf --version v1alpha1 --kind KnativeFunction --resource --controller

Enter fullscreen mode Exit fullscreen mode
  1. There are three important files to consider:
    • controllers/knativefunction_controller.go : implements the operator reconcile loop
    • api/v1alpha1/knativefunction_types.go : the KnativeFunction CRD definition
    • config/samples/knf_v1alpha1_knativefunction.yaml : an example KnativeFunction CRD
  2. Let’s take a look at api/v1alpha1/knativefunction_types.go , as you can see it defines an example Foo field:
...
type KnativeFunctionSpec struct {
    // Foo is an example field of KnativeFunction. Edit knativefunction_types.go to remove/update
    Foo string `json:"foo,omitempty"`
}
...

Enter fullscreen mode Exit fullscreen mode
  1. Let’s modify the operator reconcyle loop in controllers/knativefunction_controller.go :

  2. Test your code by deploying it. You will need two terminals, T1 and T2.

  3. [T2] In a second terminal create a sample CRD:

$ cat <<EOF | kubectl apply -f -
---
apiVersion: knf.example.com/v1alpha1
kind: KnativeFunction
metadata:
labels:
    app.kubernetes.io/name: knativefunction
    app.kubernetes.io/instance: knativefunction-sample
    app.kubernetes.io/part-of: devconf-knative-operator
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/created-by: devconf-knative-operator
name: knativefunction-sample
spec:
   foo: test
EOF

Enter fullscreen mode Exit fullscreen mode
  1. [T1] In the first terminal you should see something like:
2023-06-16T08:12:54Z    INFO    Received a request to create a new knativefunction  {"controller": "knativefunction", "controllerGroup": "knf.example.com", "controllerKind": "KnativeFunction", "KnativeFunction": {"name":"knativefunction-sample","namespace":"default"}, "namespace": "default", "name": "knativefunction-sample", "reconcileID": "9be34733-bca6-4134-bf6d-8f0ed69106bd", "Foo =": "test"}

Enter fullscreen mode Exit fullscreen mode
  1. [Challenge] The goal now is to extend the operator to deploy the existing Knative function (from step 6). Once this is accomplished, extend the operator to build, push and deploy any function located on a given github repository.

Solution

  1. Deploy a CR to force the controller to reconcile and get the function deployed. First you need to edit the config/samples/knf_v1alpha1_knativefunction.yaml with the desired options:

  2. To check the operator did its job, beside seeing the make install run logs, you can check as before:

Links

Top comments (0)