基于Consul的数据库高可用架构 - yayun - 博客园

标签: | 发表时间:2018-09-28 03:25 | 作者:
出处:http://www.cnblogs.com

      几个月没有更新博客了,已经长草了,特意来除草。本次主要分享如何利用consul来实现redis以及mysql的高可用。以前的公司mysql是单机单实例,高可用MHA加vip就能搞定,新公司mysql是单机多实例,那么显然这个方案不适用,后来也实现了故障切换调用dns api来修改域名记录,但是还是没有利用consul来实现高可用方便,后面会说明优势。redis单机多实例最正常不过了,那么redis单机多实例高可用也不太好做,当然也可以利用sentinel来实现,当failover以后调用脚本调用dns api修改域名解析也是可以的。也不是那么的优雅,有人会说怎么不用codis,redis cluster,这些方案固然好,但不适合我们,这些方案不够灵活,不能很好的处理热点数据的问题。那么consul是什么呢,接下慢慢说:

consul是HashiCorp公司(曾经开发过vgrant) 推出的一款开源工具, 基于go语言开发, 轻量级, 用于实现分布式系统的服务发现与配置。 与其他类似产品相比, 提供更“一站式”的解决方案。 consul内置有KV存储, 服务注册/发现, 健康检查, HTTP+DNS API, Web UI等多种功能。官网: https://www.consul.io/其他同类服务发现与配置的主流开源产品有:zookeeper和ETCD。

consul的优势:

1. 支持多数据中心, 内外网的服务采用不同的端口进行监听。 多数据中心集群可以避免单数据中心的单点故障, zookeeper和 etcd 均不提供多数据中心功能的支持

2. 支持健康检查. etcd 不提供此功能.

3. 支持 http 和 dns 协议接口. zookeeper 的集成较为复杂,etcd 只支持 http 协议. 有DNS功能, 支持REST API

4. 官方提供web管理界面, etcd 无此功能.

5. 部署简单, 运维友好, 无依赖, go的二进制程序copy过来就能用了, 一个程序搞定, 可以结合ansible来推送。

Consul和其他服务发现工具的对比表:

 

  Consul 架构和角色

1. Consul Cluster由部署和运行了Consul Agent的节点组成。 在Cluster中有两种角色:Server和 Client。
2. Server和Client的角色和Consul Cluster上运行的应用服务无关, 是基于Consul层面的一种角色划分.
3. Consul Server: 用于维护Consul Cluster的状态信息, 实现数据一致性, 响应RPC请求。官方建议是: 至少要运行3个或者3个以上的Consul Server。 多个server之中需要选举一个leader, 这个选举过程Consul基于Raft协议实现. 多个Server节点上的Consul数据信息保持强一致性。 在局域网内与本地客户端通讯,通过广域网与其他数据中心通讯。Consul Client: 只维护自身的状态, 并将HTTP和DNS接口请求转发给服务端。
4. Consul 支持多数据中心, 多个数据中心要求每个数据中心都要安装一组Consul cluster,多个数据中心间基于gossip protocol协议来通讯, 使用Raft算法实现一致性

基础知识就介绍这么多了,更加详细的可以参考官网。下面我们来搭建一下consul,以及如何利用consul实现redis以及mysql的高可用。

测试环境(生产环境consul server部署3个或者5个):

consul server:192.168.0.10

consul client:192.168.0.20,192.168.0.30,192.168.0.40

 consul的安装非常容易,从 https://www.consul.io/downloads.html这里下载以后,解压即可使用,就是一个二进制文件,其他的都没有了。我这里使用的是0.92版本。文件下载以后解压放到/usr/local/bin。就可以使用了。不依赖任何东西。上面的4台服务器都安装。

4台机器都创建目录,分别是放配置文件,以及存放数据的。以及存放redis,mysql的健康检查脚本

mkdir/etc/consul.d/ -p &&mkdir/data/consul/ -p
mkidr /data/consul/shell -p

然后把相关配置参数写入配置文件,其实也可以不用写,直接跟在命令后面就行,那样不方便管理。
consul server(192.168.0.10)配置文件(具体参数的意思请查询官网或者文章给的参考链接):

[root@db-server-yayun-01~]#cat/etc/consul.d/server.json 
{"data_dir":"/data/consul","datacenter":"dc1","log_level":"INFO","server":true,"bootstrap_expect":1,"bind_addr":"192.168.0.10","client_addr":"192.168.0.10","ui":true}
[root@db-server-yayun-01~]#

consul client(192.168.0.20,192.168.0.30,192.168.0.40)

[root@db-server-yayun-02~]#cat/etc/consul.d/client.json 
{"data_dir":"/data/consul","enable_script_checks":true,"bind_addr":"192.168.0.20","retry_join": ["192.168.0.10"],"retry_interval":"30s","rejoin_after_leave":true,"start_join": ["192.168.0.10"]
}
[root@db-server-yayun-02~]#

3台服务器的配置文件差异不大,唯一有区别的就是 bind_addr地方,自行修改为你自己服务器的ip。我测试环境是虚拟机,有多快网卡,所以必须指定,否则可以绑定0.0.0.0。
下面我们先启动consul server:

