First Impressions of Knative Eventing
Tags: kubernetes, knative, eventing, serverless
Published: 2019-07-10 10:26:23

You might have heard of [Knative](https://knative.dev/) – the serverless application development platform on top of Kubernetes. A lot of buzzwords, I know, but in essence it provides multiple APIs for deploying your applications without thinking (too much) about Kubernetes machinery.

At the moment, Knative can be differentiated into three components:

- [Knative Serving](https://knative.dev/docs/serving/) - Deploying HTTP-based applications – think of AWS Lambda or Google Cloud Functions 
- [Knative Build](https://knative.dev/docs/build/) - Helps you to avoid touching containers at all. You provide code and the rest will happen automagically.
- [Knative Eventing](https://knative.dev/docs/eventing/) - Helps you to build event-driven applications 💥

You might know that I'm a huge fan of event-driven architectures. This is why we will explore _Knative Eventing_ in this post a little bit.

## What do I need?

Due to the nature of _Knative_ as a serverless platform on top of Kubernetes, you need – 🎉 – a Kubernetes cluster with an installed Service Mesh, like [Istio](https://istio.io/). No worries, if you don't know what this Istio-thingy is all about. Please ignore it for the moment and just treat it as a requirement.

For everyone who is not a proud owner of a Kubernetes cluster yet, I recommend creating one on the [Google Cloud Platform (GCP)](https://cloud.google.com/). In general the following steps are required:

1. Create a cluster on GCP with the Istio addon
2. Install Knative

To keep things DRY and not duplicating content, you can find a good description in the official docs of _Knative_: [Install on Google Kubernetes Engine](https://knative.dev/docs/install/knative-with-gke/).

⚠️ Don't be quenched by the node autoscaling configuration (1 - 10 nodes). For this demo, you can just remove `--enable-autoscaling --min-nodes=1 --max-nodes=10` and replace it with `--num-nodes 1`. This results in a single node cluster which is sufficient for our little exploration here.

## Our goal

So what is our goal here? I thought of a simple scenario which demonstrates all the essential key pieces without to much ceremony involved. The result is the following simple use case:


> Establishing an emitter which sends a JSON-formatted event every minute and having an application which spins up automatically and just displays the respective event.

![](https://thepracticaldev.s3.amazonaws.com/i/l63haadl1kpelrubdrho.png)

## Let the events flow ...

The described use case above can be translated into the following architecture which demonstrates how the flow of events can be achieved with _Knative Eventing_:

![](https://thepracticaldev.s3.amazonaws.com/i/n7cft44p1bjqnrpdwir1.png)

Although that might look complex, the introduced indirection with the `Broker` and `Trigger` makes a lot of sense in the end. Let us analyze it piece by piece. 

### Broker

The `Broker` acts as a central eventing gateway. It receives events from sources and delegates it to the respective subscribers who are interested in the event.

In our scenario, we will create broker in the `default` namespace by executing:

```sh
kubectl label namespace default knative-eventing-injection=enabled
```

You can verify the creation of the broker via:

```sh
$ kubectl get broker
NAME      READY   REASON   HOSTNAME                                   AGE
default   True             default-broker.default.svc.cluster.local   22s
```

### Event Source

Acts as an origin of events. Imagine a microservice kind of architecture. In such a scenario you would have multiple of such event sources. Each of them will emit domain specific events.

There are several possibilities to establish an actual event source. Beside coding own sources, _Knative Eventing_ ships with a bunch of predefined of those. To name a few:

- GitHub: Repository / Organization events, like: PR created, commits pushed, etc.
- Apache Kafka: Stream Kafka messages to _Knative_
- Kubernetes: Brings Kubernetes cluster events to _Knative_

You can find a complete list of existing sources in the [Knative docs](https://knative.dev/docs/eventing/sources/index.html).

Another interesting predefined component is the `Cron Job` source. It allows to emit messages in a timely manner. This is exactly the one, we want to use in our demo here:

```yaml
# filename: source.yaml
apiVersion: sources.eventing.knative.dev/v1alpha1
kind: CronJobSource
metadata:
  name: event-emitter
spec:
  schedule: "* * * * *"
  data: '{"message": "Hello world!"}'
  sink:
    apiVersion: eventing.knative.dev/v1alpha1
    kind: Broker
    name: default

```

This is everything you need to define a `Cron Job` source. You can apply it via:

```sh
kubectl apply -f source.yaml
```

Afterwards, the `event-emitter` will ...

- ... send the event defined in `data` every minute
- ... send this message to the `default broker`

### Service

The service is your actual application. The one which is responsible for receiving the respective event and performing some business logic. In our example, we just use an existing container image which takes a received event and prints it on `stdout`.

The definition looks like:

```yaml
# service.yaml
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: dumper
spec:
  template:
    spec:
      containers:
        - image: gcr.io/knative-releases/github.com/knative/eventing-sources/cmd/event_display
```

The service will be alive after executing `kubectl apply -f service.yaml` and the logs can be digested via:

```sh
kubectl logs -f  -l serving.knative.dev/service=dumper -c user-container --since=10m
```

Although we have applied `Broker`, `Source` and the `Service` you might be wondering why you can't see any output yet. This leads us to our last missing puzzle piece, the `Trigger`

### Trigger

A `Trigger` is an interesting beast to be honest. It acts as an indirection layer in which you can define _which events should be send to which service_. It basically binds a respective event type to a specific service. Nice and declarative, eh?

```yaml
# trigger.yaml

apiVersion: eventing.knative.dev/v1alpha1
kind: Trigger
metadata:
  name: trigger
spec:
  subscriber:
    ref:
      apiVersion: serving.knative.dev/v1alpha1
      kind: Service
      name: dumper
```

So after a `kubectl apply -f trigger.yaml`, we basically created a component which _triggers_ the service every time a new event arrives. You should see an output in the logs of the service from now on.

## Conclusion

In some of my previous customer projects we created such an eventing infrastructure ourselves. You may already have had the experience to build such a thingy. It is quite a lot of work, right? Establishing the broker infrastructure (via [RabbitMQ](https://www.rabbitmq.com/), [Apache Kafka](https://kafka.apache.org/), etc.) and defining all the event routing manually just to name two aspects of the overall work. _Knative Eventing_ ships with the correct primitives IMHO. It enables you to perform all the work you had to do manually in the past in a nice, declarative way.

My assumption is that _Knative_ in general is facing a bright future. A lot of engineers just avoided touching Kubernetes due to its high learning curve. You might consider the ecosystem again. _Knative_ is here to let you focus on shipping code without a lot of infrastructural overhead.

What is your impression of _Knative_ and _Knative Eventing_ in general?