基于腾讯云CVM自建高可用Redis实践
时间:2019-01-23 12:00 来源:cloud.tencent.com 作者:cloud.tencent.com
一、环境说明:
1.需求与目标:
在企业实际生产环境中为了能够给业务上层应用提供高可靠、低延迟、低数据损失的Redis缓存服务,本文通过对目前主流的几种redis高可用方案进行对比分析,并基于腾讯云CVM和HAVIP等基础产品进行搭建、配置、测试、总结,供大家参考。
2.软件版本:
redis用3.2.8版本,keepalived用1.2.19版本。
3.基本环境:
采用同一网络内的三台主机(可以是物理主机、虚拟机或docker容器),要求三台主机之间都能相互访问。我这里使用腾讯云上3台CVM,每台CVM上开启一个redis-server、redis-sentinel和keepalived服务,redis-server端口为6379,redis-sentinel的端口为26379(我这里用默认端口,生产环境中可以修改默认端口),3台CVM上都安装keepalived服务。
role
IP
port
redis-master1
172.16.2.4
6379
redis-master2
172.16.2.2
6379
redis-master3
172.16.2.15
6379
redis-sentinel1
172.16.2.4
26379
redis-sentinel2
172.16.2.2
26379
redis-sentinel3
172.16.2.15
23679
二、几种redis高可用方案说明:
1.一般的主从复制方案:
由于redis目前只支持主从复制备份(不支持主主复制),当主redis挂了,从redis只能提供读服务,无法提供写服务。所以,还得想办法,当主redis挂了,让从redis升级成为主redis。
优点:
(1)实现了对master数据的备份,一旦master出现故障,slave节点可以提升为新的master,顶替旧的master继续提供服务
(2)实现读扩展。使用主从复制架构, 一般都是为了实现读扩展。Master主要实现写功能, Slave实现读的功能
缺点:
(1)一旦主节点宕机,从节点晋升成主节点,同时需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,整个过程需要人工干预
此时需要经过如下操作(假设提升Slave1为Master):
● 在Slave1上执slaveof no one命令提升Slave1为新的Master节点。
● 在Slave1上配置为可写,这是因为大多数情况下,都将slave配置只读。
● 告诉Client端(也就是连接Redis的程序)新的Master节点的连接地址。
● 配置Slave2从新的Master进行数据复制。
(2)主节点的写能力受到单机的限制。
(3)主节点的存储能力受到单机的限制。
2.sentinel高可用方案:
客户端程序(如PHP程序)连接redis时需要ip和port,但redis-server进行故障转移时,主redis是变化的,所以ip地址也是变化的。客户端程序如何感知当前主redis的ip地址和端口呢?redis-sentinel提供了接口,请求任何一个sentinel,发送SENTINEL get-master-addr-by-name <master name>就能得到当前主redis的ip和port。需要注意的是,Redis Sentinel 端口和 Redis 主节点均需要开放访问权限。如果前端业务使用 Java,有 JedisSentinelPool 可以复用;如果前端业务使用 PHP,可以在 phpredis 的基础上做二次封装。
优点:
(1)redis sentinel带有自动故障转移功能(failover),当一个主redis不能提供服务时,redis sentinel可以将一个从redis升级为主redis,并对其他从redis进行配置,让它们使用新的主redis进行复制备份;
(2)服务探测故障及时;
(3)DBA 维护成本低。
缺点:
(1)对应用有入侵性:客户端每次连接redis前,先向sentinel发送请求,获得主redis的ip和port,然后用返回的ip和port连接redis。每次操作redis至少需要发送两次连接请求,第一次请求sentinel,第二次请求redis;
(2)Sentinel服务器和Redis节点需要开放访问权限。
3.redis-sentinel+VIP方案+自定义脚本方案:
底层是Redis Sentinel 集群,代理着 Redis 主从,Web端通过VIP提供服务。在部署Redis主从的时候,需要将虚拟IP绑定到当前的Redis 节点。当主节点发生故障,比如机器故障、Redis节点故障或者网络不可达,Sentinel 集群会调用 client-reconfig-script 配置的脚本,将VIP漂移到新的主节点上。
比如:当前redis系统中主redis的ip地址是172.16.2.4,那么VIP(172.16.2.250)指向172.16.2.4,客户端程序用VIP(172.16.2.250)地址连接redis,实际上连接的就是当前主redis,这样就避免了向sentinel发送请求。
优点:
(1)脚本自定义,架构可控;
(2)对应用透明,当主redis宕机,进行故障转移时,192.168.56.102这台服务器上的redis提升为主,这时VIP(172.16.2.4)指向192.168.56.102,这样客户端程序不需要修改任何代码,连接的是192.168.56.102这台主redis;
(3)秒级切换,在 5s 内完成整个切换操作.
缺点:
(1)使用VIP增加维护成本,存在IP混乱风险;
(2)需要自行编写VIP切换脚本,需要通过ip addr手动先在主redis上配置vip,配置相对复杂;
(3)Sentinel模式存在短时间的服务不可用;
(4)应用场景局限于内网,例如部分业务只能通过外网访问Redis时,该方案不可用
注意:
VIP方案对配置的环境有一定的要求,在腾讯云上搭建redis,需要用到腾讯云HAVIP,文档见:https://cloud.tencent.com/document/product/215/18025
4.redis-sentinel+keepalived方案:
keepalived通过vrrp_script检测当前主机上的redis-server是否以master状态运行,如果当前主机上的redis-server正在以master状态运行,则将vrrp_instance标记为存活状态,并分配VIP;如果当前主机上的redis-server正在以slave状态运行,则将vrrp_instance标记为错误状态。当某台主机宕机后,其他两台主机上的keepalived会将VIP切换到新的master(当前主机上的redis-server正在以master状态运行)上。
优点:
(1)相对redis-sentinel+VIP方案,不需编写VIP切换脚本,配置更简洁、清晰;
(2)对应用透明;
(3)秒级切换。
缺点:
(1)对网络环境有要求:keepalived的核心协议VRRP使用IP多播数据包进行封装,组地址为224.0.0.18,发布范围只限于同一局域网内,而且在网络不受自己控制时基本不能用,但是腾讯云是支持组播协议,可以使用keepalived;
(2)存在脑裂;
(3)Sentinel模式存在短时间的服务不可用
注意:
keepalived方案也需要一个VIP,且网络要能支持组播协议。
三、安装部署:
方案一:sentinel高可用方案
1.首先下载安装redis:(http://download.redis.io/releases/)
$ wget http://download.redis.io/releases/redis-3.2.8.tar.gz
$ tar -zxvf redis-3.2.8.tar.gz
$ cd redis-3.2.8
$ make (如果没有安装gcc会报错,所以强烈建议在make之前先yum install gcc先安装gcc)
make报错如下:(make是用来编译的,从Makefile中读取指令,安装到指定的位置)
make3: gcc: Command not found
make3: * net.o Error 127
make3: Leaving directory `/opt/redis-3.2.8/deps/hiredis'
make2: * hiredis Error 2
make2: Leaving directory `/opt/redis-3.2.8/deps'
make1: persist-settings Error 2 (ignored)
CC adlist.o
/bin/sh: cc: command not found
make1: * adlist.o Error 127
make1: Leaving directory `/opt/redis-3.2.8/src'
make: * all Error 2
安装gcc:
yum install gcc
继续编译:
make
Make报错如下:
make1: Entering directory `/opt/redis-3.2.8/src'
CC adlist.o
In file included from adlist.c:34:0:
zmalloc.h:50:31: fatal error: jemalloc/jemalloc.h: No such file or directory
#include <jemalloc/jemalloc.h>
^
compilation terminated.
make1: * adlist.o Error 1
make1: Leaving directory `/opt/redis-3.2.8/src'
make: * all Error 2
重新编译:
make MALLOC=libc
编译成功!!!
2.基本配置:
(1)make完后 redis-3.2.8目录下会出现编译后的redis服务程序redis-server,还有用于测试的客户端程序redis-cli,两个程序位于安装目录 src 目录下:
复制redis相关命令到/usr/sbin目录下,这样就可以直接执行这些命令,不用写全路径。
$ cd src
$ cp redis-cli redis-server redis-sentinel /usr/sbin/
(2)在redis目录下有redis.conf和sentinel.conf配置文件示例,将两个配置文件复制到/etc目录下(当然也可以在/etc/目录新建配置文件),然后修改配置文件
$ cp redis.conf sentinel.conf /etc/
(3)redis.conf 是一个默认的配置文件。我们可以根据需要修改配置文件
● 修改主redis-server(172.16.2.4)配置文件内容如下:
#不修改,使用默认端口
port 6379
#修改为0.0.0.0,可以从外部连接redis服务端
bind 0.0.0.0
#默认情况下redis运行在保护模式(这种模式下,访问不需要密码),但是这种模式只允许本地回路访问,这里改为no
protected-mode no
#默认情况下,redis不是在后台模式运行的,如果需要在后台进程运行,把该项的值更改为yes,默认为no
daemonize yes
#redis服务以后台进程运行的时候,Redis默认会把pid写入/var/run/redis.pid文件组
pidfile /var/run/redis.pid
#开启AOF持久化,默认是关闭的,RDB默认是开启的,但是AOF的优先级更高,启动时Redis 会优先载入 AOF 文件来恢复数据,与 RDB 相比,AOF 的实时性更好,因此已成为主流的持久化方案
#如果不希望丢掉任何一条数据的话就该用纯累加模式:一旦开启这个模式,Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件。
appendonly yes
● 修改从redis-server(172.16.2.2和172.16.2.15)配置文件内容如下:
#不修改,使用默认端口
port 6379
#修改为0.0.0.0,可以从外部连接redis服务端
bind 0.0.0.0
#默认情况下redis运行在保护模式(这种模式下,访问不需要密码),但是这种模式只允许本地回路访问,这里改为no
protected-mode no
#默认情况下,redis不是在后台模式运行的,如果需要在后台进程运行,把该项的值更改为yes,默认为no
daemonize yes
#redis服务以后台进程运行的时候,Redis默认会把pid写入/var/run/redis.pid文件组,改为yes
pidfile /var/run/redis.pid
#开启AOF持久化,默认是关闭的,RDB默认是开启的,但是AOF的优先级更高,启动时Redis 会优先载入 AOF 文件来恢复数据,与 RDB 相比,AOF 的实时性更好,因此已成为主流的持久化方案
#如果不希望丢掉任何一条数据的话就该用纯累加模式:一旦开启这个模式,Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件。
appendonly yes
#从redis比主redis多这一行,使用slaveof实现主从复制
slaveof 172.16.2.4 6379
● 启动redis服务之前,建议先修改kernel参数,重启生效
vim /etc/sysctl.conf
#表示内核允许分配所有的物理内存,而不管当前的内存状态如何
vm.overcommit_memory = 1
#定义了TCP全连接队列长度,默认128太小,启动redis服务会报错
net.core.somaxconn = 511
#使配置文件永久生效
sysctl -p
(4)启动redis-server服务:
$ redis-server & #加上‘&’号使redis以后台程序方式运行
或
$ redis-server /etc/redis.conf #通过指定配置文件启动,在生产环境中强烈建议使用这种方式启动服务
(5)停止:
使用客户端:
$ redis-cli shutdown
因为Redis可以妥善处理SIGTERM信号,所以直接kill -9也是可以的
$ kill -9 PID
(6)启动redis服务进程后,就可以使用测试客户端程序redis-cli和redis服务交互了,连接redis-server:
$ redis-cli #本地连接redis-server,如果要连接远程redis,redis-cli -h host -p port -a password
redis> set key1 value1
OK
redis> get key1
"value1"
(7)查看主从状态:
通过redis-cli 进入主redis命令行,执行info replication查看当前主从配置,可以发现两个从节点信息,表明redis-server主从已经配置完毕。
redis> INFO replication
● 可能遇到的问题:redis主从建立失败,有可能是启动服务时没有指定正确的配置文件
● 解决思路:
(1)通过ps -ef查看服务进程ID
(2)通过lsof -p pid查看进程打开的文件,如果打开文件有误,通过redis-server /etc/redis.conf指定正确的配置文件重新启动服务
(8)搭建redis-sentinel系统:
redis-sentinel程序上面已经安装过了,这里只需要修改配置文件就可以了。修改/etc/sentinel.conf如下:
● 三台sentinel服务器配置都一致
#当前Sentinel服务运行的端口
port 26379
#监控的master的名字叫做mymaster(自定义),地址为172.16.2.4:6379,行尾最后的一个2代表在sentinel集群中,多少个sentinel认为masters死了,才能真正认为该master不可用了
sentinel monitor mymaster 172.16.2.4 6379 2
#每个Sentinel节点都要定期PING命令来判断Redis数据节点和其余Sentinel节点是否可达,如果超过30秒且没有回复,则判定不可达
sentinel down-after-milliseconds mymaster 30000
#当Sentinel节点集合对主节点故障判定达成一致时,Sentinel领导者节点会做故障转移操作,选出新的主节点,原来的从节点会向新的主节点发起复制操作,限制每次向新的主节点发起复制操作的从节点个数为1,在从Redis实例较多的情况下这个数字越小,同步的时间越长,完成故障转移所需的时间就越长
sentinel parallel-syncs mymaster 1
#failover过期时间,当failover开始后,在此时间内仍然没有触发任何failover操作,当前sentinel将会认为此次failover失败。默认180秒,即3分钟。
sentinel failover-timeout mymaster 18000
#如果Sentinel监控的主节点配置了密码,可以通过sentinel auth-pass配置通过添加主节点的密码,防止Sentinel节点无法对主节点进行监控。
#例如:sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
sentinel auth-pass
#在故障转移期间,当一些警告级别的Sentinel事件发生(指重要事件,如主观下线,客观下线等)时,会触发对应路径的脚本,想脚本发送相应的事件参数。
# 例如:sentinel notification-script mymaster /var/redis/notify.sh
sentinel notification-script
#在故障转移结束后,触发应对路径的脚本,并向脚本发送故障转移结果的参数。
#例如:sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
sentinel client-reconfig-script
● 常见问题:在172.16.2.4(主redis)上查看sentinel的信息,发现报错
● 解决方案:修改/etc/sentinel.conf文件
#原因是sentinel没有指定bind和密码访问,所以被开启了protected-mode保护模式,拒绝其他sentinel的连接。导致进入了ODWON。在sentinel.conf里加入关闭保护,protected-mode no
protected-mode no
#指定文件启动sentinel服务
redis-sentinel sentinel.conf &
3.测试验证:
(1)基本连接测试:
redis-cli -h host -p port info sentinel
三个redis-sentinel服务启动完毕后,连接任意sentinel服务可以获知当前主redis服务信息,说明sentinel服务已经成功起来
(2)测试sentinel的failover故障切换功能:
● 把主redis(172.16.2.4)停掉
redis-cli -h 172.16.2.4 -p 6379 shutdown
● 查看redis-sentinel的监控状态:
redis-cli -h 172.16.2.4 -p 26379 info sentinel
● 发现172.16.2.2这台redis-server提升为主:
redis-cli -h 172.16.2.2 -p 6379 info replication
● 控制台也输出相关信息,表示主从切换成功。
● 172.16.2.2切换成主之后,也可以执行写操作了。至此,redis的sentinel方案已经搭建完成。
4.客户端使用方式:
客户端程序(如PHP程序)连接redis时需要ip和port,但redis-server进行故障转移时,主redis是变化的,所以ip地址也是变化的。客户端程序如何感知当前主redis的ip地址和端口呢?redis-sentinel提供了接口,请求任何一个sentinel,发送SENTINEL get-master-addr-by-name <master name>
就能得到当前主redis的ip和port。
● 连接到sentinel获取当前主redis的ip和端口(因为又执行了一次切换,这里的主已经切换到172.16.2.15,这里只是说明客户端的使用方式)
方案二:redis-sentinel+vip方案
1.方案说明
VIP方案是redis系统对外始终是同一ip地址,当redis主从进行故障转移时,需要做的是将VIP从之前的redis服务器漂移到现在新的主redis服务器上。
比如:当前redis系统中主redis的ip地址是172.16.2.4,那么VIP(172.16.2.250)指向172.16.2.4,客户端程序用VIP(172.16.2.250)地址连接redis,实际上连接的就是当前主redis,这样就避免了向sentinel发送请求。
● 正常情况下VIP指向172.16.2.4
● 故障情况下,VIP漂移指向172.16.2.2
2.基本配置:
那么现在的问题是,如何在进行redis故障转移时,将VIP漂移到新的主redis服务器上。在方案一的配置基础之上增加对sentinel.conf的配置,具体配置如下:
(1)在sentinel.conf配置文件设置要执行的vip漂移的脚本
使用sentinel.conf配置文件的有一个参数client-reconfig-script,这个参数配置执行脚本,sentinel在做failover的时候会执行这个脚本,并且传递6个参数<master-name>、 <role>、 <state>、 <from-ip>、 <from-port>、 <to-ip> 、<to-port>,
其中<to-ip>是新主redis的IP地址,可以在这个脚本里做VIP漂移操作。
#修改三个服务器的redis-sentinel配置文件/etc/sentinel.conf,增加下面一行。
vi /etc/sentinel.conf
sentinel client-reconfig-script mymaster /opt/notify_mymaster.sh
(2)创建VIP漂移脚本(VIP用之前在腾讯云控制台上申请的VIP)
然后在/opt/目录下创建notify_mymaster.sh脚本文件,这个脚本做VIP漂移操作。
chmod 777 notify_mymaster.sh #赋予脚本执行权限
脚本内容如下:
#notify_mymaster.sh脚本内容
#!/bin/bash
MASTER_IP=$6 #第六个参数是新主redis的ip地址
LOCAL_IP='172.16.2.2' #其他两个服务器上为172.16.2.4,172.16.2.15
VIP='172.16.2.250'
NETMASK='24'
INTERFACE='eth0'
if [ ${MASTER_IP} = ${LOCAL_IP} ];then
sudo /sbin/ip addr add ${VIP}/${NETMASK} dev ${INTERFACE} #将VIP绑定到该服务器上
sudo /sbin/arping -q -c 3 -A ${VIP} -I ${INTERFACE}
exit 0
else
sudo /sbin/ip addr del ${VIP}/${NETMASK} dev ${INTERFACE} #将VIP从该服务器上删除
exit 0
fi
exit 1 #如果返回1,sentinel会一直执行这个脚本
(3)第一次需在主redis上手工设置VIP
只需要第一次手工在主redis上设置vip,现在当前主redis是172.16.2.2,需要手动绑定VIP到该服务器上。(注意强烈建议加sudo执行
)
sudo /sbin/ip addr add 172.16.2.250/24 dev eth0
sudo /sbin/arping -q -c 3 -A 172.16.2.250 -I eth0
3.测试验证**
(1)配置完成之后,去另一个服务器上(172.16.2.15)通过VIP地址连接redis-server和redis-sentinel。从上面可以看到主redis是172.16.2.2
(2)验证:下面关闭这台主redis服务(172.16.2.2),看看VIP是否漂移到另一台服务器上
redis-cli -h 172.16.2.2 -p 6379 shutdown
通过查询sentinel发现172.16.2.15提升为主。
(3)通过访问VIP连接查看redis sentinel信息和redis-server主从关系,发现VIP确实指向了172.16.2.15
方案三:redis-sentinel+keepalived方案
1.方案说明
VIP方案是通过sentinel服务在做redis主从切换的时候,通过配置文件sentinel.conf中的一个参数client-reconfig-script来执行相应的脚本,这种方式需要自己编写脚本。而keepalived方案通过vrrp_script检测当前主机上的redis-server是否以master状态运行,如果当前主机上的redis-server正在以master状态运行,则将vrrp_instance标记为存活状态,并分配VIP;如果当前主机上的redis-server正在以slave状态运行,则将vrrp_instance标记为错误状态。当某台主机宕机后,其他两台主机上的keepalived会将VIP切换到新的master(当前主机上的redis-server正在以master状态运行)上。三台CVM都需要安装keepalived组件。
2.在VIP方案基础之上安装keepalived组件(3台CVM都需要安装)
wget -c http://www.keepalived.org/software/keepalived-1.2.19.tar.gz
tar zxf keepalived-1.2.19.tar.gz
cd keepalived-1.2.19
./configure --prefix=/usr/local/keepalived
make
make install
3.基本配置
(1)编译安装keepalived之后,做初始化
cp /usr/local/keepalived/sbin/keepalived /usr/sbin/
cp /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/
cp /usr/local/keepalived/etc/rc.d/init.d/keepalived /etc/init.d/
cd /etc/init.d/
chkconfig --add keepalived
chkconfig keepalived on
mkdir -p /etc/keepalived
(2)关于keepalived的配置文件
keepalived的配置文件默认是没有的,当然sample&example文件还是有的,通常在PREFIX/etc/sample目录下。
keepalived master和backup(backups)之间不同的是:
1.优先级的不同,master的优先级priority的数字要高一些,我这里主redis上设置为100,从redis上设置为99;
2.global_defs段的router_id都不一样,实际中可以用任意名字区分也可以用主机名区分;
3.backup的配置文件中还有一个nopreempt字段,意思是设置为非抢占模式,作用是让master优先获取到VIP,并保证VIP是在原先的master上。
● 主redis172.16.2.4上的配置:
vim /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
notification_email { #指定keepalived在发生切换时需要发送email到的对象,一行一个
root@localhost
}
notification_email_from keepalived@localhost #指定发件人
smtp_server 127.0.0.1 #指定smtp服务器地址
smtp_connect_timeout 10 #指定smtp连接超时时间
router_id keepalivedha_1 #运行keepalived机器的一个标识
}
vrrp_script chk_http_port { #执行的脚本
script "redis-cli info | grep role:master >/dev/null 2>&1"
interval 1
timeout 2
fall 2
rise 1
}
vrrp_sync_group VG_1 { #监控多个网段的实例
group {
VI_1 #实例名
}
}
vrrp_instance VI_1 {
state BACKUP
interface eth0 #设置实例绑定的网卡
#use_vmac keepalived
#vmac_xmit_base
mcast_src_ip 172.16.2.4
smtp_alert
virtual_router_id 20 #路由器标识,MASTER和BACKUP必须是一致的
priority 100 #优先级,高优先级竞选为master
advert_int 1
authentication { #设置认证
auth_type PASS #认证方式
auth_pass password #认证密码
}
virtual_ipaddress { #设置vip
172.16.2.250
}
track_script { #监测的对象
chk_http_port
}
}
● 从redis172.16.2.2上的配置:
! Configuration File for keepalived
global_defs {
notification_email {
root@localhost
}
notification_email_from keepalived@localhost
smtp_server 127.0.0.1
smtp_connect_timeout 10
router_id keepalivedha_2
}
vrrp_script chk_http_port {
script "redis-cli info | grep role:master >/dev/null 2>&1"
interval 1
timeout 2
fall 2
rise 1
}
vrrp_sync_group VG_1 {
group {
VI_1
}
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
#use_vmac keepalived
#vmac_xmit_base
mcast_src_ip 172.16.2.2
smtp_alert
virtual_router_id 20
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass password
}
virtual_ipaddress {
172.16.2.250
}
track_script {
chk_http_port
}
nopreempt
}
● 从redis172.16.2.15上的配置:
! Configuration File for keepalived
global_defs {
notification_email {
root@localhost
}
notification_email_from keepalived@localhost
smtp_server 127.0.0.1
smtp_connect_timeout 10
router_id keepalivedha_3
}
vrrp_script chk_http_port {
script "redis-cli info | grep role:master >/dev/null 2>&1"
interval 1
timeout 2
fall 2
rise 1
}
vrrp_sync_group VG_1 {
group {
VI_1
}
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
#use_vmac keepalived
#vmac_xmit_base
mcast_src_ip 172.16.2.15
smtp_alert
virtual_router_id 20
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass password
}
virtual_ipaddress {
172.16.2.250
}
track_script {
chk_http_port
}
nopreempt
}
4.启动keepalived
service keepalived start
tail /var/log/messages
(1)如果在主redis上keepalived启动后日志如下图显示则表示启动成功
(2)用ip add查看VIP已经绑定到主redis上
5.测试验证
模拟主redis故障时,redis的set、get和复制情况
(1)停掉主Redis(172.16.2.4)上的redis-server服务
(2)主Redis(172.16.2.4)上的VIP已经被移除
(3)查看Redis(172.16.2.15)上的Redis状态已经切换成master
(4)可以看出,VIP已经漂移到新的redis master(172.16.2.15)上
(5)从sentinel进行redis主从切换,到VIP的漂移过程是需要时间的,用ping VIP来做测试,中断时间大概需要几秒左右,如下图所示:
四、总结:
以上通过搭建、配置、验证、测试,了解到几种redis高可用方案各有优缺点,如果网络环境能够支持组播协议,建议采用redis-sentinel+keepalived方案,这种方案配置更简单;如果网络环境不支持组播协议,可以使用redis-sentinel+VIP方案;如果业务代码上能够接受在每次操作redis之前都先额外进行一次sentinel查询操作,就可以采用sentinel方案。
以下是实战过程中总结出的最佳实践:
(1)Redis Sentinel 集群建议使用 >= 5 台机器;
(2)不同的大业务可以使用一套 Redis Sentinel 集群,代理该业务下的所有端口;
(3)根据不同的业务划分好 Redis 端口范围;
(4)自定义脚本建议采用 Python 实现,扩展便利;
(5)自定义脚本传入参数:<service_name> <role> <comment> <from_ip> <from_port> <to_ip> <to_port>
;
(6)自定义脚本需要远程 ssh 操作机器,建议使用 paramiko 库,避免重复建立 SSH 连接,消耗时间;
(7)加速 SSH 连接,建议关闭以下两个参数:
UseDNS no
GSSAPIAuthentication no
(8)微信或者邮件告警,建议 fork 一个进程,避免主进程阻塞;
(9)自动切换和故障切换,所有操作建议在 15s 以内完成。
以上几种方案都是针对单个redis实例的高可用,比较适合中小型业务的应用。如果业务数据量比较大,并发量比较高的情况下,建议搭建redis集群,比如官方redis cluster和开源的codis方案,或者使用腾讯云PAAS层redis集群方案,文档说明见:https://cloud.tencent.com/document/product/239。
(责任编辑:IT)
一、环境说明:1.需求与目标:在企业实际生产环境中为了能够给业务上层应用提供高可靠、低延迟、低数据损失的Redis缓存服务,本文通过对目前主流的几种redis高可用方案进行对比分析,并基于腾讯云CVM和HAVIP等基础产品进行搭建、配置、测试、总结,供大家参考。 2.软件版本:redis用3.2.8版本,keepalived用1.2.19版本。 3.基本环境:采用同一网络内的三台主机(可以是物理主机、虚拟机或docker容器),要求三台主机之间都能相互访问。我这里使用腾讯云上3台CVM,每台CVM上开启一个redis-server、redis-sentinel和keepalived服务,redis-server端口为6379,redis-sentinel的端口为26379(我这里用默认端口,生产环境中可以修改默认端口),3台CVM上都安装keepalived服务。
二、几种redis高可用方案说明:1.一般的主从复制方案:由于redis目前只支持主从复制备份(不支持主主复制),当主redis挂了,从redis只能提供读服务,无法提供写服务。所以,还得想办法,当主redis挂了,让从redis升级成为主redis。 优点: (1)实现了对master数据的备份,一旦master出现故障,slave节点可以提升为新的master,顶替旧的master继续提供服务 (2)实现读扩展。使用主从复制架构, 一般都是为了实现读扩展。Master主要实现写功能, Slave实现读的功能 缺点: (1)一旦主节点宕机,从节点晋升成主节点,同时需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,整个过程需要人工干预 此时需要经过如下操作(假设提升Slave1为Master): ● 在Slave1上执slaveof no one命令提升Slave1为新的Master节点。 ● 在Slave1上配置为可写,这是因为大多数情况下,都将slave配置只读。 ● 告诉Client端(也就是连接Redis的程序)新的Master节点的连接地址。 ● 配置Slave2从新的Master进行数据复制。 (2)主节点的写能力受到单机的限制。 (3)主节点的存储能力受到单机的限制。 2.sentinel高可用方案:客户端程序(如PHP程序)连接redis时需要ip和port,但redis-server进行故障转移时,主redis是变化的,所以ip地址也是变化的。客户端程序如何感知当前主redis的ip地址和端口呢?redis-sentinel提供了接口,请求任何一个sentinel,发送SENTINEL get-master-addr-by-name <master name>就能得到当前主redis的ip和port。需要注意的是,Redis Sentinel 端口和 Redis 主节点均需要开放访问权限。如果前端业务使用 Java,有 JedisSentinelPool 可以复用;如果前端业务使用 PHP,可以在 phpredis 的基础上做二次封装。 优点: (1)redis sentinel带有自动故障转移功能(failover),当一个主redis不能提供服务时,redis sentinel可以将一个从redis升级为主redis,并对其他从redis进行配置,让它们使用新的主redis进行复制备份; (2)服务探测故障及时; (3)DBA 维护成本低。 缺点: (1)对应用有入侵性:客户端每次连接redis前,先向sentinel发送请求,获得主redis的ip和port,然后用返回的ip和port连接redis。每次操作redis至少需要发送两次连接请求,第一次请求sentinel,第二次请求redis; (2)Sentinel服务器和Redis节点需要开放访问权限。 3.redis-sentinel+VIP方案+自定义脚本方案:底层是Redis Sentinel 集群,代理着 Redis 主从,Web端通过VIP提供服务。在部署Redis主从的时候,需要将虚拟IP绑定到当前的Redis 节点。当主节点发生故障,比如机器故障、Redis节点故障或者网络不可达,Sentinel 集群会调用 client-reconfig-script 配置的脚本,将VIP漂移到新的主节点上。 比如:当前redis系统中主redis的ip地址是172.16.2.4,那么VIP(172.16.2.250)指向172.16.2.4,客户端程序用VIP(172.16.2.250)地址连接redis,实际上连接的就是当前主redis,这样就避免了向sentinel发送请求。 优点: (1)脚本自定义,架构可控; (2)对应用透明,当主redis宕机,进行故障转移时,192.168.56.102这台服务器上的redis提升为主,这时VIP(172.16.2.4)指向192.168.56.102,这样客户端程序不需要修改任何代码,连接的是192.168.56.102这台主redis; (3)秒级切换,在 5s 内完成整个切换操作. 缺点: (1)使用VIP增加维护成本,存在IP混乱风险; (2)需要自行编写VIP切换脚本,需要通过ip addr手动先在主redis上配置vip,配置相对复杂; (3)Sentinel模式存在短时间的服务不可用; (4)应用场景局限于内网,例如部分业务只能通过外网访问Redis时,该方案不可用 注意: VIP方案对配置的环境有一定的要求,在腾讯云上搭建redis,需要用到腾讯云HAVIP,文档见:https://cloud.tencent.com/document/product/215/18025 4.redis-sentinel+keepalived方案:keepalived通过vrrp_script检测当前主机上的redis-server是否以master状态运行,如果当前主机上的redis-server正在以master状态运行,则将vrrp_instance标记为存活状态,并分配VIP;如果当前主机上的redis-server正在以slave状态运行,则将vrrp_instance标记为错误状态。当某台主机宕机后,其他两台主机上的keepalived会将VIP切换到新的master(当前主机上的redis-server正在以master状态运行)上。 优点: (1)相对redis-sentinel+VIP方案,不需编写VIP切换脚本,配置更简洁、清晰; (2)对应用透明; (3)秒级切换。 缺点: (1)对网络环境有要求:keepalived的核心协议VRRP使用IP多播数据包进行封装,组地址为224.0.0.18,发布范围只限于同一局域网内,而且在网络不受自己控制时基本不能用,但是腾讯云是支持组播协议,可以使用keepalived; (2)存在脑裂; (3)Sentinel模式存在短时间的服务不可用 注意: keepalived方案也需要一个VIP,且网络要能支持组播协议。 三、安装部署:方案一:sentinel高可用方案1.首先下载安装redis:(http://download.redis.io/releases/)$ wget http://download.redis.io/releases/redis-3.2.8.tar.gz $ tar -zxvf redis-3.2.8.tar.gz $ cd redis-3.2.8 $ make (如果没有安装gcc会报错,所以强烈建议在make之前先yum install gcc先安装gcc) make报错如下:(make是用来编译的,从Makefile中读取指令,安装到指定的位置)
安装gcc: yum install gcc 继续编译: make Make报错如下:
重新编译:
make MALLOC=libc
编译成功!!! 2.基本配置:
复制redis相关命令到/usr/sbin目录下,这样就可以直接执行这些命令,不用写全路径。 $ cd src $ cp redis-cli redis-server redis-sentinel /usr/sbin/
$ cp redis.conf sentinel.conf /etc/
● 修改主redis-server(172.16.2.4)配置文件内容如下: #不修改,使用默认端口 port 6379 #修改为0.0.0.0,可以从外部连接redis服务端 bind 0.0.0.0 #默认情况下redis运行在保护模式(这种模式下,访问不需要密码),但是这种模式只允许本地回路访问,这里改为no protected-mode no #默认情况下,redis不是在后台模式运行的,如果需要在后台进程运行,把该项的值更改为yes,默认为no daemonize yes #redis服务以后台进程运行的时候,Redis默认会把pid写入/var/run/redis.pid文件组 pidfile /var/run/redis.pid #开启AOF持久化,默认是关闭的,RDB默认是开启的,但是AOF的优先级更高,启动时Redis 会优先载入 AOF 文件来恢复数据,与 RDB 相比,AOF 的实时性更好,因此已成为主流的持久化方案 #如果不希望丢掉任何一条数据的话就该用纯累加模式:一旦开启这个模式,Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件。 appendonly yes ● 修改从redis-server(172.16.2.2和172.16.2.15)配置文件内容如下: #不修改,使用默认端口 port 6379 #修改为0.0.0.0,可以从外部连接redis服务端 bind 0.0.0.0 #默认情况下redis运行在保护模式(这种模式下,访问不需要密码),但是这种模式只允许本地回路访问,这里改为no protected-mode no #默认情况下,redis不是在后台模式运行的,如果需要在后台进程运行,把该项的值更改为yes,默认为no daemonize yes #redis服务以后台进程运行的时候,Redis默认会把pid写入/var/run/redis.pid文件组,改为yes pidfile /var/run/redis.pid #开启AOF持久化,默认是关闭的,RDB默认是开启的,但是AOF的优先级更高,启动时Redis 会优先载入 AOF 文件来恢复数据,与 RDB 相比,AOF 的实时性更好,因此已成为主流的持久化方案 #如果不希望丢掉任何一条数据的话就该用纯累加模式:一旦开启这个模式,Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件。 appendonly yes #从redis比主redis多这一行,使用slaveof实现主从复制 slaveof 172.16.2.4 6379 ● 启动redis服务之前,建议先修改kernel参数,重启生效 vim /etc/sysctl.conf #表示内核允许分配所有的物理内存,而不管当前的内存状态如何 vm.overcommit_memory = 1 #定义了TCP全连接队列长度,默认128太小,启动redis服务会报错 net.core.somaxconn = 511
#使配置文件永久生效
sysctl -p
$ redis-server & #加上‘&’号使redis以后台程序方式运行 或 $ redis-server /etc/redis.conf #通过指定配置文件启动,在生产环境中强烈建议使用这种方式启动服务
使用客户端:
$ redis-cli shutdown
因为Redis可以妥善处理SIGTERM信号,所以直接kill -9也是可以的 $ kill -9 PID
$ redis-cli #本地连接redis-server,如果要连接远程redis,redis-cli -h host -p port -a password redis> set key1 value1 OK redis> get key1 "value1"
通过redis-cli 进入主redis命令行,执行info replication查看当前主从配置,可以发现两个从节点信息,表明redis-server主从已经配置完毕。
redis> INFO replication
● 可能遇到的问题:redis主从建立失败,有可能是启动服务时没有指定正确的配置文件 ● 解决思路: (1)通过ps -ef查看服务进程ID (2)通过lsof -p pid查看进程打开的文件,如果打开文件有误,通过redis-server /etc/redis.conf指定正确的配置文件重新启动服务
redis-sentinel程序上面已经安装过了,这里只需要修改配置文件就可以了。修改/etc/sentinel.conf如下: ● 三台sentinel服务器配置都一致 #当前Sentinel服务运行的端口 port 26379 #监控的master的名字叫做mymaster(自定义),地址为172.16.2.4:6379,行尾最后的一个2代表在sentinel集群中,多少个sentinel认为masters死了,才能真正认为该master不可用了 sentinel monitor mymaster 172.16.2.4 6379 2 #每个Sentinel节点都要定期PING命令来判断Redis数据节点和其余Sentinel节点是否可达,如果超过30秒且没有回复,则判定不可达 sentinel down-after-milliseconds mymaster 30000 #当Sentinel节点集合对主节点故障判定达成一致时,Sentinel领导者节点会做故障转移操作,选出新的主节点,原来的从节点会向新的主节点发起复制操作,限制每次向新的主节点发起复制操作的从节点个数为1,在从Redis实例较多的情况下这个数字越小,同步的时间越长,完成故障转移所需的时间就越长 sentinel parallel-syncs mymaster 1 #failover过期时间,当failover开始后,在此时间内仍然没有触发任何failover操作,当前sentinel将会认为此次failover失败。默认180秒,即3分钟。 sentinel failover-timeout mymaster 18000 #如果Sentinel监控的主节点配置了密码,可以通过sentinel auth-pass配置通过添加主节点的密码,防止Sentinel节点无法对主节点进行监控。 #例如:sentinel auth-pass mymaster MySUPER--secret-0123passw0rd sentinel auth-pass #在故障转移期间,当一些警告级别的Sentinel事件发生(指重要事件,如主观下线,客观下线等)时,会触发对应路径的脚本,想脚本发送相应的事件参数。 # 例如:sentinel notification-script mymaster /var/redis/notify.sh sentinel notification-script #在故障转移结束后,触发应对路径的脚本,并向脚本发送故障转移结果的参数。 #例如:sentinel client-reconfig-script mymaster /var/redis/reconfig.sh sentinel client-reconfig-script ● 常见问题:在172.16.2.4(主redis)上查看sentinel的信息,发现报错 ● 解决方案:修改/etc/sentinel.conf文件 #原因是sentinel没有指定bind和密码访问,所以被开启了protected-mode保护模式,拒绝其他sentinel的连接。导致进入了ODWON。在sentinel.conf里加入关闭保护,protected-mode no protected-mode no #指定文件启动sentinel服务 redis-sentinel sentinel.conf & 3.测试验证:
redis-cli -h host -p port info sentinel 三个redis-sentinel服务启动完毕后,连接任意sentinel服务可以获知当前主redis服务信息,说明sentinel服务已经成功起来
● 把主redis(172.16.2.4)停掉 redis-cli -h 172.16.2.4 -p 6379 shutdown ● 查看redis-sentinel的监控状态: redis-cli -h 172.16.2.4 -p 26379 info sentinel ● 发现172.16.2.2这台redis-server提升为主: redis-cli -h 172.16.2.2 -p 6379 info replication ● 控制台也输出相关信息,表示主从切换成功。 ● 172.16.2.2切换成主之后,也可以执行写操作了。至此,redis的sentinel方案已经搭建完成。 4.客户端使用方式:
客户端程序(如PHP程序)连接redis时需要ip和port,但redis-server进行故障转移时,主redis是变化的,所以ip地址也是变化的。客户端程序如何感知当前主redis的ip地址和端口呢?redis-sentinel提供了接口,请求任何一个sentinel,发送SENTINEL get-master-addr-by-name ● 连接到sentinel获取当前主redis的ip和端口(因为又执行了一次切换,这里的主已经切换到172.16.2.15,这里只是说明客户端的使用方式) 方案二:redis-sentinel+vip方案1.方案说明VIP方案是redis系统对外始终是同一ip地址,当redis主从进行故障转移时,需要做的是将VIP从之前的redis服务器漂移到现在新的主redis服务器上。 比如:当前redis系统中主redis的ip地址是172.16.2.4,那么VIP(172.16.2.250)指向172.16.2.4,客户端程序用VIP(172.16.2.250)地址连接redis,实际上连接的就是当前主redis,这样就避免了向sentinel发送请求。 ● 正常情况下VIP指向172.16.2.4 ● 故障情况下,VIP漂移指向172.16.2.2 2.基本配置:那么现在的问题是,如何在进行redis故障转移时,将VIP漂移到新的主redis服务器上。在方案一的配置基础之上增加对sentinel.conf的配置,具体配置如下:
使用sentinel.conf配置文件的有一个参数client-reconfig-script,这个参数配置执行脚本,sentinel在做failover的时候会执行这个脚本,并且传递6个参数 #修改三个服务器的redis-sentinel配置文件/etc/sentinel.conf,增加下面一行。 vi /etc/sentinel.conf sentinel client-reconfig-script mymaster /opt/notify_mymaster.sh
然后在/opt/目录下创建notify_mymaster.sh脚本文件,这个脚本做VIP漂移操作。 chmod 777 notify_mymaster.sh #赋予脚本执行权限 脚本内容如下: #notify_mymaster.sh脚本内容 #!/bin/bash MASTER_IP=$6 #第六个参数是新主redis的ip地址 LOCAL_IP='172.16.2.2' #其他两个服务器上为172.16.2.4,172.16.2.15 VIP='172.16.2.250' NETMASK='24' INTERFACE='eth0' if [ ${MASTER_IP} = ${LOCAL_IP} ];then sudo /sbin/ip addr add ${VIP}/${NETMASK} dev ${INTERFACE} #将VIP绑定到该服务器上 sudo /sbin/arping -q -c 3 -A ${VIP} -I ${INTERFACE} exit 0 else sudo /sbin/ip addr del ${VIP}/${NETMASK} dev ${INTERFACE} #将VIP从该服务器上删除 exit 0 fi exit 1 #如果返回1,sentinel会一直执行这个脚本
只需要第一次手工在主redis上设置vip,现在当前主redis是172.16.2.2,需要手动绑定VIP到该服务器上。( sudo /sbin/ip addr add 172.16.2.250/24 dev eth0 sudo /sbin/arping -q -c 3 -A 172.16.2.250 -I eth0 3.测试验证**
redis-cli -h 172.16.2.2 -p 6379 shutdown 通过查询sentinel发现172.16.2.15提升为主。
方案三:redis-sentinel+keepalived方案1.方案说明VIP方案是通过sentinel服务在做redis主从切换的时候,通过配置文件sentinel.conf中的一个参数client-reconfig-script来执行相应的脚本,这种方式需要自己编写脚本。而keepalived方案通过vrrp_script检测当前主机上的redis-server是否以master状态运行,如果当前主机上的redis-server正在以master状态运行,则将vrrp_instance标记为存活状态,并分配VIP;如果当前主机上的redis-server正在以slave状态运行,则将vrrp_instance标记为错误状态。当某台主机宕机后,其他两台主机上的keepalived会将VIP切换到新的master(当前主机上的redis-server正在以master状态运行)上。三台CVM都需要安装keepalived组件。 2.在VIP方案基础之上安装keepalived组件(3台CVM都需要安装)wget -c http://www.keepalived.org/software/keepalived-1.2.19.tar.gz tar zxf keepalived-1.2.19.tar.gz cd keepalived-1.2.19 ./configure --prefix=/usr/local/keepalived make make install 3.基本配置
cp /usr/local/keepalived/sbin/keepalived /usr/sbin/ cp /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/ cp /usr/local/keepalived/etc/rc.d/init.d/keepalived /etc/init.d/ cd /etc/init.d/ chkconfig --add keepalived chkconfig keepalived on mkdir -p /etc/keepalived
keepalived的配置文件默认是没有的,当然sample&example文件还是有的,通常在PREFIX/etc/sample目录下。 keepalived master和backup(backups)之间不同的是: 1.优先级的不同,master的优先级priority的数字要高一些,我这里主redis上设置为100,从redis上设置为99; 2.global_defs段的router_id都不一样,实际中可以用任意名字区分也可以用主机名区分; 3.backup的配置文件中还有一个nopreempt字段,意思是设置为非抢占模式,作用是让master优先获取到VIP,并保证VIP是在原先的master上。 ● 主redis172.16.2.4上的配置: vim /etc/keepalived/keepalived.conf ! Configuration File for keepalived global_defs { notification_email { #指定keepalived在发生切换时需要发送email到的对象,一行一个 root@localhost } notification_email_from keepalived@localhost #指定发件人 smtp_server 127.0.0.1 #指定smtp服务器地址 smtp_connect_timeout 10 #指定smtp连接超时时间 router_id keepalivedha_1 #运行keepalived机器的一个标识 } vrrp_script chk_http_port { #执行的脚本 script "redis-cli info | grep role:master >/dev/null 2>&1" interval 1 timeout 2 fall 2 rise 1 } vrrp_sync_group VG_1 { #监控多个网段的实例 group { VI_1 #实例名 } } vrrp_instance VI_1 { state BACKUP interface eth0 #设置实例绑定的网卡 #use_vmac keepalived #vmac_xmit_base mcast_src_ip 172.16.2.4 smtp_alert virtual_router_id 20 #路由器标识,MASTER和BACKUP必须是一致的 priority 100 #优先级,高优先级竞选为master advert_int 1 authentication { #设置认证 auth_type PASS #认证方式 auth_pass password #认证密码 } virtual_ipaddress { #设置vip 172.16.2.250 } track_script { #监测的对象 chk_http_port } } ● 从redis172.16.2.2上的配置: ! Configuration File for keepalived global_defs { notification_email { root@localhost } notification_email_from keepalived@localhost smtp_server 127.0.0.1 smtp_connect_timeout 10 router_id keepalivedha_2 } vrrp_script chk_http_port { script "redis-cli info | grep role:master >/dev/null 2>&1" interval 1 timeout 2 fall 2 rise 1 } vrrp_sync_group VG_1 { group { VI_1 } } vrrp_instance VI_1 { state BACKUP interface eth0 #use_vmac keepalived #vmac_xmit_base mcast_src_ip 172.16.2.2 smtp_alert virtual_router_id 20 priority 99 advert_int 1 authentication { auth_type PASS auth_pass password } virtual_ipaddress { 172.16.2.250 } track_script { chk_http_port } nopreempt } ● 从redis172.16.2.15上的配置: ! Configuration File for keepalived global_defs { notification_email { root@localhost } notification_email_from keepalived@localhost smtp_server 127.0.0.1 smtp_connect_timeout 10 router_id keepalivedha_3 } vrrp_script chk_http_port { script "redis-cli info | grep role:master >/dev/null 2>&1" interval 1 timeout 2 fall 2 rise 1 } vrrp_sync_group VG_1 { group { VI_1 } } vrrp_instance VI_1 { state BACKUP interface eth0 #use_vmac keepalived #vmac_xmit_base mcast_src_ip 172.16.2.15 smtp_alert virtual_router_id 20 priority 99 advert_int 1 authentication { auth_type PASS auth_pass password } virtual_ipaddress { 172.16.2.250 } track_script { chk_http_port } nopreempt } 4.启动keepalivedservice keepalived start tail /var/log/messages
5.测试验证模拟主redis故障时,redis的set、get和复制情况
四、总结:以上通过搭建、配置、验证、测试,了解到几种redis高可用方案各有优缺点,如果网络环境能够支持组播协议,建议采用redis-sentinel+keepalived方案,这种方案配置更简单;如果网络环境不支持组播协议,可以使用redis-sentinel+VIP方案;如果业务代码上能够接受在每次操作redis之前都先额外进行一次sentinel查询操作,就可以采用sentinel方案。 以下是实战过程中总结出的最佳实践: (1)Redis Sentinel 集群建议使用 >= 5 台机器; (2)不同的大业务可以使用一套 Redis Sentinel 集群,代理该业务下的所有端口; (3)根据不同的业务划分好 Redis 端口范围; (4)自定义脚本建议采用 Python 实现,扩展便利;
(5)自定义脚本传入参数: (6)自定义脚本需要远程 ssh 操作机器,建议使用 paramiko 库,避免重复建立 SSH 连接,消耗时间; (7)加速 SSH 连接,建议关闭以下两个参数: UseDNS no GSSAPIAuthentication no (8)微信或者邮件告警,建议 fork 一个进程,避免主进程阻塞; (9)自动切换和故障切换,所有操作建议在 15s 以内完成。 以上几种方案都是针对单个redis实例的高可用,比较适合中小型业务的应用。如果业务数据量比较大,并发量比较高的情况下,建议搭建redis集群,比如官方redis cluster和开源的codis方案,或者使用腾讯云PAAS层redis集群方案,文档说明见:https://cloud.tencent.com/document/product/239。 (责任编辑:IT) |