Skip to content

Commit 9d01fb6

Browse files
authored
Dapr - flexibility in microservices development (#30)
* wip: dapr Signed-off-by: David Söderlund <[email protected]> * renamed files Signed-off-by: David Söderlund <[email protected]> * Final touches Signed-off-by: David Söderlund <[email protected]> * minor language improvements Signed-off-by: David Söderlund <[email protected]> * update post date to fit publication date Signed-off-by: David Söderlund <[email protected]> --------- Signed-off-by: David Söderlund <[email protected]>
1 parent 7beef12 commit 9d01fb6

10 files changed

+167
-0
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
---
2+
title: Testing out flexibility in microservices deployment with dapr
3+
published: true
4+
excerpt_separator: <!--more-->
5+
---
6+
7+
This blogpost will show you how using dapr (distributed application runtime) in your services will not only allow the development side to abstract away infrastructure implementation details, but also how the operations side can automate and simplify the deployment of the apps together with the chosen implementation.
8+
9+
<!--more-->
10+
11+
# Background
12+
13+
While exploring if pub/sub could be a suitable architecture for a system I am developing with my friend [Jarl](https://www.linkedin.com/in/jarllindquist/), we were talking about rabbitmq specifically. I started looking at what running that in kubernetes would be like and of course started thinking about different ways I could scale workload depending on the amount of messages flying around.
14+
15+
After a bit of research I got annoyed by the amount of work that would go in setting up an environment for development, and that is when I came across dapr. I reached out to Jarl and we sat down to begin experimenting.
16+
17+
## About dapr for pub/sub
18+
19+
Dapr allows you to abstract a lot of the platform and infrastructure that your application will run ontop of.
20+
21+
Among other things it lets you specify that your app will use pub/sub through the dapr library in your language of choice. Beyond pub/sub there is a plethora of usecases and you can check out [all building blocks in their excellent documentation](https://docs.dapr.io/concepts/building-blocks-concept/).
22+
23+
In your actual runtime environment (we run kubernetes), you install an operator that will look out for apps running with dapr.
24+
25+
On kubernetes you would annotate your apps and dapr will inject a sidecar container in the respective pods to handle what ever you need, like getting messages from a specific topic.
26+
27+
## Setting up a simple system
28+
29+
Using the [example given by Viktor Farcic](https://gist.github.com/vfarcic/8d941690a087b0de0e2731a52cfb1f51) in [his video about dapr](https://youtu.be/-4sHUvfk2Eg) which you should totally check out, we started out just making sure we understood how the sidecar injection worked.
30+
31+
### Dapr runtime in kubernetes
32+
33+
Available via helm, easy peasy to install via helm cli. One thing of note if you are deploying via argocd though is that it creates both a CRD for "components" as well as one of those components. Either sync everything but components manually first, or use helm template to get the actual yaml of the resources and then set up [sync waves](https://argo-cd.readthedocs.io/en/stable/user-guide/sync-waves/#how-do-i-configure-phases) for those resources.
34+
35+
![Caveats for gitops approach for dapr](../assets/dapr-Caveats-for-gitops-approach-for-dapr.png)
36+
37+
Once dapr was installed, we needed to do some restarting of pods for the silly demo from Victor. Eventually once dapr was fully up and running our pods started to get injected with the dapr sidecar. Notice how the "tweet" pod has one more container than "publications" or "text-to-speech". They would get their sidecars eventually.
38+
39+
![Highlights that pods get dapr side car injected](../assets/dapr-Highlights-that-pods-get-dapr-side-car-injected.png)
40+
41+
Looks good, messages sent to the publishing service gets sent out via the redis pubsub and the other apps pick up and act with what they are supposed to.
42+
43+
![Shows messages being picked up by other apps](../assets/dapr-Shows-messages-being-picked-up-by-other-apps.png)
44+
45+
## Trying out the sdk for ourselves
46+
47+
Then we went ahead and replaced one of the services with our own implementation in **typescript** since neither of us is well versed in go (but Jarl is good with typescript). This to prove to ourselves that different sdks are easy to get started with and that they can intercommunicate.
48+
49+
First we create the new app folder and initialize the dependencies.
50+
51+
``` PowerShell
52+
# Creates a new typescript backend app with dapr to send tweets (eventually, the posting to social media is left as an excercise for the reader).
53+
mkdir tweet-js # our replacement of the tweet server from the Viktor's silly demo
54+
cd tweet-js
55+
bun i @dapr/dapr --save
56+
```
57+
58+
The we write some app code by reverse engineering the go code from Victor's repo.
59+
60+
``` typescript
61+
// server.ts
62+
import { DaprServer, CommunicationProtocolEnum } from "@dapr/dapr";
63+
64+
const daprHost = "127.0.0.1";
65+
const serverHost = "127.0.0.1";
66+
const serverPort = process.env.APP_HTTP_PORT;
67+
const daprPort = process.env.DAPR_HTTP_PORT;
68+
69+
start().catch((e) => {
70+
console.error(e);
71+
process.exit(1);
72+
});
73+
74+
async function eventHandler(data: any, headers: any) {
75+
console.log(
76+
`Received Data: ${JSON.stringify(data)} with headers: ${JSON.stringify(
77+
headers
78+
)}`
79+
);
80+
console.log("Sending a tweet");
81+
return false;
82+
}
83+
84+
async function start() {
85+
const server = new DaprServer({
86+
serverHost,
87+
serverPort,
88+
communicationProtocol: CommunicationProtocolEnum.HTTP,
89+
clientOptions: {
90+
daprHost: daprHost,
91+
daprPort: daprPort,
92+
},
93+
});
94+
const topics = process.env.TOPICS?.split(",");
95+
const pubsubName = process.env.PUBSUB_NAME;
96+
const route = process.env.ROUTE;
97+
if (topics == null || pubsubName == null || route == null) {
98+
console.log("Please set TOPICS and PUBSUB_NAME environment variables");
99+
process.exit(1);
100+
}
101+
for (const topic of topics) {
102+
await server.pubsub.subscribe(pubsubName, topic, eventHandler, route);
103+
}
104+
await server.start();
105+
}
106+
console.log(`Starting the server on ${serverPort}...`);
107+
108+
109+
```
110+
111+
Then we package it nicely in a container image with this dockerfile.
112+
113+
``` Dockerfile
114+
# Dockerfile
115+
FROM oven/bun:1 AS base
116+
WORKDIR /usr/src/app
117+
118+
FROM base AS install
119+
RUN mkdir -p /temp/dev
120+
COPY package.json bun.lockb /temp/dev/
121+
RUN cd /temp/dev && bun install --frozen-lockfile
122+
123+
RUN mkdir -p /temp/prod
124+
COPY package.json bun.lockb /temp/prod/
125+
RUN cd /temp/prod && bun install --frozen-lockfile --production
126+
127+
FROM install AS prerelease
128+
COPY --from=install /temp/dev/node_modules node_modules
129+
COPY . .
130+
131+
# run the app
132+
USER bun
133+
EXPOSE 3000/tcp
134+
ENTRYPOINT [ "bun", "run", "server.ts" ]
135+
```
136+
137+
We built and pushed into our container registry to make it available for our cluster.
138+
139+
![Changing out the deployment to use our app image](../assets/dapr-Changing-out-the-deployment-to-use-our-app-image.png)
140+
141+
After patching the deployment to use our image instead of Victor's and restarting we get new functionality, without any changes to configuration, dapr components, or other microservices.
142+
143+
![Our new replacement app running](../assets/dapr-Our-new-replacement-app-running.png)
144+
145+
## Testing to see if the abstraction works
146+
147+
Then for the fun part, we desided to switch out the redis.pubsub component to rabbitmq (which was the one I was originally interested in seeing if we could use for our system).
148+
149+
We went ahead and installed rabbitmq with the bitnami helm chart.
150+
151+
![Installing rabbitmq into the cluster](../assets/dapr-Installing-rabbitmq-into-the-cluster.png)
152+
153+
Replaced the existing pubsub component with one for rabbitmq.
154+
155+
![Git history of creating dapr compoennt for pubsub.rabbitmq](../assets/dapr-Git-history-of-creating-dapr-compoennt-for-pubsub.rabbitmq.png)
156+
157+
Restarted the deployments...
158+
159+
![Showing pod rollout](../assets/dapr-Showing-pod-rollout.png)
160+
161+
And look at that, same exact app code for all three apps but the dapr sidecars in each using rabbitmq for communication instead of redis as before.
162+
163+
![Mocking publishing a video](../assets/dapr-Mocking-publishing-a-video.png)
164+
165+
## Conclusion
166+
167+
Now we are comfortable that when we continue on with developing our applications that will connect over pub/sub, equipped with the knowledge that we can ignore the choice of implementation. We can test out different infrastructure for pub/sub with our workload and make an informed decision based on what we learn next - without having to make sacrifices in our code base up front.
109 KB
Loading
60.4 KB
Loading
75.8 KB
Loading
84.9 KB
Loading
24.5 KB
Loading
327 KB
Loading
132 KB
Loading
95.1 KB
Loading
172 KB
Loading

0 commit comments

Comments
 (0)