<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Paul Kaisharis</title>
    <description>The latest articles on DEV Community by Paul Kaisharis (@pkaisharis).</description>
    <link>https://dev.to/pkaisharis</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2074139%2F024cacb5-f980-4b44-9536-0e1a7d803d21.jpg</url>
      <title>DEV Community: Paul Kaisharis</title>
      <link>https://dev.to/pkaisharis</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pkaisharis"/>
    <language>en</language>
    <item>
      <title>Building Microservices Using Knative</title>
      <dc:creator>Paul Kaisharis</dc:creator>
      <pubDate>Thu, 19 Sep 2024 01:21:36 +0000</pubDate>
      <link>https://dev.to/pkaisharis/building-microservices-using-knative-53od</link>
      <guid>https://dev.to/pkaisharis/building-microservices-using-knative-53od</guid>
      <description>&lt;h2&gt;
  
  
  Building Microservices Using Knative
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;In this article, we will explore the development, implementation, and deployment of microservices using Knative, a powerful Kubernetes-based platform. The focus will be on leveraging various technologies to build, deploy, and manage microservices efficiently. We will cover Knative, Hashicorp Vault, and Confluent Cloud, highlighting their core capabilities and how they contribute to an overall micro-services ecosystem.  All code references in this article are available at &lt;a href="https://github.com/pksurferdad/knative-microservices" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.  This article assumes a basic working knowledge of kubernetes to be able to provision the required infrastructure components and to deploy the example microservices in this article.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;The following is a brief introduction into each of the components we will be discussing in this article.&lt;/p&gt;

&lt;h4&gt;
  
  
  What are Microservices?
&lt;/h4&gt;

&lt;p&gt;By now, microservice architectures are clearly defined and understood, but here is a brief definition to get us started.  Microservices are a design approach where a single application is composed of many loosely coupled and independently deployable services. Each service typically represents a specific business function and can be developed, deployed, and scaled independently. The benefits of using a microservices architecture include improved fault isolation, more straightforward deployment, and the ability to use different technologies for different services and purposes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Knative
&lt;/h4&gt;

&lt;p&gt;Knative is an open-source platform that extends Kubernetes to provide a set of middleware components necessary for building modern, container and service based applications. It focuses on simplifying the deployment of serverless and event-driven applications. The core components of Knative are Serving, for managing the services and Eventing, for providing cloud event and messaging support. These components help developers create scalable and manageable applications by handling auto-scaling, revision management, and event-driven workflows without having to worry about the underlying infrastructure.&lt;/p&gt;

&lt;h4&gt;
  
  
  Hashicorp Vault
&lt;/h4&gt;

&lt;p&gt;Hashicorp Vault is a tool for securely accessing secrets, such as API keys, passwords, and certificates. Vault manages and protects sensitive data using a combination of encryption, access control policies, and auditing. Vault ensures that your microservices can securely store and retrieve sensitive information.  This article will demonstrate how to integrate Vault with Knative services to securely fetch secrets required for your microservices.&lt;/p&gt;

&lt;h4&gt;
  
  
  Confluent Cloud
&lt;/h4&gt;

&lt;p&gt;Confluent Cloud is a fully managed event streaming platform based on Apache Kafka. It enables the real-time streaming of data and integrates seamlessly with other cloud services. Core components of Confluent Cloud include Kafka clusters, connectors for data integration, and stream processing capabilities. Confluent Cloud supports building event-driven microservices by providing a reliable and scalable event streaming backbone.  This article will demonstrate how to use Confluent Cloud as the event streaming backbone for event driven Knative services.&lt;/p&gt;

&lt;p&gt;In this article, we will delve into each of these technologies, demonstrating how to integrate them into a microservices based ecosystem using Knative. We will start by understanding the basics of Knative and proceed to set up our environment, build and deploy a microservice, handle events, explore advanced features, and finally, look at a real-world case study. Let’s get started!&lt;/p&gt;

&lt;h3&gt;
  
  
  Section 1: Understanding the Basics
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;What is Knative?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As described above, &lt;a href="https://knative.dev/" rel="noopener noreferrer"&gt;Knative&lt;/a&gt; provides a rich ecosystem for managing and executing microservices that can be developed in a variety of programming languages.  Any language that can be crafted into a web service and packaged as a kubernetes container is a viable execution candidate for a Knative service.  Since 2018, Knative has evolved as a viable microservices platform and in 2022 was accepted by the &lt;a href="https://www.cncf.io/projects/knative/" rel="noopener noreferrer"&gt;CNCF&lt;/a&gt; at the &lt;code&gt;incubating&lt;/code&gt; maturity level.&lt;/p&gt;

&lt;p&gt;Knative provides the following infrastructure components that form the basis of its microservices platform:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://knative.dev/docs/serving/" rel="noopener noreferrer"&gt;Knative Serving&lt;/a&gt; &lt;code&gt;required&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://knative.dev/docs/eventing/" rel="noopener noreferrer"&gt;Knative Eventing&lt;/a&gt; &lt;code&gt;optional, but where it gets interesting!&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Knative Serving provides the execution platform for serverless workloads and provides the necessary components to manage the lifecycle of a microservice. Knative Services provides the following capabilities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Automated Scaling&lt;/li&gt;
&lt;li&gt;Revision Management&lt;/li&gt;
&lt;li&gt;Traffic Splitting&lt;/li&gt;
&lt;li&gt;Routing and Networking&lt;/li&gt;
&lt;li&gt;Configuration and Autoscaling&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Knative Eventing provides the plumbing to support event-driven and message-based architectures.  Knative Eventing provides the following capabilities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Event Sources&lt;/li&gt;
&lt;li&gt;Event Brokers&lt;/li&gt;
&lt;li&gt;Triggers&lt;/li&gt;
&lt;li&gt;Channels&lt;/li&gt;
&lt;li&gt;Subscriptions&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Section 2: Setting Up Your Environment
&lt;/h3&gt;