nohup consul agent -config-dir=/etc/consul.d > /data/consul/consul.log &

 查看日志:

[root@db-server-yayun-01consul]#catconsul.log==> WARNING: BootstrapExpect Mode is specified as1; this is the same as Bootstrap mode.==> WARNING: Bootstrap mode enabled!Do not enable unless necessary==>Starting Consul agent...==> Consul agent running!Version:'v0.9.2'Node ID:'5e612623-ec5b-386c-19be-d38876a9a46f'Node name:'db-server-yayun-01'Datacenter:'dc1'Server:true(bootstrap:true)
       Client Addr:192.168.0.10(HTTP:8500, HTTPS: -1, DNS:8600)
      Cluster Addr:192.168.0.10(LAN:8301, WAN:8302)
    Gossip encrypt:false, RPC-TLS:false, TLS-Incoming:false==> Log data will now streaminas it occurs:2017/12/0909:49:53[INFO] raft: Initial configuration (index=1): [{Suffrage:Voter ID:192.168.0.10:8300Address:192.168.0.10:8300}]2017/12/0909:49:53[INFO] raft: Node at192.168.0.10:8300[Follower] entering Follower state (Leader:"")2017/12/0909:49:53[INFO] serf: EventMemberJoin: db-server-yayun-01.dc1192.168.0.102017/12/0909:49:53[INFO] serf: EventMemberJoin: db-server-yayun-01192.168.0.102017/12/0909:49:53[INFO] agent: Started DNS server192.168.0.10:8600(udp)2017/12/0909:49:53[INFO] consul: Adding LAN server db-server-yayun-01(Addr: tcp/192.168.0.10:8300) (DC: dc1)2017/12/0909:49:53[INFO] consul: Handled member-joineventforserver"db-server-yayun-01.dc1"inarea"wan"2017/12/0909:49:53[INFO] agent: Started DNS server192.168.0.10:8600(tcp)2017/12/0909:49:53[INFO] agent: Started HTTP server on192.168.0.10:85002017/12/0909:50:00[ERR] agent: failed tosyncremote state: No cluster leader2017/12/0909:50:00[WARN] raft: Heartbeat timeout from""reached, starting election2017/12/0909:50:00[INFO] raft: Node at192.168.0.10:8300[Candidate] entering Candidate stateinterm22017/12/0909:50:00[INFO] raft: Election won. Tally:12017/12/0909:50:00[INFO] raft: Node at192.168.0.10:8300[Leader] entering Leader state2017/12/0909:50:00[INFO] consul: cluster leadership acquired2017/12/0909:50:00[INFO] consul: New leader elected: db-server-yayun-012017/12/0909:50:00[INFO] consul: member'db-server-yayun-01'joined, marking health alive2017/12/0909:50:03[INFO] agent: Synced nodeinfo
View Code

 可以从日志中看到(HTTP: 8500, HTTPS: -1, DNS: 8600),http端口默认8500,在reload以及web ui会用到,dns端口是8600,在使用dns解析的时候会用到。还可以看到这台机器就是leader,consul: New leader elected: db-server-yayun-01。因为只有一台机器。所以生产环境一定要3个或者5个server。

下面启动3台client,3台client启动命令是一样的。然后查看其中一台client的日志:

nohup consul agent -config-dir=/etc/consul.d > /data/consul/consul.log &
[root@db-server-yayun-02consul]#cat/data/consul/consul.log==>Starting Consul agent...==>Joining cluster...
    Join completed. Synced with1initial agents==> Consul agent running!Version:'v0.9.2'Node ID:'0ec901ab-6c66-2461-95e6-50a77a28ed72'Node name:'db-server-yayun-02'Datacenter:'dc1'Server:false(bootstrap:false)
       Client Addr:127.0.0.1(HTTP:8500, HTTPS: -1, DNS:8600)
      Cluster Addr:192.168.0.20(LAN:8301, WAN:8302)
    Gossip encrypt:false, RPC-TLS:false, TLS-Incoming:false==> Log data will now streaminas it occurs:2017/12/0910:06:10[INFO] serf: EventMemberJoin: db-server-yayun-02192.168.0.202017/12/0910:06:10[INFO] agent: Started DNS server127.0.0.1:8600(udp)2017/12/0910:06:10[INFO] agent: Started DNS server127.0.0.1:8600(tcp)2017/12/0910:06:10[INFO] agent: Started HTTP server on127.0.0.1:85002017/12/0910:06:10[INFO] agent: (LAN) joining: [192.168.0.10]2017/12/0910:06:10[INFO] agent: Retryjoinis supportedfor: aws azure gce softlayer2017/12/0910:06:10[INFO] agent: Joining cluster...2017/12/0910:06:10[INFO] agent: (LAN) joining: [192.168.0.10]2017/12/0910:06:10[INFO] serf: EventMemberJoin: db-server-yayun-01192.168.0.102017/12/0910:06:10[INFO] agent: (LAN) joined:1Err: <nil>2017/12/0910:06:10[INFO] consul: adding server db-server-yayun-01(Addr: tcp/192.168.0.10:8300) (DC: dc1)2017/12/0910:06:10[INFO] agent: (LAN) joined:1Err: <nil>2017/12/0910:06:10[INFO] agent: Join completed. Synced with1initial agents2017/12/0910:06:10[INFO] agent: Synced nodeinfo
