Knative - what is it actually?

21.12.2021 -

Knative version 1.0 has been officially available since the beginning of November. In this blog post, I'll show you what features it has and why you might want to use it.

Knative - roughly summarized

Knative is an open-source extension of classic Kubernetes that is natively based on it and enables serverless Kubernetes. Its purpose is to allow developers to focus exclusively on implementing their services and to take care of tedious ancillary activities. As Knative is based on Kubernetes, it is also independent of language and framework.

 

Knative is currently extending Kubernetes with three relevant components, but there are plans to offer even more components in the future. The components are Build, Serve and Event.1

Comparison to pure Kubernetes

Although pure Kubernetes offers support for many functions by providing the required ports, developers are always forced to use third-party software or even write their own software. This is where Knative comes in and offers two important solutions with the Serve and Event components.

Serve

In pure Kubernetes, it is only possible for containers or microservices to communicate with each other if external software such as Istio is accessed. Knative solves this problem with the Serve component. This includes features such as intelligent routing, autoscaling and snapshots. Knative even makes scale-to-zero possible. Unlike Kubernetes, which deletes a pod as soon as it is reduced to zero replicas, the pod remains in place but does not consume any system resources. As Knative is a serverless environment, this also saves a lot of costs, as many cloud providers charge for the system pay-by-use. If no resources are used, no costs are incurred.2

Event

The Event component provides a build-in event listener and, unlike with pure Kubernetes, there is no need for additional software. This component makes it possible to define triggers, for example.3

Build

To be able to work with Kubernetes, code must be written, a container image must be created and then made available in a registry so that Kubernetes can now access it via a YAML file. This is where Knative comes in. Knative enables all these steps to be carried out directly from the Kubernetes cluster. This component is called Build, but is now decoupled from Knative and part of a separate project called Tekton.4

Hello World - a simple example

To show you how such a process works, I have gone through the process from installing to running a service and documented it. I have already made some preparations and installed cURL, Docker, Minikube, kubectl, Java and Maven.

Preparation, setup and installation

First we start our mini cube:

minikube start

Once we have started our Minikube, we install Knative via YAML files. As Knative is an open source project, we can find these on GitHub:

kubectl apply -f https://github.com/knative/serving/releases/download/v0.26.0/serving-crds.yaml
kubectl apply -f https://github.com/knative/serving/releases/download/v0.26.0/serving-core.yaml

We then install a networking layer in order to communicate with our service via the Internet later on. Here we choose the Kourier recommended by Knative:

#installieren:
kubectl apply -f https://github.com/knative/net-kourier/releases/download/v0.26.0/kourier.yaml

#als Standard festlegen:
kubectl patch configmap/config-network \
  --namespace knative-serving \
  --type merge \
  --patch '{"data":{"ingress.class":"kourier.ingress.networking.knative.dev"}}'

#Test:
kubectl --namespace kourier-system get service kourier

After we have installed Knative and a networking layer, we check whether the installation was successful:

kubectl get pods -n knative-serving

If everything went well, the output should look similar to this:

The Knative pods of our VM are displayed here. Ideally, the Kourier controller pod, among others, should be running here.

Then we configure our DNS to be able to use cURL commands without host headers in the future. To do this, since we have installed Minikube locally, we must first create a tunnel in a new console window to access the load balancer:

minikube tunnel

Once our tunnel is running, we can configure our DNS settings. To do this, we use the MagicDNS Kubernetes job provided by Knative:

kubectl apply -f https://github.com/knative/serving/releases/download/v0.26.0/serving-default-domain.yaml

Create our app

Now that all the preparations have been completed, we can finally get started. First, we create a new web project. To save time and simplify the whole thing a little, we use a template:

curl https://start.spring.io/starter.zip \
   -d dependencies=web \
   -d name=helloworld \
   -d artifactId=helloworld \
   -o helloworld.zip
unzip helloworld.zip

Now we open the Java file we have just created and define the content of our application, in my case a simple Hello World:

package com.example.helloworld;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class HelloworldApplication {

@Value("${TARGET:doubleSlash}")
String target;

@RestController
class HelloworldController {
    @GetMapping("/")
    String hello() {
        return "Hello " + target + "!";
    }
}
public static void main(String[] args) {
    SpringApplication.run(HelloworldApplication.class, args);
    }
}

We then test our Hello World app locally first:

./mvnw package && java -jar target/helloworld-0.0.1-SNAPSHOT.jar

#der Output unserer App sollte nun unter http://localhost:8080/ sichtbar sein!
#hierzu einfach die localhost-Adresse im Browser öffnen, oder mit curl pingen.
#in unserem Fall steht dort nun "Hello doubleSlash!"

If our app does what we want, we can create a container image. To do this, we first create a Dockerfile. It is important that our file does not have a suffix such as '.txt':

