With so many people running microservice workloads, it is inevitable that organizations keep bumping into the same set of problems: state management, resiliency, event-handling, and more. Dapr exists to help codify the best practices for building microservice applications into building blocks that enable you to build portable applications with the language and framework of your choice. Each building block is completely independent and you can use one, some, or all of them in your application to solve common microservice problems.
Dapr provides best practices for building microservice applications using common capabilities. These common capabilities are provided over a standard API that Dapr calls building blocks that provide independent pieces of functionality to your application.
|Service Invocation||Resilient service-to-service invocation enables method calls, including retries, on remote services wherever they are located in the supported hosting environment.|
|State Management||With state management for storing key/value pairs, long running, highly available, stateful services can be easily written alongside stateless services in your application. The state store is pluggable and can include Azure CosmosDB, Azure SQL Server, PostgreSQL, AWS DynamoDB or Redis among others.|
|Publish and Subscribe Messaging||Publishing events and subscribing to topics between services enables event-driven architectures to simplify horizontal scalability and make them|
|Resource Bindings||Resource bindings with triggers builds further on event-driven architectures for scale and resiliency by receiving and sending events to and from any external source such as databases, queues, file systems, etc.|
|Actors||A pattern for stateful and stateless objects that make concurrency simple with method and state encapsulation. Dapr provides many capabilities in its actor runtime including concurrency, state, life-cycle management for actor activation/deactivation and timers and reminders to wake-up actors.|
|Observability||Dapr emit metrics, logs, and traces to debug and monitor both Dapr and user applications. Dapr supports distributed tracing to easily diagnose and serve inter-service calls in production using the W3C Trace Context standard and Open Telemetry to send to different monitoring tools.|
|Secrets||Dapr provides secrets management and integrates with public cloud and local secret stores to retrieve the secrets for use in application code.|
Each of these building blocks provides the API that your application will interact with. The building block APIs are implemented using components.
Components deliver a consistent implementation for building blocks. A component has a defined interface that can be implemented by a variety of backend technologies. For example, the state component provides a common way to interact with data storage systems. Any storage system can be used (DynamoDB, Redis, SQL Server) as long as the component interface is implemented. As of this writing, a compliant state store needs to implement one or more interfaces: Store and TransactionalStore.
The interface for Store:
The interface for TransactionalStore:
So, for example, by implementing this API using DynamoDB as a backing storage system, you allow Dapr to use the State component backed by DynamodDB to fulfill the State Management Building Block.
The claim of Dapr being a portable runtime is achieved by leveraging a sidecar architecture, allowing Dapr to deployed alongside application code without needing to make changes to the application itself. Sidecars are typically associated with running containers in Kubernetes, and this arrangement is depicted in the following figure. In this architecture, your application running in a container communicates to a separate container running Dapr over an HTTP or gRPC API. Because both your application code and Dapr are running in the same Kubernetes Pod, the HTTP or gRPC request is a local network call and is resolved on the same host.
Installing Dapr in Kubernetes
To apply what we’ve covered so far, let’s build a basic application using
Dapr and running on Kubernetes. This example should show more clearly how
the interaction between Building Blocks, Components, and the Dapr sidecar
API work in practice. I’ll use the built-in Kubernetes support for
Docker, but if you
already have a Kubernetes cluster set up for development go ahead and use
that instead. From here on out, I will assume that you have
installed and that it is configured to communicate with a running
Kubernetes cluster (either locally or elsewhere). If you need help with
that step, follow the
documentation for installing the Kubernetes CLI, and
minikube for installing
a local Kubernetes test cluster.
To deploy Dapr to Kubernetes, Dapr requires two extra services: the
dapr-sidecar-injector to add the Dapr sidecar to Pods in Kubernetes, and
dapr-operator to update components provisioned within the cluster.
In addition, two other services provide additional functionality if
dapr-sentry provides mutual tTLS support between Dapr sidecar
dapr-placement used to place actors on Pods if your
application uses the Actor model.
The simplest way to deploy Dapr to Kubernetes is using the Dapr CLI
command with a
-k flag. This command looks at your local
configuration and installs Dapr into the cluster defined by your
configuration. We can run the following commands to get the CLI and then
use the CLI to install Dapr
You can verify your installation with the
dapr status command:
An alternative way to verify your installation is to use the
tool to list the running Services in your cluster under the
After installation, any new Pods deployed to Kubernetes will have a Dapr
sidecar attached to them by the
dapr-sidecar-injector. The sidecar
injector is an implementation of Kubernete’s Admission
that intercepts requests to the Kubernetes API server and mutates that
request. In our case, the mutation is to add the Dapr sidecar to the Pod.
Deploying a Dapr Component
With Dapr installed in Kubernetes, we can begin building out our application. For this example, we will use a Dapr state store component implemented using Redis. The easiest way to deploy Redis to Kubernetes is by using Helm. The chart for Redis is maintained by Bitnami and we can add the Bitnami chart to Helm, and then install Redis using the following commands on macOS:
Or the following on Linux:
To make the Redis implementation of the State Store Component available to
Dapr applications, we define a yaml file with
kind: Component. Note the
metadata field has
name: redis to make the component easy to
spec field shows the Redis specific information. The
metadata in this example corresponds to the default configuration that is
applied by the Redis Helm chart. The host is defaulted to
redis-master.default.svc.cluster.local running on port 6379 and the
password is installed as a secret key that can be viewed with the
kubectl describe secret redis command.
We can apply the Redis component to Kubernetes using
kubectl. This will
dapr-operator to register this component as available to
applications using Dapr.
You can view the component you just deployed using the Dapr dashboard and navigating to the Components section:
Or by using the Dapr CLI to list the available components:
Deploying a Python Application with the Dapr sidecar
Now that we have a State Store component, we can deploy a Python Dapr application that uses the State Building Block API.
The following example code shows a simple Flask application with a simple
/order. Making an HTTP post request to this endpoint will
create an order, while a GET request will return the previously created
order. The data for the order is stored in the Redis state store we
To deploy this application to Kubernetes, we require a Dockerfile to package everything up.
And we can build an push the container to your local image store:
Finally, deploy your application to Kubernetes using the following configuration:
You can use the
apply kubectl command for that:
$ kubectl apply -f server.yaml
You can test your application by making POST and GET requests to the
What’s actually happening here?
In the implementation of the HTTP POST request, we use the
library to make a POST request to the state store API. This API is
a generic interface for storing state using a key-value interface. What is
important here is that as far as the application is concerned, the state
store API remains static. In this example, we implemented the state store
API using the Redis datastore. If we like, we can implement it using one
of the many other state store
without changing the application code.
Dapr runs as a sidecar, meaning that the POST request made by the Flask application is actually directed to the attached sidecar for this Kubernetes Pod. This separation of concerns allows application developers to iterate on their app, while infrastructure or platform operators can focus on the underlying implementation.