View Code

可以看到提示agent: Join completed. Synced with 1 initial agents,以及Server: false (bootstrap: false)。这也是client和server的区别。
我们继续执行命令看一下集群:

[root@db-server-yayun-02~]# consul members
Node                Address            Status  Type    Build  Protocol  DC
db-server-yayun-01192.168.0.10:8301alive   server0.9.22dc1
db-server-yayun-02192.168.0.20:8301alive   client0.9.22dc1
db-server-yayun-03192.168.0.30:8301alive   client0.9.22dc1
db-server-yayun-04192.168.0.40:8301alive   client0.9.22dc1
[root@db-server-yayun-02~]#
[root@db-server-yayun-02~]# consul operator raft list-peers
Node                ID                 Address            State   Voter  RaftProtocol
db-server-yayun-01192.168.0.10:8300192.168.0.10:8300leadertrue2[root@db-server-yayun-02~]#

我们看看web ui,consul自带的ui,非常轻便。访问: http://192.168.0.10:8500/ui/

到这来consul集群就搭建完成了,是不是很简单。对就是这么简单,但是从上面可以看到,client节点并没有注册服务,显示0 services。这也就是接下来需要讲解的。那么到底如何实现redis及mysql的高可用呢?正式开始:

Consul 使用场景一(redis sentinel)
(1)Redis 哨兵架构下,服务器部署了哨兵,但业务部门没有在app 层面,使用jedis 哨兵驱动来自动发现Redis master,而使用直连IP master。当master挂掉,其他redis节点担当新master后,应用需要手工修改配置,指向新master。
(2)Redis 客户端驱动,还没有读写分离的配置,若想slave的读负载均衡,暂时没好的办法。我们程序都是支持读写分离,所以没影响
(3)Consul 可以满足以上需求,配置两个DNS服务,一个是master的服务,利用consul自身的服务健康检查和探测功能, 自动发现新的master。 然后定义一个slave的服务,基于DNS本身, 能够对slave角色的redis IP做轮询。

架构图如下:

 

同样也可以对mysql做高可用,mha和sentinel的角色一样,架构图如下:

下面就说说redis高可用的实现过程,mysql的我就不说了,mysql用到的健康检查脚本我会贴出来。思路都是一样的。

Consul 服务定义(Redis)

上面已经搭建好了consul集群,server是192.168.0.10 client是20到40. 那么20我们就拿来当redis master,30,40拿来当redis slave。下面定义服务(20,30,40都要存在):

20,30,40的配置文件如下,除了address要修改为对应的服务器地址,其他一样。

[[email protected]]#pwd/etc/consul.d
[[email protected]]# ll
total12-rw-r--r--.1root root221Dec909:44client.json-rw-r--r--.1root root319Dec910:48r-6029-redis-test.json-rw-r--r--.1root root321Dec910:48w-6029-redis-test.json
[[email protected]]#

master的服务定义配置文件:

[[email protected]]#catw-6029-redis-test.json 
{"services": [
    {"name":"w-6029-redis-test","tags": ["master-test-6029"],"address":"192.168.0.20","port":6029,"checks": [
        {"script":"/data/consul/shell/check_redis_master.sh 6029","interval":"15s"}
      ]
    }
  ]
}

[[email protected]]#
View Code

slave的服务定义配置文件:

[[email protected]]#catr-6029-redis-test.json 
{"services": [
    {"name":"r-6029-redis-test","tags": ["slave-test-6029"],"address":"192.168.0.20","port":6029,"checks": [
        {"script":"/data/consul/shell/check_redis_slave.sh 6029","interval":"15s"}
      ]
    }
  ]
}

[[email protected]]#
View Code

每个agent都注册后, 对应有两个域名:
w-6029-redis-test.service.consul (对应唯一一个master IP)
r-6029-redis-test.service.consul  (对应两个slave IP, 客户端请求时, 随机分配一个)

其中"script": "/data/consul/shell/check_redis_slave.sh 6029 "代表对redis 6029端口进行健康检查,关于更多健康检查请查看官网介绍。

[root@db-server-yayun-03shell]#pwd/data/consul/shell
[root@db-server-yayun-03shell]# ll
total16-rwxr-xr-x.1root root480Dec910:56check_mysql_master.sh-rwxr-xr-x.1root root3004Dec910:55check_mysql_slave.sh-rwxr-xr-x.1root root254Dec910:51check_redis_master.sh-rwxr-xr-x.1root root379Dec910:51check_redis_slave.sh[root@db-server-yayun-03shell]#

/data/consul/shell目录下面有4个脚本,是对redis和mysql进行健康检查用的。脚本比较简单,大概就是如果只有一个master,那么读写都在master,如果有slave可用,那么读会在slave进行。如果slave复制不正常,或者复制延时,那么slave服务将不会注册。

