Getting started to Consul
Consul is developed by Hashicorp to provide a few features like a service discovery.
This document is to learn a little bit about setting up Consul to understand it as a first step, but not for production purpose.
So far, this includes
- To set up consul agents
 - To use a consul client to access the agent
 - Look up a web server from the consul datacenter
 
The first step for consul servers
In the first step, I refer to next page.
At first, use Docker Compose to set up a few consul servers.
The docker-compose.yml looks like next.
version: '3.3'
services:
  consul-server1:
    image: consul
    hostname: "consul-server1"
    networks:
      - cluster
    ports:
      - 8500:8500
    volumes:
      - ./server.json:/consul/config/server.json:ro
    command:
      - "agent"
      - "--retry-join"
      - "consul-server2"
  consul-server2:
    image: consul
    hostname: "consul-server2"
    networks:
      - cluster
    volumes:
      - ./server.json:/consul/config/server.json:ro
    command:
      - "agent"
      - "--retry-join"
      - "consul-server2"
networks:
  cluster:The server.json which is mounted to two containers are something like this.
{
  "server": true,
  "bootstrap_expect": 2,
  "ui_config": {
    "enabled": true
  },
  "client_addr": "0.0.0.0"
}Then, after containers started, the Web UI can be seen on the http://127.0.0.1/8500.

Add a consul-client container
Add a consul-client container into the 1st docker-compose.yml
version: '3.3'
services:
  consul-server1:
    image: consul
    hostname: "consul-server1"
    networks:
      - cluster
    ports:
      - 8500:8500
    volumes:
      - ./server.json:/consul/config/server.json:ro
    command:
      - "agent"
      - "--retry-join"
      - "consul-server2"
  consul-server2:
    image: consul
    hostname: "consul-server2"
    networks:
      - cluster
    volumes:
      - ./server.json:/consul/config/server.json:ro
    command:
      - "agent"
      - "--retry-join"
      - "consul-server2"
  consul-client:
    image: consul
    hostname: "consul-client"
    networks:
      - cluster
    command:
      - "agent"
      - "--retry-join"
      - "consul-server1"
      - "--retry-join"
      - "consul-server2"
networks:
  cluster:Once it starts, we can use consul client with new container.
> docker exec consul_consul-client_1 consul members
Node            Address          Status  Type    Build   Protocol  DC   Segment
consul-server1  172.29.0.3:8301  alive   server  1.10.3  2         dc1  <all>
consul-server2  172.29.0.4:8301  alive   server  1.10.3  2         dc1  <all>
consul-client   172.29.0.2:8301  alive   client  1.10.3  2         dc1  <default>Service discovery
I followed these documents.
- [HashiCorp Learn: Register a Service with Consul Service Discovery]https://learn.hashicorp.com/tutorials/consul/get-started-service-discovery)
 - HashiCorp Learn: Register External Services with Consul Service Discovery
 
First of all, in the context of Consul, there are 2 types of services.
- Internal service: runs on the same node (machine) as a Consul agent
 - External service: runs on nodes where you cannot run a local Consul agent
 
In this tutorial, run a web service as an external service, which means without a consul agent on the server.
Register an external service
At first, add a web service definition in docker-compose.yml file.
version: '3.3'
services:
  consul-server1:
    image: consul
    hostname: "consul-server1"
    networks:
      - cluster
    ports:
      - 8500:8500
      - 8600:8600
    volumes:
      - ./server.json:/consul/config/server.json:ro
    command:
      - "agent"
      - "--retry-join"
      - "consul-server2"
  consul-server2:
    image: consul
    hostname: "consul-server2"
    networks:
      - cluster
    volumes:
      - ./server.json:/consul/config/server.json:ro
    command:
      - "agent"
      - "--retry-join"
      - "consul-server2"
  consul-client:
    image: consul
    hostname: "consul-client"
    networks:
      - cluster
    command:
      - "agent"
      - "--retry-join"
      - "consul-server1"
      - "--retry-join"
      - "consul-server2"
  web:
    image: nginx
    ports:
      - 8080:80
    networks:
      - cluster
