Implement a Service Mesh with Consul Connect

Like other service mesh technologies such as Istio and Linkerd, HashiCorp’s Consul Connect comes with a proxy that’s deployed as a sidecar. The proxy transparently secures communication among microservices and enables policy definition through a concept known as Intentions. In my previous tutorial, we explored the concept of service discovery of Consul. In this tutorial, we will expand the service definitions to include the proxy and then switch the service URLs to use the sidecar. After this, we will learn how to allow and deny traffic between two endpoints.
Adding a Proxy to the Consul Service Definition
To implement service mesh and take advantage of its capabilities, we need to add the upstream services also known as backend services to the service definition. In our use case, the order service has an upstream called invoice, which in turn has tax as an upstream service. Even though the call chain ends with the tax service, we will include a proxy for that.
Let’s start with the tax service. Create a file with the name tax.json with the below contents.
1 2 3 4 5 6 7 |
{ "service": { "name": "tax", "port": 5002, "connect": { "sidecar_service": {} } } |
Notice the addition of a new element called connect. The sidecar_service node is empty because the tax service is the last one in the chain.
We will connect the tax service proxy with the invoice service. Below is the service definition of the invoice service.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{ "service": { "name": "invoice", "port": 5001, "connect": { "sidecar_service": { "proxy": { "upstreams": [{ "destination_name": "tax", "local_bind_port": 6002 }] } |
Notice how the upstreams section is declared. It instructs Consul runtime to route the traffic to the tax service proxy listening on 6002 instead of the default port of 5002.
The order service definition has the same rule but for the invoice proxy.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{ "service": { "name": "order", "port": 5000, "connect": { "sidecar_service": { "proxy": { "upstreams": [{ "destination_name": "invoice", "local_bind_port": 6001 }] } |
We essentially instructed Consul Connect to run respective proxies for tax and invoice services on 6001 and 6002 while the original end points continue to run on 5001 and 5002.
By switching to the proxy endpoints, we gain two advantages – secure communication, and traffic routing policies.
Replace the original service definition files in consul.d directory with the updates JSON files.
Running the Proxy Sidecar for Microservices
If Consul agent is still running, stop and restart it to ensure that the new definitions are in effect.
Apart from restarting Consult agent, we also need to run the proxies for each of the service definitions. This can be done from the command line.
The below commands run the service proxies and deploy a sidecar for each microservice.
1 2 3 |
consul connect proxy -sidecar-for order & consul connect proxy -sidecar-for invoice & consul connect proxy -sidecar-for tax & |
This manual step is not required when Consul Connect is run as a part of a container orchestration engine such as Nomad or Kubernetes.
Notice that Consul is adding a TLS certificate to the endpoint to implement mutual TLS (mTLS).
Access the Consul dashboard at http://localhost:8500/ui/zone1/services to see the proxy services added to the list.
Each microservice now has an associated sidecar proxy service exposing a secure endpoint.
Switching from the Default to Secure Microservices Endpoint
To take advantage of mTLS-based communication, we will switch to the new endpoints exposed by the sidecar proxies.
Kill the Python processes running the Flask REST endpoint and relaunch with the below config:
1 2 3 |
python tax.py & TAX_SVC_URL=http://tax.service.consul:6002 python invoice.py & INV_SVC_URL=http://invoice.service.consul:6001 python order.py & |
Notice how we pointed the upstream services to the sidecar proxy through the environment variable of the URI.
When we invoke the order service, it is talking to the secure endpoints of invoice and tax.
Defining Intentions to Allow or Deny Traffic
With the sidecar proxies in place, we can now take advantage of Consul Connect Intentions, a mechanism to allow or deny requests.
Run the below commands to explore the concept.
1 2 |
consul intention check order invoice consul intention check invoice tax |
As shown by the above output, the traffic flows from the order service to invoice to tax. We can categorically deny the traffic between invoice and tax to test the functionality.
Now, when we try to invoke the order service, it fails because of the deny intent between invoice and tax.
This can also be verified through the web UI.
Deleting the deny intent resumes the traffic flow between microservices.
Consul from HashiCorp is a powerful tool for implementing service discovery, storing key/value pairs, and configuring service mesh.
Janakiram MSV’s Webinar series, “Machine Intelligence and Modern Infrastructure (MI2)” offers informative and insightful sessions covering cutting-edge technologies. Sign up for the upcoming MI2 webinar at http://mi2.live.