[root@db-server-yayun-03shell]#catcheck_redis_master.sh#!/bin/bash
myport=$1auth=$2if[ ! -n"$auth"]thenauth='\"\"'ficomm="/usr/local/bin/redis-cli -p $myport -a $auth"role=`echo'INFO Replication'|$comm |grep-Ec'role:master'`echo'INFO Replication'|$commif[ $role -ne1]thenexit2fi[root@db-server-yayun-03shell]#
View Code
[root@db-server-yayun-03shell]#catcheck_redis_slave.sh#!/bin/bash
myport=$1auth=$2if[ ! -n"$auth"]thenauth='\"\"'ficomm="/usr/local/bin/redis-cli -p $myport -a $auth"role=`echo'INFO Replication'|$comm |grep-Ec'^role:slave|^master_link_status:up'`
single=`echo'INFO Replication'|$comm |grep-Ec'^role:master|^connected_slaves:0'`echo'INFO Replication'|$commif[ $role -ne2-a $single -ne2]thenexit2fi[root@db-server-yayun-03shell]#
View Code
[root@db-server-yayun-02shell]#catcheck_mysql_master.sh#!/bin/bash
port=$1user="root"passwod="123"comm="/usr/local/mysql/bin/mysql -u$user -h 127.0.0.1 -P $port -p$passwod"slave_info=`$comm -e"show slave status"|wc-l`
value=`$comm -Nse"select 1"`

# 判断是不是从库if[ $slave_info -ne0]thenecho"MySQL $port  Instance is Slave........"$comm-e"show slave status\G"|egrep-w"Master_Host|Master_User|Master_Port|Master_Log_File|Read_Master_Log_Pos|Relay_Log_File|Relay_Log_Pos|Relay_Master_Log_File|Slave_IO_Running|Slave_SQL_Running|Exec_Master_Log_Pos|Relay_Log_Space|Seconds_Behind_Master"exit2fi# 判断mysql是否存活if[ -z $value ]thenexit2fiecho"MySQL $port  Instance is Master........"$comm-e"select * from information_schema.PROCESSLIST where user='repl' and COMMAND like '%Dump%'"[root@db-server-yayun-02shell]#
View Code
[root@db-server-yayun-02shell]#catcheck_mysql_slave.sh#!/bin/bash
port=$1user="root"passwod="123"repl_check_user="root"repl_check_pwd="123"master_comm="/usr/local/mysql/bin/mysql -u$user -h 127.0.0.1 -P $port -p$passwod"slave_comm="/usr/local/mysql/bin/mysql -u$repl_check_user  -P $port -p$repl_check_pwd"# 判断mysql是否存活
value=`$master_comm -Nse"select 1"`if[ -z $value ]thenecho"MySQL Server is Down....."exit2figet_slave_count=0is_slave_role=0slave_mode_repl_delay=0master_mode_repl_delay=0master_mode_repl_dead=0slave_mode_repl_status=0max_delay=120get_slave_hosts=`$master_comm -Nse"select substring_index(HOST,':',1) from information_schema.PROCESSLIST where user='repl' and COMMAND like '%Binlog Dump%';"`
get_slave_count=`$master_comm -Nse"select count(1) from information_schema.PROCESSLIST where user='repl' and COMMAND like '%Binlog Dump%';"`
is_slave_role=`$master_comm -e"show slave status\G"|grep-Ewc"Slave_SQL_Running|Slave_IO_Running"`


### 单点模式(如果 get_slave_count=0and is_slave_role=0)functionsingle_mode
{if[ $get_slave_count -eq0-a $is_slave_role -eq0]thenecho"MySQL $port  Instance is Single Master........"exit0fi}

### 从节点模式(如果 get_slave_count=0and is_slave_role=2)functionslave_mode
{
#如果是从节点,必须满足不延迟,if[  $is_slave_role -ge2]thenecho"MySQL $port  Instance is Slave........"$master_comm-e"show slave status\G"|egrep-w"Master_Host|Master_User|Master_Port|Master_Log_File|Read_Master_Log_Pos|Relay_Log_File|Relay_Log_Pos|Relay_Master_Log_File|Slave_IO_Running|Slave_SQL_Running|Exec_Master_Log_Pos|Relay_Log_Space|Seconds_Behind_Master"slave_mode_repl_delay=`$master_comm -e"show slave status\G"|grep-w"Seconds_Behind_Master"|awk'{print $NF}'`
        slave_mode_repl_status=`$master_comm -e"show slave status\G"|grep-Ec"Slave_IO_Running: Yes|Slave_SQL_Running: Yes"`if[ X"$slave_mode_repl_delay"== X"NULL"]thenslave_mode_repl_delay=99999fiif[ $slave_mode_repl_delay !="NULL"-a $slave_mode_repl_delay -lt $max_delay -a $slave_mode_repl_status -ge2]thenexit0fifi}functionmaster_mode
{
###如果是主节点,必须满足从节点为延迟或复制错误。才可读if[ $get_slave_count -gt0-a $is_slave_role -eq0]thenecho"MySQL $port  Instance is Master........"$master_comm-e"select * from information_schema.PROCESSLIST where user='repl' and COMMAND like '%Dump%'"formy_slavein$get_slave_hostsdomaster_mode_repl_delay=`$slave_comm -h $my_slave -e"show slave status\G"|grep-w"Seconds_Behind_Master"|awk'{print $NF}'`
master_mode_repl_thread=`$slave_comm -h $my_slave -e"show slave status\G"|grep-Ec"Slave_IO_Running: Yes|Slave_SQL_Running: Yes"`if[ X"$master_mode_repl_delay"== X"NULL"]thenmaster_mode_repl_delay=99999fiif[ $master_mode_repl_delay -lt $max_delay -a $master_mode_repl_thread -ge2]thenexit2fidoneexit0fi} 

