使用Consul进行服务发现

目录

作者:杨冬 欢迎转载,也请保留这段声明。谢谢!
出处:https://andyyoung01.github.io/http://andyyoung01.16mb.com/

当应用程序由多个服务或层次组成,为了使这些应用程序或服务能够彼此间进行通信,需要知道这些服务部署的位置。这些信息可以存储在类似于Etcd或ZooKeeper等分布式的键值存储系统中,也可以通过DNS查找到服务的部署位置。本篇来看看Consul提供的服务发现功能。

当有多个彼此独立的服务需要协同工作时,就需要一种机制来可以让一个应用或者组件发现其运行环境以及其它应用或组件的信息。Consul就可以提供这些功能:

  • 服务配置 - 它可以提供一个键值存储系统来保存和共享键值对,类似于etcd和ZooKeeper。
  • 服务发现 - 它提供一个可以用来注册服务和提供DNS的API,类似于SkyDNS。
  • 服务监控 - 它提供一个API用于注册健康检查,类似于Nagios。

在使用Consul时,可以使用它的全部或部分功能,这些功能之间并没有依赖。比如如果已经有了监控的基础设施,没必要再用Consul取代它们。
一个典型的Consul setup类似于下图:
“典型的Consul setup”
在Consul中数据的存储是由server agent负责的。它们组成一个consensus集群-这个概念存在于大多数的分布式数据存储系统中。简单来说,如果有半数以上的server agents可用,则可以确保数据可以恢复。
所有想要与Consul交互的机器都应该运行一个Consul client agent。这些agents将请求转发给servers并且进行健康检查。
这里我们通过Docker进行Consul的部署,为了简单起见,Consul server并没有组成consensus集群,我们只是使用了一个节点进行搭建,此节点的机器名为centos7-A,IP地址为192.168.71.167:

1
2
3
4
5
6
7
8
9
[yangdong@centos7-A ~]$ docker pull consul
...
[yangdong@centos7-A ~]$ docker run -d --net=host \
-e 'CONSUL_ALLOW_PRIVILEGED_PORTS=' \
--name consul_server consul agent -server \
-bind=192.168.71.167 -bootstrap-expect=1 \
-dns-port=53 -client=192.168.71.167 \
-recursor 8.8.8.8 -recursor 8.8.4.4
4f580aa073cfc4108c3bdad23dacd140bd90b4a40467422d895252b09da8ef21

上面的docker run命令有几点需要注意:

  • 这里使用的是consul镜像的最新版本v0.7.0。
  • 使用了--net=host的命令行参数。此参数使得docker容器越过了net namespace的隔离,解决了潜在的UDP通信的问题,而且这种用法不用手动指定所有的端口映射。
  • 使用了-e 'CONSUL_ALLOW_PRIVILEGED_PORTS='环境变量,使用了-dns-port=53命令行选项。这允许容器绑定到标准的DNS端口53上。如果不指定这两项,consul会默认绑定到本地回环lo地址的8600端口上。
  • 提供了两个recursor参数。这告诉Consul当请求地址并不能由consul自己解析的话,它到哪里去查询其它的DNS服务器。这里指定了google提供的两个dns服务器的ip。
  • -bootstrap-expect=1参数意味着Consul集群使用1个agent便可以开始操作,这没有提供高可用的特性。此参数通常设置为3(或更多)来确保集群直到需要数量的服务器加入集群后才能开始操作。为了将更多的server agent加入集群,需要使用-join参数。
  • bind指定的是集群间通信使用的ip地址,client指定的是客户端需要访问的ip地址。

下面在第二台机器上,启动一个client agent,并且将其加入集群,此节点的机器名为centos7,IP地址为192.168.71.131:

1
2
3
4
5
[yangdong@centos7 ~]$ docker run -d --net=host -e 'CONSUL_ALLOW_PRIVILEGED_PORTS=' \
--name consul_client consul agent -dns-port=53 \
-bind=192.168.71.131 -client=172.17.0.1 -join 192.168.71.167 \
-recursor 8.8.8.8 -recursor 8.8.4.4
c2567b364729b23980c8229249a4060d3facd87667387c523cbafdf8d9da4a63

上面需要注意的是我们指定-client的ip地址为主机上docker0(docker网桥)的ip地址,这样指定是便于本机的其它docker容器使用此地址与consul交互。

