Native serverless computing in Kubernetes
Go Native
If you check out the undisputed star of the container scene and its serverless capabilities, you might be somewhat disappointed to find that Kubernetes (K8s) really does not shine out of the box. Ultimately, Kubernetes sees itself as an orchestrator for container workloads across the boundaries of individual compute nodes. The keyword is "orchestrator." Kubernetes looks to manage existing containers and sees its strengths precisely there. Creating matching workloads is something it tends not to consider its job. At times, this perspective has devastating consequences from the user's point of view.
When you look at the K8s landscape from the Cloud Native Computing Foundation (CNCF), you will find countless solutions from which you are expected somehow to put together your own setup, including many continuous integration/continuous delivery (CI/CD) tools that let admins create serverless workloads for Kubernetes. That said, managing the external resources for serverless operation that are somehow grafted onto Kubernetes often turns out to be a bumpy road. Knative is the name of the project and the tool that aims to change just that.
The "K" in Knative stands – you guessed it – for Kubernetes, so it's about native integration of workloads into Kubernetes. Like many components from the K8s universe, Knative is not particularly open or flexible for newcomers, but people in the function-as-a-service (FaaS) or platform-as-a-service (PaaS) universes will benefit from the advantages of running serverless workloads with Knative. In this article, I introduce you to Knative and explain the most important terms and features.
The Beginning
If you haven't had much to do with serverless computing or Knative, you're likely to find yourself sitting in front of the Knative documentation fairly confused. Some of it reads like it was written by a marketing department – serverless here, cloud native there. The buzz will more likely worry than instill confidence in experienced administrators of classic workloads.
What you need are a few clearly defined terms before setting off into the Knative adventure. Like other solutions, Knative suffers greatly from terms like "serverless" or "cloud native" being thrown around everywhere and all the time with either no definition or everyone simply ignoring it. At the beginning of this article, I already defined what serverless is all about: being able to access services such as databases or load balancers without having to go down the classic route of a virtual instance with the installed service and matching configuration.
What sounds like nitpicking from the professional admin's point of view is hugely important for developers. The advantage to developers is huge if they can click together a database with a standardized API and not have to worry about its maintenance afterward. The main idea behind serverless computing is to hide factors such as hardware and operating systems, as well as their maintenance, from an environment's users. Instead, these tasks are handled by the platform operators, who build their own tools for this purpose and maintain specific processes for the tasks.
In the Knative context, though, a few more technical terms need clarification. These terms are particularly interesting for administrators who do not currently work with Kubernetes on a daily basis. They are directly related to how Kubernetes manages resources and how Knative is integrated. One of these terms is the custom resource definition (CRD). Kubernetes itself is known to be controlled by an administrator or developer through a REST API. Commands to Kubernetes always go from the client to the server and use the HTTP format.
Kubernetes handles objects internally, and resources are an object type. In Kubernetes, you have containers, pods, virtual IP addresses, and other facilities out of the box. Custom resource definitions are a bridge to external applications that let developers and admins create their own resource types in Kubernetes, which they can then use and manage later just like resources that are included in Kubernetes out of the box. Knative makes almost excessive use of CRDs, extending an existing K8s cluster to include a large number of CRDs that establish Knative-specific resources at the K8s cluster level.
Knative uses CRDs as a point to dock onto the container orchestrator, which is absolutely essential: It is the only way that Knative can deliver on its own promise that running serverless applications is running first-class citizens in Kubernetes. In the cloud native world, "first-class citizens" refers to resources that can be fully controlled by the existing API of the orchestrator or fleet manager.
Two Features
If you search online with the Knative keyword, you will regularly come across older documentation and presentations that describe three of Knative's core components: Serving, Eventing, and Building. Some caution is advised here. Today, the Building component is no longer a part of Knative. Instead, it is being continued as an independent project named Tekton Pipelines [1], partly with contributions from other developers. Knative has returned to its roots, so to speak, and now just comprises Serving and Eventing. Tekton is discussed in more detail later. For the moment, however, the focus is on Knative; for starters, understanding it is complicated enough. To look under the Knative hood, you should visualize the basic tasks that Knative is designed to handle from the developer's point of view.
At the top of all considerations is, as usual, the concept of the app. If you are operating serverless applications in Knative, the ultimate aim is to roll out and operate a specific application automatically, without external intervention. However, an app can comprise multiple services, which is why Knative uses the term "services" internally, although it means a function rather than a specific service. A service is a central object in a K8s cluster extended by Knative. All other resources to be created in Kubernetes depend on this object to ensure that a service can reliably handle the task expected of it. In MariaDB, for example, the service would be the active mysqld
instance – not because it is a single process, but because this service provides the central function of the database. Several processes might well be required to perform a function as part of an overall structure.
Several resources are usually attached to a service, typically including at least one route and a service-specific configuration. The route is a reference to the element of a virtual network through which the actual function can be reached. In addition to services, routes, and configurations, Knative has a fourth resource type, revision, which denotes a specific configuration consisting of a service, a route, and a configuration and can occur any number of times per serverless service. A revision is a kind of static mapping to the combination of the three previously mentioned factors.
If you think about the four aspects that form the Serving component (Figure 1), a coherent picture quickly emerges. The term "services" describes logical Knative functions in Kubernetes that take care of managing the entire lifecycle of an application. In other words, the services component creates all the services required for a virtual setup, provides them with a valid route and a valid configuration, and creates a revision for them in its own database. How the parts needed for a service come together is decided by the administrator or developer as part of the configuration. You can use Knative to store configurations and define routes or, instead, tell Knative to find the best possible route to access a network element itself.
Here, Knative also indirectly competes with other solutions such as Istio and the like, which use Kubernetes-native resources but then smuggle some of the tasks past Kubernetes.
Events Count, Too
The other major Knative subcomponent is less about what and how and far more about when, and once again goes by a technical term – event-driven architecture – which in information technology refers to an architectural approach in software development that allows an event A to be followed by a response B. Put simply, an event-based architecture in Kubernetes is based on the principle that changes to resources automatically trigger other actions. These actions are recognized as events to which a higher level instance responds. In the example here, the parent instance is Knative Eventing. A separate standard named CloudEvents, under the auspices of the CNCF, governs the way events are sent in the cloud native context.
A functional Eventing architecture requires a sender as well as a receiver for the event. In simple terms, the running setup needs to send a notification if its state changes somehow, and another component needs to receive the event and take action on it. In the context of event-based architectures, the entity that outputs events is also referred to as an agent, whereas the entity that receives the events and responds to them is also known as a sink. Again, Knative Eventing exclusively acts inside of Kubernetes and does not require external resources. Like Knative Serving, Eventing extends the K8s API to include several CRDs that can be used from within running programs.
Buy this article as PDF
(incl. VAT)