Kubernetes Serverless: you build it Knative runs it!

Hoe ga je serverless in de cloud? Hoe hou je als ontwikkelaar vooral focus op het schrijven van code? Hoe combineer je dat met het makkelijk uitrollen van je applicatie? Een interessante, op K8s gebaseerde, ontwikkeling op dit vlak is Knative.

Knative biedt handige features zoals scale-to-zero, scale-from-zero, routing naar verschillende revisies van je applicatie en nog meer. In dit artikel geven we jullie een korte introductie wat Knative is en willen we jullie laten zien hoe je er zelf mee aan de slag kunt gaan.

Wat is Knative

Knative staat voor Kubernetes-native. Het is een platform gebaseerd op Kubernetes waarmee je een applicatie kan deployen en beheren. Het richt zich op het minimaliseren van de tijd die ontwikkelaars besteden aan deployment- en beheerprocessen, zodat zij hun focus kunnen houden op het bouwen van software.

Het open source project Knative is door Google op 24 juli 2018 in San Francisco aangekondigd en vandaag de dag wordt er in 30 verschillende landen aan ontwikkeld. Het project bevindt zich momenteel in de pre-release fase.

Installeer Knative op je Kubernetes cluster

Knative wordt als een custom-resource gedeployed op een Kubernetes cluster. Hiervoor zijn drie custom resource definitions (CRDs) beschikbaar. Deze installeer je middels kubectl, de command-line interface van Kubernetes.

 kubectl apply \
--filename https://github.com/knative/serving/releases/download/v0.11.0/serving.yaml \
--filename https://github.com/knative/eventing/releases/download/v0.11.0/release.yaml \
--filename https://github.com/knative/serving/releases/download/v0.11.0/monitoring.yaml

Wanneer deze geïnstalleerd zijn, zie je in je cluster drie namespaces: knative-eventing, knative-monitoring en knative-serving.

Knative Eventing biedt een abstractie bovenop verschillende type events om integratie met andere systemen te uniformeren. Denk hierbij aan events uit Kafka, triggers uit GitHub of events van je onderliggende cloud-storage.

Knative Monitoring levert metrics en dashboards voor het monitoren van zowel de Knative componenten als de gedeployde applicaties. Denk hierbij aan resource gebruik of inkomend verkeer per Service.

Knative Serving draagt zorg voor het beschikbaar stellen van je applicaties. Het biedt functionaliteit voor het managen van je applicatie lifecycle, scaling en routing. Binnen dit artikel zal de focus hierop liggen.

Knative Serving

Knative Serving levert componenten voor het versimpelen van deployments, het op- en afschalen van applicaties, routing en service-mesh configuratie en het bijhouden van deployment revisies.

Hiervoor stelt Knative een viertal objecten ter beschikking.

Service – de top-level entiteit verantwoordelijk voor het managen van de lifecycle van onderliggende objecten. Dit zijn bijvoorbeeld een Route, Configuration en Deployment.
De term ‘service’ wordt ook door Kubernetes zelf gebruikt. Dit kan enigszins verwarrend zijn.

Route – de koppeling tussen een Service en de op dat moment actieve Revision. Een Route bepaalt naar welke Revision een request gerouteerd wordt.
Om dit te realiseren gebruikt Knative een service-mesh. Standaard wordt uitgegaan van Istio, ook andere implementaties zijn mogelijk.

Configuration – een combinatie van de code (applicatie versie) en configuratie van je applicatie. Hier worden properties zoals het te gebruiken image of het maximaal aantal parallelle connecties ondergebracht. Het Configuration object kan runtime aangepast worden. Dit resulteert in een nieuwe Revision.

Revision – een immutable snapshot van een Configuration. Aangezien een Revision immutable is, zal elke wijziging van een Configuration resulteren in een nieuwe Revision. Meerdere Revisions kunnen tegelijk actief zijn.

