- Consul 架构
- 一、什么是服务发现">一、什么是服务发现
- 二、consul 简介">二、consul 简介
- 三、consul的几个概念">三、consul的几个概念
- 四、安装 Consul">四、安装 Consul
- 五、运行 Consul Agent">五、运行 Consul Agent
- 六、集群成员">六、集群成员
- 七、停止 Agent">七、停止 Agent
- 八、注册服务">八、注册服务
- 九、Consul 集群">九、Consul 集群
- 十、健康检查">十、健康检查
- 十一、KV Data">十一、KV Data
- 十二、Consul Web UI">十二、Consul Web UI
- 十三、Docker下安装consul">十三、Docker下安装consul
Consul 架构
在一个数据中心工作的领事的体系结构图可以最好地描述如下所示 :Raft算法
Raft是用于管理复制日志的一致性算法.它依赖于 **CAP定理**的原理,该原理指出在存在网络分区时,必须在一致性和可用性之间进行选择.并非CAP定理的所有三个基本原理都可以在任何给定的时间点实现.必须在最好的情况下权衡其中任何两个. A Raft群集包含多个服务器,通常是奇数计数.例如,如果我们有五台服务器,它将允许系统容忍两个故障.在任何给定时间,每个服务器都处于以下三种状态之一:领导者,追随者或候选人.在正常操作中,只有一个领导者,所有其他服务器都是粉丝.这些粉丝处于被动状态,即他们自己不发出请求,只是回应领导者和候选人的请求. 下图描述了使用筏子的工作流模型算法工作 :键值数据
自领事版本0.7.1以来,已经引入了单独的键值数据. KV命令用于通过命令行与Consul的键值存储进行交互.它公开了商店中插入,更新,阅读和删除的顶级命令.为了获得Key/Value对象存储,我们为consul客户端调用KV方法 :
kv:= consul.KV()
KVPair结构用于表示单个键/值条目.我们可以在以下程序中查看Consul KV Pair的结构.
这里,上面代码中提到的各种结构可以定义如下 :
type KVPair struct {
Key string
CreateIndex uint64
ModifyIndex uint64
LockIndex uint64
Flags uint64
Value []byte
Session string
}
- 键 : 它是一个斜杠URL名称.例如 - sites/1/domain.
- CreateIndex** **: 首次创建密钥时分配的索引号.
- ModifyIndex** **: 上次更新密钥时分配的索引号.
- LockIndex** **: 在键/值条目上获得新锁时创建的索引号
- 标志 : 应用程序可以使用它来设置自定义值.
- 值 : 它是一个最大512kb的字节数组.
- 会话 : 它可以在创建会话对象后设置.
协议类型
有两个Consul中的协议类型,称为 :- 共识协议和
- 八卦协议
共识协议
Consul使用共识协议来提供CAP定理所描述的一致性.该协议基于Raft算法.在实施共识协议时,使用筏算法,其中筏节点始终处于以下三种状态中的任何一种:跟随者,候选者或领导者.Gossip Protocol
八卦协议可用于管理成员资格,在群集中发送和接收消息.在领事中,八卦协议的使用以两种方式发生, **WAN** (无线区域网络)和 **LAN** (局域网).有三个已知的库,可以实现一个Gossip算法来发现对等网络中的节点 :- teknek-gossip** **: 它适用于UDP,用Java编写.
- gossip-python** **: 它利用TCP堆栈,也可以通过构建的网络共享数据.
- Smudge** **: 它是用Go编写的,并使用UDP来交换状态信息.
远程过程调用
RPC可以表示为远程过程调用的简写形式.它是一个程序用来从另一个程序请求服务的协议.该协议可以位于网络上的另一台计算机上,而无需确认网络详细信息. 在Consul中使用RPC的真正好处在于,它可以帮助我们避免大多数延迟问题发现服务工具确实有一段时间了.在RPC之前,Consul过去只有 TCP 和基于UDP 的连接,这对大多数系统都很好,但对于分布式系统则不行. RPC通过减少从一个地方到另一个地方的分组信息传输的时间段来解决这些问题.在这方面,谷歌的 GRPC 是一个很好的工具,如果有人希望观察基准并比较性能.一、什么是服务发现
微服务的框架体系中,服务发现是不能不提的一个模块。我相信了解或者熟悉微服务的童鞋应该都知道它的重要性。这里我只是简单的提一下,毕竟这不是我们的重点。我们看下面的一幅图片:- 需要配置N个服务的网络位置,加大配置的复杂性
- 服务的网络位置变化,都需要改变每个调用者的配置
- 集群的情况下,难以做负载(反向代理的方式除外)
总结起来一句话:服务多了,配置很麻烦,问题多多
既然有这些问题,那么服务发现就是解决这些问题的。话说,怎么解决呢?我们再看一张图二、consul 简介
做服务发现的框架常用的有 zookeeper eureka etcd consul 这里就不比较哪个好哪个差了,需要的童鞋自己谷歌百度。 那么consul是啥?consul就是提供服务发现的工具。然后下面是简单的介绍: consul是分布式的、高可用、横向扩展的。consul提供的一些关键特性: service discovery:consul通过DNS或者HTTP接口使服务注册和服务发现变的很容易,一些外部服务,例如saas提供的也可以一样注册。 health checking:健康检测使consul可以快速的告警在集群中的操作。和服务发现的集成,可以防止服务转发到故障的服务上面。 key/value storage:一个用来存储动态配置的系统。提供简单的HTTP接口,可以在任何地方操作。 multi-datacenter:无需复杂的配置,即可支持任意数量的区域。 我们这里会介绍服务发现,健康检查,还有一些基本KV存储。多数据中心有机会另一篇文章再说。 总结:只要知道它是解决我上一部分提出的问题就行,其它的东西慢慢理解三、consul的几个概念
四、安装 Consul
Consul 下载地址:https://www.consul.io/downloads.html,下载后解压就是一个可执行的二进制文件consul,配置好环境变量,检查 consul 是否可用:如果出现上面这样代表consul是没问题的。
[root@localhost ~]# consul
Usage: consul [--version] [--help] <command> [<args>]
Available commands are:
agent Runs a Consul agent
catalog Interact with the catalog
event Fire a new event
exec Executes a command on Consul nodes
force-leave Forces a member of the cluster to enter the "left" state
info Provides debugging information for operators.
join Tell Consul agent to join cluster
keygen Generates a new encryption key
keyring Manages gossip layer encryption keys
kv Interact with the key-value store
leave Gracefully leaves the Consul cluster and shuts down
lock Execute a command holding a lock
maint Controls node or service maintenance mode
members Lists the members of a Consul cluster
monitor Stream logs from a Consul agent
operator Provides cluster-level tools for Consul operators
reload Triggers the agent to reload configuration files
rtt Estimates network round trip time between nodes
snapshot Saves, restores and inspects snapshots of Consul server state
validate Validate config files/directories
version Prints the Consul version
watch Watch for changes in Consul
五、运行 Consul Agent
Consul安装之后,代理必须运行。 代理可以在服务器或客户端模式下运行。 每个数据中心都必须至少有一台服务器,但推荐使用3台或5台服务器。 一个单一的服务器部署是非常不推荐的,因为在故障情况下数据丢失是不可避免的。 所有其他代理以客户端模式运行。 客户端是一个非常轻量级的进程,它注册服务,运行健康检查,并将查询转发给服务器。 代理程序必须在集群中的每个节点上运行。 为了简单起见,我们现在将以开发模式启动Consul代理。 这种模式对于快速简单地启动单节点Consul环境非常有用。 它并不打算在生产中使用,因为它不会持续任何状态。如您所见,Consul代理已经启动并输出了一些日志数据。 从日志数据中,您可以看到我们的代理正在服务器模式下运行,并声称拥有集群领导权。 此外,当地成员已被标记为该集群的健康成员。
[root@localhost consul]# consul agent -dev
==> Starting Consul agent...
==> Consul agent running!
Version: 'v1.0.1'
Node ID: '590309a6-71f6-6145-fe40-d2c5e203687f'
Node name: 'localhost.localdomain'
Datacenter: 'dc1' (Segment: '<all>')
Server: true (Bootstrap: false)
Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, DNS: 8600)
Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false
==> Log data will now stream in as it occurs:
2017/11/25 15:15:54 [DEBUG] Using random ID "590309a6-71f6-6145-fe40-d2c5e203687f" as node ID
2017/11/25 15:15:54 [INFO] raft: Initial configuration (index=1): [{Suffrage:Voter ID:590309a6-71f6-6145-fe40-d2c5e203687f Address:127.0.0.1:8300}]
2017/11/25 15:15:54 [INFO] serf: EventMemberJoin: localhost.localdomain.dc1 127.0.0.1
2017/11/25 15:15:54 [INFO] serf: EventMemberJoin: localhost.localdomain 127.0.0.1
2017/11/25 15:15:54 [INFO] agent: Started DNS server 127.0.0.1:8600 (udp)
2017/11/25 15:15:54 [INFO] raft: Node at 127.0.0.1:8300 [Follower] entering Follower state (Leader: "")
2017/11/25 15:15:54 [INFO] consul: Adding LAN server localhost.localdomain (Addr: tcp/127.0.0.1:8300) (DC: dc1)
2017/11/25 15:15:54 [INFO] consul: Handled member-join event for server "localhost.localdomain.dc1" in area "wan"
2017/11/25 15:15:54 [INFO] agent: Started DNS server 127.0.0.1:8600 (tcp)
2017/11/25 15:15:54 [INFO] agent: Started HTTP server on 127.0.0.1:8500 (tcp)
2017/11/25 15:15:54 [INFO] agent: started state syncer
2017/11/25 15:15:54 [WARN] raft: Heartbeat timeout from "" reached, starting election
2017/11/25 15:15:54 [INFO] raft: Node at 127.0.0.1:8300 [Candidate] entering Candidate state in term 2
2017/11/25 15:15:54 [DEBUG] raft: Votes needed: 1
2017/11/25 15:15:54 [DEBUG] raft: Vote granted from 590309a6-71f6-6145-fe40-d2c5e203687f in term 2. Tally: 1
2017/11/25 15:15:54 [INFO] raft: Election won. Tally: 1
2017/11/25 15:15:54 [INFO] raft: Node at 127.0.0.1:8300 [Leader] entering Leader state
2017/11/25 15:15:54 [INFO] consul: cluster leadership acquired
2017/11/25 15:15:54 [DEBUG] consul: Skipping self join check for "localhost.localdomain" since the cluster is too small
2017/11/25 15:15:54 [INFO] consul: member 'localhost.localdomain' joined, marking health alive
2017/11/25 15:15:54 [INFO] consul: New leader elected: localhost.localdomain
2017/11/25 15:15:54 [DEBUG] Skipping remote check "serfHealth" since it is managed automatically
2017/11/25 15:15:54 [INFO] agent: Synced node info
2017/11/25 15:15:54 [DEBUG] agent: Node info in sync
2017/11/25 15:15:57 [DEBUG] Skipping remote check "serfHealth" since it is managed automatically
2017/11/25 15:15:57 [DEBUG] agent: Node info in sync
2017/11/25 15:16:54 [DEBUG] consul: Skipping self join check for "localhost.localdomain" since the cluster is too small
2017/11/25 15:17:51 [DEBUG] Skipping remote check "serfHealth" since it is managed automatically
2017/11/25 15:17:51 [DEBUG] agent: Node info in sync
2017/11/25 15:17:54 [DEBUG] manager: Rebalanced 1 servers, next active server is localhost.localdomain.dc1 (Addr: tcp/127.0.0.1:8300) (DC: dc1)
2017/11/25 15:17:54 [DEBUG] consul: Skipping self join check for "localhost.localdomain" since the cluster is too small
六、集群成员
在另一个终端运行consul members,可以看到Consul集群的成员。 应该只看到一个成员(你自己):输出显示了我们自己的节点,它正在运行的地址,运行状况,在集群中的角色以及一些版本信息。 额外的元数据可以通过提供-detailed标志来查看。
[root@localhost ~]# consul members
Node Address Status Type Build Protocol DC Segment
localhost.localdomain 127.0.0.1:8301 alive server 1.0.1 2 dc1 <all>
members命令的输出基于gossip协议,并最终一致。 也就是说,在任何时候,当地代理所看到的可能与服务器上的状态不完全一致。 要获得完全一致,请使用HTTP API再将HTTP请求转发给Consul服务器:
[root@localhost ~]# consul members -detailed
Node Address Status Tags
localhost.localdomain 127.0.0.1:8301 alive build=1.0.1:9564c29,dc=dc1,id=590309a6-71f6-6145-fe40-d2c5e203687f,port=8300,raft_vsn=3,role=consul,segment=<all>,vsn=2,vsn_max=3,vsn_min=2,wan_join_port=8302
除了HTTP API之外,还可以使用DNS接口查询节点。 请注意,必须确保将DNS查找默认情况下指向在端口8600上运行的Consul代理的DNS服务器。 DNS条目的格式(如“Armons-MacBook-Air.node.consul”)将在后面详细介绍。
[root@localhost ~]# curl localhost:8500/v1/catalog/nodes
[
{
"ID": "590309a6-71f6-6145-fe40-d2c5e203687f",
"Node": "localhost.localdomain",
"Address": "127.0.0.1",
"Datacenter": "dc1",
"TaggedAddresses": {
"lan": "127.0.0.1",
"wan": "127.0.0.1"
},
"Meta": {
"consul-network-segment": ""
},
"CreateIndex": 5,
"ModifyIndex": 6
}
]
[root@localhost ~]# dig @127.0.0.1 -p 8600 localhost.localdomain.node.consul
'
; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> @127.0.0.1 -p 8600 localhost.localdomain.node.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43915
;; 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:
;localhost.localdomain.node.consul. IN A
;; ANSWER SECTION:
localhost.localdomain.node.consul. 0 IN A 127.0.0.1
;; Query time: 0 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: 六 11月 25 15:39:49 CST 2017
;; MSG SIZE rcvd: 78
七、停止 Agent
可以使用Ctrl-C(中断信号)正常停止代理。 中断代理之后,您应该看到它离开集群并关闭。 通过优雅地离开,Consul通知其他集群成员该节点离开。 如果强行杀死了代理进程,则集群的其他成员将检测到该节点失败。 成员离开时,其服务和检查将从目录中删除。 当一个成员失败时,其健康被简单地标记为关键,但不会从目录中删除。 Consul将自动尝试重新连接到失败的节点,使其能够从特定的网络条件恢复,而不再联系离开的节点。 此外,如果代理正在作为服务器运行,那么优雅的离开对于避免造成影响一致协议的潜在可用性中断很重要。 有关如何安全地添加和删除服务器的详细信息,请参阅指南部分。八、注册服务
1、服务定义 服务可以通过提供服务定义或通过对HTTP API进行适当的调用来注册。 服务定义是注册服务最常用的方式,所以我们将在这一步中使用这种方法。 我们将建立在上一步中介绍的代理配置。 首先,为Consul配置创建一个目录。 Consul将所有配置文件加载到配置目录中,因此Unix系统上的一个通用约定是将目录命名为/etc/consul.d(.d后缀意味着“该目录包含一组配置文件”)。接下来,我们将编写一个服务定义配置文件。 假设我们有一个名为“web”的服务在端口80上运行。另外,我们给它一个标签,我们可以使用它作为查询服务的附加方式:
[root@localhost etc]# sudo mkdir /etc/consul.d
现在,重新启动代理程序,提供配置目录:
[root@localhost ~]# echo '{"service": {"name": "web", "tags": ["rails"], "port": 80}}' | sudo tee /etc/consul.d/web.json
您会注意到它在输出中“同步”了Web服务。 这意味着代理程序从配置文件加载了服务定义,并已成功将其注册到服务目录中。 如果您想注册多个服务,您可以在Consul配置目录中创建多个服务定义文件。 2、查询服务 一旦代理启动并且服务同步,我们可以使用DNS或HTTP API来查询服务。 DNS API 我们首先使用DNS API来查询我们的服务。 对于DNS API,服务的DNS名称是NAME.service.consul。 默认情况下,所有DNS名称始终在consul命名空间中,尽管这是可配置的。 服务子域告诉Consul我们正在查询服务,NAME是服务的名称。 对于我们注册的Web服务,这些约定和设置会生成web.service.consul的完全限定的域名:
[root@localhost ~]# consul agent -dev -config-dir=/etc/consul.d
==> Starting Consul agent...
==> Consul agent running!
Version: 'v1.0.1'
Node ID: '94236f1c-2a29-85c5-b235-dd916485be5b'
Node name: 'localhost.localdomain'
Datacenter: 'dc1' (Segment: '<all>')
Server: true (Bootstrap: false)
Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, DNS: 8600)
Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false
==> Log data will now stream in as it occurs:
2017/11/25 16:16:51 [DEBUG] Using random ID "94236f1c-2a29-85c5-b235-dd916485be5b" as node ID
2017/11/25 16:16:51 [INFO] raft: Initial configuration (index=1): [{Suffrage:Voter ID:94236f1c-2a29-85c5-b235-dd916485be5b Address:127.0.0.1:8300}]
2017/11/25 16:16:51 [INFO] serf: EventMemberJoin: localhost.localdomain.dc1 127.0.0.1
2017/11/25 16:16:51 [INFO] serf: EventMemberJoin: localhost.localdomain 127.0.0.1
2017/11/25 16:16:51 [INFO] agent: Started DNS server 127.0.0.1:8600 (udp)
2017/11/25 16:16:51 [INFO] raft: Node at 127.0.0.1:8300 [Follower] entering Follower state (Leader: "")
2017/11/25 16:16:51 [INFO] consul: Adding LAN server localhost.localdomain (Addr: tcp/127.0.0.1:8300) (DC: dc1)
2017/11/25 16:16:51 [INFO] consul: Handled member-join event for server "localhost.localdomain.dc1" in area "wan"
2017/11/25 16:16:51 [INFO] agent: Started DNS server 127.0.0.1:8600 (tcp)
2017/11/25 16:16:51 [INFO] agent: Started HTTP server on 127.0.0.1:8500 (tcp)
2017/11/25 16:16:51 [INFO] agent: started state syncer
2017/11/25 16:16:52 [WARN] raft: Heartbeat timeout from "" reached, starting election
2017/11/25 16:16:52 [INFO] raft: Node at 127.0.0.1:8300 [Candidate] entering Candidate state in term 2
2017/11/25 16:16:52 [DEBUG] raft: Votes needed: 1
2017/11/25 16:16:52 [DEBUG] raft: Vote granted from 94236f1c-2a29-85c5-b235-dd916485be5b in term 2. Tally: 1
2017/11/25 16:16:52 [INFO] raft: Election won. Tally: 1
2017/11/25 16:16:52 [INFO] raft: Node at 127.0.0.1:8300 [Leader] entering Leader state
2017/11/25 16:16:52 [INFO] consul: cluster leadership acquired
2017/11/25 16:16:52 [DEBUG] consul: Skipping self join check for "localhost.localdomain" since the cluster is too small
2017/11/25 16:16:52 [INFO] consul: member 'localhost.localdomain' joined, marking health alive
2017/11/25 16:16:52 [INFO] consul: New leader elected: localhost.localdomain
2017/11/25 16:16:52 [DEBUG] Skipping remote check "serfHealth" since it is managed automatically
2017/11/25 16:16:52 [INFO] agent: Synced service "web"
2017/11/25 16:16:52 [DEBUG] agent: Node info in sync
2017/11/25 16:16:52 [DEBUG] agent: Service "web" in sync
2017/11/25 16:16:52 [DEBUG] agent: Node info in sync
2017/11/25 16:16:52 [DEBUG] Skipping remote check "serfHealth" since it is managed automatically
2017/11/25 16:16:52 [DEBUG] agent: Service "web" in sync
2017/11/25 16:16:52 [DEBUG] agent: Node info in sync
正如你所看到的,一个A记录返回了服务可用的节点的IP地址。 A记录只能保存IP地址。 您也可以使用DNS API来检索整个地址/端口对作为SRV记录:
[root@localhost ~]# dig @127.0.0.1 -p 8600 web.service.consul
; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> @127.0.0.1 -p 8600 web.service.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58483
;; 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 A 127.0.0.1
;; Query time: 0 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: 六 11月 25 16:22:29 CST 2017
;; MSG SIZE rcvd: 63
SRV记录表示Web服务正在端口80上运行,并且存在于节点localhost.localdomain.node.dc1.consul.上。DNS使用该记录的A记录返回附加部分。 最后,我们也可以使用DNS API来按标签过滤服务。 基于标记的服务查询的格式是TAG.NAME.service.consul。 在下面的例子中,我们向Consul询问所有带有“rails”标签的web服务。 自从我们使用该标签注册我们的服务后,我们得到了成功的回应:
[root@localhost ~]# dig @127.0.0.1 -p 8600 web.service.consul SRV
; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> @127.0.0.1 -p 8600 web.service.consul SRV
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 65288
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 3
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;web.service.consul. IN SRV
;; ANSWER SECTION:
web.service.consul. 0 IN SRV 1 1 80 localhost.localdomain.node.dc1.consul.
;; ADDITIONAL SECTION:
localhost.localdomain.node.dc1.consul. 0 IN A 127.0.0.1
localhost.localdomain.node.dc1.consul. 0 IN TXT "consul-network-segment="
;; Query time: 0 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: 六 11月 25 16:25:21 CST 2017
;; MSG SIZE rcvd: 156
HTTP API 除了DNS API之外,HTTP API还可以用来查询服务:
[root@localhost ~]# dig @127.0.0.1 -p 8600 rails.web.service.consul
; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> @127.0.0.1 -p 8600 rails.web.service.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41016
;; 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:
;rails.web.service.consul. IN A
;; ANSWER SECTION:
rails.web.service.consul. 0 IN A 127.0.0.1
;; Query time: 0 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: 六 11月 25 16:29:10 CST 2017
;; MSG SIZE rcvd: 69
目录API提供了托管给定服务的所有节点。 正如我们稍后将看到的健康检查一样,您通常只需要查询检查通过的健康实例。 这是DNS正在做的事情。 这是一个查询只查找健康的实例:
[root@localhost ~]# curl http://localhost:8500/v1/catalog/service/web
[
{
"ID": "94236f1c-2a29-85c5-b235-dd916485be5b",
"Node": "localhost.localdomain",
"Address": "127.0.0.1",
"Datacenter": "dc1",
"TaggedAddresses": {
"lan": "127.0.0.1",
"wan": "127.0.0.1"
},
"NodeMeta": {
"consul-network-segment": ""
},
"ServiceID": "web",
"ServiceName": "web",
"ServiceTags": [
"rails"
],
"ServiceAddress": "",
"ServicePort": 80,
"ServiceEnableTagOverride": false,
"CreateIndex": 6,
"ModifyIndex": 6
}
]
3、更新服务 服务定义可以通过更改配置文件并向代理发送SIGHUP来更新。 这使您可以更新服务,而不会出现任何停机或无法提供服务查询的情况。 或者,可以使用HTTP API动态地添加,删除和修改服务。
[root@localhost ~]# curl 'http://localhost:8500/v1/health/service/web?passing'
[
{
"Node": {
"ID": "94236f1c-2a29-85c5-b235-dd916485be5b",
"Node": "localhost.localdomain",
"Address": "127.0.0.1",
"Datacenter": "dc1",
"TaggedAddresses": {
"lan": "127.0.0.1",
"wan": "127.0.0.1"
},
"Meta": {
"consul-network-segment": ""
},
"CreateIndex": 5,
"ModifyIndex": 6
},
"Service": {
"ID": "web",
"Service": "web",
"Tags": [
"rails"
],
"Address": "",
"Port": 80,
"EnableTagOverride": false,
"CreateIndex": 6,
"ModifyIndex": 6
},
"Checks": [
{
"Node": "localhost.localdomain",
"CheckID": "serfHealth",
"Name": "Serf Health Status",
"Status": "passing",
"Notes": "",
"Output": "Agent alive and reachable",
"ServiceID": "",
"ServiceName": "",
"ServiceTags": [],
"Definition": {},
"CreateIndex": 5,
"ModifyIndex": 5
}
]
}
]
九、Consul 集群
我们已经开始了我们的第一个代理,并注册和查询该代理的服务。 这显示了使用Consul是多么的容易,但并没有表明如何将其扩展到可扩展的生产级服务发现基础设施。 在这一步中,我们将创建我们的第一个真正的集群与多个成员。 当一个Consul代理启动时,它不知道任何其他节点:它是一个孤立的集群。 要了解其他集群成员,代理必须加入现有集群。 要加入现有的集群,只需要知道一个现有的成员。 代理加入后,会与该成员通讯,并迅速发现集群中的其他成员。 Consul代理可以加入任何其他代理,而不仅仅是服务器模式下的代理。 1、启动代理 在我们之前的例子中,我们使用了-dev标志来快速设置一个开发服务器。 但是,这不足以在集群环境中使用。 我们将从这里省略-dev标志,而是指定我们的集群标志。 集群中的每个节点都必须具有唯一的名称。 默认情况下,Consul使用机器的主机名,但我们将使用-node命令行选项手动覆盖它。 我们还将指定一个-bind:这是Consul侦听的地址,它必须可以被集群中的所有其他节点访问。 虽然绑定地址不是绝对必要的,但最好提供一个。 Consul将默认尝试侦听系统上的所有IPv4接口,但如果找到多个私有IP,将无法启动错误。 由于生产服务器通常具有多个接口,因此指定一个绑定地址可确保您永远不会将Consul绑定到错误的接口。 第一个节点将作为我们在这个集群中唯一的服务器,我们用-server来指明这一点。 -bootstrap-expect选项向Consul服务器提示我们期望加入的其他服务器节点的数量。 此选项的用途是延迟复制日志的引导,直到预期数量的服务器成功加入。 我们已经将-enable_script_checks选项设置为true,以启用可以执行外部脚本的运行状况检查。 这将在后面的例子中使用。 对于生产用途,您希望将ACL配置为与此配合使用,以控制注册任意脚本的能力。 最后,我们添加-config-dir选项,标记可以找到服务和检查定义的位置。 总而言之,这些设置产生一个这样的consul代理命令:现在,在另一个终端中,我们将连接到第二个节点。 这次,我们将-bind设置为第二个节点的IP,并将-node设置为agent-two。 由于这个节点不会是Consul服务器,所以我们不提供-server。 总而言之,这些设置产生一个这样的consul代理命令:
[root@localhost ~]# consul agent -server -bootstrap-expect=1 -data-dir=/tmp/consul -node=agent-one -bind=192.168.100.101 -enable-script-checks=true -config-dir=/etc/consul.d
此时,您有两个Consul代理正在运行:一个服务器和一个客户端。 两个Consul代理人对彼此还是一无所知,都是他们自己的单节点集群的一部分。 您可以通过对每个代理运行consul members来验证这一点,并注意到每个代理只能看到一个成员。 2、加入集群 现在,我们将通过在新终端中运行以下命令来告诉第一个代理加入第二个代理:
[root@localhost ~]# consul agent -data-dir=/tmp/consul -node=agent-two -bind=192.168.100.102 -enable-script-checks=true -config-dir=/etc/consul.d
如果在虚拟机里面运行上面的命令提示下面的失败的话,在每台虚拟机上执行下这个命令:
[root@localhost ~]# consul join 192.168.100.101
Successfully joined cluster by contacting 1 nodes.
您应该在每个代理日志中看到一些日志输出。 如果仔细阅读,您会看到他们收到了加入信息。 如果你对每个代理执行consul members,你会看到两个代理人现在彼此了解:
[root@localhost ~]# consul join 192.168.100.101
Error joining address '192.168.100.101': Unexpected response code: 500 (1 error(s) occurred:
* Failed to join 192.168.100.101: dial tcp 192.168.100.101:8301: getsockopt: no route to host)
Failed to join any nodes.
[root@localhost ~]# sudo iptables -F
记住:要加入集群,Consul代理只需要了解一个现有的成员。 加入集群后,代理人互相传播完整的会员信息。 3、在启动时自动加入集群 理想情况下,每当新节点出现在您的数据中心时,它就会自动加入Consul集群,无需人工干预。 Consul通过启用AWS,Google Cloud或Azure中的实例的自动发现功能,使用给定的标签 key/value来促进自动加入。 要使用集成,请将retry_join_ec2,retry_join_gce或retry_join_azure嵌套对象添加到您的Consul配置文件。 这将允许新的节点加入集群,而不需要任何硬编码的配置。 或者,您可以在启动时使用-join选项或start_join设置以及其他已知Consul代理的硬编码地址加入集群。 4、查询节点 就像查询服务一样,Consul也有查询节点的API。 您可以通过DNS或HTTP API执行此操作。 对于DNS API,名称的结构是NAME.node.consul或NAME.node.DATACENTER.consul。 如果数据中心被省略,Consul将仅搜索本地数据中心。 例如,从“agent-one”中,我们可以查询节点“agent-two”的地址:
[root@localhost ~]# consul members
Node Address Status Type Build Protocol DC Segment
agent-one 192.168.100.101:8301 alive server 1.0.1 2 dc1 <all>
agent-two 192.168.100.102:8301 alive client 1.0.1 2 dc1 <default>
除了服务之外,查找节点的能力对于系统管理任务来说是非常有用的。 例如,知道要通过SSH连接的节点的地址与将节点作为Consul集群的一部分并查询它一样简单。 5、离开集群 要离开集群,可以正常退出代理(使用Ctrl-C)或强制终止其中一个代理。 优雅地离开允许节点转换到离开状态; 否则,其他节点将检测到它失败。
[root@localhost etc]# dig @127.0.0.1 -p 8600 agent-two.node.consul
; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> @127.0.0.1 -p 8600 agent-two.node.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57127
;; 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:
;agent-two.node.consul. IN A
;; ANSWER SECTION:
agent-two.node.consul. 0 IN A 192.168.100.102
;; Query time: 0 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: 六 11月 25 17:31:29 CST 2017
;; MSG SIZE rcvd: 66
十、健康检查
现在我们已经看到了运行Consul,添加节点和服务以及查询这些节点和服务的简单性。 在本节中,我们将介绍为节点和服务添加健康检查。 健康检查是服务发现的关键组件,可以防止使用不健康的服务。 此步骤建立在之前创建的Consul集群上。 此时,您应该运行一个双节点集群。 1、检查定义 与服务类似,可以通过提供检查定义或通过对HTTP API进行适当的调用来注册检查。 我们将使用检查定义方法,因为就像服务一样,定义是设置检查最常用的方法。 在第二个节点的Consul配置目录中创建两个定义文件:第一个定义添加了一个名为“ping”的主机级别的检查。 此检查运行间隔30秒,调用ping -c1 google.com。 在基于脚本的运行状况检查上,检查以与启动Consul进程相同的用户身份运行。 如果该命令以非零退出码退出,则该节点将被标记为不健康。 这是任何基于脚本的健康检查的约定。 第二个命令修改名为web的服务,添加一个检查,每隔10秒通过curl发送一个请求,以验证Web服务器是否可访问。 与主机级运行状况检查一样,如果脚本以非零退出代码退出,服务将被标记为不健康。 现在,重新启动第二个代理,用consul reload加载它,或者发送一个SIGHUP信号。 您应该看到以下日志行:
[root@localhost ~]# echo '{"check": {"name": "ping", "script": "ping -c1 google.com >/dev/null", "interval": "30s"}}' >/etc/consul.d/ping.json
[root@localhost ~]# echo '{"service": {"name": "web", "tags": ["rails"], "port": 80, "check": {"script": "curl localhost >/dev/null 2>&1", "interval": "10s"}}}' >/etc/consul.d/web.json
前几行表示代理已经同步了新的定义。 最后一行表明我们为Web服务添加的检查是至关重要的。 这是因为我们实际上没有运行Web服务器,所以curl测试失败了! 2、检查健康状态 现在我们已经添加了一些简单的检查,我们可以使用HTTP API来检查它们。 首先,我们可以使用这个命令查找任何失败的检查(注意,这可以在任一节点上运行):
[root@localhost ~]# consul reload
Configuration reload triggered
2017/11/26 10:47:41 [INFO] Reloading configuration...
2017/11/26 10:47:41 [WARN] agent: check "service:web" has the 'script' field, which has been deprecated and replaced with the 'args' field. See https://www.consul.io/docs/agent/checks.html
2017/11/26 10:47:41 [WARN] agent: check "service:web" has the 'script' field, which has been deprecated and replaced with the 'args' field. See https://www.consul.io/docs/agent/checks.html
2017/11/26 10:47:41 [WARN] agent: check "ping" has the 'script' field, which has been deprecated and replaced with the 'args' field. See https://www.consul.io/docs/agent/checks.html
2017/11/26 10:47:41 [WARN] agent: check "ping" has the 'script' field, which has been deprecated and replaced with the 'args' field. See https://www.consul.io/docs/agent/checks.html
2017/11/26 10:47:41 [INFO] agent: Synced service "web"
2017/11/26 10:47:41 [INFO] agent: Synced check "ping"
2017/11/26 10:47:47 [WARN] agent: Check "service:web" is now critical
2017/11/26 10:47:57 [WARN] agent: Check "service:web" is now critical
2017/11/26 10:48:04 [WARN] agent: Check "ping" is now warning
2017/11/26 10:48:04 [INFO] agent: Synced check "ping"
2017/11/26 10:48:07 [WARN] agent: Check "service:web" is now critical
2017/11/26 10:48:17 [WARN] agent: Check "service:web" is now critical
2017/11/26 10:48:27 [WARN] agent: Check "service:web" is now critical
我们可以看到,只有一个检查,我们的Web服务检查,在危险(critical)状态。 另外,我们可以尝试使用DNS查询Web服务。 由于服务不健康,Consul不会返回任何结果:
[root@localhost etc]# curl http://localhost:8500/v1/health/state/critical
[{"Node":"agent-two","CheckID":"service:web","Name":"Service 'web' check","Status":"critical","Notes":"","Output":"","ServiceID":"web","ServiceName":"web","ServiceTags":["rails"],"Definition":{},"CreateIndex":230,"ModifyIndex":262}]
[root@localhost ~]# dig @127.0.0.1 -p 8600 web.service.consul
; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> @127.0.0.1 -p 8600 web.service.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 38998
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;web.service.consul. IN A
;; AUTHORITY SECTION:
consul. 0 IN SOA ns.consul. hostmaster.consul. 1511666146 3600 600 86400 0
;; Query time: 1 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: 日 11月 26 11:15:46 CST 2017
;; MSG SIZE rcvd: 97
十一、KV Data
Consul除了提供服务发现和综合健康检查之外,还提供一个易于使用的KV存储。 这可以用来保存动态配置,协助服务协调,建立leader选举,并使开发人员可以考虑构建的任何东西。 1、简单使用 为了演示有多简单,我们将操作K / V存储中的几个键。 有两种方式与Consul KV存储进行交互:通过HTTP API和Consul KV CLI。 以下示例显示使用Consul KV CLI,因为它是最容易入门的。 对于更高级的集成,您可能需要使用Consul KV HTTP API。 首先让我们探索KV存储。 我们可以向Consul询问名为redis / config / minconns的路径上的key的值:正如你所看到的,我们没有得到任何结果,这是合理的,因为KV存储没有数据。 接下来,我们可以insert或put 值到 KV 存储中。
[root@localhost ~]# consul kv get redis/config/minconns
Error! No key exists at: redis/config/minconns
现在我们在存储中有key,我们可以查询单个 key 的 value:
[root@localhost ~]# consul kv put redis/config/minconns 1
Success! Data written to: redis/config/minconns
[root@localhost ~]# consul kv put redis/config/maxconns 25
Success! Data written to: redis/config/maxconns
[root@localhost ~]# consul kv put -flags=42 redis/config/users/admin abcd1234
Success! Data written to: redis/config/users/admin
Consul保留有关使用-detailed标志检索的字段的其他元数据:
[root@localhost ~]# consul kv get redis/config/minconns 1
对于“redis / config / users / admin”这个键,我们设置了一个标志值42。所有的键都支持设置一个64位的整数标志值。 这不是Consul在内部使用的,但客户可以使用它为任何KV添加有意义的元数据。 可以使用递归选项列出存储的所有 key和 value。 结果将以字典顺序返回:
[root@localhost ~]# consul kv get -detailed redis/config/minconns
CreateIndex 517
Flags 0
Key redis/config/minconns
LockIndex 0
ModifyIndex 517
Session -
Value 1
要从Consul KV中删除一个 key,发出“删除” 命令:
[root@localhost ~]# consul kv get -recurse
redis/config/maxconns:25
redis/config/minconns:1
redis/config/users/admin:abcd1234
也可以使用递归选项删除整个前缀:
[root@localhost ~]# consul kv delete redis/config/minconns Success! Deleted key: redis/config/minconns
要更新现有key的值,请在相同路径上put一个值:
[root@localhost ~]# consul kv delete -recurse redis Success! Deleted keys with prefix: redis
Consul可以使用Check-And-Set操作提供原子键更新。 要执行CAS操作,请指定-cas选项:
[root@localhost ~]# consul kv put foo bar
Success! Data written to: foo
[root@localhost ~]# consul kv get foo
bar
[root@localhost ~]# consul kv put foo zip
Success! Data written to: foo
[root@localhost ~]# consul kv get foo
zip
在这种情况下,第一个CAS更新成功,因为索引是716。第二个操作失败,因为索引不再是716。
[root@localhost ~]# consul kv get -detailed foo
CreateIndex 710
Flags 0
Key foo
LockIndex 0
ModifyIndex 716
Session -
Value bar
[root@localhost ~]# consul kv put -cas -modify-index=716 foo bar
Success! Data written to: foo
[root@localhost ~]# consul kv put -cas -modify-index=716 foo bar
Error! Did not write to foo: CAS failed
十二、Consul Web UI
Consul支持web ui界面。UI可用于查看所有服务和节点,查看所有运行状况检查及其当前状态,以及读取和设置键/值数据。 用户界面自动支持多数据中心。 要设置自带的UI,请使用-ui参数启动Consul代理:UI可以在与HTTP API相同的端口上的/ui路径中使用。 默认情况下,这是http://localhost:8500/ui。 您可以在这里查看Consul Web UI的现场演示。
consul agent -ui
十三、Docker下安装consul
1、拉取镜像2、启动consul
docker search consul
- 启动节点1(server模式)
-node:节点的名称 -bind:绑定的一个地址,用于节点之间通信的地址,可以是内外网,必须是可以访问到的地址 -server:这个就是表示这个节点是个SERVER -bootstrap-expect:这个就是表示期望提供的SERVER节点数目,数目一达到,它就会被激活,然后就是LEADER了
docker run -d -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' --name=node1 consul agent -server -bind=172.17.0.2 -bootstrap-expect=3 -node=node1
- 启动节点2-3(server模式)
docker run -d -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' --name=node2 consul agent -server -bind=172.17.0.3 -join=172.17.0.2 -node-id=$(uuidgen | awk '{print tolower($0)}') -node=node2 docker run -d -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' --name=node3 consul agent -server -bind=172.17.0.4 -join=172.17.0.2 -node-id=$(uuidgen | awk '{print tolower($0)}') -node=node3 -client=172.17.0.4
-join:这个表示启动的时候,要加入到哪个集群内,这里就是说要加入到节点1的集群
-node-id:这个貌似版本8才加入的,这里用这个来指定唯一的节点ID,可以查看这个issue -client:这个表示注册或者查询等一系列客户端对它操作的IP,如果不指定这个IP,默认是127.0.0.1。- 启动节点4(client模式) docker run -d -e ‘CONSUL_LOCAL_CONFIG={“leave_on_terminate”: true}’ —name=node4 consul agent -bind=172.17.0.5 -retry-join=172.17.0.2 -node-id=$(uuidgen | awk ‘{print tolower($0)}’) -node=node4 除了没有-server,其它都是一样的,没有这个就说明这个节点是CLIENT
- 查看下集群的状态
docker exec -t node1 consul members
- 节点异常consul的处理
日志打印,心跳检查node1的ip超时,接着开始选举。node2被选举为新的leader。我们查看下现在的leader: curl http://172.17.0.4:8500/v1/status/leader 返回的内容: “172.17.0.3:8300” 172.17.0.3 就是 node2节点的IP - LEADER 挂了 leader挂了,consul会重新选取出新的leader,只要超过一半的SERVER还活着,集群是可以正常工作的。node1是leader,所以把这个容器停了。 docker stop node1 看看其他节点的日志(node2):
使用curl调用
{
"ID": "userServiceId", //服务id
"Name": "userService", //服务名
"Tags": [ //服务的tag,自定义,可以根据这个tag来区分同一个服务名的服务
"primary",
"v1"
],
"Address": "127.0.0.1",//服务注册到consul的IP,服务发现,发现的就是这个IP
"Port": 8000, //服务注册consul的PORT,发现的就是这个PORT
"EnableTagOverride": false,
"Check": { //健康检查部分
"DeregisterCriticalServiceAfter": "90m",
"HTTP": "http://www.baidu.com", //指定健康检查的URL,调用后只要返回20X,consul都认为是健康的
"Interval": "10s" //健康检查间隔时间,每隔10s,调用一次上面的URL
}
}
4、发现个服务 刚刚注册了名为userService的服务,我们现在发现(查询)下这个服务
curl http://172.17.0.4:8500/v1/agent/service/register -X PUT -i -H "Content-Type:application/json" -d '{
"ID": "userServiceId",
"Name": "userService",
"Tags": [
"primary",
"v1"
],
"Address": "127.0.0.1",
"Port": 8000,
"EnableTagOverride": false,
"Check": {
"DeregisterCriticalServiceAfter": "90m",
"HTTP": "http://www.baidu.com",
"Interval": "10s"
}
}'
返回的响应:
curl http://172.17.0.4:8500/v1/catalog/service/userService
内容有了吧,这个就是我们刚刚注册的服务的信息,就可以获取到 服务的名称是“userService” 服务地址是“127.0.0.1” 服务的端口是“8000” 5、存储个K/V 设置一个值到user/config/connections 内容为5
[
{
"Address": "172.17.0.4",
"CreateIndex": 880,
"ID": "e6e9a8cb-c47e-4be9-b13e-a24a1582e825",
"ModifyIndex": 880,
"Node": "node3",
"NodeMeta": {},
"ServiceAddress": "127.0.0.1",
"ServiceEnableTagOverride": false,
"ServiceID": "userServiceId",
"ServiceName": "userService",
"ServicePort": 8000,
"ServiceTags": [
"primary",
"v1"
],
"TaggedAddresses": {
"lan": "172.17.0.4",
"wan": "172.17.0.4"
}
}
]
6、获取特定的值
docker exec -t node1 consul kv put user/config/connections 5
docker exec -t node1 consul kv get -detailed user/config/connections