&lt;p&gt;Let's start with setting up the required infrastructure components to deploy, execute, and manage a Knative service.  The manifests referenced below are also available at &lt;a href="https://github.com/pksurferdad/knative-microservices/blob/main/manifests/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will utilize &lt;a href="https://aws.amazon.com/eks/" rel="noopener noreferrer"&gt;AWS EKS&lt;/a&gt; to host our infrastructure components on Kubernetes (K8s).  The simplest method to create a k8s cluster on AWS is to utilize &lt;a href="https://eksctl.io/" rel="noopener noreferrer"&gt;eksctl&lt;/a&gt; following this &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.html" rel="noopener noreferrer"&gt;guidance&lt;/a&gt; from AWS.  &lt;/p&gt;

&lt;p&gt;Below is a sample &lt;code&gt;eksctl&lt;/code&gt; manifest to create and EKS cluster with an associated managed node group for running our workloads.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: my-knative-cluster
  region: us-east-1

availabilityZones: ['us-east-1d', 'us-east-1f']

iam:
  withOIDC: true
  serviceAccounts:
  - metadata:
      name: cluster-autoscaler
      namespace: kube-system
      labels: {aws-usage: "cluster-ops"}
    wellKnownPolicies:
      autoScaler: true
    roleName: eksctl-knative-cluster-autoscaler-role
    roleOnly: true

managedNodeGroups:
  - name: knative-private-ng
    instanceType: m5.large
    privateNetworking: true
    iam:
      withAddonPolicies:
        autoScaler: true
        certManager: true
        ebs: true
        efs: false
        cloudWatch: true
        albIngress: true
        externalDNS: true
    minSize: 2
    maxSize: 5
    desiredCapacity: 3
    volumeSize: 150
    ssh:
      allow: false
    labels: {
      role: worker,
      subnetType: private
      }
    tags:
      nodegroup-role: worker
      subnetType: private
      environment: dev

vpc:  
  clusterEndpoints:
    publicAccess: true
    privateAccess: true

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the manifest as &lt;code&gt;eksctl-kantive.yaml&lt;/code&gt; and run the command below to create the cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eksctl create cluster -f eksctl-knative.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood, ekctsl is using AWS CloudFormation to create the required AWS resources.  The creation process should take around 20 minutes.  &lt;/p&gt;

&lt;p&gt;To delete the cluster and associated node group, run the command below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eksctl delete cluster -f eksctl-knative.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Installing Knative&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Next, let's install Knative Serving and Eventing following this &lt;a href="https://knative.dev/docs/install/yaml-install/" rel="noopener noreferrer"&gt;guidance&lt;/a&gt;.  I typically prefer the YAML-based installation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Knative Serving
&lt;/h4&gt;

&lt;p&gt;Follow this &lt;a href="https://knative.dev/docs/install/yaml-install/serving/install-serving-with-yaml/" rel="noopener noreferrer"&gt;guidance&lt;/a&gt; from Knative to install the Serving components.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;Contour&lt;/code&gt; as the networking layer. I prefer &lt;code&gt;Contour&lt;/code&gt; as the networking layer given some of its enhanced networking and routing capabilities.&lt;/li&gt;
&lt;li&gt;Configure &lt;code&gt;Real DNS&lt;/code&gt; to allow your Knative Services to be accessed outside of the k8s cluster.  We'll use &lt;code&gt;knative.example.com&lt;/code&gt; as the sample domain name, similar to the Knative docs.&lt;/li&gt;
&lt;li&gt;Install &lt;code&gt;cert-manager&lt;/code&gt; to provide certificate management utilizing &lt;code&gt;lets-encrypt&lt;/code&gt; as the certificate issuer.  See this &lt;a href="https://knative.dev/docs/serving/encryption/external-domain-tls/" rel="noopener noreferrer"&gt;guidance&lt;/a&gt; from Knative that provides additional insights into the settings below.

&lt;ul&gt;
&lt;li&gt;Install &lt;code&gt;cert-manager&lt;/code&gt; following this &lt;a href="https://cert-manager.io/docs/installation/kubectl/" rel="noopener noreferrer"&gt;guidance&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Configure the cert-manager integration to use wild card certs at namespace level to simplify the use of secured certificates.  You can use the following &lt;code&gt;patch&lt;/code&gt; command to set which namespaces should use the certificate.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl patch --namespace knative-serving configmap config-network -p '{"data": {"namespace-wildcard-cert-selector": "{\"matchExpressions\": [{\"key\":\"networking.knative.dev/disableWildcardCert\", \"operator\": \"In\", \"values\":[\"false\"]}]}"}}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Next, add a label to the namespace where you'll be running your Knative services from to instruct Knative to generate an namespaced certificate used by all services in the namespace.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# create the namespace
kucectl create ns my-knative-services

# add the label
kubectl label ns my-knative-services networking.knative.dev/disableWildcardCert=false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Then, create a cluster-wide &lt;code&gt;cluster issuer&lt;/code&gt; utilizing lets-encrypt as the certificate provider.  Below is a sample cluster issuer utilizing AWS Route53.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-dns
  namespace: cert-manager
spec:
  acme:
    # The ACME server URL
    server: https://acme-v02.api.letsencrypt.org/directory
    # Email address used for ACME registration
    email: email@example.com
    # Name of a secret used to store the ACME account private key from step 3
    privateKeySecretRef:
      name: letsencrypt-private-key-dns
    solvers:
    - selector:
        dnsZones:
         - "example.com"
      dns01:
        route53:
          region: "us-east-1"
          hostedZoneID: AWSRoute53HostedZoneID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Finally, set &lt;code&gt;external-domain-tls&lt;/code&gt; to &lt;code&gt;Enabled&lt;/code&gt; in the &lt;code&gt;config-network&lt;/code&gt; config map in the &lt;code&gt;knative-serving&lt;/code&gt; namespace.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these settings in place, Knative can now create a secured HTTPS end-point to securely execute your Knative services.  More details to follow as we create and deploy our sample Knative services.&lt;/p&gt;