Om deze componenten op een Kubernetes cluster te laten werken, maakt Knative gebruik van een aantal default Kubernetes componenten, zoals een (Kubernetes) Service, Deployment, ReplicaSet en natuurlijk Pods.
Ook gaan we uit van een vooraf geïnstalleerd service-mesh, zoals Istio.

 

Knative command line interface

Na installatie op het cluster, biedt Knative API’s om deze objecten te creëren en beheren. Deze API’s kunnen, zoals gebruikelijk is bij Kubernetes, aangeroepen worden middels de kubectl command-line-interface.

kubectl apply –f my-service.yaml

De gebruikte YAML-bestanden zijn vaak moeilijk leesbaar en daarmee foutgevoelig. De insteek is dan ook om deze klein te houden en beperkt te gebruiken.
Om dit nog verder te beperken kan tooling geschreven worden. Knative biedt een referentie implementatie in de vorm van een command-line-interface geschreven. Deze biedt mogelijkheden voor het deployen en beheren van je Services.

Je eerste deployment

We gaan in dit voorbeeld uit van een web-applicatie in een Docker container. Gebruikmakend van de Knative command-line interface deployen we onze applicatie als volgt:

kn service create my-app --image docker.io/.../deepthought:latest

Service 'my-app' successfully created in namespace ‘default’.

Waiting for service 'my-app' to become ready ... OK

Service URL:

http://my-app.default.${CLUSTER_IP}.xip.io

 

Onze applicatie is nu beschikbaar en klaar om requests af te handelen.

$ curl -s http://my-app.default.${CLUSTER_IP}.xip.io/answer

The answer is: 42

 

Scaling down

Hoewel Knative bovenstaand proces makkelijker gemaakt heeft, zou dit ook met Kubernetes kunnen. Je zou hetzelfde bereiken middels YAML files en de kubectl commandline. Dat is anders bij het schalen van de applicatie.

Kubernetes gebruikt standaard de Horizonal Pod Autoscaler (HPA). Knative komt met een eigen Knative Pod Autoscaler (KPA). Daar waar de HPA schaalt op basis van CPU wordt de KPA gestuurd door het aantal requests ‘in-flight’. Een Knative service kan met beiden geconfigureerd worden. Ook is het mogelijk om je eigen implementatie te configureren.
In dit voorbeeld gaan we uit van de Knative default: KPA.

Wanneer onze applicatie geen verkeer ontvangt, zien we dat dat de Autoscaler de Deployment gaat schalen. Pods worden uit de lucht gehaald, wanneer ze langer dan vijf minuten geen requests ontvangen.

$ kubectl get pods

NAME                                         READY     STATUS        RESTARTS   AGE

my-app-fvwdb-2-deployment-77c44f69c9-485vh   2/2       Terminating   0          1m

Om een applicatie live te houden, dient er binnen een Kubernetes cluster altijd één Pod actief te blijven. Knative maakt het mogelijk een Deployment tot nul Pods terug te schalen.
Na enkele minuten zijn de Pods uitgeschakeld.

$ kubectl get pods

No resources found.

Scaling up

Wanneer nu een request voor onze applicatie het cluster bereikt, zal deze door een Pod afgehandeld zijn. Bij ontbrekende Pods zal Knative er een creëren.

Dit kan vanuit twee uitgangssituaties: er zijn geen actieve pods of er zijn nog actieve pods. Hiervoor zijn respectievelijk de Activator en Autoscaler verantwoordelijk.

 

$ kubectl get pods -n knative-serving

NAME                                READY     STATUS    RESTARTS   AGE

activator-684ff4496f-pkxv4          1/1       Running   0          16h

autoscaler-78cbbdbbc5-h6hph         1/1       Running   0          16h

controller-55dd997dcd-z6gdh         1/1       Running   0          16h

networking-istio-5d968f57b4-pnn55   1/1       Running   0          16h

webhook-68bc7c786b-4nkv6            1/1       Running   0          16h

Wanneer er geen actieve pods zijn, is ook de Route inactief. In dit geval wordt het request doorgegeven aan de Activator, die op zijn beurt een Revision activeert. Op dat moment wordt een nieuwe deployment gedaan van die Revision. Wanneer dat gedaan is, zal de Activator het request doorzetten naar de nieuw gemaakte Pod.