# Use official maven/Java 11 image to create build artifact: https://hub.docker.com/_/maven
FROM maven:3.8-jdk-11 as builder

# Copy local code to container image.
WORKDIR /app
COPY pom.xml .
COPY src ./src

# Build release artifact.
RUN mvn package -DskipTests

# Use Official OpenJDK image for lean production stage of multi-stage build.
# https://hub.docker.com/_/openjdk
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM openjdk:11-jre

# Copy jar to the production image from builder stage.
COPY --from=builder /app/target/helloworld-*.jar /helloworld.jar

# Run web service on container startup.
CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/helloworld.jar"

Danach bauen wir über Docker einen Container und laden diesen dann in unser Docker-Registry. Hierbei entspricht {username} Deinem Docker Hub Username:

# Build
docker build -t {username}/helloworld-java-spring .
# Push
docker push {username}/helloworld-java-spring

Nun deployen wir unsere App in ein Cluster. Hierzu erstellen wir zuerst ein YAML-File namens ’service.yaml‘ mit folgendem Inhalt. Auch hier entspricht {username} Deinem Username:

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: helloworld-java-spring
  namespace: default
spec:
  template:
    spec:
      containers:
        - image: docker.io/{username}/helloworld-java-spring
          env:
            - name: TARGET
              value: "World"

Then we call up the YAML file and deploy our app:

kubectl apply -f service.yaml

We can check whether everything has worked by calling up our service. To do this, we first determine its URL:

kubectl get ksvc helloworld-java-spring  --output=custom-columns=NAME:.metadata.name,URL:.status.url

Anschließend pingen wir unseren Service – wobei {URL} der URL entspricht, die wir im vorherigen Schritt zurück bekommen haben – oder öffnen die URL einfach direkt im Browser:

curl {URL}

#Unseren Service via 'curl' zu pingen kann einige Sekunden dauern. Schneller ist hier definitiv den Link im Browser zu öffnen!
#Im Vergleich zu unserem lokalen Versuch steht hier nun nicht mehr "Hello doubleSlash!"
#als Antwort, sondern "Hello World!" - genau so wie wir es in unserem Code festgelegt haben!

Scale-to-zero

If we now observe our pod, we can see more details about its status and how the Autoscale feature of the Serve component works:

kubectl get pods -l serving.knative.dev/service=helloworld-java-spring -w

# Zuerst sehen wir, dass unser Pod läuft und Traffic besteht,
# da wir diesen im vorherigen Schritt angepingt haben:

NAME                                                       READY   STATUS    RESTARTS   AGE
helloworld-java-spring-00003-deployment-776b9c79bc-2rbwt   2/2     Running   0          32s

# Wenn wir nun den Traffic abbrechen, also unseren Service nicht mehr pingen,
# sehen wir, dass unsere Pods terminiert werden. Es sind 0 Pods aktiv,
# aber dennoch werden die Pods nicht vollständig gelöscht.
# Dieses Feature nennt sich Scale-to-Zero:

helloworld-java-spring-00003-deployment-776b9c79bc-2rbwt   2/2     Terminating   0          80s
helloworld-java-spring-00003-deployment-776b9c79bc-2rbwt   0/2     Terminating   0          112s

# Entsteht nun wieder Traffic, da wir zum Beispiel unseren Service pingen,
# baut Knative erneut einen Container für uns:

helloworld-java-spring-00003-deployment-776b9c79bc-qj7bv   0/2     ContainerCreating   0          0s
helloworld-java-spring-00003-deployment-776b9c79bc-qj7bv   1/2     Running             0          3s
helloworld-java-spring-00003-deployment-776b9c79bc-qj7bv   2/2     Running             0          18s

If we no longer need our service, it can simply be deleted:

kubectl delete -f service.yaml

You can find more details on the steps I have taken and other features such as traffic splitting and eventing here.

 

Conclusion

If you already use Kubernetes, Knative only offers advantages for you. It is natively based on Kubernetes, so it does not require any conversions or similar and also provides useful features. Knative also offers its own command line interface, which makes the steps required when working with Knative even easier. For example, you no longer need to create YAML files for deployment. In addition, as it is a brand new open source project, further features are constantly being developed and added.


Sources:

1 Dev insider: https://www.dev-insider.de/was-ist-knative-a-917621/

2 Red Hat: https://www.redhat.com/en/topics/microservices/what-is-knative

3 IBM Technology: https://www.ibm.com/cloud/learn/knative

4 brainDOSE: https://braindose.blog/2020/08/13/differences-between-function-serverless-knative/

5 Knative: https://knative.dev/docs/serving/samples/hello-world/helloworld-java-spring/

Back to overview

Write a comment

Your e-mail address will not be published. Required fields are marked with *

*Mandatory fields

*