Posted in:

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:

http://localhost:3500/v1.0/invoke/catalog/method/products

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.

mDNS problems

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 http://127.0.0.1/8500

Create a configuration file

By default, when you run locally, Dapr uses your global config file which is found in %USERPROFILE%\.dapr\config.yaml

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 dapr run

Now when you call dapr run for each microservice, it needs to point to the updated config and components folder, with the --config and --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")

curl http://127.0.0.1:3500/v1.0/invoke/catalog/method/event

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:

Consul Service List

Summary

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.

Want to get up learn more about how Dapr can greatly simplify the task of building microservices? Be sure to check out my Pluralsight course, Dapr 1 Fundamentals.

Comments

Comment by alkapa

Dear Mark, Thank you for great courses about darp on Pluralsight!
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?

alkapa