Running Dapr on Kubernetes
Tags: kubernetes, dotnet, microservices, architecture
Published: 2021-09-07 10:53:46

The distributed application runtime, Dapr, is a portable, event-driven runtime that can run on the cloud or any edge infrastructure. It puts together the best practices for building microservice applications into components called building blocks.

Each building block is completely independent so you can use one, some, or all of them in your application. Building blocks are extensible, so you can also write your own.

Dapr supports a wide range of programming languages and frameworks such as .NET, Java, Node.js, Go and Python. That means you can write microservice apps using your favorite tools and deploy them literally anywhere.

![Architecture Diagram](https://docs.dapr.io/images/overview.png)

Basically, building blocks are just HTTP or gRPC APIs that can be called from application code and use one or more Dapr components. They abstract some of the major challenges during development such as service-to-service communication, state managment, pub/sub, observability and more. Building blocks do not depend on underlying technology. This means if you need to implement, for example, pub/sub functionality you can use Apache Kafka, RabbitMQ, Redis Streams, Azure Service Bus or any other supported broker that interface with Dapr.

In this example we will show how to run Dapr on the Kubernetes cluster with two .NET applications. First one will send messages to Apache Kafka while the second one will read those messages and store them in Redis. Communication to Kafka and Redis will be realized using the Dapr Client, which means that we will not have any dependencies on NuGet packages like `Confluent.Kafka` or `StackExchange.Redis`.

#### Architecture diagram

![Architecture Diagram](https://raw.githubusercontent.com/cvitaa11/dapr-demo/main/Architecture_diagram.jpeg)


#### Prerequisites

This demo requires you to have the following installed on your machine:

- Kubernetes CLI [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/)
- Kubernetes cluster, such as [Minikube](https://docs.dapr.io/operations/hosting/kubernetes/cluster/setup-minikube/) or [Docker Desktop](https://www.docker.com/products/docker-desktop)

Also, clone the repository and `cd` into the right directory:

```
git clone https://github.com/cvitaa11/dapr-demo
cd dapr-demo
```

#### Step 1 - Setup Dapr on your Kubernetes cluster

The first thing you need is an RBAC enabled Kubernetes cluster. This could be running on your machine using Minikube/Docker Desktop, or it could be a fully-fledged cluser in Azure using [AKS](https://azure.microsoft.com/en-us/services/kubernetes-service/) or some other managed Kubernetes instance from different cloud vendor.

Once you have a cluster, follow the steps below to deploy Dapr to it. For more details, look [here](https://docs.dapr.io/getting-started/install-dapr/#install-dapr-on-a-kubernetes-cluster)

```
$ dapr init -k
⌛  Making the jump to hyperspace...
ℹ️  Note: To install Dapr using Helm, see here: https://docs.dapr.io/getting-started/install-dapr-kubernetes/#install-with-helm-advanced

✅  Deploying the Dapr control plane to your cluster...
✅  Success! Dapr has been installed to namespace dapr-system. To verify, run `dapr status -k' in your terminal. To get started, go here: https://aka.ms/dapr-getting-started
```

The `dapr` CLI will exit as soon as the kubernetes deployments are created. Kubernetes deployments are asyncronous, so you will need to make sure that the dapr deployments are actually completed before continuing.


#### Step 2 - Setup Apache Kafka

The easiest way to setup Apache Kafka on your Kubernetes cluster is by using [Helm](https://helm.sh/) package manager. To install Helm on your development machine follow this [guide](https://helm.sh/docs/intro/install/). 
We will use [Bitnamy Library for Kubernetes](https://github.com/bitnami/charts) to launch Zookeper and Kafka message broker.

```
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install my-release bitnami/kafka
```

#### Step 3 - Setup Redis

Just like Apache Kafka, easy way to spin up Redis on your Kubernetes cluster is by using Helm.

```
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install redis bitnami/redis
```

To verify the installation of Kafka and Redis run `kubectl get all` and you should see similiar output:

```
NAME                             READY   STATUS        RESTARTS   AGE
pod/my-release-kafka-0           1/1     Running       0          18m
pod/my-release-zookeeper-0       1/1     Running       0          18m
pod/redis-master-0               1/1     Running       1          11m
pod/redis-slave-0                1/1     Running       1          11m
pod/redis-slave-1                1/1     Running       1          11m

NAME                                    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
service/kubernetes                      ClusterIP   10.96.0.1        <none>        443/TCP                      15d
service/my-release-kafka                ClusterIP   10.110.225.238   <none>        9092/TCP                     18m
service/my-release-kafka-headless       ClusterIP   None             <none>        9092/TCP,9093/TCP            18m
service/my-release-zookeeper            ClusterIP   10.99.95.252     <none>        2181/TCP,2888/TCP,3888/TCP   18m
service/my-release-zookeeper-headless   ClusterIP   None             <none>        2181/TCP,2888/TCP,3888/TCP   18m
service/redis-headless                  ClusterIP   None             <none>        6379/TCP                     11m
service/redis-master                    ClusterIP   10.111.109.148   <none>        6379/TCP                     11m
service/redis-slave                     ClusterIP   10.111.66.85     <none>        6379/TCP                     11m

NAME                                    READY   AGE
statefulset.apps/my-release-kafka       1/1     18m
statefulset.apps/my-release-zookeeper   1/1     18m
statefulset.apps/redis-master           1/1     11m
statefulset.apps/redis-slave            2/2     11m
```
#### Step 4 - Create Dapr components in Kubernetes cluster

To deploy pub/sub and state store components make sure you are positioned in the right directory and then apply Dapr YAML manifests.

```
cd dapr-components
kubectl apply -f .\kafka.yaml
kubectl apply -f .\redis.yaml
```

#### Step 5 - Deploy .NET Core applications

Now when all prerequisites are ready we can deploy our apps. To deploy .NET Core publisher and consumer applications make sure you are positioned in the right directory and then apply Kubernetes manifests.

```
cd k8s
kubectl apply -f .\publisher.yaml
kubectl apply -f .\consumer.yaml
```

Each manifest contains `Deployment` object for the application and `Service` object for accessing the application through a browser.

Navigate to the `localhost:8081/swagger` and you will se our publisher app with a POST method on `MessageController`. This action sends a message to *newMessage* topic on Kafka pub/sub component. Communication between application and message broker is not performed directly. Dapr is running as a sidecar container inside publisher pod and handles the entire process of sending a message.

Our consumer application is running on `localhost:9091` and is subscribed to *newMessage* topic on Kafka pub/sub component. When new message arrives it reads the content and trough the Dapr client saves it to Redis state store uneder the key *message*.

To test the entire process we can run Redis client pod and check if content is stored. First of all we will export password to REDIS_PASSWORD variable: 
```
export REDIS_PASSWORD=$(kubectl get secret --namespace default redis -o jsonpath="{.data.redis-password}" | base64 --decode)
```
Then run the client with the following command: 
```
kubectl run --namespace default redis-client --rm --tty -i --restart='Never' \
    --env REDIS_PASSWORD=$REDIS_PASSWORD \
   --image docker.io/bitnami/redis:6.0.12-debian-10-r3 -- bash
```
and connect using Redis CLI:
```
redis-cli -h redis-master -a $REDIS_PASSWORD
```
Now that you are connected to Redis you can use command `HGETALL message` that will return content of the message we sent to Kafka. With this we have confirmed that the whole process works.

If you want to find out more about Dapr, the best place to start is the official [documentation](https://dapr.io/).