I’ve been looking at Knative eventing a fair bit lately and one of the things I have been doing is building an eventing demo (the first part of which can be found here). As part of this demo, I wanted to understand how I could get CloudEvents that were being sent by my producer to display in real time via a web UI (event display service UI).
Here is a bit of info and an overview of the approach I took. The code to run through this tutorial can be found here.
Prerequisites and set-up
First, you will need to have Knative and your chosen Gateway provider installed (I tried this with both Istio and Gloo, which both worked fine). You can follow the instructions here.
Initially deploy the
001-namespace.yaml by running:
kubectl apply -f 001-namespace.yaml
Verify you have a broker:
kubectl -n knative-eventing-websocket-source get broker default
You will see that the broker has a URL, this is what we will use as our SINK in the next step.
Deploy the Blockchain Events Sender Application
The application that sends the events was discussed in my Knative Eventing: Part 1 post and you can find the repo with all the code for this application here.
To get up and running you can simply run the 010-deployment.yaml file. Here is a reminder of what it looks like:
apiVersion: apps/v1 kind: Deployment metadata: name: wseventsource namespace: knative-eventing-websocket-source spec: replicas: 1 selector: matchLabels: &labels app: wseventsource template: metadata: labels: *labels spec: containers: - name: wseventsource image: docker.io/josiemundi/wssourcecloudevents:latest env: - name: SINK value: "http://default-broker.knative-eventing-websocket-source.svc.cluster.local"
This is a Kubernetes app deployment. The name of the deployment is
wseventsource and the namespace is
knative-eventing-websocket-source. We have defined an environmental variable of
SINK, for which we set the
value as the address of our broker.
Verify events are being sent by running:
kubectl --namespace knative-eventing-websocket-source logs -l app=wseventsource --tail=100
This is what we currently have deployed:
Add a trigger – Send CloudEvents to Event-Display
Now we can deploy our trigger, which will set our event-display service as the subscriber.
# Knative Eventing Trigger to trigger the helloworld-go service apiVersion: eventing.knative.dev/v1alpha1 kind: Trigger metadata: name: wsevent-trigger namespace: knative-eventing-websocket-source spec: broker: default filter: sourceAndType: type: "" source: "" subscriber: ref: apiVersion: v1 kind: Service name: event-display
In the file above, we define our trigger name as wsevent-trigger and the namespace. In spec > filter I am basically specifying for the broker to send all events to the subscriber. The subscriber in this case is a Kubernetes services rather than a Knative Service.
kubectl apply -f 030-trigger.yaml
Now we have the following:
A trigger can exist before the service and vice versa. Let’s set up our event display.
Stream CloudEvents to Event Display service
I used the following packages to build the Event Display service:
- Go SDK for CloudEvents
- kncloudevents – event delivery for CloudEvents (built on top of the prior to build a cloudevents server)
- Event Source – server sent events in go
Originally I deployed my
event-display application as a Knative Service and this was fine but I could only access the events through the logs or by using curl.
Ideally, I wanted to build a stream of events that was push all the way to the UI. However, I discovered that for this use case it wasn’t possible to deploy this way. This is because Knative serving does not allow multiple ports in a service deployment.
I asked the question about it in the Knative Slack channel and the response was mainly to use mux and specify a path (I saw something similar in the sockeye GitHub project).
In the end, I chose to deploy as a native Kubernetes service instead. The reason is that it seemed like the most applicable way to do this, both in terms of functionality and also security. I was a little unsure about the feasibility of using mux in production as you may not want to expose an internal port externally.
For the kncloudevents project, I struggled to find detailed info or examples but the code is built on top of the Go sdk for CloudEvents and there are some detailed docs for the Python version.
We can use it to listen for HTTP cloudevents requests. By default it will listen on port 8080. When we use the
StartReceiver function, this is essentially telling our code to start listening. Because this happens on one port, we need another to
So here are the two yaml files that we deploy for the event-display.
apiVersion: apps/v1 kind: Deployment metadata: name: event-display namespace: knative-eventing-websocket-source spec: replicas: 1 selector: matchLabels: &labels app: event-display template: metadata: labels: *labels spec: containers: - name: event-display image: docker.io/josiemundi/bitcoinfrontendnew
apiVersion: v1 kind: Service metadata: name: event-display namespace: knative-eventing-websocket-source spec: type: NodePort ports: - port: 80 protocol: TCP targetPort: 8080 name: consumer - port: 9080 protocol: TCP targetPort: 9080 nodePort: 31234 name: dashboard selector: app: event-display
With everything deployed we now have the following:
Now if you head to the nodeport specified in the yaml:
Next time, we will look at how to send a reply event back into the Knative eventing space.