基于consul的Redis高可用方案
一 前言
这几天在研究如何做Redis的高可用容灾方案,查询了资料和咨询DBA同行,了解到Redis可以基于consul和sentinel实现读写分离以及HA高可用方案。本文讲述基于consul的Redis高可用方案实践。
感谢邓亚运的提示和资料协助。
二 consul 是什么?
Consul是HashiCorp公司基于go语言研发用于服务发现和配置共享开的分布式高可用的系统。提供内服务注册与发现框架,分布一致性协议实现,健康检查,Key/Value存储,多数据中心方案,以及Web UI支持,相比于Zookeeper,Consul不再需要依赖其他工具。
Consul提供的如下关键特性:
-
Consul采用Raft一致性协议算法来保证服务的高可用,使用GOSSIP协议管理成员和广播消息,并且支持ACL访问控制。
-
服务注册与发现: Consul 同时支持DNS或者HTTP两种接口进行服务注册和服务发现。
-
支持健康检查: Consul可以通过定期运行脚本进行健康检查,根据脚本的返回值判断机器或服务是否健康,返回值为0时才代表健康。用户可以自定义的任意shell/Python脚本进行服务(Redis/MySQL等)的健康检查,这点相比其他同类工具更灵活。值得注意的是consul中 返回值0是没问题(passed),1是警告(warning),其他值是检查失败(critical)
-
Key/Value存储: 一个用来存储动态配置的系统。提供简单的HTTP接口,可以在任何地方操作。
-
支持多数据中心方案:支持多机房配置,可以避免单机房单点故障,多个数据中心要求每个数据中心都要安装一组Consul cluster,多个数据中心间基于gossip protocol协议来通讯,使用Raft算法实现一致性。
-
简易安装部署:安装包仅包含一个二进制文件,支持跨平台(*Nix,WIN)运行。可与Docker等轻量级容器实现无缝配合。
-
官方提供web管理界面, etcd无此功能.
三 Consul 架构组成
Consul是分布式的高可用系统,该系统中有两种角色Server 和Client。通过启动agent时指定Server或者client模式实现Consul集群中角色划分。
Consul Server:服务端用于保存Consul Cluster的状态信息, 实现数据一致性,响应RPC请求。在局域网内与本地客户端通讯, 通过广域网与其他数据中心通讯。每个Consul Cluster的Server数量推荐为3或5个。
Consul Client:客户端,只维护自身的状态, 并将HTTP和DNS接口请求转发给服务端。
注意:
Server和Client的角色和Consul Cluster上运行的应用服务无关, 是基于Consul层面的一种角色划分。
四 Consul和同类工具的对比表
五 如何使用
环境准备
server: 10.9.51.13
client: 10.215.20.13 10.215.20.7 10.9.141.52
-
下载和安装
wget "https://releases.hashicorp.com/consul/1.0.7/consul_1.0.7_linux_amd64.zip"
如果前面的特性介绍所说,consul其实就是一个二进制文件,下载以后解压放到/usr/local/bin之后就可以使用,不依赖任何东西。
cp consul /usr/local/bin/
-
初始化目录
mkdir /etc/consul.d/ -p && mkdir /data/consul/shell -p vim /etc/consul.d/client.json
-
配置文件 在server 节点 10.9.51.13 编辑配置文件 /etc/consul.d/server.json
{ "data_dir": "/data/consul", "datacenter": "dc1", "log_level": "INFO", "server": true, "bootstrap_expect": 1, "bind_addr": "10.9.51.13", "client_addr": "10.9.51.13", "ui":true }
在三个client节点配置文件
/etc/consul.d/client.json { "data_dir": "/data/consul", "enable_script_checks": true, "bind_addr": "10.215.20.13", "retry_join": ["10.9.51.13"], "retry_interval": "15s", "rejoin_after_leave": true, "start_join": ["10.9.51.13"] }
其中bind_addr 是client 节点的ip地址, retry_join和start_join是server节点的ip地址。
-
启动server和client节点
先启动server节点
nohup consul agent -config-dir=/etc/consul.d > /data/consul/consul.log &
再分别启动三个client节点
nohup consul agent -config-dir=/etc/consul.d > /data/consul/consul.log &
2018/05/04 12:02:23 [INFO] agent: Join LAN completed. Synced with 1 initial agents 说明client节点已经加入到了consul集群。
-
查看各个节点的状态
consul members
consul operator raft list-peers
Node ID Address State Voter RaftProtocol qabb-rdsa0 93b8e2a9-1ae1-3726-2993-c4f068ffd9b4 10.9.51.13:8300 leader true 3
-
通过 server节点 10.9.51.13:8500/ui 查看web ui
现在 qabb-r1db13 qabb-r1db7 上还没部署服务,所以显示为0个服务。 接下来我们开始部署redis服务,构建基于Consul的Redis的高可用架构
六 基于Consul的Redis 高可用架构
Redis现有高可用架构sentinel遇到的问题
-
Redis 哨兵架构下,服务器节点部署了哨兵,但业务部门没有在应用程序层面使用jedis哨兵驱动来自动发现Redis master,而使用直连IP master。当master挂掉,其他redis节点担当新master后,应用需要手工修改配置指向新master。
-
Redis 客户端驱动还没有读写分离的配置,若想slave的读负载均衡,暂时没好的办法。
Consul 可以满足以上需求,配置两个DNS服务,一个是master的写服务,利用consul自身的服务健康检查和探测功能,自动发现新的master。 然后定义一个slave的读服务,基于DNS本身,能够对slave角色的redis IP做轮询,实现负载均衡的效果。
环境搭建
其实首先我们要在搭建一主两从的redis集群,因为本文是模拟练习,所以在client节点上部署。具体搭建过程省略,Redis的主从配置如下
master 10.215.20.13:6380
slave 10.215.20.7:6380
10.9.141.52:6380
定义服务配置文件
我们需要在每个client节点的/etc/consul.d/下面服务配置文件如下:
$ ll
total 12
-rw-r--r-- 1 root root 217 May 4 18:54 client.json
-rw-r--r-- 1 root root 317 May 4 18:55 r-6380-redis-test.json
-rw-r--r-- 1 root root 320 May 4 18:54 w-6380-redis-test.json
redis 主节点的服务定义配置文件 W-6380-redis-test.json
{
"services": [
{
"name": "w-6380-redis-test",
"tags": [
"master-test-6380"
],
"address": "10.215.20.13",
"port": 6380,
"checks": [
{
"args":["sh","-c","/data/consul/shell/check_redis_master.sh 6380 "],
"interval": "15s"
}
]
}
]
}
redis 从节点的服务定义配置文件 r-6380-redis-test.json
{
"services": [
{
"name": "r-6380-redis-test",
"tags": [
"slave-test-6380"
],
"address": "10.215.20.7",
"port": 6380,
"checks": [
{
"args": ["sh","-c","/data/consul/shell/check_redis_slave.sh 6380 "],
"interval": "10s"
}
]
}
]
}
关于服务的定义文件这里做简单说明:
Consul 默认会根据names的值定义DNS域名,默认以 .service.consul 结尾。不过我们可以利用domain参数修改。
根据上面的Redis主从服务定义配置文件,Consul client节点向server集群注册后,对应有两个域名:
w-6380-redis-test.service.consul 对应Redis master服务器ip。
r-6380-redis-test.service.consul 对应两个slave服务器ip,客户端发送请求时DNS轮训随机分配一个slave ip。
其中"args": ["sh","-c","/data/consul/shell/check redisslave.sh 6380 "]代表对redis 6380 端口进行健康检查,如果异常返回2,consul client 会通知server端对异常服务进行服务治理。
动态加载配置
此时我们检查web-ui,
检查dns 配置
dig @10.9.51.13 -p 8600 w-6380-redis-test.service.consul
显示 10.215.20.13 对应于 w-6380-redis-test.service.consul
dig @10.9.51.13 -p 8600 r-6380-redis-test.service.consul
显示2个slave ip 10.215.20.7 ,10.9.141.52对应 r-6380-redis-test.service.consul.
注意因为是测试,dns配置并未在我们公司的dns 域名服务器器做解析,所以日志中提示
;; WARNING: recursion requested but not available
切换演练
我们需要做2种容灾演练
-
读写分离,如一个slave (10.215.20.13:6380)挂了之后,观察系统的表现
继续关闭其他redis slave (10.9.141.52:6380)实例
可以看到结果符合预期: 当多个从库依次关闭,dns域名后端ip发生变化,会逐步剔除关闭的实例对应的ip,所有的slave关闭之后,域名r-6380-redis-test.service.consul 后端的ip指定到redis master对应的ip上。
-
配合sentinel,实现高可用,master挂了之后,观察系统的表现
在这里我选择consul集群中的server节点部署了sentinel监控,(生产中要3或者5个节点,测试过程只是部署一个)。模拟redis 主库 10.215.20.13:6380 crash。
127.0.0.1:6380> shutdown
Sentinel 监控日志显示选举从库 10.215.20.7:6380 为主库。
此时我们再检查dns 域名 主库对应的dns域名 w-6380-redis-test.service.consul.对应ip已经变为10.215.20.7
r-6380-redis-test.service.consul. 对应的ip 减少了一个,变为10.9.141.52。
从目前的结果来看 切换符合预期。
另外说一下不符合预期的地方,在老的master 重新起来之后,sentinel会将老的master设置为新的slave,但是Consul 对应的dns 并未及时更新准确,需要执行consul reload 才可以。
生产过程中要注意这点,切换之后及时更新DNS缓存。
总结
从环境搭建以及测试结果来看,Consul这款工具使用起来十分简便,配合Redis sentinel 切换速度符合预期,上线生产环境时需要注意dns缓存时间的配置,以及DNS域名管理方面的支持。另外更多的技术知识点还是要多阅读Consul官方文档。
推荐阅读
-
本文实践参考邓亚运的文章,也推荐给大家看看http://www.cnblogs.com/gomysql/p/8010552.html
-
Consul DNS接口相关说明 https://www.consul.io/docs/agent/dns.html
-
关于check用法请参考 https://www.consul.io/docs/agent/checks.html 0.92版本之后有很多改变。