&lt;h4&gt;
  
  
  Knative Eventing
&lt;/h4&gt;

&lt;p&gt;Now, let's install Knative Eventing following this &lt;a href="https://knative.dev/docs/install/yaml-install/eventing/install-eventing-with-yaml/" rel="noopener noreferrer"&gt;general guidance&lt;/a&gt;.  We'll add additional components as needed as we build out the examples that utilize Eventing capabilities.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://knative.dev/docs/install/yaml-install/eventing/install-eventing-with-yaml/#install-knative-eventing" rel="noopener noreferrer"&gt;Install&lt;/a&gt; the Knative CRD's and associated manifests to install the core Eventing components.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://knative.dev/docs/install/yaml-install/eventing/install-eventing-with-yaml/#optional-install-a-broker-layer" rel="noopener noreferrer"&gt;Install&lt;/a&gt; the kafka broker to install the necessary components to integrate with a Kafka cluster.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The diagram below provides an overview of the Eventing workflow that will be further illustrated with samples later in this article.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3jw9jb8npxny22587zb0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3jw9jb8npxny22587zb0.jpg" alt="Eventing Workflow" width="757" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Installing / Configuring Confluent Cloud&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Kafka is a powerful, but also complicated, data streaming and eventing platform. The easiest way to run a kafka cluster is to utilize Confluent's &lt;a href="https://www.confluent.io/confluent-cloud/" rel="noopener noreferrer"&gt;cloud offering&lt;/a&gt; to provide messaging and eventing services to Knative.  Follow the &lt;a href="https://www.confluent.io/confluent-cloud/tryfree/" rel="noopener noreferrer"&gt;guidance&lt;/a&gt; provided by Confluent to spin up a Kafka cluster.&lt;/p&gt;

&lt;p&gt;Execute the following to connect your Knative cluster to the Confluent Cloud Kafka cluster and to create a default kafka topic that will be used by the sample event-based Knative services.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the Confluent Kafka secret to provide secure communications between your Knative services and Confluent Cloud.

&lt;ul&gt;
&lt;li&gt;Confluent Cloud utilizes TLS certificates from &lt;a href="https://letsencrypt.org/" rel="noopener noreferrer"&gt;Let's Encrypt&lt;/a&gt;.  You can download the referenced &lt;code&gt;.pem&lt;/code&gt; file from the Let's Encrypt &lt;a href="https://letsencrypt.org/certificates/" rel="noopener noreferrer"&gt;Chain of Trust&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create a Confluent Cloud API key and secret to use as the &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl create secret --namespace knative-eventing generic ccloud \
  --from-literal=protocol=SASL_SSL \
  --from-literal=sasl.mechanism=PLAIN \
  --from-file=ca.crt=isrgrootx1.pem
  --from-literal=user=&amp;lt;API KEY&amp;gt;
  --from-literal=password=&amp;lt;API SECRET&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create a &lt;a href="https://knative.dev/docs/install/yaml-install/eventing/install-eventing-with-yaml/#optional-install-a-broker-layer" rel="noopener noreferrer"&gt;Kafka broker&lt;/a&gt; which will auto-create a topic in Confluent.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Note the reference below to a &lt;code&gt;kafka-knative-dlq&lt;/code&gt;, this is a Knative service that can be implemented to handle messages and events that fail to be delivered to their target destination.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: eventing.knative.dev/v1
kind: Broker
metadata:
  annotations:
    # case-sensitive
    eventing.knative.dev/broker.class: Kafka
  name: knative-kafka-broker
  namespace: my-knative-services
spec:
  # Configuration specific to this broker.
  config:
    apiVersion: v1
    kind: ConfigMap
    name: kafka-broker-config
    namespace: knative-eventing
  delivery:
    deadLetterSink:
      ref:
        apiVersion: serving.knative.dev/v1
        kind: Service
        name: knative-kafka-dlq
        namespace: my-knative-services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create the broker config map. Note the end-point to the bootstrap server is provided by Confluent Cloud
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: ConfigMap
metadata:
  name: knative-broker-config
  namespace: knative-eventing
data:
  # Number of topic partitions
  default.topic.partitions: "10"
  # Replication factor of topic messages.
  default.topic.replication.factor: "3"
  # A comma separated list of bootstrap servers. (It can be in or out the k8s cluster)
  bootstrap.servers: "GetFromConfluentCloud"
  auth.secret.ref.name: ccloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Configure the &lt;a href="https://knative.dev/docs/install/yaml-install/eventing/install-eventing-with-yaml/#optional-install-a-broker-layer" rel="noopener noreferrer"&gt;default broker&lt;/a&gt; implementation by applying the manifest below.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: ConfigMap
metadata:
  name: config-br-defaults
  namespace: knative-eventing
data:
  default-br-config: |
    clusterDefault:
      brokerClass: Kafka
      apiVersion: v1
      kind: ConfigMap
      name: kafka-broker-config
      namespace: knative-eventing
    namespaceDefaults:
      my-knative-services:
        brokerClass: Kafka
        apiVersion: v1
        kind: ConfigMap
        name: kafka-broker-config
        namespace: knative-eventing  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install and configure the &lt;a href="https://knative.dev/docs/install/yaml-install/eventing/install-eventing-with-yaml/#optional-install-a-broker-layer" rel="noopener noreferrer"&gt;kafka sink&lt;/a&gt; implementation which we'll describe in more detail later in this article, but in short, this Knative Eventing add-on will allow us to write cloud events to a Kafka topic hosted in our Confluent Cloud cluster.  The Kafka Controller has already been installed and only the &lt;a href="https://knative.dev/docs/eventing/sinks/kafka-sink/#installation" rel="noopener noreferrer"&gt;KafkaSink Data Plane&lt;/a&gt; is required at this point.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Installing / Configuring Hashicorp Vault&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is beyond the scope of this article to cover all the capabilities of Hashicorp Vault; however, this article will demonstrate how to utilize Vault to provide secrets and configuration settings to Knative services.  Vault can be installed as a self-managed service in your own &lt;a href="https://developer.hashicorp.com/vault/docs/platform/k8s/helm" rel="noopener noreferrer"&gt;K8s cluster&lt;/a&gt; or you can utilize Hashicorp's &lt;a href="https://portal.cloud.hashicorp.com/" rel="noopener noreferrer"&gt;hosted solution&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Section 3: Building and Deploying a Microservice
