Consul
微服务的框架体系中,服务发现是不能不提的一个模块,传统的服务框架必须知道服务的网络位置,而这些位置信息都是通过配置文件完成,当服务大量扩张的时候,这管理规模别提多么庞大,更别提现在的docker容器化的大量部署的时代,如何可以动态的让程序间明白我所需要的服务在哪?是微服务时代必须解决的事情,而随着微服务越来越流行,大量涌现的服务发现框架,我们比较常见的用:zookeeper,eureka,etcd,consul……
存在即合理,多种服务发现框架也都有自己的适用地方,而这里之所以我学习consul,就是项目上使用了consul。
consul是分布式的、高可用的、横向扩展的,consul提供了一些关键特性:
- 服务发现:consul通过DNS或者HTTP接口使服务注册和服务发现的很容易。
- 健康检查:健康检查使consul可以快速告警集群中的操作。和服务发现的集成,可以防止服务转发到故障服务上。
- K/V存储:一个用来存储动态配置的系统,提供简单的HTTP接口,可以在任何地方操作。
- 多数据中心:无需任何配置即可支持任意的区域。
从图上发现,consul的集群是由多个client和server组成的。而不管server还是client,都是consul的一个节点,我们都可以将服务注册上任意节点。
client
client表示consul的client模式,在这个模式下注册的服务会被转发到server,本身不会持久化这些信息。server
server表示consul的server模式,这个模式下,基本功能都和client一样,唯一不同的是,它会把所有的信息持久化到本地。server-leader
顾名思义,这是server模式的leader,它除了普通server的功能外,还需要负责同步注册的信息给其他的server,同时也要负责各个节点的健康监测。
Start Consul
Consul的安装十分简单,可以到官网直接下载:https://www.consul.io/downloads.html
1 | ~]# consul agent -server -data-dir=/tmp/consul -bootstrap-expect 1 -node server -bind=10.211.55.6 |
2 | BootstrapExpect is set to 1; this is the same as Bootstrap mode. |
3 | bootstrap = true: do not enable unless necessary |
4 | ==> Starting Consul agent... |
5 | ==> Consul agent running! |
6 | Version: 'v1.0.0' |
7 | Node ID: '0c99b5a9-f603-9fa6-fcf3-9703e07ffb24' |
8 | Node name: 'server' |
9 | Datacenter: 'dc1' (Segment: '<all>') |
10 | Server: true (Bootstrap: true) |
11 | Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, DNS: 8600) |
12 | Cluster Addr: 10.211.55.6 (LAN: 8301, WAN: 8302) |
13 | Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false |
14 | |
15 | ==> Log data will now stream in as it occurs: |
16 | |
17 | 2017/10/27 13:14:05 [INFO] raft: Initial configuration (index=1): [{Suffrage:Voter ID:0c99b5a9-f603-9fa6-fcf3-9703e07ffb24 Address:10.211.55.6:8300}] |
18 | 2017/10/27 13:14:05 [INFO] raft: Node at 10.211.55.6:8300 [Follower] entering Follower state (Leader: "") |
19 | 2017/10/27 13:14:05 [INFO] serf: EventMemberJoin: server.dc1 10.211.55.6 |
20 | 2017/10/27 13:14:05 [INFO] serf: EventMemberJoin: server 10.211.55.6 |
21 | 2017/10/27 13:14:05 [INFO] consul: Adding LAN server server (Addr: tcp/10.211.55.6:8300) (DC: dc1) |
22 | 2017/10/27 13:14:05 [INFO] agent: Started DNS server 127.0.0.1:8600 (udp) |
23 | 2017/10/27 13:14:05 [INFO] consul: Handled member-join event for server "server.dc1" in area "wan" |
24 | 2017/10/27 13:14:05 [INFO] agent: Started DNS server 127.0.0.1:8600 (tcp) |
25 | 2017/10/27 13:14:05 [INFO] agent: Started HTTP server on 127.0.0.1:8500 (tcp) |
26 | 2017/10/27 13:14:10 [WARN] raft: Heartbeat timeout from "" reached, starting election |
27 | 2017/10/27 13:14:10 [INFO] raft: Node at 10.211.55.6:8300 [Candidate] entering Candidate state in term 2 |
28 | 2017/10/27 13:14:10 [INFO] raft: Election won. Tally: 1 |
29 | 2017/10/27 13:14:10 [INFO] raft: Node at 10.211.55.6:8300 [Leader] entering Leader state |
30 | 2017/10/27 13:14:10 [INFO] consul: cluster leadership acquired |
31 | 2017/10/27 13:14:10 [INFO] consul: New leader elected: server |
32 | 2017/10/27 13:14:10 [INFO] consul: member 'server' joined, marking health alive |
33 | 2017/10/27 13:14:11 [INFO] agent: Synced node info |
34 | |
35 | ~]# consul members |
36 | Node Address Status Type Build Protocol DC Segment |
37 | server 10.211.55.6:8301 alive server 1.0.0 2 dc1 <all> |
38 | |
39 | ~]# curl 127.0.0.1:8500/v1/catalog/nodes |
40 | [{"ID":"0c99b5a9-f603-9fa6-fcf3-9703e07ffb24","Node":"server","Address":"10.211.55.6","Datacenter":"dc1","TaggedAddresses":{"lan":"10.211.55.6","wan":"10.211.55.6"},"Meta":{"consul-network-segment":""},"CreateIndex":5,"ModifyIndex":6}] |
41 | |
42 | ~]# dig @127.0.0.1 -p 8600 server.node.consul |
43 | |
44 | ; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> @127.0.0.1 -p 8600 server.node.consul |
45 | ; (1 server found) |
46 | ;; global options: +cmd |
47 | ;; Got answer: |
48 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 44902 |
49 | ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 |
50 | ;; WARNING: recursion requested but not available |
51 | |
52 | ;; OPT PSEUDOSECTION: |
53 | ; EDNS: version: 0, flags:; udp: 4096 |
54 | ;; QUESTION SECTION: |
55 | ;server.node.consul. IN A |
56 | |
57 | ;; ANSWER SECTION: |
58 | server.node.consul. 0 IN A 10.211.55.6 |
59 | |
60 | ;; Query time: 0 msec |
61 | ;; SERVER: 127.0.0.1#8600(127.0.0.1) |
62 | ;; WHEN: Fri Oct 27 13:19:51 EDT 2017 |
63 | ;; MSG SIZE rcvd: 63 |
Register Service
服务发现的前提是我们需要注册直接的服务,现在我们来注册自己的服务。
1 | ~]# mkdir /etc/consul.d |
2 | ~]# cat > /etc/consul.d/web.json << EOF |
3 | { |
4 | "service": { |
5 | "name": "web", |
6 | "tags": ["Django"], |
7 | "port": 80 |
8 | } |
9 | } |
10 | EOF |
11 | ~]# consul agent -server -data-dir=/tmp/consul -bootstrap-expect 1 -node server -bind=10.211.55.6 -config-dir=/etc/consul.d/ |
12 | BootstrapExpect is set to 1; this is the same as Bootstrap mode. |
13 | bootstrap = true: do not enable unless necessary |
14 | ==> Starting Consul agent... |
15 | ==> Consul agent running! |
16 | Version: 'v1.0.0' |
17 | Node ID: '0c99b5a9-f603-9fa6-fcf3-9703e07ffb24' |
18 | Node name: 'server' |
19 | Datacenter: 'dc1' (Segment: '<all>') |
20 | Server: true (Bootstrap: true) |
21 | Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, DNS: 8600) |
22 | Cluster Addr: 10.211.55.6 (LAN: 8301, WAN: 8302) |
23 | Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false |
24 | |
25 | ==> Log data will now stream in as it occurs: |
26 | |
27 | 2017/10/27 13:32:26 [INFO] raft: Initial configuration (index=1): [{Suffrage:Voter ID:0c99b5a9-f603-9fa6-fcf3-9703e07ffb24 Address:10.211.55.6:8300}] |
28 | 2017/10/27 13:32:26 [INFO] raft: Node at 10.211.55.6:8300 [Follower] entering Follower state (Leader: "") |
29 | 2017/10/27 13:32:26 [INFO] serf: EventMemberJoin: server.dc1 10.211.55.6 |
30 | 2017/10/27 13:32:26 [WARN] serf: Failed to re-join any previously known node |
31 | 2017/10/27 13:32:26 [INFO] serf: EventMemberJoin: server 10.211.55.6 |
32 | 2017/10/27 13:32:26 [INFO] agent: Started DNS server 127.0.0.1:8600 (udp) |
33 | 2017/10/27 13:32:26 [INFO] consul: Handled member-join event for server "server.dc1" in area "wan" |
34 | 2017/10/27 13:32:26 [WARN] serf: Failed to re-join any previously known node |
35 | 2017/10/27 13:32:26 [INFO] consul: Adding LAN server server (Addr: tcp/10.211.55.6:8300) (DC: dc1) |
36 | 2017/10/27 13:32:26 [INFO] agent: Started DNS server 127.0.0.1:8600 (tcp) |
37 | 2017/10/27 13:32:26 [INFO] agent: Started HTTP server on 127.0.0.1:8500 (tcp) |
38 | 2017/10/27 13:32:33 [ERR] agent: failed to sync remote state: No cluster leader |
39 | 2017/10/27 13:32:35 [WARN] raft: Heartbeat timeout from "" reached, starting election |
40 | 2017/10/27 13:32:35 [INFO] raft: Node at 10.211.55.6:8300 [Candidate] entering Candidate state in term 3 |
41 | 2017/10/27 13:32:35 [INFO] raft: Election won. Tally: 1 |
42 | 2017/10/27 13:32:35 [INFO] raft: Node at 10.211.55.6:8300 [Leader] entering Leader state |
43 | 2017/10/27 13:32:35 [INFO] consul: cluster leadership acquired |
44 | 2017/10/27 13:32:35 [INFO] consul: New leader elected: server |
45 | 2017/10/27 13:32:37 [INFO] agent: Synced service 'web' |
46 | |
47 | ~]# curl http://127.0.0.1:8500/v1/catalog/service/web |
48 | [{"ID":"0c99b5a9-f603-9fa6-fcf3-9703e07ffb24","Node":"server","Address":"10.211.55.6","Datacenter":"dc1","TaggedAddresses":{"lan":"10.211.55.6","wan":"10.211.55.6"},"NodeMeta":{"consul-network-segment":""},"ServiceID":"web","ServiceName":"web","ServiceTags":["Django"],"ServiceAddress":"","ServicePort":80,"ServiceEnableTagOverride":false,"CreateIndex":61,"ModifyIndex":61}] |
49 | |
50 | ~]# curl http://127.0.0.1:8500/v1/catalog/service/web?passing #健康状态检查 |
51 | [{"ID":"0c99b5a9-f603-9fa6-fcf3-9703e07ffb24","Node":"server","Address":"10.211.55.6","Datacenter":"dc1","TaggedAddresses":{"lan":"10.211.55.6","wan":"10.211.55.6"},"NodeMeta":{"consul-network-segment":""},"ServiceID":"web","ServiceName":"web","ServiceTags":["Django"],"ServiceAddress":"","ServicePort":80,"ServiceEnableTagOverride":false,"CreateIndex":61,"ModifyIndex":61}] |
52 | |
53 | ~]# dig @127.0.0.1 -p 8600 web.service.consul |
54 | |
55 | ; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> @127.0.0.1 -p 8600 web.service.consul |
56 | ; (1 server found) |
57 | ;; global options: +cmd |
58 | ;; Got answer: |
59 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21835 |
60 | ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 |
61 | ;; WARNING: recursion requested but not available |
62 | |
63 | ;; OPT PSEUDOSECTION: |
64 | ; EDNS: version: 0, flags:; udp: 4096 |
65 | ;; QUESTION SECTION: |
66 | ;web.service.consul. IN A |
67 | |
68 | ;; ANSWER SECTION: |
69 | web.service.consul. 0 IN A 10.211.55.6 |
70 | |
71 | ;; Query time: 0 msec |
72 | ;; SERVER: 127.0.0.1#8600(127.0.0.1) |
73 | ;; WHEN: Fri Oct 27 13:38:07 EDT 2017 |
74 | ;; MSG SIZE rcvd: 63 |
75 | |
76 | ~]# dig @127.0.0.1 -p 8600 Django.web.service.consul |
77 | |
78 | ; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> @127.0.0.1 -p 8600 Django.web.service.consul |
79 | ; (1 server found) |
80 | ;; global options: +cmd |
81 | ;; Got answer: |
82 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43935 |
83 | ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 |
84 | ;; WARNING: recursion requested but not available |
85 | |
86 | ;; OPT PSEUDOSECTION: |
87 | ; EDNS: version: 0, flags:; udp: 4096 |
88 | ;; QUESTION SECTION: |
89 | ;Django.web.service.consul. IN A |
90 | |
91 | ;; ANSWER SECTION: |
92 | Django.web.service.consul. 0 IN A 10.211.55.6 |
93 | |
94 | ;; Query time: 0 msec |
95 | ;; SERVER: 127.0.0.1#8600(127.0.0.1) |
96 | ;; WHEN: Fri Oct 27 13:38:38 EDT 2017 |
97 | ;; MSG SIZE rcvd: 70 |
Consul Cluster
新起一台client consul:
1 | ~]# consul agent -data-dir=/tmp/consul -node=client -bind=10.211.55.43 |
2 | ==> Starting Consul agent... |
3 | ==> Consul agent running! |
4 | Version: 'v1.0.0' |
5 | Node ID: 'd008bc60-ed76-1feb-3ed5-372e81fc35ef' |
6 | Node name: 'client' |
7 | Datacenter: 'dc1' (Segment: '') |
8 | Server: false (Bootstrap: false) |
9 | Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, DNS: 8600) |
10 | Cluster Addr: 10.211.55.43 (LAN: 8301, WAN: 8302) |
11 | Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false |
12 | |
13 | ==> Log data will now stream in as it occurs: |
14 | |
15 | 2017/08/13 02:27:34 [INFO] serf: EventMemberJoin: client 10.211.55.43 |
16 | 2017/08/13 02:27:34 [INFO] agent: Started DNS server 127.0.0.1:8600 (udp) |
17 | 2017/08/13 02:27:34 [INFO] agent: Started DNS server 127.0.0.1:8600 (tcp) |
18 | 2017/08/13 02:27:34 [INFO] agent: Started HTTP server on 127.0.0.1:8500 (tcp) |
19 | 2017/08/13 02:27:34 [WARN] manager: No servers available |
20 | 2017/08/13 02:27:34 [ERR] agent: failed to sync remote state: No known Consul servers |
21 | |
22 | ~]# consul join 10.211.55.6 |
23 | Successfully joined cluster by contacting 1 nodes. |
24 | |
25 | ~]# consul members |
26 | Node Address Status Type Build Protocol DC Segment |
27 | server 10.211.55.6:8301 alive server 1.0.0 2 dc1 <all> |
28 | client 10.211.55.43:8301 alive client 1.0.0 2 dc1 <default> |
29 | |
30 | ~]# dig @127.0.0.1 -p 8600 web.service.consul #服务同步 |
31 | |
32 | ; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> @127.0.0.1 -p 8600 web.service.consul |
33 | ; (1 server found) |
34 | ;; global options: +cmd |
35 | ;; Got answer: |
36 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50027 |
37 | ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 |
38 | ;; WARNING: recursion requested but not available |
39 | |
40 | ;; OPT PSEUDOSECTION: |
41 | ; EDNS: version: 0, flags:; udp: 4096 |
42 | ;; QUESTION SECTION: |
43 | ;web.service.consul. IN A |
44 | |
45 | ;; ANSWER SECTION: |
46 | web.service.consul. 0 IN A 10.211.55.6 |
47 | |
48 | ;; Query time: 5 msec |
49 | ;; SERVER: 127.0.0.1#8600(127.0.0.1) |
50 | ;; WHEN: Sun Aug 13 02:33:25 CST 2017 |
51 | ;; MSG SIZE rcvd: 63 |
Health Check
定义health check,检查内容只针对单节点的服务。
1 | ~]# cat > /etc/consul.d/ping.json << EOF |
2 | {"check": {"name": "ping","script": "ping -c1 google.com >/dev/null", "interval": "30s"}} |
3 | EOF |
4 | ~]# cat > /etc/consul.d/web1.json << EOF |
5 | {"service": {"name": "web", "tags": ["rails"], "port": 80,"check": {"script": "curl localhost >/dev/null 2>&1", "interval": "10s"}}} |
6 | EOF |
7 | |
8 | ~]# consul agent -data-dir=/tmp/consul -node=client -bind=10.211.55.43 -config-dir=/etc/consul.d/ -enable-script-checks=true |
9 | .... |
10 | 2017/08/13 02:43:08 [INFO] agent: Synced service 'web' |
11 | 2017/08/13 02:43:08 [INFO] agent: Synced check 'ping' |
12 | 2017/08/13 02:43:18 [WARN] agent: Check 'ping' is now warning |
13 | 2017/08/13 02:43:18 [INFO] agent: Synced check 'ping' |
14 | 2017/08/13 02:43:58 [WARN] agent: Check 'ping' is now warning |
15 | 2017/08/13 02:44:11 [WARN] agent: Check 'service:web' is now critical |
16 | 2017/08/13 02:44:11 [INFO] agent: Synced check 'service:web' |
17 | 2017/08/13 02:44:21 [WARN] agent: Check 'service:web' is now critical |
18 | 2017/08/13 02:44:31 [WARN] agent: Check 'service:web' is now critical |
19 | |
20 | ~]# curl http://127.0.0.1:8500/v1/health/state/critical |
21 | [{"Node":"client","CheckID":"service:web","Name":"Service 'web' check","Status":"critical","Notes":"","Output":"","ServiceID":"web","ServiceName":"web","ServiceTags":["rails"],"CreateIndex":174,"ModifyIndex":204}] |
k/v存储
consul也是一个k/v数据库,这样我们就可以实现配置共享。
client
1 | ~]# consul kv put redis/config/port 6666 |
2 | Success! Data written to: redis/config/port |
server
1 | ~]# consul kv get redis/config/port |
2 | 6666 |
3 | ~]# consul kv get -detailed redis/config/port 获取详细信息 |
4 | CreateIndex 281 |
5 | Flags 0 |
6 | Key redis/config/port |
7 | LockIndex 0 |
8 | ModifyIndex 281 |
9 | Session - |
10 | Value 6666 |
11 | ~]# consul kv get -recurse |
12 | redis/config/port:6666 |
check-and-set
1 | $ consul kv put -cas -modify-index=123 foo bar |
2 | Success! Data written to: foo |
3 | |
4 | $ consul kv put -cas -modify-index=123 foo bar |
5 | Error! Did not write to foo: CAS failed |
delete
1 | ~]# consul kv delete redis/config/port |
2 | Success! Deleted key: redis/config/port |
3 | ~]# consul kv delete -recurse redis #递归删除 |
4 | Success! Deleted keys with prefix: redis |
Web UI
1 | ~]# consul agent -server -data-dir=/tmp/consul -bootstrap-expect 1 -node server -bind=10.211.55.6 -ui |