一、Consul简介
Consul 是一套开源的分布式服务发现和配置管理系统,由 HashiCorp 公司用 Go 语言开发。它具有很多优点。包括:基于 raft 协议,比较简洁; 支持健康检查, 同时支持 HTTP 和 DNS 协议 支持跨数据中心的 WAN(广域网) 集群 提供图形界面 跨平台,支持 Linux、Mac、Windows。
consul是使用go语言开发的服务发现、配置管理中心服务。内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案,不再需要依赖其他工具(比如ZooKeeper等)。服务部署简单,只有一个可运行的二进制的包。每个节点都需要运行agent,他有两种运行模式server和client。每个数据中心官方建议需要3或5个server节点以保证数据安全,同时保证server-leader的选举能够正确的进行。
@client
CLIENT表示consul的client模式,就是客户端模式。是consul节点的一种模式,这种模式下,所有注册到当前节点的服务会被转发到SERVER,本身是不持久化这些信息。
@server
SERVER表示consul的server模式,表明这个consul是个server,这种模式下,功能和CLIENT都一样,唯一不同的是,它会把所有的信息持久化的本地,这样遇到故障,信息是可以被保留的。
@server-leader
中间那个SERVER下面有LEADER的字眼,表明这个SERVER是它们的老大,它和其它SERVER不一样的一点是,它需要负责同步注册的信息给其它的SERVER,同时也要负责各个节点的健康监测。
@raft(分布式一致性协议)
server节点之间的数据一致性保证,一致性协议使用的是raft,而zookeeper用的paxos,etcd采用的也是raft。
@服务发现协议
consul采用http和dns协议,etcd只支持http
@服务注册
consul支持两种方式实现服务注册,一种是通过consul的服务注册http API,由服务自己调用API实现注册,另一种方式是通过json个是的配置文件实现注册,将需要注册的服务以json格式的配置文件给出。consul官方建议使用第二种方式。
@服务发现
consul支持两种方式实现服务发现,一种是通过http API来查询有哪些服务,另外一种是通过consul agent 自带的DNS(8600端口),域名是以NAME.service.consul的形式给出,NAME即在定义的服务配置文件中,服务的名称。DNS方式可以通过check的方式检查服务。
@服务间的通信协议
Consul使用gossip协议管理成员关系、广播消息到整个集群,他有两个gossip pool(LAN pool和WAN pool),LAN pool是同一个数据中心内部通信的,WAN pool是多个数据中心通信的,LAN pool有多个,WAN pool只有一个。
二、基本概念
在描述架构之前,这里提供了一些术语来帮助声明正在探讨的东西:
- Agent——agent是一直运行在Consul集群中每个成员上的守护进程。通过运行 consul agent 来启动。agent可以运行在client或者server模式。指定节点作为client或者server是非常简单的,除非有其他agent实例。所有的agent都能运行DNS或者HTTP接口,并负责运行时检查和保持服务同步。
- Client——一个Client是一个转发所有RPC到server的代理。这个client是相对无状态的。client唯一执行的后台活动是加入LAN gossip池。这有一个最低的资源开销并且仅消耗少量的网络带宽。
- Server——一个server是一个有一组扩展功能的代理,这些功能包括参与Raft选举,维护集群状态,响应RPC查询,与其他数据中心交互WAN gossip和转发查询给leader或者远程数据中心。
- DataCenter——虽然数据中心的定义是显而易见的,但是有一些细微的细节必须考虑。例如,在EC2中,多个可用区域被认为组成一个数据中心?我们定义数据中心为一个私有的,低延迟和高带宽的一个网络环境。这不包括访问公共网络,但是对于我们而言,同一个EC2中的多个可用区域可以被认为是一个数据中心的一部分。
- Consensus——在我们的文档中,我们使用Consensus来表明就leader选举和事务的顺序达成一致。由于这些事务都被应用到有限状态机上,Consensus暗示复制状态机的一致性。
- Gossip——Consul建立在Serf的基础之上,它提供了一个用于多播目的的完整的gossip协议。Serf提供成员关系,故障检测和事件广播。更多的信息在gossip文档中描述。这足以知道gossip使用基于UDP的随机的点到点通信。
- LAN Gossip——它包含所有位于同一个局域网或者数据中心的所有节点。
- WAN Gossip——它只包含Server。这些server主要分布在不同的数据中心并且通常通过因特网或者广域网通信。
- RPC——远程过程调用。这是一个允许client请求server的请求/响应机制。
Consul Architecture 架构图
拆解开这个体系,从每一个组件开始了解。首先,可以看到有两个数据中心,分别标记为“one”和“two”。Consul是支持多数据中心一流,并且是常用业务场景。
每个数据中心都是由Server和client组成。建议有3~5 Server——基于故障处理和性能的平衡之策。如果增加越多的机器,则Consensus会越来越慢。对client没有限制,可以很容易地扩展到成千上万或数万。
同一个数据中心的所有节点都要加入Gossip协议。这意味着gossip pool包含给定数据中心的所有节点。有以下目的:首先,没有必要为client配置服务器地址参数;发现是自动完成的。第二,节点故障检测的工作不是放置在服务器上,而是分布式的。这使故障检测比心跳机制更可扩展性。第三,可用来作为消息层通知重要的事件,如leader选举。
每个数据中心的服务器都是属于一个Raft peer。这意味着,他们一起工作,选出一个的Leader,Leader server是有额外的职责。负责处理所有的查询和事务。事务也必须通过Consensus协议复制到所有的伙伴。由于这一要求,当非Leader Server接收到一个RPC请求,会转发到集群的leader。
Server节点也是作为WAN gossip pool的一部分。这个pool是与LAN gossip pool是不同的,它为具有更高延迟的网络响应做了优化,并且可能包括其他consul集群的server节点。设计WANpool的目的是让数据中心能够以low-touch的方式发现彼此。将一个新的数据中心加入现有的WAN Gossip是很容易的。因为池中的所有Server都是可控制的,这也使跨数据中心的要求。当一个Serfer接收到不同的数据中心的要求时,它把这个请求转发给相应数据中心的任一Server。然后,接收到请求的Server可能会转发给Leader。
多个数据中心之间是低耦合,但由于故障检测、连接缓存复用、跨数据中心要求快速和可靠的响应。
三、Docker中创建Consul集群
1 使用docker下载consul镜像,默认下载最consul最新版本,目前版本号为1.4.0,如果需要其他版本请登录https://hub.docker.com/进行搜索
2 下载完毕后分别创建/home/docker/consul、consul-server1-data、consul-server2-data、consul-server3-data、consul-client-data、consul-server1-conf、consul-server2-conf、consul-server3-conf、consul-client-conf这九个文件夹
[root@localhost ~]# cd /home
[root@localhost home]# mkdir docker
[root@localhost home]# cd docker
[root@localhost docker]# mkdir consul
[root@localhost docker]# cd consul
[root@localhost consul]# mkdir consul-server1-data consul-server2-data consul-server3-data consul-client-data consul-server1-conf consul-server2-conf consul-server3-conf consul-client-conf
3 创建Consul配置文件
3.1 在docker中每个Consul成员都是docker中的一个容器,docker会给每个容器分配容器的IP地址,容器IP地址只能用于容器之间内部通讯不能被宿主机直接访问,每个Consul容器IP同时也是Consul成员的Agentd守护进程的IP地址,创建Consul集群需要其他Consul容器加入同一个Consul容器的Agentd守护进程的IP地址,将该Consul容器作为Consul容器的leader,当该Consul容器挂掉时,Consul集群会从所有Agentd守护进程的IP地址中再选举出一个leader,但当宿主及重启,docker中所有容器的IP地址都会发生变化,Consul集群中的每个成员的IP地址也发生变化,原本是Consul容器的IP地址可能变成了Mysql容器的IP地址,这样每个Consul成员无法自动加入原来Consul容器的Agentd守护进程的IP地址,Consul集群就会报错,解决方案是在所有Consul节点服务的配置文件中,配置参数"retry_join",将docker中所有容器的IP都作为加入同一个Consul容器的Agentd守护进程的IP地址,任何一个Consul成员的Agent守护进程只需要知道集群中任意一个节点即可,加入到集群之后,集群节点之间会根据GOSSIP协议互相发现彼此的关系
要先统计docker中所有容器已经被使用的IP地址,将没有被使用的空闲的IP地址作为Consul容器Agentd守护进程的IP地址
3.1.1 查询docker中所有容器IP地址
[root@bogon consul-server1-conf]# docker inspect -f '{{.NetworkSettings.IPAddress}}' $(docker ps -q)
172.17.0.2
172.17.0.5
172.17.0.8
172.17.0.7
3.1.2 统计出已经使用的IP地址后,将空闲的172.17.0.6、172.17.0.9、172.17.0.4、172.17.0.3这四个准备使用的IP地址,作为Consul容器的IP地址,并将所有docker容器中所有已经使用的IP地址和准备使用的IP地址写入每个Consul节点服务的配置文件的配置参数"retry_join"中, "retry_join": ["172.17.0.2","172.17.0.3","172.17.0.4","172.17.0.5","172.17.0.6","172.17.0.7","172.17.0.8","172.17.0.9"]
3.2 创建consul-server1节点服务配置文件
3.2.1 进入consul-server1-conf文件夹创建consul-server1.json文件
[root@localhost consul]# cd consul-server1-conf/
[root@localhost consul-server1-conf]# touch consul-server1.json
3.2.2 vi编辑consul-server1.json配置文件,复制下列代码
{
"datacenter": "DC1",
"data_dir": "/consul/data",
"log_level": "INFO",
"node_name": "consul-server1",
"server": true,
"bootstrap_expect": 1,
"retry_join": ["172.17.0.2","172.17.0.3","172.17.0.4","172.17.0.5","172.17.0.6","172.17.0.7","172.17.0.8","172.17.0.9"],
"retry_interval": "3s",
"enable_debug": false,
"rejoin_after_leave": true,
"enable_syslog": false
}
3.3 创建consul-server2节点服务配置文件
3.3.1 进入consul-server2-conf文件夹创建consul-server2.json文件
[root@localhost consul]# cd consul-server2-conf/
[root@localhost consul-server2-conf]# touch consul-server2.json
3.3.2 vi编辑consul-server2.json配置文件,复制下列代码
{
"datacenter": "DC1",
"data_dir": "/consul/data",
"log_level": "INFO",
"node_name": "consul-server2",
"server": true,
"bootstrap_expect": 2,
"retry_join": ["172.17.0.2","172.17.0.3","172.17.0.4","172.17.0.5","172.17.0.6","172.17.0.7","172.17.0.8","172.17.0.9"],
"retry_interval": "3s",
"enable_debug": false,
"rejoin_after_leave": true,
"enable_syslog": false
}
3.4 创建consul-server3节点服务配置文件
3.4.1 进入consul-server3-conf文件夹创建consul-server3.json文件
[root@localhost consul]# cd consul-server3-conf/
[root@localhost consul-server3-conf]# touch consul-server3.json
3.4.2 vi编辑consul-server3.json配置文件,复制下列代码
{
"datacenter": "DC1",
"data_dir": "/consul/data",
"log_level": "INFO",
"node_name": "consul-server3",
"server": true,
"bootstrap_expect": 2,
"retry_join": ["172.17.0.2","172.17.0.3","172.17.0.4","172.17.0.5","172.17.0.6","172.17.0.7","172.17.0.8","172.17.0.9"],
"retry_interval": "3s",
"enable_debug": false,
"rejoin_after_leave": true,
"enable_syslog": false
}
3.4 创建consul-client节点服务配置文件
3.4.1 进入consul-client-conf文件夹创建consul-client.json文件
[root@localhost consul]# cd consul-client-conf/
[root@localhost consul-client-conf]# touch consul-client.json
3.4.2 vi编辑consul-client.json配置文件,复制下列代码
{
"datacenter": "DC1",
"data_dir": "/consul/data",
"log_level": "INFO",
"node_name": "consul-client",
"server": false,
"ui": true,
"bootstrap_expect": 0,
"bind_addr": "192.168.43.234",
"client_addr": "192.168.43.234",
"retry_join": ["172.17.0.2","172.17.0.3","172.17.0.4","172.17.0.5","172.17.0.6","172.17.0.7","172.17.0.8","172.17.0.9"],
"retry_interval": "3s",
"enable_debug": false,
"rejoin_after_leave": true,
"enable_syslog": false
}
3.5 配置参数说明
datacenter: 数据中心名称
data_di:ConsulServer模式节点的数据目录
log_level: "INFO":日志级别
node_name:当前节点名称
server:是否为 Server 模式,true 为 Server 模式,false 为 Client 模式
ui:是否开启 UI 访问
bootstrap_expect:启动时期望的就绪节点,1 代表启动为 bootstrap 模式,等待其他节点加入
bind_addr:绑定的 IP,ConsulServer模式无需指定,ConsulClient模式必须绑定宿主机IP地址,否则报错[Consul]Error starting agent: Failed to get advertise address: Multiple private IPs found.
client_addr:作为 Client 接受请求的绑定 IP地址,该IP地址必须为宿主机IP地址,否则访问宿主机无法访问到Client模式的Consul节点,端口使用了 HTTP: 8500, DNS: 8600
retry_join:尝试加入的其他节点
retry_interval:每次尝试间隔
raft_protocol:Raft 协议版本
enable_debug:是否开启 Debug 模式
rejoin_after_leave:允许重新加入集群
enable_syslog:是否开启 syslog
4 启动三个server模式的consul节点consul-server1、consul-server2、consul-server3,启动一个client模式的consul节点consul-client
docker run -d --name consul-server1 --restart=always -v /home/docker/consul/consul-server1-data:/consul/data -v /home/docker/consul/consul-server1-conf:/consul/config consul agent -data-dir /consul/data -config-dir /consul/config
docker run -d --name consul-server2 --restart=always -v /home/docker/consul/consul-server2-data:/consul/data -v /home/docker/consul/consul-server2-conf:/consul/config consul agent -data-dir /consul/data -config-dir /consul/config
docker run -d --name consul-server3 --restart=always -v /home/docker/consul/consul-server3-data:/consul/data -v /home/docker/consul/consul-server3-conf:/consul/config consul agent -data-dir /consul/data -config-dir /consul/config
docker run -d --net=host --name consul-client --restart=always -p 8400:8400 -p 8500:8500 -p 8600:53/udp -v /home/docker/consul/consul-client-data:/consul/data -v /home/docker/consul/consul-client-conf:/consul/config consul agent -data-dir /consul/data -config-dir /consul/config
5 查看consul客户端、consul容器是否启动
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e46bf7bae81d consul "docker-entrypoint..." About an hour ago Up About an hour consul-client
954f366b5a9a consul "docker-entrypoint..." About an hour ago Up About an hour 8300-8302/tcp, 8500/tcp, 8301-8302/udp, 8600/tcp, 8600/udp consul-server3
54feb6ee26c4 consul "docker-entrypoint..." About an hour ago Up About an hour 8300-8302/tcp, 8500/tcp, 8301-8302/udp, 8600/tcp, 8600/udp consul-server2
5beadc6c1bec consul "docker-entrypoint..." About an hour ago Up About an hour 8300-8302/tcp, 8500/tcp, 8301-8302/udp, 8600/tcp, 8600/udp consul-server1
6 查看ConsulServer模式、Client模式节点是否启动成功
[root@localhost ~]# docker exec -it consul-server1 /bin/sh
/ # consul members
Node Address Status Type Build Protocol DC Segment
consul-server1 172.17.0.4:8301 alive server 1.4.0 2 dc1
consul-server2 172.17.0.9:8301 alive server 1.4.0 2 dc1
consul-server3 172.17.0.6:8301 alive server 1.4.0 2 dc1
consul-client 192.168.43.234:8301 alive client 1.4.0 2 dc1
7 查看目前全部的consul节点的角色状态
/ # consul operator raft list-peers
Node ID Address State Voter RaftProtocol
consul-server3 bce5f51e-fada-8a13-7639-e205da35efe6 172.17.0.6:8300 follower true 3
consul-server2 0b4cbb28-b03a-948f-c4f2-a6e00b6e0d06 172.17.0.9:8300 leader true 3
consul-server1 952d468c-0391-2f98-4959-f62965401551 172.17.0.4:8300 follower true 3
8 consult集群容器启动成功,打开浏览器输入http://192.168.43.234:8500,consul默认页面ui端口号8500
可以看到Node Health为3,则说明consul集群配置成功
9 错误处理
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
79032d57a304 consul "docker-entrypoint..." 3 seconds ago Exited (1) 2 seconds ago consul-client
53ef8a6fdb56 consul "docker-entrypoint..." 11 minutes ago Up 11 minutes 8300-8302/tcp, 8500/tcp, 8301-8302/udp, 8600/tcp, 8600/udp consul-server3
d1f7c98d07f5 consul "docker-entrypoint..." 12 minutes ago Up 12 minutes 8300-8302/tcp, 8500/tcp, 8301-8302/udp, 8600/tcp, 8600/udp consul-server2
86e45d805eb8 consul "docker-entrypoint..." 12 minutes ago Up 12 minutes 8300-8302/tcp, 8500/tcp, 8301-8302/udp, 8600/tcp, 8600/udp consul-server1
8.1 如果consul客户端、consul服务端容器是未能正常启动,则查看该consul容器日志
docker logs consulclient
9 命令说明
--net=host:指定 docker网络模式为host模式共享宿主机的网络,若采用默认的bridge模式,则会存在容器跨主机间通信失败的问题
-v /data/consul_data/data:/consul/data:主机的数据目录挂载到容器的/consul/data下,因为该容器默认的数据写入位置即是/consul/data
-v /data/consul_data/conf:/consul/config:主机的配置目录挂载到容器的/consul/conf下,因为该容器默认的数据写入位置即是/consul/conf
consul agent -server:consul的server启动模式
consul agent -client:consul的client启动模式
consul agent -bind=192.168.43.234:consul绑定到主机的ip上
consul agent -bootstrap-expect=3:server要想启动,需要至少3个server
consul agent -data-dir /consul/data:consul的数据目录
consul agent -config-dir /consul/config:consul的配置目录
consul agent -join:加入的consul-server1节点IP地址建立consul集群,启动之后,集群就开始了Vote(投票选Leader)的过程
–net=host docker参数, 使得docker容器越过了netnamespace的隔离,免去手动指定端口映射的步骤
-e或--env 使用-e设置的环境变量,容器内部的进程可以直接拿到
-server consul支持以server或client的模式运行, server是服务发现模块的核心, client主要用于转发请求
-client consul绑定在哪个client地址上,这个地址提供HTTP、DNS、RPC等服务,默认是127.0.0.1,0.0.0.0 表示任何地址可以访问
-node - 群集中此节点的名称。这在群集中必须是唯一的。默认情况下,这是计算机的主机名
-bootstrap-expect 指定consul集群中有多少代理
-retry-join 指定要加入的consul节点地址,失败会重试, 可多次指定不同的地址
-bind 绑定IP地址用来在集群内部的通讯,集群内的所有节点到地址都必须是可达的,默认是0.0.0.0,但当宿主机重启后所有docker容器IP地址会发生变化,-bind 绑定IP作用就会失效,集群无法找到leader报错,如果将集群单独部署在一个宿主机内可以使用
-allow_stale 设置为true, 表明可以从consul集群的任一server节点获取dns信息, false则表明每次请求都会经过consul server leader
--name DOCKER容器的名称
-ui 提供图形化的界面
其他命令请查看consul官方文档:https://www.consul.io/docs/agent/options.html#ports