集群管理工具consul学习笔记
Consul功能与原理
consul是一个集群管理工具,特点:
- 服务发现:不需要手动记录各个服务的ip地址,中心能够主动搜寻服务并进行健康检查下线掉不健康的节点
- 健康检查:每个agent上线和下线时都会向服务目录添加或者删除自己,此外会根据agent规模定时(几分钟)同步服务目录
- KV存储
解决了手动配置服务的痛点。
每个consul agent(节点)有3种模式:
- Client模式,所有注册到当前节点的服务会被转发到SERVER,本身是不持久化这些信息
- Server模式,功能和Client完全一样,唯一不同的是,它会把所有的信息持久化的本地,这样遇到故障,信息是可以被保留的。一般我们的部署模型是 Server + Client的模式(当然也可以纯Server), Server 具有Client的全部功能, 但是由于Server负责存储数据,并且强一致性模型的缘故, Server数是有限的(3-5个Server节点,Client可以无限扩展的)。之所以是有限的是在权衡故障场景下可用性和性能之间取得平衡的结果,因为随着机器的增加,共识的速度会逐渐变慢。然而,Client的数量没有限制,它们可以轻松地扩展到数千或数万
- Leader(ServerLeader)模式,和其它Server不一样的一点是,它需要负责1.同步注册的信息给其它的Server,2.同时也要负责各个节点的健康监测。Leader是自动选举的,当Leader下线后会自动选举新的Leader
工作流程:每个agent都可以注册一些RPC或者说是服务,从网络传给server类型的agent一些RPC,这些RPC会根据服务列表直接访问服务层或者分发给其他Client,那些client再调用服务层。其中LeaderServer由其他server投票选出,通过轮询记录其他agent的地址与注册在agent的服务,这也即服务列表,当然会同步给其他server。当Leader下线后,会触发重新投票选出LeaderServer。