&lt;/h3&gt;

&lt;p&gt;Now that we have installed and configured Knative Serving and Eventing, backed by a kafka cluster hosted on Confluent Cloud, let's create a simple Knative service and deploy the service to the Knative cluster. In this article, we'll build our services using Python utilizing &lt;a href="https://flask.palletsprojects.com/" rel="noopener noreferrer"&gt;Flask&lt;/a&gt;, [Gunicorn[(&lt;a href="https://gunicorn.org/)" rel="noopener noreferrer"&gt;https://gunicorn.org/)&lt;/a&gt;], and &lt;a href="https://werkzeug.palletsprojects.com/" rel="noopener noreferrer"&gt;Werkzeug&lt;/a&gt; to implement the service end-point.  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Creating, Deploying, and Executing a Simple Microservice&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To ensure we set up everything correctly and to understand the basics of a Knative service, let's create a simple Python service implemented using Flask.  We will continue to build upon this basic service demonstrating more advanced Knative capabilities.&lt;/p&gt;

&lt;p&gt;In this section of the article, we will...&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;build a simple python service&lt;/li&gt;
&lt;li&gt;create the YAML configuration to deploy the service&lt;/li&gt;
&lt;li&gt;demonstrate how to call the deployed service&lt;/li&gt;
&lt;li&gt;run the service on a schedule using a cronjob like resource from Knative called PingSource&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Simple Python Service&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Below is a simple python web service we can start with. Also available at &lt;a href="https://github.com/pksurferdad/knative-microservices/tree/main/samples/simple-service" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.  The service simply returns a 200 OK response and also implements logging and some basic exception handling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
import os
import flask
from flask import Flask, request, jsonify
from flask.logging import create_logger
from werkzeug.exceptions import HTTPException

# flask app configuration
app = Flask(__name__)
log = create_logger(app)
log.setLevel(os.environ.get('LOG_LEVEL', 'DEBUG'))

# Implement the main route of our application
@app.route('/', methods=['GET','POST'])
def main():

  # stuff your service does

  return 'Your knative service completed successfully!', 200

@app.errorhandler(HTTPException)
def handle_http_exception(e):
    log.error('HTTP Exception: %s', (e))
    response = {
        'success': False,
        'error': {
            'type': e.name,
            'message': e.description,
        }
    }    # replace the body with JSON
    return jsonify(response), e.code

@app.errorhandler(RuntimeError)
def handle_runtime_error(error):
    message = [str(x) for x in error.args]
    log.error(message)
    response = {
        'success': False,
        'error': {
            'type': error.__class__.__name__,
            'message': message
        }
    }

    return jsonify(response), 422

@app.errorhandler(Exception)
def unhandled_exception(error):
    log.error('Unhandled Exception: %s', (error))
    response = {
        'success': False,
        'error': {
            'type': error.__class__.__name__,
            'message': 'An unexpected error has occurred.',
        }
    }

    return jsonify(response), 500

if __name__ == "__main__":
    app.run(host='0.0.0.0',port=int(os.environ.get('PORT', 8080)))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;YAML Configuration to Deploy the Service&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To deploy the service, apply the YAML file below.  Note that the &lt;code&gt;kind&lt;/code&gt; is a service; however, the manifest represents a Knative Custom Resource Definition (ksvc) that implements all the necessary kubernetes resources, listed below, to manage the full lifecycle and execution of a Knative microservice&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw26xqkcxvfuqpw4iqfvy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw26xqkcxvfuqpw4iqfvy.jpg" alt="Knative Components" width="800" height="135"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note the annotation &lt;code&gt;autoscaling.knative.dev/minScale: "0"&lt;/code&gt; which allows a knative service to scale to 0, meaning the service will be terminated after a period of time, but will activate when a request is made against the service.  A nice way to save on Kubernetes resources.  &lt;code&gt;minScale&lt;/code&gt; is one of many annotations that Knative provides to manage scaling of Knative services.  Others can be found in the Knative docs under &lt;a href="https://knative.dev/docs/serving/autoscaling/" rel="noopener noreferrer"&gt;autoscaling&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Save the manifest as &lt;code&gt;service.yaml&lt;/code&gt; and run the command &lt;code&gt;kubectl apply -f service.yaml&lt;/code&gt; to deploy the service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: simple-service
  namespace: my-knative-services
spec:
  template:
    metadata:
      annotations:
        autoscaling.knative.dev/minScale: "0"
    spec:
      containers:
        - image: ghcr.io/pksurferdad/knative-microservices/simple-service:latest
          name: simple-service
          command: ["/bin/bash", "-ec"]
          args: ["exec gunicorn --bind :$PORT --workers 1 --threads 8 service:app"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure the &lt;code&gt;simple-service&lt;/code&gt; starts successfully in the &lt;code&gt;my-knative-services&lt;/code&gt; namespace and if all goes well, we should now be able to call the service using the &lt;code&gt;curl&lt;/code&gt; command below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;my-knative-services&lt;/code&gt; is the namespace where the service is running&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;knative.example.com&lt;/code&gt; is the host name we set up when we first installed and configured the Knative Serving component.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --location 'https://simple-service.my-knative-services.knative.example.com'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run The Service On A Schedule&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now that the service is running, we can utilize another Custom Resource Definition provided by Knative Serving, &lt;code&gt;PingSource&lt;/code&gt; to run our services on a schedule.  Essentially, a PingSource is an event source that produces events with a fixed payload on a specified cron schedule.  You can use &lt;code&gt;PingSource&lt;/code&gt; to simply run your services on a schedule or can also pass a payload to your service to execute a specific action.  Note the &lt;code&gt;timezone&lt;/code&gt; attribute in the spec that controls which time zone the schedule should executed in.  See the &lt;a href="https://knative.dev/docs/eventing/sources/ping-source/reference/#pingsource" rel="noopener noreferrer"&gt;PingSource&lt;/a&gt; reference for additional configurations the &lt;code&gt;PingSource&lt;/code&gt; CRD supports.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: sources.knative.dev/v1
kind: PingSource
metadata:
  name: simple-service-pingsource
  namespace: my-knative-services
spec:
  timezone: America/New_York
  schedule: "0 10 * * *"
  sink:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: simple-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Adding in Vault&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that we have a basic service deployed running on a schedule, let's add in Hashicorp Vault to make secrets available to our service.  The YAML below incorporates annotations for Vault which includes incorporating Vault's integration of Go &lt;a href="https://developer.hashicorp.com/vault/docs/agent-and-proxy/agent/template" rel="noopener noreferrer"&gt;Consul Templates&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some things to note about the service deployment incorporating Vault secrets.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The role &lt;code&gt;app-user&lt;/code&gt; is authorized to read and list secrets from Vault&lt;/li&gt;
&lt;li&gt;The serviceAccountName &lt;code&gt;vault-auth&lt;/code&gt; is authorized by Kubernetes to call the Vault secrets injection API&lt;/li&gt;
&lt;li&gt;The Vault injection template loops through each secret defined in &lt;code&gt;my-secrets&lt;/code&gt; creating a key-value template that is utilized by the &lt;code&gt;args&lt;/code&gt; directive to mount the environment variables in the kubernetes container which makes the variables available to the service&lt;/li&gt;
&lt;li&gt;The deployment also illustrates using standard Kubernetes environment variables allowing both methods, secrets from vault, and variables from the deployment manifest to make secrets and non-secured variables available to a Knative service
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: simple-service-with-vault
  namespace: my-knative-services
spec:
  template:
    metadata:
      annotations:
        autoscaling.knative.dev/minScale: "0"
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "app-user"
        vault.hashicorp.com/tls-skip-verify: "true"
        vault.hashicorp.com/agent-inject-secret-my-secret.env: "secrets/my-secrets"
        vault.hashicorp.com/agent-inject-template-my-secrets.env: |
          {{- with secret "secrets/my-secrets" -}}
          {{ range $key, $value := .Data -}}
          export {{ $key }}="{{ $value }}"
          {{ end }}
          {{- end -}}
    spec:      
      imagePullSecrets:
        - name: docker-json
      serviceAccountName: vault-auth
      containers:
        - image: ghcr.io/pksurferdad/knative-microservices/simple-service:latest
          name: simple-service-with-vault
          command: ["/bin/bash", "-ec"]
          args: ["source /vault/secrets/my-secret.env &amp;amp;&amp;amp;
                  exec gunicorn --bind :$PORT --workers 1 --threads 8 service:app"]
          env:
          - name: VARIABLE_NAME
            value: VARIABLE_VALUE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Section 4: Handling Events with Knative Eventing
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Introduction to Knative Eventing&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this section of the article, we will focus on on how to implement Knative services that support the &lt;a href="https://cloudevents.io/" rel="noopener noreferrer"&gt;cloudevents&lt;/a&gt; specification.  As mentioned previously, the diagram below illustrates the workflow of cloud events in Knative:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7mf7slacj3e8dhwr60z.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7mf7slacj3e8dhwr60z.jpg" alt="Eventing Workflow" width="757" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll demonstrate the capabilities of Knative Eventing by implementing an event handler that publishes events which are picked up by an event subscriber.  All the resources mentioned in this section are available at &lt;a href="https://github.com/pksurferdad/knative-microservices/tree/main/samples" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.  The pattern of event producer and event subscriber provides the foundation for implementing a variety of event based applications and services. Later in the article, we'll elaborate on a real-world use-case using this pattern.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Event Handler&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Build the Event Handler&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's start with the &lt;code&gt;event handler&lt;/code&gt; that can handle incoming event requests and publish the requests to the Knative Eventing Broker which we created earlier in this article. &lt;/p&gt;

&lt;p&gt;Below is a simple &lt;code&gt;event handler&lt;/code&gt; that validates if the required cloud event headers are included with the request and if they are included, publishes the event to the Knative Eventing Broker.  As mentioned previously, the Knative Eventing Broker is backed by a Kafka cluster hosted on Confluent Cloud that provides the necessary fault tolerant and guaranteed message delivery capabilities a mission critical eventing system would need.&lt;/p&gt;

&lt;p&gt;Some things to note about the &lt;code&gt;event handler&lt;/code&gt; service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The service needs an environment variable named &lt;code&gt;broker_url&lt;/code&gt; which is the url to the Knative Broker we created earlier.  To get the url of the broker, run the following kubectl command
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   kubectl get broker -n my-knative-services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The service checks to ensure two cloud event headers, &lt;code&gt;ce_type&lt;/code&gt; and &lt;code&gt;ce_source&lt;/code&gt; are included in the request. These two cloud event attributes are an important component to how the events gets brokered or routed to the desired target to handle the payload of the event&lt;/li&gt;
&lt;li&gt;The service utilizes the &lt;code&gt;cloudevents&lt;/code&gt; python library to build and structure the event to be published
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
import os
import requests
import flask
from flask import Flask, request, jsonify
from flask.logging import create_logger
from werkzeug.datastructures import Headers
from werkzeug.exceptions import HTTPException
from cloudevents.http import CloudEvent, to_structured

# flask app configuration
app = Flask(__name__)
log = create_logger(app)
log.setLevel(os.environ.get('LOG_LEVEL', 'DEBUG'))


# environment variables
broker_url = os.environ.get('BROKER_URL', None)

@app.route('/', methods=['POST'])
def main():
    # process the request message and send it to the knative broker
    event_headers = request.headers
    event_message = request.get_json(force=True)

    required_headers = ['ce_type', 'ce_source']
    missing_headers = [header for header in required_headers if header not in event_headers]

    if missing_headers:
        log.debug(f'event message: {event_message}')
        raise RuntimeError(f'Message cannot be processed. Missing required event headers: {", ".join(missing_headers)}')

    # build the event and http request
    attributes = {
        'type' : event_headers['ce_type'],
        'source' : event_headers['ce_source']
    }

    event = CloudEvent(attributes,event_message)
    headers, body = to_structured(event)

    resp = requests.post(broker_url,
                         headers=headers,
                         data=body)

    if resp.status_code != 202:
        raise RuntimeError(f"Unexpected status code {resp.status_code}: {resp.text}")

    log.info("sent message for event: {}. broker response: response code {} response text {}".format(event_headers['ce_type'],resp.status_code, resp.text))

    response = {
    'success' : True,
    'message' : 'Message successfully processed!'
    }

    return jsonify(response), 200


@app.errorhandler(HTTPException)
def handle_http_exception(e):
    log.error('HTTP Exception: {}'.format(e))
    response = {
        'success': False,
        'error': {
            'type': e.name,
            'message': e.description,
        }
    }    # replace the body with JSON
    return jsonify(response), e.code


@app.errorhandler(RuntimeError)
def handle_runtime_error(error):
    message = [str(x) for x in error.args]
    log.error(message)
    response = {
        'success': False,
        'error': {
            'type': error.__class__.__name__,
            'message': message
        }
    }

    return jsonify(response), 422


@app.errorhandler(Exception)
def unhandled_exception(error):
    log.error('Unhandled Exception: {}'.format(error))
    response = {
        'success': False,
        'error': {
            'type': error.__class__.__name__,
            'message': 'An unexpected error has occurred.',
        }
    }

    return jsonify(response), 500


if __name__ == "__main__":
    app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Deploy the Event Handler&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, let's deploy the event handler to our Knative cluster.  Apply the manifest below to deploy the service.&lt;/p&gt;

&lt;p&gt;Some things to note about the Knative service deployment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The deployment is using Hashicorp Vault to maintain secrets and we can include the &lt;code&gt;broker_url&lt;/code&gt; variable in vault&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;minScale&lt;/code&gt; annotation is set to &lt;code&gt;3&lt;/code&gt; which indicates we want 3 load balanced services to run continuously.  This is desirable to handle concurrent requests hitting the Knative service.  Knative Serving also has the ability to scale up services based on Knative Serving's built in &lt;a href="https://knative.dev/docs/serving/autoscaling/" rel="noopener noreferrer"&gt;autoscaling&lt;/a&gt; capabilities.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: event-handler
  namespace: my-knative-services
spec:
  template:
    metadata:
      annotations:
        autoscaling.knative.dev/minScale: "3"
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "app-user"
        vault.hashicorp.com/tls-skip-verify: "true"
        vault.hashicorp.com/agent-inject-secret-my-secrets.env: "secrets/my-secrets"
        vault.hashicorp.com/agent-inject-template-my-secrets.env: |
          {{- with secret "secrets/my-secrets" -}}
          {{ range $key, $value := .Data -}}
          export {{ $key }}="{{ $value }}"
          {{ end }}
          {{- end -}}
    spec:      
      imagePullSecrets:
        - name: docker-json
      serviceAccountName: vault-auth
      containers:
        - image: ghcr.io/pksurferdad/knative-microservices/event-handler:latest
          name: event-handler
          command: ["/bin/bash", "-ec"]
          args: ["source /vault/secrets/my-secrets.env &amp;amp;&amp;amp;
                  exec gunicorn --bind :$PORT --workers 1 --threads 8 service:app"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Event Subscriber&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Build the Event Subscriber&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now that we have an event handler running, let's build the other side the eventing implementation and create the event subscriber.  The event subscriber will handle events and the associated payloads that are pushed to the subscriber. &lt;/p&gt;

&lt;p&gt;Below is a simple event subscriber that will process the published event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
import os
import requests
import flask
from flask import Flask, request, jsonify
from flask.logging import create_logger
from werkzeug.datastructures import Headers
from werkzeug.exceptions import HTTPException

# flask app configuration
app = Flask(__name__)
log = create_logger(app)
log.setLevel(os.environ.get('LOG_LEVEL', 'DEBUG'))

@app.route('/', methods=['POST'])
def main():
    # process the event message delivered by the event broker
    event_message = request.get_json(force=True)

    # do something with the event message
    log.info('Received event message: {}'.format(json.dumps(event_message)))

    response = {
    'success' : True,
    'message' : 'Message successfully processed!'
    }

    return jsonify(response), 200


@app.errorhandler(HTTPException)
def handle_http_exception(e):
    log.error('HTTP Exception: {}'.format(e))
    response = {
        'success': False,
        'error': {
            'type': e.name,
            'message': e.description,
        }
    }    # replace the body with JSON
    return jsonify(response), e.code


@app.errorhandler(RuntimeError)
def handle_runtime_error(error):
    message = [str(x) for x in error.args]
    log.error(message)
    response = {
        'success': False,
        'error': {
            'type': error.__class__.__name__,
            'message': message
        }
    }

    return jsonify(response), 422


@app.errorhandler(Exception)
def unhandled_exception(error):
    log.error('Unhandled Exception: {}'.format(error))
    response = {
        'success': False,
        'error': {
            'type': error.__class__.__name__,
            'message': 'An unexpected error has occurred.',
        }
    }

    return jsonify(response), 500


if __name__ == "__main__":
    app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Deploy the Event Subscriber&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Apply the manifest below to deploy the &lt;code&gt;event subscriber&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: event-subscriber
  namespace: my-knative-services
spec:
  template:
    metadata:
      annotations:
        autoscaling.knative.dev/minScale: "1"
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "app-user"
        vault.hashicorp.com/tls-skip-verify: "true"
        vault.hashicorp.com/agent-inject-secret-my-secrets.env: "secrets/my-secrets"
        vault.hashicorp.com/agent-inject-template-my-secrets.env: |
          {{- with secret "secrets/my-secrets" -}}
          {{ range $key, $value := .Data -}}
          export {{ $key }}="{{ $value }}"
          {{ end }}
          {{- end -}}
    spec:      
      imagePullSecrets:
        - name: docker-json
      serviceAccountName: vault-auth
      containers:
        - image: ghcr.io/pksurferdad/knative-microservices/event-subscriber:latest
          name: event-handler
          command: ["/bin/bash", "-ec"]
          args: ["source /vault/secrets/my-secrets.env &amp;amp;&amp;amp;
                  exec gunicorn --bind :$PORT --workers 1 --threads 8 service:app"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Event Trigger&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We now have the &lt;code&gt;event handler&lt;/code&gt; and the &lt;code&gt;event subscriber&lt;/code&gt;, but how do we now connect the two together?  Introducing the Knative Eventing &lt;a href="https://knative.dev/docs/eventing/triggers/#example-triggers" rel="noopener noreferrer"&gt;trigger&lt;/a&gt;.  Using the Knative Broker we created earlier, the Knative trigger will instruct the Knative Broker to publish events to the &lt;code&gt;event-subscriber&lt;/code&gt; service for events that have a &lt;code&gt;type&lt;/code&gt; of &lt;code&gt;event-subscriber-type&lt;/code&gt; and a &lt;code&gt;source&lt;/code&gt; of &lt;code&gt;event-subscriber-source&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Apply the manifest below to deploy the trigger.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
  name: event-subscriber-trigger
  namespace: my-knative-services
spec:
  broker: knative-kafka-broker
  filter:
    attributes:
      type: event-subscriber-type
      source: event-subscriber-source
  subscriber:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: event-subscriber
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the kubectl command below to ensure the trigger is in a ready state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl get trigger event-subscriber-trigger -n my-knative-services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Test the Event Workflow&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To test the overall eventing workflow, you can run the curl command below which will call the &lt;code&gt;event handler&lt;/code&gt; and pass in the &lt;code&gt;cloud event&lt;/code&gt; headers and the payload to be routed to the &lt;code&gt;event subscriber&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --location 'https://event-handler.my-knative-services.knative.example.com' \
--header 'Content-Type: application/json' \
--header 'ce_source: event-subscriber-source' \
--header 'ce_type: event-subscriber-type' \
--data '{
    "event_data_1": "data value 1",
    "event_data_2": "data value 2",
    "event_data_3": "data value 2"
}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To ensure everything worked as expected, you can use the kubectl command below to tail the logs of both the &lt;code&gt;event handler&lt;/code&gt; and the &lt;code&gt;event subscriber&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl logs -f &amp;lt;event handler pod&amp;gt; -c event-handler -n my-knative-services
kubectl logs -f &amp;lt;event subscriber pod&amp;gt; -c event-subscriber -n my-knative-services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Incorporate the KafkaSink Add-on&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Earlier in this article, we installed the KafkaSink add-on. This add-on gives us the ability to reliably publish cloud events to a Kafka topic hosted on our Confluent Cloud kafka cluster.  In this section of the article, we'll set up an instance of the KafkaSink add-on and incorporate the add-on in a Knative Service that publishes a cloud event to a Kafka topic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configure the KafkaSink Add-on&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's first configure and create a KafkaSink resource using the manifest below.  Before applying the manifest, you can manually create the topic &lt;code&gt;mytopic&lt;/code&gt; in the Confluent Cloud cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: eventing.knative.dev/v1alpha1
kind: KafkaSink
metadata:
   name: my-kafka-sink
   namespace: my-knative-services
spec:
   topic: mytopic
   bootstrapServers:
      - "GetFromConfluentCloud"
   auth.secret.ref.name: ccloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get the URL to the &lt;code&gt;KafkaSink&lt;/code&gt; resource, run the kubectl command below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl get kafkasink -n knative-eventing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;KafkaSink&lt;/code&gt; URL should look something like &lt;code&gt;http://kafka-sink-ingress.knative-eventing.svc.cluster.local/my-knative-services/my-kafka-sink&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Service Using the KafkaSink Add-on&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Knative Service below is a variant of the &lt;code&gt;event subscriber&lt;/code&gt; service with the addition of publishing an event to a Kafka topic using the &lt;code&gt;KafkaSink&lt;/code&gt; resource we created above.  Note that the service takes an environment variable &lt;code&gt;KAFKA_SINK_URL&lt;/code&gt; which is the URL we got from the above kubectl command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
import json
import requests
import os
from flask import Flask, request, jsonify
from flask.logging import create_logger
from werkzeug.datastructures import Headers
from werkzeug.exceptions import HTTPException
from cloudevents.http import CloudEvent, to_structured 

# flask app configuration
app = Flask(__name__)
log = create_logger(app)
log.setLevel(os.environ.get('LOG_LEVEL', 'DEBUG'))

# environment variables
KAFKA_SINK_URL = os.environ.get('KAFKA_SINK_URL', None)
headers = {'content-type': 'application/json'}

@app.route('/', methods=['POST'])
def main():
    # process the request message and send it to the knative kafka sink resource
    event_headers = request.headers
    event_message = request.get_json(force=True)

    # do something with the event message
    log.info('event message: {}'.format(event_message))

    # build the event and http request
    attributes = {
        'type' : 'dev.kafka.type',
        'source' : 'dev.kafka.source'
    }

    event = CloudEvent(attributes,event_message)
    headers, body = to_structured(event)

    # send the event to the kafka-sink-url
    resp = requests.post(KAFKA_SINK_URL, headers=headers, data=body)
    log.info('response code: {}'.format(resp.status_code))

    if resp.status_code != 202:
        raise RuntimeError(str(resp.status_code) + ' ' + resp.text)

    return '', 200


@app.errorhandler(HTTPException)
def handle_http_exception(e):
    log.error('HTTP Exception: {}'.format(e))
    response = {
        'success': False,
        'error': {
            'type': e.name,
            'message': e.description,
        }
    }    # replace the body with JSON
    return jsonify(response), e.code


@app.errorhandler(RuntimeError)
def handle_runtime_error(error):
    message = [str(x) for x in error.args]
    log.error(message)
    response = {
        'success': False,
        'error': {
            'type': error.__class__.__name__,
            'message': message
        }
    }

    return jsonify(response), 422


@app.errorhandler(Exception)
def unhandled_exception(error):
    log.error('Unhandled Exception: {}'.format(error))
    response = {
        'success': False,
        'error': {
            'type': error.__class__.__name__,
            'message': 'An unexpected error has occurred.',
        }
    }

    return jsonify(response), 500


if __name__ == "__main__":
    app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Deploy the Event Subscriber with Sink Service&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can use the manifest below to deploy the event subscriber with the added KafkaSink support.  Note that if you are using Hashicorp Vault integration, the &lt;code&gt;KAFKA_SINK_URL&lt;/code&gt; is in the &lt;code&gt;my-secrets&lt;/code&gt; secret.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: event-subscriber-with-sink
  namespace: my-knative-services
spec:
  template:
    metadata:
      annotations:
        autoscaling.knative.dev/minScale: "1"
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "app-user"
        vault.hashicorp.com/tls-skip-verify: "true"
        vault.hashicorp.com/agent-inject-secret-my-secrets.env: "secrets/my-secrets"
        vault.hashicorp.com/agent-inject-template-my-secrets.env: |
          {{- with secret "secrets/my-secrets" -}}
          {{ range $key, $value := .Data -}}
          export {{ $key }}="{{ $value }}"
          {{ end }}
          {{- end -}}
    spec:      
      imagePullSecrets:
        - name: docker-json
      serviceAccountName: vault-auth
      containers:
        - image: ghcr.io/pksurferdad/knative-microservices/event-subscriber-with-sink:latest
          name: event-handler
          command: ["/bin/bash", "-ec"]
          args: ["source /vault/secrets/my-secrets.env &amp;amp;&amp;amp;
                  exec gunicorn --bind :$PORT --workers 1 --threads 8 service:app"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Event Trigger with Sink&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use the trigger below to connect the &lt;code&gt;event handler&lt;/code&gt; to the &lt;code&gt;event subscriber with sink&lt;/code&gt; service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
  name: event-subscriber-with-sink-trigger
  namespace: my-knative-services
spec:
  broker: knative-kafka-broker
  filter:
    attributes:
      type: event-subscriber-type
      source: event-subscriber-source
  subscriber:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: event-subscriber-with-sink

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Section 6: Case Study
&lt;/h3&gt;

&lt;p&gt;Now that we have all the building blocks for building out event-based microservices, let's bring this all together with a real-world case study. At &lt;a href="https://www.molecule.io" rel="noopener noreferrer"&gt;Molecule&lt;/a&gt;, where I'm responsible for software and platform engineering, we utilize the eventing pattern described in this article to support integrations we do with 3rd party data providers that implements the workflow depicted below. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdd7k4ufdrfuauk9fkv5z.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdd7k4ufdrfuauk9fkv5z.jpg" alt="Data Integration Workflow" width="800" height="111"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data Provider&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;data provider&lt;/code&gt; is the component that connects to the data source, in Molecule's case, typically a commodity trade or commodity pricing data provider, utilizing the data provider's API.  The data provider component fetches the data from the provider and calls the &lt;code&gt;event handler&lt;/code&gt; including the &lt;code&gt;ce-source&lt;/code&gt; (e.g. my-exchange) and &lt;code&gt;ce-type&lt;/code&gt; (e.g. my-trade) in the request header and the payload of the data in the request body.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Event Handler&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As described in this article, the &lt;code&gt;event handler&lt;/code&gt; will package up the cloud event and post the event to the Knative event broker which will route the event to the &lt;code&gt;event subscriber&lt;/code&gt; based on the configuration in the Knative trigger.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
  name: transformation-service-1-trigger
  namespace: my-knative-services
spec:
  broker: knative-kafka-broker
  filter:
    attributes:
      type: my-trade
      source: my-exchange
  subscriber:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: transformation-service-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Transformation Service&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this use case, the &lt;code&gt;transformation service&lt;/code&gt; is the event subscriber which will receive the event with the payload from the &lt;code&gt;data provider&lt;/code&gt; component.  Utilizing mapping configurations, the transformation service will transform attributes of the source payload to data attribute formats and structures required by the Molecule SaaS application.  Utilizing the KafkaSink resource, the transformation services publishes the event to the sink end-point which persists the event to the configured Kafka topic hosted on our Confluent Cloud cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: eventing.knative.dev/v1alpha1
kind: KafkaSink
metadata:
   name: my-trade-sink
   namespace: my-knative-services
spec:
   topic: my-trades
   bootstrapServers:
      - "GetFromConfluentCloud"
   auth.secret.ref.name: ccloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Kafka Consumer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Within the Molecule application boundary, Molecule implements multiple Kafka consumers that subscribe to the Kafka topics to pull and post the trade and market data to the Molecule SaaS application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Molecule SaaS App&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Molecule SaaS application then does its part with the data providing Commodity Trading and Risk Management services to our customers.   &lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Hopefully this article has provided the reader with some guidance and direction on how to implement, deploy, and manage Knative Services to support business critical event-based applications.  The patterns outlined in this article can be used for a variety of use cases and business problems.  If you have any questions or run into any issues with the samples that are published in this article, feel free to reach out to me at &lt;a href="mailto:paul@molecule.io"&gt;paul@molecule.io&lt;/a&gt; or post an issue to the &lt;a href="https://github.com/pksurferdad/knative-microservices/issues" rel="noopener noreferrer"&gt;GitHub project&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>microservices</category>
      <category>knative</category>
      <category>kafka</category>
      <category>vault</category>
    </item>
  </channel>
</rss>
