Introduction to Knative Eventing

Knative Eventing is a part of Knative that provides features for implementing event-driven architecture in a serverless way. Knative eventing has tools for routing events from event sources to sinks, enabling developers to create event-driven serverless applications.

You can use Knative eventing for the following:

Publishing an Event Without Creating a Consumer

You can send events to a broker as an HTTP POST from your application, which produces events. You can use subscriptions to decouple the producer applications from event consumers.

Consuming an Event Without Creating a Publisher

You can use a trigger to consume events from a broker, and filter them based on event attributes. The application receives events as an HTTP POST.

Eventing Patterns

There are three main patterns that you can implement using Knative Eventing:

  • Source to Sink

  • Channel and Subscription

  • Broker and Trigger

Source to Sink

Source to sink is the most basic eventing pattern. It has two kinds of Knative resource interfaces, Source and Sink.

Source

Source is responsible for sending the events to a Sink. Sources are the primary event producers. Sources are abstract Knative resources, so you must use an implementation of it to send events from it.

There are many types of Knative Sources. Here are a few that the Knative team maintains:

  • PingSource: Produces events with a fixed payload on a specified Cron schedule.

  • APIServerSource: Brings Kubernetes API server events into Knative. The APIServerSource fires a new event each time a Kubernetes resource is created, updated or deleted.

  • Apache Kafka: Brings Apache Kafka messages into Knative. The KafkaSource reads events from an Apache Kafka cluster, and passes these events to a sink to be consumed.

  • GitHub: Registers for events of the specified types on the specified GitHub organization or repository, and brings those events into Knative. The GitHubSource fires a new event for selected GitHub event types.

    For more information on other Knative Source types, you can visit the Knative Event Sources official documentation.

Sink

Sink is an adressable or a callable resource that can receive incoming events from other resources. Knative Services, Channels, and Brokers are all examples of sinks. You must use a Knative Service as a sink to implement the source to sink pattern. Channels and Brokers are the topics of other eventing patterns.

Creating Knative Sink Services

You can create a Knative service application as a sink by using the kn CLI or kubectl CLI. The following command creates a Service with the name knative-hello.

kn service create knative-hello \
  --concurrency-target=1 \
  --image=quay.io/redhattraining/kbe-knative-hello:0.0.1

The output must be as follows:

...output omitted...
 27.810s Ingress has not yet been reconciled.
 27.949s Waiting for load balancer to be ready
 28.053s Ready to serve.

Service 'knative-hello' created to latest revision 'knative-hello-00001' is available at URL:
https://knative-hello-YOUR_NAMESPACE.apps-crc.testing

Note

Note that the concurrency-target equals to 1. This means the service can handle only one request at a time. This configuration helps to demonstrate the auto-scale mechanism of Knative services.

Creating Eventing Sources

To create an Event Source, you can use the kn CLI, or kubectl CLI. The following command creates a PingSource that sends a JSON message every minute to the Knative Service sink knative-hello you’ve created.

kn source ping create knative-hello-ping-source \
  --schedule "* * * * *" \
  --data '{"message": "Hello from KBE!"}' \
  --sink ksvc:knative-hello
Ping source 'knative-hello-ping-source' created in namespace 'YOUR_NAMESPACE'.

The preceding command creates a PingSource resource as follows:

apiVersion: sources.knative.dev/v1beta2
kind: PingSource
metadata:
  name: knative-hello-ping-source
spec:
  data: '{"message": "Hello from KBE!"}'
  schedule: '* * * * *'
  sink:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: knative-hello

You can verify that you have the ping source created:

kn source ping list

The output must be as follows:

NAME                        SCHEDULE    SINK                 AGE    CONDITIONS   READY   REASON
knative-hello-ping-source   * * * * *   ksvc:knative-hello   2m5s   3 OK / 3     True

List the running pods of the knative-hello service and examine the logs:

kubectl get pods
NAME                                             READY   STATUS        RESTARTS   AGE
knative-hello-00001-deployment-cf6bb989b-jldk6   2/2     Running       0          6s
knative-hello-00001-deployment-cf6bb989b-vfj6m   2/2     Running       0          10s
kubectl logs \
-f knative-hello-00001-deployment-xxxx-xxxx \
-c user-container

The log output of the service must be as follows:

...output omitted...
2022-04-25 22:26:00,105 INFO  [eventing-hello] (executor-thread-1) ce-specversion=1.0
2022-04-25 22:26:00,105 INFO  [eventing-hello] (executor-thread-1) ce-time=2022-04-25T22:26:00.090162485Z
2022-04-25 22:26:00,106 INFO  [eventing-hello] (executor-thread-1) ce-type=dev.knative.sources.ping
2022-04-25 22:26:00,106 INFO  [eventing-hello] (executor-thread-1) content-type=null
2022-04-25 22:26:00,106 INFO  [eventing-hello] (executor-thread-1) content-length=30
2022-04-25 22:26:00,106 INFO  [eventing-hello] (executor-thread-1) POST:{"message": "Hello from KBE!"}
...output omitted...

