Dapr version 0.2.0 comes with a bunch of new components added to the runtime. One such component includes pubsub capability with NATS which is a Go based open source messaging system for cloud native applications, IoT messaging, and microservices architectures. This blog will provide a step-by-step walk through of how to use it.
the code is available on GitHub as always
We will deploy Dapr
on Kubernetes
(minikube
) and use the NATS server at demo.nats.io
for demonstration purposes.
Hello Dapr!
Dapr stands for Distributed Application Runtime. It is an open source, portable runtime to help developers build resilient, microservice stateless and stateful applications by codifying the best practices for building such applications into independent components.
If you're new to Dapr, I would recommend starting off with the overview and concepts. Try the getting-started guide and then move on to samples and how-to guides. As you advance further, you can dig into the Dapr runtime API reference and individual components
Setup
Start by installing the Dapr CLI (you will need Docker
to be installed) using the documentation - https://github.com/dapr/docs/blob/master/getting-started/environment-setup.md
If you're on a Mac, simply:
curl -fsSL https://raw.githubusercontent.com/dapr/cli/master/install/install.sh | /bin/bash
Install Dapr
to Kubernetes
(we'll use minikube
to keep things simple). See the docs to install minikube if needed.
Once minikube
is setup, make sure you start it
minikube start
Now all you need is a single command to install Dapr on Kubernetes!
dapr init --kubernetes
You should see this message
Success! Dapr has been installed. To verify, run 'kubectl get pods -w' in your terminal
If you want to use
Helm
for installingDapr
, check https://github.com/dapr/docs/blob/master/getting-started/environment-setup.md#using-helm-advanced
To confirm, use the below command and proceed when Dapr pods are in Running
state. The related Pods are Dapr operator, Dapr sidecar injector and Dapr placement
kubectl get pods -w
You are ready to go!
Try Dapr with NATS pubsub
Start by cloning the repo and change to the correct directory
git clone https://github.com/abhirockzz/dapr-nats-pubsub
cd dapr-nats-pubsub
Deploy the NATS pubsub component
A Component
is a Dapr CRD (Custom Resource Definition). Here is the configuration - note that we are using the demo NATS server at demo.nats.io:4222
as specified by natsURL
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: messagebus-nats
spec:
type: pubsub.nats
metadata:
- name: natsURL
value: nats://demo.nats.io:4222
To create:
kubectl apply -f deploy/nats-pubsub.yaml
//component.dapr.io/messagebus-nats created
Confirm:
kubectl get component.dapr.io
NAME AGE
messagebus-nats 12s
Deploy subscriber application
This is a simple application that subscribes to a subject called testsubject
.
http.HandleFunc("/dapr/subscribe", func(w http.ResponseWriter, r *http.Request) {
response := `["` + natsSubject + `"]`
fmt.Println("subscribed to NATS subject", natsSubject)
w.Write([]byte(response))
})
The app itself is defined as a Kubernetes Deployment
resource. It uses annotations to ensure that the Dapr sidecar container is "injected" inside the Pod along with the application container
dapr.io/enabled: "true"
dapr.io/id: "natsapp"
dapr.io/port: "8080"
dapr.io/log-level: "debug"
To create the app:
kubectl apply -f deploy/app.yaml
The Deployment uses a pre-built Docker image (
abhirockzz/dapr-nats-pubsub
), but you can build your own using theDockerfile
provided in thenats-app
folder
Check the new Pod
and wait for it to transition
to Running
state
kubectl get pods -l=app=natsapp -w
At this point, you can check the logs for the app as well as the Dapr sidecar container
//for the app
kubectl logs <POD_NAME> -c natsapp
//logs for the Dapr sidecar
kubectl logs <POD_NAME> -c daprd
For the app, you should just see the following logs which indicate that the application has subscribed to the NATS subject and is listening for further messages
starting HTTP server....
subscribed to NATS subject testsubject
For the Dapr sidecar, you should see something on these lines - notice that the NATS pusbsub component is set up properly and Dapr has been able to connect to NATS and subscribe to NATS server specified in the component.
time="2019-11-19T10:00:18Z" level=info msg="starting Dapr Runtime -- version 0.2.0 -- commit c75b111"
time="2019-11-19T10:00:18Z" level=info msg="log level set to: debug"
time="2019-11-19T10:00:18Z" level=info msg="kubernetes mode configured"
time="2019-11-19T10:00:18Z" level=info msg="dapr id: natsapp"
time="2019-11-19T10:00:19Z" level=info msg="loaded component messagebus-nats (pubsub.nats)"
time="2019-11-19T10:00:19Z" level=info msg="application protocol: http. waiting on port 8080"
time="2019-11-19T10:00:19Z" level=info msg="application discovered on port 8080"
time="2019-11-19T10:00:20Z" level=debug msg="connected to nats at nats://demo.nats.io:4222"
time="2019-11-19T10:00:20Z" level=debug msg="nats: subscribed to subject testsubject with queue group natsapp"
time="2019-11-19T10:00:20Z" level=warning msg="failed to init actors: actors: state store must be present to initialize the actor runtime"
time="2019-11-19T10:00:20Z" level=info msg="http server is running on port 3500"
time="2019-11-19T10:00:20Z" level=info msg="gRPC server is running on port 50001"
time="2019-11-19T10:00:20Z" level=info msg="dapr initialized. Status: Running. Init Elapsed 1211.203177ms"
Start tailing the application logs, as you will need to check the end to end functionality
kubectl logs -f <POD_NAME> -c natsapp
Deploy the client application
The client application is a React front end app which has been picked up from the Dapr samples repo and has been modified ever so slightly for the purposes of this scenario
Switch to a new terminal to deploy the client app. This too is a Kubernetes Deployment
. It additionally includes a LoadBalancer
so that we can access the app in our browser
kubectl apply -f deploy/react-app.yaml
Wait for the application to be deployed
kubectl get pods -l=app=react-form -w
Once it's deployed, you are ready to check the end to end flow of events.
Produce/publish messages using the front end app
Since we're using minikube
, we can open React app in a browser with:
minikube service react-form
You should see the following screen:
testsubject
is the NATS subject we want to send messages to (ignore the other ones in the dropdown). Simply enter the message and press the Submit
button. This will use the publisher part of the NATS pubsub component and send a message to the NATS server at demo.nats.io:4222
Check the other terminal where the subscriber application logs are being tailed - you should see the logs along with the message you sent e.g.
Recieved Message from NATS - {"id":"48825607-1f10-4789-8d99-84b927c1900e","source":"react-form","type":"com.dapr.event.sent","specversion":"0.3","datacontenttype":"application/json","data":{"messageType":"testsubject","message":"looking dapr"}}
The message is encapsulated within the Cloud Events
payload. You can send a few more messages and double-check the results.
Horizontal scaling...
So far we just had one subscriber app instance. The NATS pubsub component in Dapr works as a queue subscriber, meaning that you can spawn more instances and the events from the NATS server will be distributed amongs them. This is results in a horizontally scalable system
To scale up our Deployment:
kubectl scale deployment/natsapp --replicas=2
Wait for the new application Pod
to start
kubectl get pods -l=app=natsapp -w
Once it's ready, start tailing the logs, just like you did for the first instance
kubectl logs -f <NAME_OF_NEW_POD> -c natsapp
Send messages using the front end app and confirm that they will be distributed among both the instances. You can scale out the app even further and confirm that the behavior remains the same.
What about a different application?
Imagine you had a totally different application that also wanted to process messages from the same subject but process them differently compared to the existing app. Well, you can create a new Dapr powered app in the same way as the existing one, include your processing logic and deploy it to Kubernetes. It will also receive the same set of messages which the earlier app did.
Summary
In this blog post, you saw how to use NATS as a pubsub layer for communication amongst your Dapr apps. Your app was not even aware of NATS - all it did was to interact with the Dapr runtime (just a sidecar) using the Dapr HTTP API!
It is also possible to do it using gRPC or language specific SDKs
As the time of writing, Dapr
is in alpha state (v0.2.0
) and gladly accepting community contributions 😃 Vist https://github.com/dapr/dapr to dive in!
If you found this article helpful, please like and follow 🙌 Happy to get feedback via Twitter or just drop a comment.
Top comments (2)
Thanks Abhishek, very interesting. For the component, is it possible to use a NATS server requiring authentication?
It's not yet possible. Feel free to raise an issue here github.com/dapr/components-contrib and would be awesome if you can send a pull request :-)