single_mode
slave_mode
master_mode
exit2[root@db-server-yayun-02shell]#
View Code

"name": "r-6029-redis-test",这个就是域名了,默认后缀是servers.consul,consul可以利用domain参数修改。配置文件生成以后安装redis,搭建主从复制(省略)。主从复制完成以后就可以重新reload consul了。redis info信息:

127.0.0.1:6029>inforeplication
# Replication
role:master
connected_slaves:2slave0:ip=192.168.0.40,port=6029,state=online,offset=6786,lag=0slave1:ip=192.168.0.30,port=6029,state=online,offset=6786,lag=1master_repl_offset:6786repl_backlog_active:1repl_backlog_size:67108864repl_backlog_first_byte_offset:2repl_backlog_histlen:6785127.0.0.1:6029>

reload consul(3台client,也就是20-40):

[root@db-server-yayun-02~]# consul reload
Configuration reload triggered
[root@db-server-yayun-02~]#

在其中一台服务器查看consul日志(20):

[root@db-server-yayun-02consul]#tail-f consul.log2017/12/0910:09:59[INFO] serf: EventMemberJoin: db-server-yayun-04192.168.0.402017/12/0911:14:55[INFO] Caught signal:  hangup2017/12/0911:14:55[INFO] Reloading configuration...2017/12/0911:14:55[INFO] agent: Synced service'r-6029-redis-test'2017/12/0911:14:55[INFO] agent: Synced service'w-6029-redis-test'2017/12/0911:14:55[INFO] agent: Synced check'service:w-6029-redis-test'2017/12/0911:15:00[WARN] agent: Check'service:r-6029-redis-test'is now critical2017/12/0911:15:15[WARN] agent: Check'service:r-6029-redis-test'is now critical2017/12/0911:15:30[WARN] agent: Check'service:r-6029-redis-test'is now critical2017/12/0911:15:45[WARN] agent: Check'service:r-6029-redis-test'is now critical

可以看到r-6029-redis-test,w-6029-redis-test服务都已经注册,但是只有w-6029-redis-test注册成功,也就是写的,因为服务器20上面的redis是master,slave的服务当然无法注册成功。我们通过web ui看看。

 可以看到3个client节点每个节点都已经注册了2个服务。还可以看到我们自定义的输出:

下面我们使用dns来解析看看,是否是我们想要的。我们注册两个服务。r-6029-redis-test,w-6029-redis-test,那么就是就产生了2个域名,分别是r-6029-redis-test.service.consul和w-6029-redis-test.service.consul。我们使用dig来看看:

[root@db-server-yayun-02~]# dig @192.168.0.10-p8600r-6029-redis-test.service.consul

;<<>> DiG9.8.2rc1-RedHat-9.8.2-0.17.rc1.el6_4.6<<>> @192.168.0.10-p8600r-6029-redis-test.service.consul
; (1server found)
;; global options:+cmd
;; Got answer:
;;->>HEADER<<- opcode: QUERY, status: NOERROR,id:34508;; flags: qr aa rd; QUERY:1, ANSWER:2, AUTHORITY:0, ADDITIONAL:0;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;r-6029-redis-test.service.consul. IN   A

;; ANSWER SECTION:
r-6029-redis-test.service.consul.0IN  A192.168.0.30r-6029-redis-test.service.consul.0IN  A192.168.0.40;; Querytime:1msec
;; SERVER:192.168.0.10#8600(192.168.0.10)
;; WHEN: Sat Dec911:26:382017;; MSG SIZE  rcvd:82[root@db-server-yayun-02~]#
View Code

我们可以看到读的域名r-6029-redis-test.service.consul解析到了两台服务器。那么我们就能够对从库进行负载均衡了。那么写的域名呢?

[root@db-server-yayun-02~]# dig @192.168.0.10-p8600w-6029-redis-test.service.consul

;<<>> DiG9.8.2rc1-RedHat-9.8.2-0.17.rc1.el6_4.6<<>> @192.168.0.10-p8600w-6029-redis-test.service.consul
; (1server found)
;; global options:+cmd
;; Got answer:
;;->>HEADER<<- opcode: QUERY, status: NOERROR,id:7451;; flags: qr aa rd; QUERY:1, ANSWER:1, AUTHORITY:0, ADDITIONAL:0;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;w-6029-redis-test.service.consul. IN   A