Important

Because the ping source you’ve created sends event data in a 1 minute interval, you might have to wait before the ping occurs in the logs.

Channel and Subscription

Channels and Subscriptions provide an event pipe pattern that routes events between channels by using subscriptions.

Channel

Channel is an interface between the event source and the subscriber. A channel is a special kind of a sink, because they can store the incoming event data and distribute them to the subscribers.

There are a 3 types of channels in Knative:

  • In-memory Channel

  • Apache Kafka Channel

  • Google Cloud Platform Pub-sub Channel

    The In-memory Channel is the default channel in Knative. This channel does not provide any durability, message recovery or persistence. If you need a more reliable channel, you must use a channel such as the Apache Kafka Channel.

Subscription

A Subscription is responsible for connecting a channel with a service. In other words, services can subscribe to a channel via subscriptions. Once a service subscribes to a channel, it starts receiving the events.

Creating a Channel

To create a channel, you can run the following command:

kn channel create knative-hello-channel

You can verify the created channel by running the following command:

kn channel list

The output must be as follows:

NAME                    TYPE              ...
knative-hello-channel   InMemoryChannel

The YAML representation of this channel must be as follows:

apiVersion: messaging.knative.dev/v1
kind: Channel
metadata:
  name: knative-hello-channel

Note

Because no extra configurations were applied, Knative creates channels as an InMemoryChannel by default. Other types of channels are out of the scope of this tutorial. For more information, check out the resources, which are at the end of this tutorial.

To make the channel available for a Knative source usage, you must configure its sink as this channel. You can create a file called knative-hello-ping-source.yaml and paste the following content into it.

apiVersion: sources.knative.dev/v1beta2
kind: PingSource
metadata:
  name: knative-hello-ping-source
spec:
  schedule: "* * * * *"
  data: '{"message": "Hello from KBE!"}'
  sink:
   ref:
    apiVersion: messaging.knative.dev/v1
    kind: Channel
    name: knative-hello-channel

Notice that this is the YAML representation of the knative-hello-ping-source you have already created, but with a slight difference. The sink points to the channel knative-hello-channel in this configuration. This means that the ping source will use this channel to send the events. Any sink that subscribes to this channel must receive those events.

If you want to update the source, save the YAML file and apply it by using the following command:

kubectl apply -f knative-hello-ping-source.yaml

This command should update the Knative source knative-hello-ping-source to use the channel knative-hello-channel as sink.

Note

You can also use the kn update command to update the Knative resources.

Creating Subscriptions

We have our service knative-hello, but unless it subscribes to the channel, it cannot recieve the event messages. You can create a subscription for the knative-hello service by applying the following YAML configuration.

apiVersion: messaging.knative.dev/v1
kind: Subscription
metadata:
  name: knative-hello-subs
spec:
  channel:
    apiVersion: messaging.knative.dev/v1
    kind: Channel
    name: knative-hello-channel
  subscriber:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: knative-hello

You can save this YAML in a file called knative-hello-subs.yaml, and apply it by running the following command:

kubectl apply -f knative-hello-subs.yaml

Another way to create a subscription is to use the kn CLI. Before creating another subscription by using kn, you can create another sink service for a better demonstration of the channel and the subscriptions. Let’s name this service as knative-hello-2. This is the same service as knative-hello because it uses the same container image.

kn service create knative-hello-2 \
  --concurrency-target=1 \
  --image=quay.io/redhattraining/kbe-knative-hello:0.0.1

You have the knative-hello-2. You can create the subscription to enable the knative-hello-2 service to subscribe to the knative-hello-channel. The following command creates the subscription:

kn subscription create knative-hello-2-subs \
  --channel knative-hello-channel \
  --sink knative-hello-2

Feel free to run the following command to verify the created subscriptions:

kn subscription list

The output must be as follows:

NAME                   CHANNEL                         SUBSCRIBER             ...
knative-hello-2-subs   Channel:knative-hello-channel   ksvc:knative-hello-2
knative-hello-subs     Channel:knative-hello-channel   ksvc:knative-hello

When you run the kubectl get pods command, you can see that there are several pods running for the Knative services.

NAME                                                READY   STATUS        RESTARTS   AGE
knative-hello-00001-deployment-cf6bb989b-2zzxc      2/2     Running       0          2m33s
knative-hello-2-00001-deployment-6d65b95bd8-j4j4l   2/2     Running       0          93s
knative-hello-2-00001-deployment-6d65b95bd8-m4ks5   2/2     Running       0          93s

You can choose one of the pods to verify that the Channel-Subscription mechanism works. You can examine other pods' logs as well.

kubectl logs -f \
knative-hello-2-00001-deployment-xxxx-xxxx \
-c user-container

The logs output must be as follows:

...output omitted...
2022-04-26 13:30:00,289 INFO  [eventing-hello] (executor-thread-1) ce-specversion=1.0
2022-04-26 13:30:00,289 INFO  [eventing-hello] (executor-thread-1) ce-time=2022-04-26T13:30:00.26845184Z
2022-04-26 13:30:00,289 INFO  [eventing-hello] (executor-thread-1) ce-type=dev.knative.sources.ping
2022-04-26 13:30:00,290 INFO  [eventing-hello] (executor-thread-1) content-type=null
2022-04-26 13:30:00,290 INFO  [eventing-hello] (executor-thread-1) content-length=30
2022-04-26 13:30:00,290 INFO  [eventing-hello] (executor-thread-1) POST:{"message": "Hello from KBE!"}
...output omitted...

Important

Before continuing to the next topic, if you have created the resources in this tutorial so far then you must remove the ping source, channel, and subscriptions you have created.

kn subscription delete knative-hello-subs && \
kn subscription delete knative-hello-2-subs && \
kn channel delete knative-hello-channel && \
kn source ping delete knative-hello-ping-source

The output should be as follows:

Subscription 'knative-hello-subs' deleted in namespace 'functionia'.
Subscription 'knative-hello-2-subs' deleted in namespace 'functionia'.
Channel 'knative-hello-channel' deleted in namespace 'functionia'.

Broker and Trigger

Broker and Trigger provide an event mesh model. They allow distributing the events uniformly to consumers.

Brokers and Triggers implement the Content Based Router Enterprise Integration Pattern. They allow custom filtering for the events so that events can be distributed selectively.

Broker

Brokers are Knative custom resources that define an event mesh for collecting a pool of events.

Trigger

Triggers represent a subscription to events from a specific broker. Triggers are like subscriptions with a filtering configuration, they help you to decide which events to subscribe to for a particular business need.

Creating a Broker

You can create a broker called knative-hello-broker by using the following command:

kn broker create knative-hello-broker

If you have created it then you can verify the broker configuration by using the following command:

kn broker describe knative-hello-broker -o yaml
apiVersion: eventing.knative.dev/v1
kind: Broker
metadata:
  annotations:
    eventing.knative.dev/broker.class: MTChannelBasedBroker
  name: knative-hello-broker
...output omitted...
spec:
  config:
    apiVersion: v1
    kind: ConfigMap
    name: config-br-default-channel
    namespace: knative-eventing
...output omitted...

Note

By default, brokers use the in-memory channels on the backend. A set of configurations, which are configured via ConfigMaps, manage this behavior. These configurations are out of the scope of this tutorial. For more information, check out the resources, which are at the end of this tutorial.

Creating Triggers

Similar to the subscriptions, you must create triggers for each Knative service to create an eventing bridge.

You can run the following command to create a trigger for the knative-hello service:

kn trigger create knative-hello-trigger \
  --broker=knative-hello-broker \
  --sink=ksvc:knative-hello \
  --filter=type=merhaba

Notice that the trigger uses a filter type=merhaba. The type is the CloudEvent type that is mapped to the ce-type HTTP header. A Trigger can filter by any CloudEvent attributes such as type, source, or extension.

You can run a similar command for the knative-hello-2 service, this time for the filter type=hola:

kn trigger create knative-hello-2-trigger \
  --broker=knative-hello-broker \
  --sink=ksvc:knative-hello-2 \
  --filter=type=hola

The knative-hello service must handle events with the type merhaba, and the knative-hello-2 service must handle the ones with the type hola.

To verify your broker and triggers, you need the broker Kubernetes service URL. You can get the URL by running the following command:

kubectl get broker knative-hello-broker \
-o jsonpath='{.status.address.url}'

This must return an output as follows:

http://broker-ingress.knative-eventing.svc.cluster.local/YOUR_NAMESPACE/knative-hello-broker

The following command runs an application called greeting-requester, which sends twenty HTTP POST requests to the given URL in a CloudEvent format. If you want to try out this example, replace the YOUR_NAMESPACE with the namespace you are working on.

You can send some greetings in the type "merhaba" by using the following command.

kubectl run greeting-requester \
--image quay.io/redhattraining/kbe-greeting-requester:latest \
--env="BROKER_URL=http://broker-ingress.knative-eventing.svc.cluster.local/YOUR_NAMESPACE/knative-hello-broker"  \
--env="GREETING=merhaba"  \
--rm=True --attach=true --restart=Never

In this case, knative-hello service should scale up but the knative-hello-2 service should not.

You can send some greetings in the type "hola" by using the following command.

kubectl run greeting-requester \
--image quay.io/redhattraining/kbe-greeting-requester:latest \
--env="BROKER_URL=http://broker-ingress.knative-eventing.svc.cluster.local/YOUR_NAMESPACE/knative-hello-broker"  \
--env="GREETING=hola"  \
--rm=True --attach=true

In this case, the knative-hello-2 service should scale up but the knative-hello service should not. You can also set the GREETING environment variable in the preceding command to something like bonjour, and observe the services do not receive any requests.

These examples show that the filtering mechanism of triggers works successfully.