I recently left Red Hat to join Diagrid and work on the Dapr project. I spoke about Dapr when it was initially announced by Microsoft, but hadn’t looked into it since it joined CNCF. Two years later, during my onboarding into the new role, I spent some time looking into it and here are the steps I took in the journey and my impressions so far.
What is Dapr?
- Similar to Camel, Dapr has connectors (called bindings) that let you connect to various external systems.
- Similar to HashiCorp Consul, Dapr offers services discovery which can be backed by Consul.
- Similar to Spring Integration, Spring Cloud, (remember Netflix Hystrix?) and many other frameworks, Dapr has error handling capabilities with retries, timeouts, circuit breakers which are called resiliency policies.
- Similar to Spring Data KeyValue, Dapr offers Key/Value-based state abstractions.
- Similar to Kafka, Dapr offers pub/sub-based service interactions.
- Similar to ActiveMQ clients, Dapr offers DLQs, but these are not specific to a messaging technology, which means they can be used even with things such as AWS SQS or Redis for example.
- Similar to Spring Cloud Config, Dapr offers configuration and secret management
- Similar to Zookeeper or Redis clients, Dapr offers distributed locks
- Similar to a Service Mesh, Dapr offers mTLS and additional security between your application and the sidecar.
- Similar to Envoy, Dapr offers enhanced observability through automatic metrics, tracing and log collection.
You could say, Dapr is a collection of stable APIs exposed through a sidecar and swappable implementations running somewhere else. It is the cloudnative incarnation of integration technologies that makes integration capabilities previously available only in a few languages, available to everybody, and portable everywhere: Kubernetes, on-premise, or literally on the International Space Station (I mean the edge).
Getting started
The project is surprisingly easy to get up and running regardless of your developer background and language of choice. I was able to follow the getting started guides and run various quickstarts in no time on my MacOS. Here are roughly the steps I followed.
Install Dapr CLI
Dapr CLI is the main tool for performing Dapr-related tasks such as running an application with Dapr, seeing the logs, running Dapr dashboard, or deploying all to Kubernetes.
brew install dapr/tap/dapr-cli
With the CLI installed, we have a few different options for installing and running Dapr. I’ll start from the least demanding and flexible option and progress from there.
Option 1: Install Dapr without Docker
This is the lightest but not the most useful way to run Dapr.
dapr init --slim
In this slim mode only daprd and placement binaries are installed on the machine which is sufficient for running Dapr sidecars locally.
Run a Dapr sidecar
The following command will start a Dapr sidecar called no-app listening on HTTP port 3500 and a random gRPC port.
dapr run --app-id no-app --dapr-http-port 3500
Congratulations, you have your first Dapr sidecar running. You can see the sidecar instance through this command:
dapr list
or query its health status:
curl -i http://localhost:3500/v1.0/healthz
Dapr sidecars are supposed to run next to an application and not on their own. Let’s stop this instance and run it with an application.
dapr stop --app-id no-app
For this demonstration we will use a simple NodeJS application:
git clone https://github.com/dapr/samples.git
cd samples/hello-dapr-slim
npm install
This is a Hello World the Dapr way and here is the gist of it:
app.post('/neworder', bodyParser.json(), (req, res) => {
const data = req.body.data;
const orderId = data.orderId;
res.status(200).send("Got a new order! Order ID: " + orderId); });
The application has one /neworder endpoint listening on port 3000. We can run this application and the sidecar with the following command:
dapr run --app-id nodeapp --app-port 3000 --dapr-http-port 3500 node app.js
The command starts the NodeJS application on port 3000 and Dapr HTTP endpoint on 3500. Once you see in the logs that the app has started successfully, we can poke it. But rather than hitting the /neworder endpoint directly on port 3000, we will instead interact with the application through the sidecar. We do that using Dapr CLI like this:
dapr invoke --verb POST --app-id nodeapp --method neworder --data '{"data": { "orderId": "41" } }'
And see the response from the app. If you noticed, the CLI only needs the app-id (instead of host and port) to locate where the service is running. The CLI is just a handy way to interact with your service. It that seems like too much magic, we can use bare-bones curl command too:
curl -XPOST -d @sample.json -H "Content-Type:application/json" http://localhost:3500/v1.0/invoke/nodeapp/method/neworder
This command uses the service Dapr’s invocation API to synchronously interact with the application. Here is a visual representation of what just happened:
Now, with Dapr on the request path, we get the Daprized service invocation benefits such as resiliency policies such as retries, timeouts, circuit breakers, concurrency control; observability enhancements such as: metrics, tracing, logs; security enhancements such as mTLS, allow lists, etc. At this point, you can try out metadata, metrics endpoints, play with the configuration options, or see your single microservice in Dapr dashboard.
dapr dashboard
The slim mode we are running on is good for the Hello World scenario, but not the best setup for local development purposes as it lacks state store, pub/sub, metric server, etc. Let’s stop the nodeapp using the command from earlier (or CTL +C), and remove the slim Dapr binary:
dapr uninstall
One thing to keep in mind is that this command will not remove the default configuration and component specification files usually located in: ~/.dapr folder. We didn’t create any files in the steps so far, but if you follow other tutorials and change those files, they will remain and get applied with every dapr run command in the future (unless overridden). This caused me some confusion, keep it in mind.
Option 2: Install Dapr with Docker
This is the preferred way for running Dapr locally for development purposes but it requires Docker. Let’s set it up:
dapr init
The command will download and run 3 containers
- Dapr placement container used with actors(I wish this was an optional feature)
- Zipkin for collecting tracing information from our sidecars
- And a single node Redis container used for state store, pub/sub, distributed-lock implementations.
docker ps
Run the Quickstarts
My next step from here was to try out the quickstarts that demonstrate the building blocks for service invocation, pub/sub, state store, bindings, etc. The awesome thing about these quickstarts is that they demonstrate the same example in multiple ways:- With Dapr SDK and w/o any dependency to Dapr SDK i.e. using HTTP only.
- In multiple languages: Java, Javascript, .Net, Go, Python, etc.
Option 3: Install Dapr on Kubernetes
If you have come this far, you should have a good high-level understanding of what Dapr can do for you. The next step would be to deploy Dapr on Kubernetes where most of the Dapr functionalities are available and closest to a production deployment. For this purpose, I used minikube locally with default settings and no custom tuning.
dapr init --kubernetes --wait
- dapr-operator: manages all components for state store, pub/sub, configuration, etc
- dapr-sidecar-injector: injects dapr sidecars into annotated deployment pods
- dapr-placement: required with actors only.
- dapr-sentry: manages mTLS between services and acts as a certificate authority.
- dapr-dashboard: a simple webapp to explore what is running within a Dapr cluster
Injecting a sidecar
From here on, adding a Dapr sidecar to an application (this would be Dapr dataplane) is as easy as adding the following annotations to your Kubernetes Deployments:
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "nodeapp"
dapr.io/app-port: "3000"
The dapr-sidecar-injector service watches for new Pods with the dapr.io/enabled annotation and injects a container with the daprd process within the pod. It also adds DAPR_HTTP_PORT and DAPR_GRPC_PORT environment variables to your container so that it can easily communicate with Dapr without hard-coding Dapr port values.
To deploy a complete application on Kubernetes I suggest this step-by-step example. It has a provider and consumer services and it worked the first time for me.
Transparent vs explicit proxy
Summary
Liked more
- I love the fact that Dapr is one of the few CNCF projects targeting developers creating applications, and not only operations team who are running these applications. We need more cloudnative tools for implementing applications.
- I love the non-intrusive nature of Dapr where capabilities are exposed over clear APIs and not through some black magic. I prefer transparent actions for instrumentation, observability, and general application insight, but not for altering application behavior.
- I loved the polyglot nature of Dapr offering its capabilities to all programming languages and runtimes. This is what attracted me to Kubernetes and cloudnative in the first place.
- I loved how easy it is to get started with Dapr and the many permutations of each quickstart. There is something for everyone regardless of where you are coming from into Dapr.
- I’m excited about WASM modules and remote components features coming into Dapr. These will open new surface areas for more contributions and integrations.
Liked less
- I haven’t used actors before and it feels odd to have a specific programming model included in a generic distributed systems toolkit. Luckily you don’t have to use it if you don’t want to.
- The documentation is organized, but too sparse into multiple short pages. Learning a topic will require navigating a lot of pages multiple times, and it is still hard to find what you are looking for.
0 comments:
Post a Comment