基于Consul的数据库高可用架构
几个月没有更新博客了,已经长草了,特意来除草。本次主要分享如何利用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- ~]# cat /etc/consul.d/server.json
{
"data_dir": "/data/consul",
"datacenter": "dc1",
"log_level": "INFO",
"server": true,
"bootstrap_expect": ,
"bind_addr": "192.168.0.10",
"client_addr": "192.168.0.10",
"ui":true
}
[root@db-server-yayun- ~]#
consul client(192.168.0.20,192.168.0.30,192.168.0.40)
[root@db-server-yayun- ~]# 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- ~]#
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- consul]# cat consul.log
==> WARNING: BootstrapExpect Mode is specified as ; 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: , HTTPS: -, DNS: )
Cluster Addr: 192.168.0.10 (LAN: , WAN: )
Gossip encrypt: false, RPC-TLS: false, TLS-Incoming: false ==> Log data will now stream in as it occurs: // :: [INFO] raft: Initial configuration (index=): [{Suffrage:Voter ID:192.168.0.10: Address:192.168.0.10:}]
// :: [INFO] raft: Node at 192.168.0.10: [Follower] entering Follower state (Leader: "")
// :: [INFO] serf: EventMemberJoin: db-server-yayun-.dc1 192.168.0.10
// :: [INFO] serf: EventMemberJoin: db-server-yayun- 192.168.0.10
// :: [INFO] agent: Started DNS server 192.168.0.10: (udp)
// :: [INFO] consul: Adding LAN server db-server-yayun- (Addr: tcp/192.168.0.10:) (DC: dc1)
// :: [INFO] consul: Handled member-join event for server "db-server-yayun-01.dc1" in area "wan"
// :: [INFO] agent: Started DNS server 192.168.0.10: (tcp)
// :: [INFO] agent: Started HTTP server on 192.168.0.10:
// :: [ERR] agent: failed to sync remote state: No cluster leader
// :: [WARN] raft: Heartbeat timeout from "" reached, starting election
// :: [INFO] raft: Node at 192.168.0.10: [Candidate] entering Candidate state in term
// :: [INFO] raft: Election won. Tally:
// :: [INFO] raft: Node at 192.168.0.10: [Leader] entering Leader state
// :: [INFO] consul: cluster leadership acquired
// :: [INFO] consul: New leader elected: db-server-yayun-
// :: [INFO] consul: member 'db-server-yayun-01' joined, marking health alive
// :: [INFO] agent: Synced node info
可以从日志中看到(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- consul]# cat /data/consul/consul.log
==> Starting Consul agent...
==> Joining cluster...
Join completed. Synced with initial 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: , HTTPS: -, DNS: )
Cluster Addr: 192.168.0.20 (LAN: , WAN: )
Gossip encrypt: false, RPC-TLS: false, TLS-Incoming: false ==> Log data will now stream in as it occurs: // :: [INFO] serf: EventMemberJoin: db-server-yayun- 192.168.0.20
// :: [INFO] agent: Started DNS server 127.0.0.1: (udp)
// :: [INFO] agent: Started DNS server 127.0.0.1: (tcp)
// :: [INFO] agent: Started HTTP server on 127.0.0.1:
// :: [INFO] agent: (LAN) joining: [192.168.0.10]
// :: [INFO] agent: Retry join is supported for: aws azure gce softlayer
// :: [INFO] agent: Joining cluster...
// :: [INFO] agent: (LAN) joining: [192.168.0.10]
// :: [INFO] serf: EventMemberJoin: db-server-yayun- 192.168.0.10
// :: [INFO] agent: (LAN) joined: Err: <nil>
// :: [INFO] consul: adding server db-server-yayun- (Addr: tcp/192.168.0.10:) (DC: dc1)
// :: [INFO] agent: (LAN) joined: Err: <nil>
// :: [INFO] agent: Join completed. Synced with initial agents
// :: [INFO] agent: Synced node info
可以看到提示agent: Join completed. Synced with 1 initial agents,以及Server: false (bootstrap: false)。这也是client和server的区别。
我们继续执行命令看一下集群:
[root@db-server-yayun- ~]# consul members
Node Address Status Type Build Protocol DC
db-server-yayun- 192.168.0.10: alive server 0.9. dc1
db-server-yayun- 192.168.0.20: alive client 0.9. dc1
db-server-yayun- 192.168.0.30: alive client 0.9. dc1
db-server-yayun- 192.168.0.40: alive client 0.9. dc1
[root@db-server-yayun- ~]#
[root@db-server-yayun- ~]# consul operator raft list-peers
Node ID Address State Voter RaftProtocol
db-server-yayun- 192.168.0.10: 192.168.0.10: leader true
[root@db-server-yayun- ~]#
我们看看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要修改为对应的服务器地址,其他一样。
[root@db-server-yayun- consul.d]# pwd
/etc/consul.d
[root@db-server-yayun- consul.d]# ll
total
-rw-r--r--. root root Dec : client.json
-rw-r--r--. root root Dec : r--redis-test.json
-rw-r--r--. root root Dec : w--redis-test.json
[root@db-server-yayun- consul.d]#
master的服务定义配置文件:
[root@db-server-yayun- consul.d]# cat w--redis-test.json
{
"services": [
{
"name": "w-6029-redis-test",
"tags": [
"master-test-6029"
],
"address": "192.168.0.20",
"port": ,
"checks": [
{
"script": "/data/consul/shell/check_redis_master.sh 6029 ",
"interval": "15s"
}
]
}
]
} [root@db-server-yayun- consul.d]#
slave的服务定义配置文件:
[root@db-server-yayun- consul.d]# cat r--redis-test.json
{
"services": [
{
"name": "r-6029-redis-test",
"tags": [
"slave-test-6029"
],
"address": "192.168.0.20",
"port": ,
"checks": [
{
"script": "/data/consul/shell/check_redis_slave.sh 6029 ",
"interval": "15s"
}
]
}
]
} [root@db-server-yayun- consul.d]#
每个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- shell]# pwd
/data/consul/shell
[root@db-server-yayun- shell]# ll
total
-rwxr-xr-x. root root Dec : check_mysql_master.sh
-rwxr-xr-x. root root Dec : check_mysql_slave.sh
-rwxr-xr-x. root root Dec : check_redis_master.sh
-rwxr-xr-x. root root Dec : check_redis_slave.sh
[root@db-server-yayun- shell]#
/data/consul/shell目录下面有4个脚本,是对redis和mysql进行健康检查用的。脚本比较简单,大概就是如果只有一个master,那么读写都在master,如果有slave可用,那么读会在slave进行。如果slave复制不正常,或者复制延时,那么slave服务将不会注册。
[root@db-server-yayun- shell]# cat check_redis_master.sh
#!/bin/bash
myport=$
auth=$
if [ ! -n "$auth" ]
then
auth='\"\"'
fi
comm="/usr/local/bin/redis-cli -p $myport -a $auth "
role=`echo 'INFO Replication'|$comm |grep -Ec 'role:master'`
echo 'INFO Replication'|$comm if [ $role -ne ]
then
exit
fi [root@db-server-yayun- shell]#
[root@db-server-yayun- shell]# cat check_redis_slave.sh
#!/bin/bash
myport=$
auth=$
if [ ! -n "$auth" ]
then
auth='\"\"'
fi
comm="/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'|$comm if [ $role -ne -a $single -ne ]
then
exit
fi [root@db-server-yayun- shell]#
[root@db-server-yayun- shell]# cat check_mysql_master.sh
#!/bin/bash
port=$
user="root"
passwod="" 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 -ne ]
then
echo "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"
exit
fi # 判断mysql是否存活
if [ -z $value ]
then
exit
fi echo "MySQL $port Instance is Master........"
$comm -e "select * from information_schema.PROCESSLIST where user='repl' and COMMAND like '%Dump%'"
[root@db-server-yayun- shell]#
[root@db-server-yayun- shell]# cat check_mysql_slave.sh
#!/bin/bash
port=$
user="root"
passwod=""
repl_check_user="root"
repl_check_pwd="" 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 ]
then
echo "MySQL Server is Down....."
exit
fi get_slave_count=
is_slave_role=
slave_mode_repl_delay=
master_mode_repl_delay=
master_mode_repl_dead=
slave_mode_repl_status=
max_delay= get_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= and is_slave_role=)
function single_mode
{
if [ $get_slave_count -eq -a $is_slave_role -eq ]
then
echo "MySQL $port Instance is Single Master........"
exit
fi
} ### 从节点模式(如果 get_slave_count= and is_slave_role= )
function slave_mode
{
#如果是从节点,必须满足不延迟,
if [ $is_slave_role -ge ]
then
echo "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" ]
then
slave_mode_repl_delay=
fi
if [ $slave_mode_repl_delay != "NULL" -a $slave_mode_repl_delay -lt $max_delay -a $slave_mode_repl_status -ge ]
then
exit
fi
fi
} function master_mode
{
###如果是主节点,必须满足从节点为延迟或复制错误。才可读
if [ $get_slave_count -gt -a $is_slave_role -eq ]
then
echo "MySQL $port Instance is Master........"
$master_comm -e "select * from information_schema.PROCESSLIST where user='repl' and COMMAND like '%Dump%'"
for my_slave in $get_slave_hosts
do
master_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" ]
then
master_mode_repl_delay=
fi if [ $master_mode_repl_delay -lt $max_delay -a $master_mode_repl_thread -ge ]
then
exit
fi
done
exit
fi
} single_mode
slave_mode
master_mode
exit
[root@db-server-yayun- shell]#
"name": "r-6029-redis-test",这个就是域名了,默认后缀是servers.consul,consul可以利用domain参数修改。配置文件生成以后安装redis,搭建主从复制(省略)。主从复制完成以后就可以重新reload consul了。redis info信息:
127.0.0.1:> info replication
# Replication
role:master
connected_slaves:
slave0:ip=192.168.0.40,port=,state=online,offset=,lag=
slave1:ip=192.168.0.30,port=,state=online,offset=,lag=
master_repl_offset:
repl_backlog_active:
repl_backlog_size:
repl_backlog_first_byte_offset:
repl_backlog_histlen:
127.0.0.1:>
reload consul(3台client,也就是20-40):
[root@db-server-yayun- ~]# consul reload
Configuration reload triggered
[root@db-server-yayun- ~]#
在其中一台服务器查看consul日志(20):
[root@db-server-yayun- consul]# tail -f consul.log
// :: [INFO] serf: EventMemberJoin: db-server-yayun- 192.168.0.40
// :: [INFO] Caught signal: hangup
// :: [INFO] Reloading configuration...
// :: [INFO] agent: Synced service 'r-6029-redis-test'
// :: [INFO] agent: Synced service 'w-6029-redis-test'
// :: [INFO] agent: Synced check 'service:w-6029-redis-test'
// :: [WARN] agent: Check 'service:r-6029-redis-test' is now critical
// :: [WARN] agent: Check 'service:r-6029-redis-test' is now critical
// :: [WARN] agent: Check 'service:r-6029-redis-test' is now critical
// :: [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- ~]# dig @192.168.0.10 -p r--redis-test.service.consul ; <<>> DiG 9.8.2rc1-RedHat-9.8.-0.17.rc1.el6_4. <<>> @192.168.0.10 -p r--redis-test.service.consul
; ( server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id:
;; flags: qr aa rd; QUERY: , ANSWER: , AUTHORITY: , ADDITIONAL:
;; WARNING: recursion requested but not available ;; QUESTION SECTION:
;r--redis-test.service.consul. IN A ;; ANSWER SECTION:
r--redis-test.service.consul. IN A 192.168.0.30
r--redis-test.service.consul. IN A 192.168.0.40 ;; Query time: msec
;; SERVER: 192.168.0.10#(192.168.0.10)
;; WHEN: Sat Dec ::
;; MSG SIZE rcvd: [root@db-server-yayun- ~]#
我们可以看到读的域名r-6029-redis-test.service.consul解析到了两台服务器。那么我们就能够对从库进行负载均衡了。那么写的域名呢?
[root@db-server-yayun- ~]# dig @192.168.0.10 -p w--redis-test.service.consul ; <<>> DiG 9.8.2rc1-RedHat-9.8.-0.17.rc1.el6_4. <<>> @192.168.0.10 -p w--redis-test.service.consul
; ( server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id:
;; flags: qr aa rd; QUERY: , ANSWER: , AUTHORITY: , ADDITIONAL:
;; WARNING: recursion requested but not available ;; QUESTION SECTION:
;w--redis-test.service.consul. IN A ;; ANSWER SECTION:
w--redis-test.service.consul. IN A 192.168.0.20 ;; Query time: msec
;; SERVER: 192.168.0.10#(192.168.0.10)
;; WHEN: Sat Dec ::
;; MSG SIZE rcvd: [root@db-server-yayun- ~]#
和我们预料的没错,解析在了20上面。那么我们如果关闭其中一个从库会是怎样的?
[root@db-server-yayun- ~]# ifconfig eth1 | grep -oP '(?<=inet addr:)\S+'
192.168.0.30
[root@db-server-yayun- ~]# pgrep -fl redis-server | awk '{print $1}' | xargs kill
[root@db-server-yayun- ~]#
127.0.0.1:> info replication
# Replication
role:master
connected_slaves:
slave0:ip=192.168.0.40,port=,state=online,offset=,lag=
master_repl_offset:
repl_backlog_active:
repl_backlog_size:
repl_backlog_first_byte_offset:
repl_backlog_histlen:
127.0.0.1:>
可以看到只有一个从了,我们再次dig 读域名看看:
[root@db-server-yayun- ~]# dig @192.168.0.10 -p r--redis-test.service.consul ; <<>> DiG 9.8.2rc1-RedHat-9.8.-0.17.rc1.el6_4. <<>> @192.168.0.10 -p r--redis-test.service.consul
; ( server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id:
;; flags: qr aa rd; QUERY: , ANSWER: , AUTHORITY: , ADDITIONAL:
;; WARNING: recursion requested but not available ;; QUESTION SECTION:
;r--redis-test.service.consul. IN A ;; ANSWER SECTION:
r--redis-test.service.consul. IN A 192.168.0.40 ;; Query time: msec
;; SERVER: 192.168.0.10#(192.168.0.10)
;; WHEN: Sat Dec ::
;; MSG SIZE rcvd: [root@db-server-yayun- ~]#
可以看到踢掉了另外一台机器。如果我再次关闭40这个从呢?
[root@db-server-yayun- shell]# ifconfig eth1 | grep -oP '(?<=inet addr:)\S+'
192.168.0.40
[root@db-server-yayun- shell]# pgrep -fl redis-server | awk '{print $1}' | xargs kill
[root@db-server-yayun- shell]#
那么我们的redis就没有可用从库了,那么读写都将在master上面。
[root@db-server-yayun- ~]# dig @192.168.0.10 -p r--redis-test.service.consul ; <<>> DiG 9.8.2rc1-RedHat-9.8.-0.17.rc1.el6_4. <<>> @192.168.0.10 -p r--redis-test.service.consul
; ( server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id:
;; flags: qr aa rd; QUERY: , ANSWER: , AUTHORITY: , ADDITIONAL:
;; WARNING: recursion requested but not available ;; QUESTION SECTION:
;r--redis-test.service.consul. IN A ;; ANSWER SECTION:
r--redis-test.service.consul. IN A 192.168.0.20 ;; Query time: msec
;; SERVER: 192.168.0.10#(192.168.0.10)
;; WHEN: Sat Dec ::
;; MSG SIZE rcvd: [root@db-server-yayun- ~]# dig @192.168.0.10 -p w--redis-test.service.consul ; <<>> DiG 9.8.2rc1-RedHat-9.8.-0.17.rc1.el6_4. <<>> @192.168.0.10 -p w--redis-test.service.consul
; ( server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id:
;; flags: qr aa rd; QUERY: , ANSWER: , AUTHORITY: , ADDITIONAL:
;; WARNING: recursion requested but not available ;; QUESTION SECTION:
;w--redis-test.service.consul. IN A ;; ANSWER SECTION:
w--redis-test.service.consul. IN A 192.168.0.20 ;; Query time: msec
;; SERVER: 192.168.0.10#(192.168.0.10)
;; WHEN: Sat Dec ::
;; MSG SIZE rcvd: [root@db-server-yayun- ~]#
这里测试的就差不多了,下面结合sentinel来实现高可用。我会恢复刚才的环境。也就是20是master,30,40是slave。10是sentinel。生产环境sentinel也要部署3个或5个。我的10上面已经有sentinel,端口是36029,我直接添加对20的6029监控。
127.0.0.1:> sentinel monitor my-test- 192.168.0.20
OK
127.0.0.1:>
127.0.0.1:> info Sentinel
# Sentinel
sentinel_masters:
sentinel_tilt:
sentinel_running_scripts:
sentinel_scripts_queue_length:
master0:name=my-test-,status=ok,address=192.168.0.20:,slaves=,sentinels=
127.0.0.1:>
再次看看读写域名是否正常了,我已经恢复环境:
[root@db-server-yayun- ~]# dig @192.168.0.10 -p w--redis-test.service.consul ; <<>> DiG 9.8.2rc1-RedHat-9.8.-0.17.rc1.el6_4. <<>> @192.168.0.10 -p w--redis-test.service.consul
; ( server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id:
;; flags: qr aa rd; QUERY: , ANSWER: , AUTHORITY: , ADDITIONAL:
;; WARNING: recursion requested but not available ;; QUESTION SECTION:
;w--redis-test.service.consul. IN A ;; ANSWER SECTION:
w--redis-test.service.consul. IN A 192.168.0.20 ;; Query time: msec
;; SERVER: 192.168.0.10#(192.168.0.10)
;; WHEN: Sat Dec ::
;; MSG SIZE rcvd: [root@db-server-yayun- ~]# dig @192.168.0.10 -p r--redis-test.service.consul ; <<>> DiG 9.8.2rc1-RedHat-9.8.-0.17.rc1.el6_4. <<>> @192.168.0.10 -p r--redis-test.service.consul
; ( server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id:
;; flags: qr aa rd; QUERY: , ANSWER: , AUTHORITY: , ADDITIONAL:
;; WARNING: recursion requested but not available ;; QUESTION SECTION:
;r--redis-test.service.consul. IN A ;; ANSWER SECTION:
r--redis-test.service.consul. IN A 192.168.0.30
r--redis-test.service.consul. IN A 192.168.0.40 ;; Query time: msec
;; SERVER: 192.168.0.10#(192.168.0.10)
;; WHEN: Sat Dec ::
;; MSG SIZE rcvd: [root@db-server-yayun- ~]#
可以看到已经正常,现在关闭redis master:
[root@db-server-yayun- ~]# ifconfig eth1 | grep -oP '(?<=inet addr:)\S+'
192.168.0.20
[root@db-server-yayun- ~]# pgrep -fl redis-server | awk '{print $1}' | xargs kill
看看sentinel信息:
127.0.0.1:> info Sentinel
# Sentinel
sentinel_masters:
sentinel_tilt:
sentinel_running_scripts:
sentinel_scripts_queue_length:
master0:name=my-test-,status=ok,address=192.168.0.30:,slaves=,sentinels=
127.0.0.1:>
可以看到master已经是30了,dig域名看看:
[root@db-server-yayun- ~]# dig @192.168.0.10 -p w--redis-test.service.consul ; <<>> DiG 9.8.2rc1-RedHat-9.8.-0.17.rc1.el6_4. <<>> @192.168.0.10 -p w--redis-test.service.consul
; ( server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id:
;; flags: qr aa rd; QUERY: , ANSWER: , AUTHORITY: , ADDITIONAL:
;; WARNING: recursion requested but not available ;; QUESTION SECTION:
;w--redis-test.service.consul. IN A ;; ANSWER SECTION:
w--redis-test.service.consul. IN A 192.168.0.30 ;; Query time: msec
;; SERVER: 192.168.0.10#(192.168.0.10)
;; WHEN: Sat Dec ::
;; MSG SIZE rcvd: [root@db-server-yayun- ~]# dig @192.168.0.10 -p r--redis-test.service.consul ; <<>> DiG 9.8.2rc1-RedHat-9.8.-0.17.rc1.el6_4. <<>> @192.168.0.10 -p r--redis-test.service.consul
; ( server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id:
;; flags: qr aa rd; QUERY: , ANSWER: , AUTHORITY: , ADDITIONAL:
;; WARNING: recursion requested but not available ;; QUESTION SECTION:
;r--redis-test.service.consul. IN A ;; ANSWER SECTION:
r--redis-test.service.consul. IN A 192.168.0.40 ;; Query time: msec
;; SERVER: 192.168.0.10#(192.168.0.10)
;; WHEN: Sat Dec ::
;; MSG SIZE rcvd: [root@db-server-yayun- ~]#
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://book-consul-guide.vnzmi.com/
http://www.liangxiansen.cn/2017/04/06/consul/
总结:
对于单机多实例的mysql以及redis,利用consul能够很好的实现高可用,当然要结合mha或者sentinel,最大的好处是consul足够轻量,方便,简单。如果程序支持读写分离的,那么用起来更加方便。从挂掉一个或者多个也不会影响服务。
基于Consul的数据库高可用架构的更多相关文章
- 基于Consul的数据库高可用架构【转】
几个月没有更新博客了,已经长草了,特意来除草.本次主要分享如何利用consul来实现redis以及mysql的高可用.以前的公司mysql是单机单实例,高可用MHA加vip就能搞定,新公司mysql是 ...
- 美团点评基于MGR的CMDB高可用架构搭建之路【转】
王志朋 美团点评DBA 曾在京东金融担任DBA,目前就职于美团点评,主要负责金融业务线数据库及基础组件数据库的运维. MySQL Group Replication(以下简称MGR),于5.7.17版 ...
- 美团点评MySQL数据库高可用架构从MMM到MHA+Zebra以及MHA+Proxy的演进
本文介绍最近几年美团点评MySQL数据库高可用架构的演进过程,以及我们在开源技术基础上做的一些创新.同时,也和业界其它方案进行综合对比,了解业界在高可用方面的进展,和未来我们的一些规划和展望. MMM ...
- MySQL MGR+ Consul之数据库高可用方案
背景说明: 基于目前存在很多MySQL数据库单点故障,传统的MHA,PXC等方案用VIP或者DNS切换的方式可以实现.基于数据库的数据强一致性考虑,采用MGR集群,采用consul服务注册发现 ...
- 数据库高可用架构(MySQL、Oracle、MongoDB、Redis)
一.MySQL MySQL小型高可用架构 方案:MySQL双主.主从 + Keepalived主从自动切换 服务器资源:两台PC Server 优点:架构简单,节省资源 缺点:无法线性扩展,主从失 ...
- [转]数据库高可用架构(MySQL、Oracle、MongoDB、Redis)
一.MySQL MySQL小型高可用架构 方案:MySQL双主.主从 + Keepalived主从自动切换 服务器资源:两台PC Server 优点:架构简单,节省资源 缺点:无法线性扩展,主从失 ...
- 【mysql】Mgr实现数据库高可用架构
转载:https://www.cnblogs.com/luoahong/articles/8043035.html MGR简介 MySQL Group Replication(下简称:MGR)是MyS ...
- vivo 基于原生 RabbitMQ 的高可用架构实践
一.背景说明 vivo 在 2016 年引入 RabbitMQ,基于开源 RabbitMQ 进行扩展,向业务提供消息中间件服务. 2016~2018年,所有业务均使用一个集群,随着业务规模的增长,集群 ...
- mysql5.6基于主从复制的mmm高可用架构详解
MMM规划192.168.3.12 master192.168.3.13 slave1192.168.3.198 slave2 MMM部署步骤1.配置主主复制及主从同步集群2.安装主从节点所需要的支持 ...
随机推荐
- jQuery获取元素的方法
1·$(".box").offset().left 获取盒子左边到浏览器左侧的距离(上右下相同): 2·$("span").width() 获取盒子的宽度(高度 ...
- web应用/路由控制/视图函数/单表多表操作
一. 1.wen应用:BS架构的应用程序,B是浏览器,S:server(实现了wsgi协议)+ application https://www.cnblogs.com/liuqingzheng/art ...
- 如何安装ubuntu系统
https://www.cnblogs.com/Chinasf/archive/2010/05/06/1728840.html [Ubuntu 下挂ISO到虚拟光驱的方法] 各种方法参考如下论坛 ...
- SpringCloud-分布式配置中心(基于SpringBoot2.X)
一.配置中心的作用:可以将多种应用的配置进行集中式的管理,将这些配置统一存放到git或svn里面存储: 二.搭建SpringCloud-Config-Server 2.1如图后续步骤勾选Config ...
- WEB服务器搭建(Apache+Tomcat+eclipse)
1.下载xampp安装,选择Apache+MySQL+Tomcat 官方下载链接:https://www.apachefriends.org/zh_cn/download.html 2.下载安装jav ...
- Ubuntu安装pyenv实现python多版本控制
Ubuntu安装pyenv实现python多版本控制 git clone git://github.com/yyuu/pyenv.git ~/.pyenv echo 'export PYENV_ROO ...
- DOM对象和jQuery对象的转换
<script type="text/javascript"> //js的页面加载事件 window.onload = function () { //获取DOM对象 ...
- python读取文件时提示"UnicodeDecodeError: 'gbk' codec can't decode
解决办法1. FILE_OBJECT= open('order.log','r', encoding='UTF-8') 解决办法2. FILE_OBJECT= open('order.log','rb ...
- 性能测试---CPU内存部分
CPU内存的测试可以通过top命令来测试 ,如下是我写的bat脚本,其中的测试进程可以替换为你自己需要测试的进程. @echo offecho============================= ...
- 画PCB
1.AD16Design中Boardshape没有redefine board shape选项来修改板子的形状:{ 解决办法:在英文输入模式下按“1”键整个界面会变绿,此时就会有redefine bo ...