;; ANSWER SECTION:w-6029-redis-test.service.consul.0IN  A192.168.0.20;; Querytime:1msec
;; SERVER:192.168.0.10#8600(192.168.0.10)
;; WHEN: Sat Dec911:27:592017;; MSG SIZE  rcvd:66[root@db-server-yayun-02~]#
View Code

和我们预料的没错,解析在了20上面。那么我们如果关闭其中一个从库会是怎样的?

[root@db-server-yayun-03~]#ifconfigeth1 |grep-oP'(?<=inet addr:)\S+'192.168.0.30[root@db-server-yayun-03~]# pgrep -fl redis-server |awk'{print $1}'|xargskill[root@db-server-yayun-03~]#
127.0.0.1:6029>inforeplication
# Replication
role:master
connected_slaves:1slave0:ip=192.168.0.40,port=6029,state=online,offset=8200,lag=0master_repl_offset:8200repl_backlog_active:1repl_backlog_size:67108864repl_backlog_first_byte_offset:2repl_backlog_histlen:8199127.0.0.1:6029>

可以看到只有一个从了,我们再次dig 读域名看看:

[root@db-server-yayun-02~]# dig @192.168.0.10-p8600r-6029-redis-test.service.consul

;<<>> DiG9.8.2rc1-RedHat-9.8.2-0.17.rc1.el6_4.6<<>> @192.168.0.10-p8600r-6029-redis-test.service.consul
; (1server found)
;; global options:+cmd
;; Got answer:
;;->>HEADER<<- opcode: QUERY, status: NOERROR,id:41984;; flags: qr aa rd; QUERY:1, ANSWER:1, AUTHORITY:0, ADDITIONAL:0;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;r-6029-redis-test.service.consul. IN   A

;; ANSWER SECTION:
r-6029-redis-test.service.consul.0IN  A192.168.0.40;; Querytime:8msec
;; SERVER:192.168.0.10#8600(192.168.0.10)
;; WHEN: Sat Dec911:32:462017;; MSG SIZE  rcvd:66[root@db-server-yayun-02~]#
View Code

可以看到踢掉了另外一台机器。如果我再次关闭40这个从呢?

[root@db-server-yayun-04shell]#ifconfigeth1 |grep-oP'(?<=inet addr:)\S+'192.168.0.40[root@db-server-yayun-04shell]# pgrep -fl redis-server |awk'{print $1}'|xargskill[root@db-server-yayun-04shell]#

那么我们的redis就没有可用从库了,那么读写都将在master上面。

[root@db-server-yayun-02~]# dig @192.168.0.10-p8600r-6029-redis-test.service.consul

;<<>> DiG9.8.2rc1-RedHat-9.8.2-0.17.rc1.el6_4.6<<>> @192.168.0.10-p8600r-6029-redis-test.service.consul
; (1server found)
;; global options:+cmd
;; Got answer:
;;->>HEADER<<- opcode: QUERY, status: NOERROR,id:58564;; flags: qr aa rd; QUERY:1, ANSWER:1, AUTHORITY:0, ADDITIONAL:0;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;r-6029-redis-test.service.consul. IN   A

;; ANSWER SECTION:
r-6029-redis-test.service.consul.0IN  A192.168.0.20;; Querytime:4msec
;; SERVER:192.168.0.10#8600(192.168.0.10)
;; WHEN: Sat Dec911:35:112017;; MSG SIZE  rcvd:66[root@db-server-yayun-02~]# dig @192.168.0.10-p8600w-6029-redis-test.service.consul

;<<>> DiG9.8.2rc1-RedHat-9.8.2-0.17.rc1.el6_4.6<<>> @192.168.0.10-p8600w-6029-redis-test.service.consul
; (1server found)
;; global options:+cmd
;; Got answer:
;;->>HEADER<<- opcode: QUERY, status: NOERROR,id:56965;; flags: qr aa rd; QUERY:1, ANSWER:1, AUTHORITY:0, ADDITIONAL:0;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;w-6029-redis-test.service.consul. IN   A

;; ANSWER SECTION:w-6029-redis-test.service.consul.0IN  A192.168.0.20;; Querytime:5msec
;; SERVER:192.168.0.10#8600(192.168.0.10)
;; WHEN: Sat Dec911:35:162017;; MSG SIZE  rcvd:66[root@db-server-yayun-02~]#
View Code

这里测试的就差不多了,下面结合sentinel来实现高可用。我会恢复刚才的环境。也就是20是master,30,40是slave。10是sentinel。生产环境sentinel也要部署3个或5个。我的10上面已经有sentinel,端口是36029,我直接添加对20的6029监控。

127.0.0.1:36029> sentinel monitor my-test-6029192.168.0.2060291OK127.0.0.1:36029>
127.0.0.1:36029>infoSentinel
# Sentinel
sentinel_masters:1sentinel_tilt:0sentinel_running_scripts:0sentinel_scripts_queue_length:0master0:name=my-test-6029,status=ok,address=192.168.0.20:6029,slaves=2,sentinels=1127.0.0.1:36029>

再次看看读写域名是否正常了,我已经恢复环境:

[root@db-server-yayun-02~]# dig @192.168.0.10-p8600w-6029-redis-test.service.consul

;<<>> DiG9.8.2rc1-RedHat-9.8.2-0.17.rc1.el6_4.6<<>> @192.168.0.10-p8600w-6029-redis-test.service.consul
; (1server found)
;; global options:+cmd
;; Got answer:
;;->>HEADER<<- opcode: QUERY, status: NOERROR,id:62669;; flags: qr aa rd; QUERY:1, ANSWER:1, AUTHORITY:0, ADDITIONAL:0;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;w-6029-redis-test.service.consul. IN   A

;; ANSWER SECTION:w-6029-redis-test.service.consul.0IN  A192.168.0.20;; Querytime:2msec
;; SERVER:192.168.0.10#8600(192.168.0.10)
;; WHEN: Sat Dec911:43:042017;; MSG SIZE  rcvd:66[root@db-server-yayun-02~]# dig @192.168.0.10-p8600r-6029-redis-test.service.consul

;<<>> DiG9.8.2rc1-RedHat-9.8.2-0.17.rc1.el6_4.6<<>> @192.168.0.10-p8600r-6029-redis-test.service.consul
; (1server found)
;; global options:+cmd
;; Got answer:
;;->>HEADER<<- opcode: QUERY, status: NOERROR,id:41305;; flags: qr aa rd; QUERY:1, ANSWER:2, AUTHORITY:0, ADDITIONAL:0;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;r-6029-redis-test.service.consul. IN   A

;; ANSWER SECTION:
r-6029-redis-test.service.consul.0IN  A192.168.0.30r-6029-redis-test.service.consul.0IN  A192.168.0.40;; Querytime:2msec
;; SERVER:192.168.0.10#8600(192.168.0.10)
;; WHEN: Sat Dec911:43:082017;; MSG SIZE  rcvd:82[root@db-server-yayun-02~]#
View Code

可以看到已经正常,现在关闭redis master:

[root@db-server-yayun-02~]#ifconfigeth1 |grep-oP'(?<=inet addr:)\S+'192.168.0.20[root@db-server-yayun-02~]# pgrep -fl redis-server |awk'{print $1}'|xargskill

看看sentinel信息:

127.0.0.1:36029>infoSentinel
# Sentinel
sentinel_masters:1sentinel_tilt:0sentinel_running_scripts:0sentinel_scripts_queue_length:0master0:name=my-test-6029,status=ok,address=192.168.0.30:6029,slaves=2,sentinels=1127.0.0.1:36029>

可以看到master已经是30了,dig域名看看:

[root@db-server-yayun-02~]# dig @192.168.0.10-p8600w-6029-redis-test.service.consul

;<<>> DiG9.8.2rc1-RedHat-9.8.2-0.17.rc1.el6_4.6<<>> @192.168.0.10-p8600w-6029-redis-test.service.consul
; (1server found)
;; global options:+cmd
;; Got answer:
;;->>HEADER<<- opcode: QUERY, status: NOERROR,id:55527;; flags: qr aa rd; QUERY:1, ANSWER:1, AUTHORITY:0, ADDITIONAL:0;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;w-6029-redis-test.service.consul. IN   A

;; ANSWER SECTION:w-6029-redis-test.service.consul.0IN  A192.168.0.30;; Querytime:2msec
;; SERVER:192.168.0.10#8600(192.168.0.10)
;; WHEN: Sat Dec911:45:462017;; MSG SIZE  rcvd:66[root@db-server-yayun-02~]# dig @192.168.0.10-p8600r-6029-redis-test.service.consul

;<<>> DiG9.8.2rc1-RedHat-9.8.2-0.17.rc1.el6_4.6<<>> @192.168.0.10-p8600r-6029-redis-test.service.consul
; (1server found)
;; global options:+cmd
;; Got answer:
;;->>HEADER<<- opcode: QUERY, status: NOERROR,id:11563;; flags: qr aa rd; QUERY:1, ANSWER:1, AUTHORITY:0, ADDITIONAL:0;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;r-6029-redis-test.service.consul. IN   A

;; ANSWER SECTION:
r-6029-redis-test.service.consul.0IN  A192.168.0.40;; Querytime:1msec
;; SERVER:192.168.0.10#8600(192.168.0.10)
;; WHEN: Sat Dec911:45:502017;; MSG SIZE  rcvd:66[root@db-server-yayun-02~]#
View Code

ok,可以看到已经是我们想要的结果了。最后说说dns的问题。

App端配置域名服务器IP来解析consul后缀的域名,DNS解析及跳转, 有三个方案:
1. 原内网dns服务器,做域名转发,consul后缀的,都转到consul server上(我们线上是采用这个)
2. dns全部跳到consul DNS服务器上,非consul后缀的,使用 recursors 属性跳转到原DNS服务器上
3. dnsmaq 转: server=/consul/10.16.X.X#8600 解析consul后缀的

我们内网dns是用的bind,对于bind的如何做域名转发consul官网也有栗子: https://www.consul.io/docs/guides/forwarding.html,另外也对consul的dns进行了压力测试,不存在性能问题:

 