关于Consul DNS的更详细介绍请参考https://www.consul.io/docs/agent/dns.html

下面验证一下client agent是否已经加连接上了server agent。这里使用curl命令:

1
2
3
[yangdong@centos7 ~]$ curl -sSL 172.17.0.1:8500/v1/agent/members| python -m json.tool | grep Addr
"Addr": "192.168.71.131",
"Addr": "192.168.71.167",

可见client已经连接上了server。下面探索一下怎样进行服务注册和服务发现。典型的注册过程是当容器或服务启动后,其通过对本地consul client agent的API调用,促使client agent将信息分发到server agents上。这里我们使用手动的注册步骤:

1
2
3
4
5
6
7
8
9
10
11
12
[yangdong@centos7 ~]$ docker run -d --name files -p 8000:80 ubuntu:14.04 \
python3 -m http.server 80
6eda22ee7bddaf6ebd18aeb5bffe5e26b3e366fae5a8a31d2433c72f04d3574e
[yangdong@centos7 ~]$ docker inspect -f '{{.NetworkSettings.IPAddress}}' files
172.17.0.2
[yangdong@centos7 ~]$ curl 172.17.0.2
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
...
[yangdong@centos7 ~]$ curl -X PUT --data-binary '{"Name":"files","Port":8000}' \
172.17.0.1:8500/v1/agent/service/register
[yangdong@centos7 ~]$ docker logs consul_client | tail -1
2016/09/27 06:21:41 [INFO] agent: Synced service 'files'

上面第1、2行 命令创建了一个HTTP server容器;第4行命令得到该容器的IP;第6行验证了容器的正常运行;最关键的是第9、10行命令,它通过curl访问了Consul的HTTP API来注册服务,其中服务名称是必须的,这里我们指定为files,其它都是可选项;第11行命令通过consul的日志确认了service已经同步,所以我们可以通过DNS接口来得到其信息。下面我们就通过dig命令来查询服务的DNS信息,来验证刚才的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[yangdong@centos7 ~]$ dig @192.168.71.167 files.service.consul +short
192.168.71.131
[yangdong@centos7 ~]$ dig @172.17.0.1 files.service.consul +short
192.168.71.131
[yangdong@centos7 ~]$ dig @172.17.0.1 files.service.consul srv +short
1 1 8000 centos7.node.dc1.consul.
[yangdong@centos7 ~]$ docker run -it --dns 172.17.0.1 ubuntu:14.04 bash
root@c07724037814:/# ping www.baidu.com
PING www.a.shifen.com (220.181.112.244) 56(84) bytes of data.
64 bytes from 220.181.112.244: icmp_seq=1 ttl=127 time=5.05 ms
64 bytes from 220.181.112.244: icmp_seq=2 ttl=127 time=2.40 ms
^C
--- www.a.shifen.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 2.406/3.729/5.052/1.323 ms
root@c07724037814:/# ping files.service.consul
PING files.service.consul (192.168.71.131) 56(84) bytes of data.
64 bytes from centos7.node.dc1.consul (192.168.71.131): icmp_seq=1 ttl=64 time=0.056 ms
64 bytes from centos7.node.dc1.consul (192.168.71.131): icmp_seq=2 ttl=64 time=0.076 ms
^C
--- files.service.consul ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 0.056/0.066/0.076/0.010 ms

上面第1行命令从server agent DNS查询服务的IP地址,这个DNS服务对不在consul集群中的任何可以访问到此IP的机器都可用;
第3行命令从本机的client agent DNS来查询服务的IP地址;
第5行命令从client agent DNS查询SRV的记录,SRV记录是通过DNS给出服务信息的一种方式,上面的命令返回了服务的端口号以及提供服务的机器的节点名称;
第7行命令启动了一个新的容器,并且指定该容器应该使用本地client agent提供的DNS服务,然后进入容器的bash测试是否可以解析地址;
第8行通过ping百度确认可以解析百度的域名;
第16行通过ping前面配置的服务名,确认该服务也可以正常解析。

通过上面的测试,我们发现Resolvable和Consul DNS服务非常相似。它们之间最大的不同就是Consul可以让你在多个节点之间查找容器,而Resolvable只是在单台节点上查找。