Using Consul for Name Resolution in Dapr
Dapr supports "service invocation" out of the box, allowing you to easily make requests to another microservice, addressing it by name, without needing to know the IP address. So for example, simply by making a request to my Dapr sidecar on localhost port 3500, I can direct a request through to the "catalog" microservice like this, and let Dapr work out where it is:
When you're running Dapr in "self-hosted" mode, it uses mDNS as the name resolution service, and if you're running on Kubernetes, then it makes use of the Kubernetes DNS service.
Unfortunately, there has been a long-standing issue that certain VPN or networking software commonly found on corporate developer machines can interfere with the mDNS name resolution. Some people have been able to work around this by temporarily disabling their VPN or networking software.
However, on one of my development environments it has not been possible to work around the issue, and as of Dapr v1.8 the only alternative is to switch to using Consul for name resolution.
This is especially unfortunate for those new to Dapr as this results in a poor initial experience where the basic quickstart demos don't work at all, and it is non-trivial to set up Consul. I'm hopeful that Dapr will resolve this by offering a simple alternative to mDNS for local development scenarios.
Using Consul for name resolution
Although there are some basic instructions to configure Consul, I found they were not sufficient to get it working on my machine.
In the rest of this post, I'll run through the steps I took to get round the mDNS issue by configuring Dapr to use Consul for name resolution.
Running Consul locally with Docker
First of all we need to start the Consul container locally with the following command:
docker run -d -p 8500:8500 --name=dev-consul -e CONSUL_BIND_INTERFACE=eth0 consul
n.b. I happen to be using Rancher Desktop, but doubt the command would need to change if you're using something different such as Docker Desktop
The important thing is making sure that port 8500 is accessible. If not the Dapr Consul name resolution component will fail to initialize and you'll get crashes in your Dapr process with null reference exceptions in Dapr sidecar (I'm hoping that they'll fix that and make it handle the error more gracefully in the future).
You can check that Consul is running successfully locally by visiting
Create a configuration file
By default, when you run locally, Dapr uses your global config file which is found in
You could of course change set up Consul globally for your machine, but I chose to create my own
daprConfig.yaml file specific to my application. The contents I used are the same as the default
config.yaml file, but with the extra
nameResolution section. Note that it's very important to include
selfRegister: true - this was missing from some of the guides I read, but it won't work without it.
apiVersion: dapr.io/v1alpha1 kind: Configuration metadata: name: daprConfig spec: nameResolution: component: "consul" configuration: selfRegister: true tracing: samplingRate: "1" zipkin: endpointAddress: http://localhost:9411/api/v2/spans
Create a Consul component definition file
Now create a
consul.yaml file in your local components folder with the following contents. I wasn't sure what I was supposed to put for
datacenter but it seems that
dc1 is the default value for Consul so I recommend leaving it as is:
apiVersion: dapr.io/v1alpha1 kind: Component metadata: name: consul namespace: default spec: type: state.consul version: v1 metadata: - name: datacenter value: dc1 # Required. Example: dc1 - name: httpAddr value: 127.0.0.1:8500 # Required. Example: "consul.default.svc.cluster.local:8500"
Pass correct arguments to
Now when you call
dapr run for each microservice, it needs to point to the updated config and components folder, with the
--components-path arguments. Here's an example PowerShell script I used for my GloboTicket Dapr demo application:
dapr run ` --app-id frontend ` --app-port 5266 ` --dapr-http-port 3500 ` --components-path ../dapr/components ` --config ../dapr/components/daprConfig.yaml ` dotnet run
Check its working
Now all that remains is to start all your services. Hopefully it will be obvious that it's working because you can make service to service invocations.
You can also test by directly making a network request to a Dapr sidecar, passing in the target application name (in this example "catalog") and method (in this example "products")
You can also use the Dapr CLI to try it out with the
dapr invoke command, again passing in the application and method names. You can also pass in the HTTP verb if it's not the default of POST.
dapr invoke -a catalog -m event -v GET
Finally, you can use the Consul UI to check that each service has correctly registered itself by visiting http://127.0.0.1/8500. Here's what it looks like for my GloboTicket Dapr sample app:
In an ideal world, you wouldn't need to change the name resolution component in Dapr from its default, but it's nice that you can, and it's a helpful workaround until the mDNS issue is finally resolved.
If you'd like to take a look at a version of my app that uses this approach, you can look here on the "consul" branch.
Dear Mark, Thank you for great courses about darp on Pluralsight!alkapa
I was impress after watching it and thought it could help me to start migrate my monolith to microservices. I have .net framework web api app and would like to start using service invocation component. I run several instances of app on different machines. I also can't use kubernates right now because app is not .net core yet. I would like to ask for some example/description of how will service invocation work with self-hosted mode if my service instances are running on separate machines?