Brief overview of using consul tags
consul allows a service to associate itself with tags
. These are arbitrary
metadata that can be associated with the service and can be used for different purposes. Below I outline
a few examples of making use of tags and discuss some related topics.
Use case #1: Dedicated service instances based on requests
Let’s say our service is a HTTP server (REST API) acting as a routing point for multiple independent resources with the following service definition:
{
"service": {
"name": "api",
"address": "",
"port": 8080,
"checks": [],
}
}
We can then communicate with service using the DNS, api.service.consul
.
Let’s assume we are running N copies of this service, but want to have dedicated sub-pools for separate resource groups. We will assign the services in each pool a different tag as follows:
projects
{
"service": {
"name": "api",
"address": "",
"port": 8080,
"checks": [],
"tags":["projects"],
}
}
users
{
"service": {
"name": "api",
"address": "",
"port": 8080,
"checks": [],
"tags":["users"],
}
}
Once we register the services using the different tags, they automatically become discoverable via DNS
as projects.api.service.consul
and users.api.service.consul
respectively. Assuming that the routing
to our HTTP server is happening in a higher layer, we will then direct traffic to these pools as follows:
api/projects/ -> projects.api.service.consul
api/users/ -> users.api.service.consul
Use case #2: Running different versions of your service
We can use tags to run two different versions of our application for testing, gathering performance data, blue-green deployment or any other reason:
v1
{
"service": {
"name": "api",
"address": "",
"port": 8080,
"checks": [],
"tags":["v1"],
}
}
v2
{
"service": {
"name": "api",
"address": "",
"port": 8080,
"checks": [],
"tags":["v2"],
}
}
We can then use a tag based weighted mechanism at a higher level proxy (such as linkerd) to send traffic to these different service versions.
Use case #3: Other metadata
This issue on consul’s project discusses using tags as a way to have artbitary metadata for a service due to the lack of support for key-value labels.
Using tags for discovery
Besides using the DNS interface for communicating with the services, we can use tags
as filter with
the consul catalog API. However, it currently supports a single
tag. There is a feature request open to support multiple
tags.
Demo: Running two versions of a service
I have two versions of a service, api
. Each service is running in a separate docker container on port 8080.
v1
and v2
are also the tags associated with the respective instances. The demo source code can be found
here. To follow along, clone the repository, install docker
and
docker-compose
.
Start consul and the two versions of the API
$ pushd tags/api/v1
$ ./build-image.sh
$ popd
$ pushd tags/api/v2
$ ./build-image.sh
$ popd
$ pushd tags
$ docker-compose up
We should see the following output from docker-compose up
:
consul | 2017/12/01 04:01:03 [DEBUG] http: Request PUT /v1/agent/service/register (1.020389ms) from=172.21.0.4:34030
consul | 2017/12/01 04:01:03 [DEBUG] agent: Service 'apiv2' in sync
consul | 2017/12/01 04:01:03 [DEBUG] agent: Node info in sync
consul | 2017/12/01 04:01:03 [DEBUG] agent: Service 'apiv2' in sync
consul | 2017/12/01 04:01:03 [DEBUG] agent: Node info in sync
consul | 2017/12/01 04:01:04 [DEBUG] agent: Service 'apiv2' in sync
consul | 2017/12/01 04:01:04 [INFO] agent: Synced service 'apiv1'
consul | 2017/12/01 04:01:04 [DEBUG] agent: Node info in sync
consul | 2017/12/01 04:01:04 [DEBUG] http: Request PUT /v1/agent/service/register (3.333932ms) from=172.21.0.3:42486
consul | 2017/12/01 04:01:04 [DEBUG] agent: Service 'apiv2' in sync
consul | 2017/12/01 04:01:04 [DEBUG] agent: Service 'apiv1' in sync
consul | 2017/12/01 04:01:04 [DEBUG] agent: Node info in sync
consul | 2017/12/01 04:01:04 [DEBUG] agent: Service 'apiv1' in sync
consul | 2017/12/01 04:01:04 [DEBUG] agent: Service 'apiv2' in sync
consul | 2017/12/01 04:01:04 [DEBUG] agent: Node info in sync
Start the dnsmasq container
Next, we are going to start a new docker container running dnsmasq:
$ < repo root >
$ pushd support/dnsmasq
$ ./start-dnsmasq.sh
Start the API client container
Now, let’s start a container which will act as an API client:
$ < repo root >
$ cd support/apiclient
$ ./start-client.sh
/ # dig api.service.consul +short
172.21.0.4
172.21.0.3
/ # dig v1.api.service.consul +short
172.21.0.3
/ # dig v2.api.service.consul +short
172.21.0.4
/ # curl v1.api.service.consul:8080/ping/
Hi there! I am v1/ #
/ # curl v2.api.service.consul:8080/ping/
Hi there! I am v2/ #
Points to note
While working on the demo, I found out that I needed to specify the IP address of the service I was registering.
Otherwise, they were being registered with empty IP addresses. This could be due to those services running in the
docker
container. I am not sure.
I also learned that since I was running a single consul agent, I had to specify a unique service ID for the two service instances.