networks:
  cluster:Then define the web service as the Consul external service by next JSON file web.json.
{
  "Node": "web",
  "Address": "web",
  "NodeMeta": {
    "external-node": "true",
    "external-probe": "true"
  },
  "Service": {
    "ID": "web",
    "Service": "web",
    "Port": 80
  },
  "Checks": [
    {
      "Name": "http-check",
      "status": "passing",
      "Definition": {
        "http": "http://web/",
        "interval": "10s"
      }
    }
  ]
}With this service definition file, the service can be registered by HTTP API.
In order to register it and also look up DNS later, add 2 service definitions in docker-compose.yml, curl and dnsutils.
version: '3.3'
services:
  consul-server1:
    image: consul
    hostname: "consul-server1"
    networks:
      - cluster
    ports:
      - 8500:8500
      - 8600:8600
    volumes:
      - ./server.json:/consul/config/server.json:ro
    command:
      - "agent"
      - "--retry-join"
      - "consul-server2"
  consul-server2:
    image: consul
    hostname: "consul-server2"
    networks:
      - cluster
    volumes:
      - ./server.json:/consul/config/server.json:ro
    command:
      - "agent"
      - "--retry-join"
      - "consul-server2"
  consul-client:
    image: consul
    hostname: "consul-client"
    networks:
      - cluster
    command:
      - "agent"
      - "--retry-join"
      - "consul-server1"
      - "--retry-join"
      - "consul-server2"
  web:
    image: nginx
    ports:
      - 8080:80
    networks:
      - cluster
  curl:
    image: curlimages/curl
    networks:
      - cluster
    volumes:
      - ./web.json:/web.json
    command:
      - "-XPUT"
      - "--data"
      - "@/web.json"
      - "http://consul-server1:8500/v1/catalog/register"
    depends_on:
      - consul-server1
      - consul-server2
  dnsutils:
    image: tutum/dnsutils
    networks:
      - cluster
    command:
      - "dig"
      - "@consul-server1"
      - "-p"
      - "8600"
      - "consul.service.consul"
networks:
  cluster:Then when you start containers, you can see new service web separeted from consul on the UI.
By a CLI, you can also see a web service.
> docker exec consul_consul-client_1 consul catalog services
consul
web
> docker exec consul_consul-client_1 consul catalog nodes
Node            ID        Address        DC
consul-client   6ae54162  192.168.176.5  dc1
consul-server1  e1cfae12  192.168.176.6  dc1
consul-server2  33535a6e  192.168.176.4  dc1
web                       web            dc1Look up DNS of the external service
In Consul, the DNS of a service is registered as NAME.service.consul according to this tutorial.
It looks CNAME is registered for an external service in Consul.
> docker run -it --net=consul_cluster tutum/dnsutils dig @consul-server1 -p 8600 web.service.consul
; <<>> DiG 9.9.5-3ubuntu0.2-Ubuntu <<>> @consul-server1 -p 8600 web.service.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32415
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;web.service.consul.		IN	A
;; ANSWER SECTION:
web.service.consul.	0	IN	CNAME	web.
;; Query time: 0 msec
;; SERVER: 192.168.176.6#8600(192.168.176.6)
;; WHEN: Sun Nov 07 02:20:23 UTC 2021
;; MSG SIZE  rcvd: 64Troubleshootings
Outside of consul-server container, it’s not possible to look up DNS records for some reasons.
> docker run -it --net=consul_cluster tutum/dnsutils dig @consul-server1 -p 8600 web.service.consul
; <<>> DiG 9.16.8-Ubuntu <<>> @127.0.0.1 -p 8600 web.service.consul
; (1 server found)
;; global options: +cmd
;; connection timed out; no servers could be reachedSolution: I changed next configuration in server.json with client_addr: "0.0.0.0".
  "addresses": {
      "http": "0.0.0.0"
  }This can also be solved by adding “addresses.dns = “0.0.0.0”.