Als er al Pods zijn gestart dan zal de Route het request doorzetten naar de Revision. De daar actieve Pod levert metrics aan de Autoscaler. Wanneer de Autoscaler detecteert dat parallel meer requests binnen komen dan het ingestelde maximum, zal hij de Deployment vergroten.

De standaardwaarde voor het aantal concurrent requests is 100. Om dit eenvoudiger te demonstreren kunnen we deze grens op 1 zetten.

$ kn service update my-app \

--concurrency-limit=2 \

--concurrency-target=1

 

Wanneer we nu twee requests ontvangen zien we dat de Autoscaler de Deployment heeft vergroot en er meerdere Pods draaien.

 

$ for i in `seq 5`; do curl -s http://my-app.default.${CLUSTER_IP}.xip.io/answer &; done

$ kubectl get pods

NAME                                         READY     STATUS    RESTARTS   AGE

my-app-fvwdb-2-deployment-77c44f69c9-h6zvn   2/2       Running   0          34s

my-app-fvwdb-2-deployment-77c44f69c9-hllnd   2/2       Running   0          36s

my-app-fvwdb-2-deployment-77c44f69c9-m2fhg   2/2       Running   0          30s

my-app-fvwdb-2-deployment-77c44f69c9-mpm4f   2/2       Running   0          30s

 

Deze Pods verdwijnen wanneer het aantal parallelle requests minder wordt.

Een nieuwe deployment live zetten

Op dit moment hebben we één versie van de applicatie live staan. Wanneer we de service updaten zal Knative default alle verkeer routeren naar deze nieuwe release. Dit is niet altijd wat je wilt. Denk hierbij aan blue-green-deployments of een canary-deployment.

Om dit te voorkomen configureren we de Route om al het verkeer naar de huidige Revision te routeren.

$ kn revision list

NAME             SERVICE   GENERATION   AGE   CONDITIONS   READY   REASON

my-app-lpxcd-1   my-app    1            25s   4 OK / 4     True


$ kn service update my-app --traffic my-app-lpxcd-1=100

Waiting for service 'my-app' to become ready ... OK

Service 'my-app' updated in namespace ‘default’.

 

Vervolgens gaan we de nieuwe release live zetten.

$ kn service update my-app –image docker.io/.../deepthought:v2

Waiting for service 'my-app' to become ready ... OK

Service 'my-app' updated in namespace ‘default’.



$ kn revision list

NAME             SERVICE   GENERATION   AGE     CONDITIONS   READY   REASON

my-app-qttmw-3   my-app    2            7m15s   3 OK / 4     True

my-app-lpxcd-1   my-app    1            9m17s   3 OK / 4     True

 

Op dit moment wordt al het verkeer nog afgehandeld door de vorige versie van de applicatie. Om de load tussen de twee beschikbare Revisions te verdelen voeren we het volgende commando uit:

$ kn service update my-app \

--traffic my-app-lpxcd-1=50 \

--traffic my-app-qttmw-3=50

Waiting for service 'my-app' to become ready ... OK

Service 'my-app' updated in namespace ‘default’.

 

What’s the question again?

What’s the question again?

The answer is: 42

What’s the question again?$ for i in `seq 1 5`; do curl -s http://my-app.default.${CLUSTER_IP}.xip.io/answer ; done

The answer is :42

Nu zien we dat het verkeer verdeeld wordt over de twee actieve Revisions.

Conclusie

Hoe deploy je serverless naar de cloud? Een vraag die Knative probeert te beantwoorden. Knative meldt zich als nieuwe speler in een markt waarin anderen al een voorsprong hebben. Met organisaties als Pivotal, Google, IBM en Red Hat die hieraan bijdragen is een actieve community ontstaan. In aanloop naar release 1.0 is Knative een ontwikkeling die de moeite waard is om in het oog te houden.

 

auteurs: Maarten Veldink en Tijmen Buis van Ordina