本文來源 | 雲+社區專欄文章
做者 | 萬守兵,騰訊雲資深架構師。8年以上大型互聯網公司運維工做經驗,騰訊雲資深遷雲架構師,一直從事大型互聯網服務端架構設計和優化工做。我的專一於雲計算、k8s和 DevOps領域。php
導讀:在企業實際生產環境中爲了可以給業務上層應用提供高可靠、低延遲、低數據損失的Redis緩存服務,本文經過對目前主流的幾種redis高可用方案進行對比分析,並基於騰訊雲CVM和HAVIP等基礎產品進行搭建、配置、測試、總結,供你們參考。前端
01環境說明redis
1.需求與目標docker
本文將經過對目前主流的幾種redis高可用方案進行對比分析,並基於騰訊雲CVM和HAVIP等基礎產品進行搭建、配置、測試、總結。vim
2.軟件版本緩存
redis用3.2.8版本,keepalived用1.2.19版本。bash
3.基本環境服務器
採用同一網絡內的三臺主機(能夠是物理主機、虛擬機或docker容器),要求三臺主機之間都能相互訪問。我這裏使用騰訊雲上3臺CVM,每臺CVM上開啓一個redis-server、redis-sentinel和keepalived服務,redis-server端口爲6379,redis-sentinel的端口爲26379(我這裏用默認端口,生產環境中能夠修改默認端口),3臺CVM上都安裝keepalived服務。微信
02幾種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/doc...
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,且網絡要能支持組播協議。
03安裝部署
方案一:sentinel高可用方案
1.首先下載安裝redis:(http://download.redis.io/rele...)
$ wget http://download.redis.io/rele...
$ 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
^
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
bind 0.0.0.0
protected-mode no
daemonize yes
pidfile /var/run/redis.pid
appendonly yes
● 修改從redis-server(172.16.2.2和172.16.2.15)配置文件內容以下:
port 6379
bind 0.0.0.0
protected-mode no
daemonize yes
pidfile /var/run/redis.pid
appendonly yes
slaveof 172.16.2.4 6379
● 啓動redis服務以前,建議先修改kernel參數,重啓生效
vim /etc/sysctl.conf
vm.overcommit_memory = 1
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主從關係已經創建
● 可能遇到的問題:redis主從創建失敗,有多是啓動服務時沒有指定正確的配置文件
● 解決思路:
(1)經過ps -ef查看服務進程ID
(2)經過lsof -p pid查看進程打開的文件,若是打開文件有誤,經過redis-server /etc/redis.conf指定正確的配置文件從新啓動服務
(8)搭建redis-sentinel系統:
redis-sentinel程序上面已經安裝過了,這裏只須要修改配置文件就能夠了。修改/etc/sentinel.conf以下:
● 三臺sentinel服務器配置都一致
port 26379
sentinel monitor mymaster 172.16.2.4 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 18000
sentinel auth-pass
sentinel notification-script
sentinel client-reconfig-script
● 常見問題:在172.16.2.4(主redis)上查看sentinel的信息,發現報錯
查看sentinel服務報錯
● 解決方案:修改/etc/sentinel.conf文件
protected-mode no
redis-sentinel sentinel.conf &
3.測試驗證:
(1)基本鏈接測試:
redis-cli -h host -p port info sentinel
三個redis-sentinel服務啓動完畢後,鏈接任意sentinel服務能夠獲知當前主redis服務信息,說明sentinel服務已經成功起來
查看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
查看sentinel信息發現master已經切換爲172.16.2.2
● 發現172.16.2.2這臺redis-server提高爲主:
redis-cli -h 172.16.2.2 -p 6379 info replication
在master上查看主從狀態
● 控制檯也輸出相關信息,表示主從切換成功。
控制檯log,redis主從切換成功
● 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.4
● 故障狀況下,VIP漂移指向172.16.2.2
master故障狀況下,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漂移操做。
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 #賦予腳本執行權限
腳本內容以下:
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
VIP成功綁定在master 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提高爲主。
sentinel自動failover,將172.16.2.15提高爲master
(3)經過訪問VIP鏈接查看redis sentinel信息和redis-server主從關係,發現VIP確實指向了172.16.2.15
經過VIP成功查看sentinel狀態
經過VIP成功查看redis的狀態
方案三: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/sof...
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啓動後日志以下圖顯示則表示啓動成功
keepalived日誌輸出
(2)用ip add查看VIP已經綁定到主redis上
查看vip地址已經綁定
5.測試驗證
模擬主redis故障時,redis的set、get和複製狀況
(1)停掉主Redis(172.16.2.4)上的redis-server服務
停掉主Redis(172.16.2.4)上的redis-server服務
(2)主Redis(172.16.2.4)上的VIP已經被移除
主Redis(172.16.2.4)上的VIP已經被移除
(3)查看Redis(172.16.2.15)上的Redis狀態已經切換成master
查看Redis(172.16.2.15)上的Redis狀態已經切換成master
(4)能夠看出,VIP已經漂移到新的redis master(172.16.2.15)上
能夠看出,VIP已經漂移到新的redis-server master了
(5)從sentinel進行redis主從切換,到VIP的漂移過程是須要時間的,用ping VIP來作測試,中斷時間大概須要幾秒左右,以下圖所示:
04總結
以上經過搭建、配置、驗證、測試,瞭解到幾種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/doc...。