参考资料:

https://www.consul.io/docs/

https://book-consul-guide.vnzmi.com/

http://www.liangxiansen.cn/2017/04/06/consul/

 

总结:

        对于单机多实例的mysql以及redis,利用consul能够很好的实现高可用,当然要结合mha或者sentinel,最大的好处是consul足够轻量,方便,简单。如果程序支持读写分离的,那么用起来更加方便。从挂掉一个或者多个也不会影响服务。

 

相关 [consul 数据库 架构] 推荐:

基于Consul的数据库高可用架构 - yayun - 博客园

- -
      几个月没有更新博客了,已经长草了,特意来除草. 本次主要分享如何利用consul来实现redis以及mysql的高可用. 以前的公司mysql是单机单实例,高可用MHA加vip就能搞定,新公司mysql是单机多实例,那么显然这个方案不适用,后来也实现了故障切换调用dns api来修改域名记录,但是还是没有利用consul来实现高可用方便,后面会说明优势.

基于Nginx和Consul构建高可用及自动发现的Docker服务架构 - DockOne.io

- -
如果你在大量接触或使用微服务的话,你可能会碰到一个问题:当你创建的服务数量越来越多时,这些服务之间的通信便越难管理,而且维护代价会越来越高. 针对这个问题,Consul给出了一份完美的答卷. Consul是一套开源的分布式服务发现和配置管理系统,支持多数据中心分布式高可用. Consul是HashiCorp(Vagrant的创建者)开发的一个服务发现与配置项目,用Go语言开发,基于 Mozilla Public License 2.0 的协议开源.

安装Consul集群

- - 周立的博客 - 关注Spring Cloud、Docker
本文基于Consul 1.5.3,理论适用于Consul 1.6及更低版本. 运行一个consul agent. 将agent加入到consul集群. 列出consul cluster集群中的members. 这里只列出几个常用的命令,consul有将近20个命令,本文不作展开,详见: https://www.consul.io/docs/commands/index.html.

对数据库架构的再思考

- - 人月神话的BLOG
前面在谈PaaS的时候曾经谈到过共享数据库,私有数据库的问题,在这里再谈谈在多业务系统建设过程中的数据架构模式问题. 首先来看下传统的数据交换解决方案如下图:. 业务场景为单独构建的四个业务系统,在四个业务系统中SID数据为需要跨四个应用交互和共享的数据. 传统的做法则是对四个应用存在的SID库数据进行数据集成和交换,则后续的每一个业务系统中都有全部的共享基础数据,任何一个应用的SID库数据需要通过数据交换和集成同步四份.

OceanBase 数据库的系统架构

- -
OceanBase 数据库采用 Shared-Nothing 架构,各个节点之间完全对等,每个节点都有自己的 SQL 引擎、存储引擎,运行在普通 PC 服务器组成的集群之上,具备可扩展、高可用、高性能、低成本、云原生等核心特性. OceanBase 数据库的整体架构如下图所示. OceanBase 数据库支持数据跨地域(Region)部署,每个地域可能位于不同的城市,距离通常比较远,所以 OceanBase 数据库可以支持多城市部署,也支持多城市级别的容灾.

[转]consul VS zookeeper、etcd、doozerd

- - Xiao_Qiang_的专栏
  zookeeper、doozerd、etcd都有着相似的架构,这三者的服务节点都需要一个仲裁节点来操作,它们是强一致的,并提供各种操作原语. 应用程序可以通过客户端lib库来构建分布式的系统. 在一个单datacenter中,consul的server节点工作在一种简单的方式下,consul server需要一个仲裁操作,并提供强一致性.

Consul 常用url链接

- - Share

优良的数据库架构--让网站飞起来

- We_Get - 博客园-首页原创精华区
   很少谈架构方面的事情,主要是因为这确实是个对知识面和知识深度要求很高的领域,无论是开发语言的选择、代码的架构,服务器的搭配、网络的架构、数据库的架构还是第三方软件的选用等,每一方面都是个很大的方向,每个方向都值得一个人去研究一辈子;每每看到某某网站的首席架构师之类的人(不过很多是海绵派),总觉得那就是乐于做技术的人的终极目标,总是有种崇拜感.

MySQL云数据库服务的架构探索

- - 博客园_知识库
  MySQL作为一种低成本、高性能、可靠性良好而且开源的数据库产品,在互联网企业中应用非常广泛. 例如,淘宝网就有数千台MySQL服务器. 虽然近两年来NoSQL的发展很快,新产品层出不穷,但在业务中应用NoSQL对开发者来说要求比较高,而MySQL拥有成熟的中间件、运维工具, 已经形成一个良性的生态圈.

[转][转]列式数据库之infobright以及架构

- - heiyeluren的blog(黑夜路人的开源世界)
文章来源:http://www.cnblogs.com/inmanhust/tag/infobright/. 列式数据库之infobright.   年前听过Sybase中国区副总裁的关于列式数据库的讲座之后就一直被列式数据库强大的性能吸引. 最近邂逅了infobright,列式数据库的学习展开了.