端口功能:
- 8300:集群内数据的读写和复制
- 8301:单个数据中心gossip协议通讯
- 8302:跨数据中心gossip协议通讯
- 8500:提供获取服务列表、注册服务、注销服务等HTTP接口;提供UI服务
- 8600:采用DNS协议提供服务发现功能
一致性算法-Gossip协议
朴素原理:Gossip协议是基于六度分隔理论(Six Degrees of Separation)哲学的体现,简单的来说,一个人通过6个中间人可以认识世界任何人。这意味着不需要很多次信息交换,就可以蔓延到整个网络。
Gossip协议在计算机系统通常以随机的“对等选择”形式实现:以给定的频率,每台计算机随机选择另一台计算机,并共享任何消息。定义十分简单,所以实现方式非常多。
一种原始的Gossip协议的执行过程:
- 种子节点周期性的散播消息 【假定把周期限定为 1 秒】
- 被感染节点随机选择N个邻接节点散播消息【假定fan-out(扇出)设置为6,每次最多往6个节点散播】
- 节点只接收消息不反馈结果
- 每次散播消息都选择尚未发送过的节点进行散播
- 谣言传播收到消息的节点不回传散播:A -> B,那么B进行散播的时候,不再发给 A
- 反熵传播则是相互交换全部信息
Gossip 协议的消息传播方式有两种:Anti-Entropy(反熵传播)和Rumor-Mongering(谣言传播),反熵传播使用“simple epidemics(SI model)”的方式:以固定的概率传播所有的数据。所有参与节点只有两种状态:
Suspective(病原):处于 susceptible 状态的节点代表其并没有收到来自其他节点的更新。
Infective(感染):处于 infective 状态的节点代表其有数据更新,并且会将这个数据分享给其他节点。
反熵传播过程是每个节点周期性地随机选择其他节点,然后通过互相交换自己的所有数据来消除两者之间的差异。反熵传播方法每次节点两两交换自己的所有数据会带来非常大的通信负担,因此不会频繁使用,通常只用于新加入节点的数据初始化。
谣言传播使用“complex epidemics”(SIR model)的方式:以固定的概率仅传播新到达的数据。所有参与节点有三种状态:Suspective(病原)、Infective(感染)、Removed(愈除)。
Removed(愈除):其已经接收到来自其他节点的更新,但是其并不会将这个更新分享给其他节点。
谣言传播过程是消息只包含最新 update,谣言消息在某个时间点之后会被标记为removed,并且不再被传播。缺点是系统有一定的概率会不一致,通常用于节点间数据增量同步。
一般来说,为了在通信代价和可靠性之间取得折中,需要将这两种方法结合使用。
Gossip的优势与缺点:
- 可扩展性(Scalable):允许节点的任意增加和减少,新增节点的状态最终会与其他节点一致
- 容错(Fault-tolerance):网络中任何节点的重启或者宕机都不会影响 gossip 协议的运行,具有天然的分布式系统容错特性
- 健壮性(Robust):gossip 协议是去中心化的协议,所以集群中的所有节点都是对等的,没有特殊的节点,所以任何节点出现问题都不会阻止其他节点继续发送消息。任何节点都可以随时加入或离开,而不会影响系统的整体服务质量
- 最终一致性(Convergent consistency):谣言传播可以是指数级的快速传播,因此新信息传播时,消息可以快速地发送到全局节点,在有限的时间内能够做到所有节点都拥有最新的数据
- 简单
缺点:
- 消息延迟:节点随机向少数几个节点发送消息,消息最终是通过多个轮次的散播而到达全网,不可避免的造成消息延迟
- 消息冗余:节点定期随机选择周围节点发送消息,而收到消息的节点也会重复该步骤,因此不可避免地引起同一节点多次接收同一消息,增加消息处理的压力。一次通信会对网路带宽、CUP资源造成很大的负载,而这些负载又受限于 通信频率,该频率又影响着算法收敛的速度
- 拜占庭问题:如果有一个恶意传播消息的节点,Gossip协议的分布式系统就会出问题。
Consul操作
创建服务端
consul agent -server -bootstrap-expect 3 -data-dir /tmp/consul -bind=192.168.0.104 -client 0.0.0.0 -node=s_hallen -rejoin -ui -join 192.168.0.104
参数说明:
- agent:必须有,区分于members,用来承接service
- -server: 定义agent运行在server模式,不加则为client模式
- -bootstrap-expect :在一个datacenter中期望提供的server节点数目,当该值提供的时候,consul一直等到达到指定sever数目的时候才会引导整个集群,否则一直不启动,该标记不能和bootstrap共用
- -data-dir = dir:存储数据的目录(必填)
- -node=name:节点在集群中的名称,在一个集群中必须是唯一的,默认是该节点的主机名
- -bind:该服务器节点名,该地址用来在集群内部的通讯,集群内的所有节点到地址都必须是可达的,默认是0.0.0.0,即该服务给集群通信的地址
- -ui-dir(-ui): 提供存放web ui资源的路径,该目录必须是可读的
- -rejoin:默认情况下,Consul将休假视为永久意图,并且在启动时不会再尝试加入集群。该标志允许先前的状态用于重新加入群集
- -config-dir:配置文件目录,里面所有以.json结尾的文件都会被加载
- -client:consul服务侦听地址,这个地址提供HTTP、DNS、RPC等服务,默认是127.0.0.1所以不对外提供服务,如果你要对外提供服务改成0.0.0.0。Consul将绑定客户端接口的地址
- -join:启动时加入的另一位代理的地址,代理在启动时不会加入任何节点
挂载服务
将服务挂载到agent,可以在启动agent时使用-config-dir指定配置文件,文件内加上service字段,在其中写上挂载服务的信息。当服务挂载到agent并启动时,agent会自动检测该服务的健康状态(需要配置checks字段)。
{
"service":{
"id": "consul-service-demo",
"name": "consul-service-demo",
"address": "127.0.0.1",
"port": 8080,
"tags": ["uth","report"],
"checks":[
{
"http": "http://127.0.01:8080/health",
"interval": "10s"
}
]
}
}
参数说明与其他参数
id:节点id
name:节点name
address:节点IP
port:节点端口
tags:节点标签,是个列表
checks:服务检查,列表,否则逻辑层下线后检测不到
1.Script+ Interval
{
"id": "mem-util",
"name": "Memoryutilization",
"script": "/usr/local/bin/check_mem", // 必须是shell脚本
"interval": "10s",
"timeout": "15s" // 默认是30s
}
2.HTTP+ Interval
请求超时时间等于调用请求的间隔时间
{
"id": "api",
"name": "HTTPAPI on port 5000",
"http": "http://localhost:5000/health",
"interval": "10s",
"timeout": "15s"
}
常用
3.TCP+ Interval
{
"check": {
"id": "ssh",
"name": "SSHTCP on port 22",
"tcp": "localhost:22",
"interval": "10s",
"timeout": "1s"
}
}
interval:每隔多长时间检测一次
datacenter:同命令行参数-datacenter
data_dir:同命令行参数-data_dir
bootstrap:同命令行参数-bootstrap
bootstrap_expect:同命令行参数-bootstrap_expect
bind_addr:同命令行参数-bind
enable_syslog:同命令行参数-syslog
log_level:同命令行参数-log_level
node_name:同命令行参数node
server:是否是server节点
服务发现
调用方获取相应服务地址的过程就是服务发现。一个service可以只挂载到一个agent上,当然这样就体现不出来微服务的容错性,可以同一个service挂载到多个agent上这样就可以实现容错性(注意虽然节点不同,但是服务名字相同)。那么调用端要选择哪个agent进行调用呢?这个问题留给调用端来解决。
方案1:在调用端内将agentIP地址写死,if-else不断尝试连接,这样理论上可行,但是实际上没人用,因为这样是硬编码,如果需要新增agent会改代码,亦或者单纯的agent够多(几千个client是常见的事情)代码会很难看。这种方法忽略了consul自带的重要的功能:服务发现。
方案2:使用consul自带的功能:服务发现。抛弃了硬编码形式。有几种方式可以获得在consul注册的服务
-
http api形式,直接从consul里获取到服务(健康的或者不健康的)。调用api直接获得列表
config := api.DefaultConfig() // 任意agent地址 config.Address = "127.0.0.1:8500" //waitIndex是blocking query的实现机制,是watch机制的核心,只有改变了才返回 var waitIndex uint64 client_api,_ := api.NewClient(config) // 获取健康的服务,名称为“my_say_hi” services,_,_ := client_api.Health().Service("my_say_hi","say_hi",true,&api.QueryOptions{ WaitIndex:waitIndex, }) //选择一个,获取其服务地址 address := services[0].Service.Address port := services[0].Service.Port url := fmt.Sprintf("%s:%d",address,port) //连接与调用 conn,err := grpc.Dial(url,grpc.WithInsecure())