一、前言
1、哨兵的介紹
sentinal,中文名是哨兵 哨兵是redis集群架構中非常重要的一個組件,主要功能如下 (1)集群監控,負責監控redis master和slave進程是否正常工作 (2)消息通知,如果某個redis實例有故障,那么哨兵負責發送消息作為報警通知給管理員 (3)故障轉移,如果master node掛掉了,會自動轉移到slave node上 (4)配置中心,如果故障轉移發生了,通知client客戶端新的master地址 哨兵本身也是分布式的,作為一個哨兵集群去運行,互相協同工作 (1)故障轉移時,判斷一個master node是宕機了,需要大部分的哨兵都同意才行,涉及到了分布式選舉的問題 (2)即使部分哨兵節點掛掉了,哨兵集群還是能正常工作的,因為如果一個作為高可用機制重要組成部分的故障轉移系統本身是單點的,那就很坑爹了 目前采用的是sentinal 2版本,sentinal 2相對於sentinal 1來說,重寫了很多代碼,主要是讓故障轉移的機制和算法變得更加健壯和簡單
2、哨兵的核心知識
(1)哨兵至少需要3個實例,來保證自己的健壯性 (2)哨兵 + redis主從的部署架構,是不會保證數據零丟失的,只能保證redis集群的高可用性 (3)對於哨兵 + redis主從這種復雜的部署架構,盡量在測試環境和生產環境,都進行充足的測試和演練
3、為什么redis哨兵集群只有2個節點無法正常工作?
哨兵集群必須部署2個以上節點 如果哨兵集群僅僅部署了個2個哨兵實例,quorum=1 +----+ +----+ | M1 |---------| R1 | | S1 | | S2 | +----+ +----+ Configuration: quorum = 1 master宕機,s1和s2中只要有1個哨兵認為master宕機就可以還行切換,同時s1和s2中會選舉出一個哨兵來執行故障轉移 同時這個時候,需要majority,也就是大多數哨兵都是運行的,2個哨兵的majority就是2(2的majority=2,3的majority=2,5的majority=3,4的majority=2),2個哨兵都運行着,就可以允許執行故障轉移 但是如果整個M1和S1運行的機器宕機了,那么哨兵只有1個了,此時就沒有majority來允許執行故障轉移,雖然另外一台機器還有一個R1,但是故障轉移不會執行
4、經典的3節點哨兵集群
+----+ | M1 | | S1 | +----+ | +----+ | +----+ | R2 |----+----| R3 | | S2 | | S3 | +----+ +----+ Configuration: quorum = 2,majority 如果M1所在機器宕機了,那么三個哨兵還剩下2個,S2和S3可以一致認為master宕機,然后選舉出一個來執行故障轉移 同時3個哨兵的majority是2,所以還剩下的2個哨兵運行着,就可以允許執行故障轉移
二、redis哨兵主備切換的數據丟失問題及解決方法
1、兩種數據丟失的情況
主備切換的過程,可能會導致數據丟失 (1)異步復制導致的數據丟失 因為master -> slave的復制是異步的,所以可能有部分數據還沒復制到slave,master就宕機了,此時這些部分數據就丟失了 (2)腦裂導致的數據丟失 腦裂,也就是說,某個master所在機器突然脫離了正常的網絡,跟其他slave機器不能連接,但是實際上master還運行着 此時哨兵可能就會認為master宕機了,然后開啟選舉,將其他slave切換成了master 這個時候,集群里就會有兩個master,也就是所謂的腦裂 此時雖然某個slave被切換成了master,但是可能client還沒來得及切換到新的master,還繼續寫向舊master的數據可能也丟失了 因此舊master再次恢復的時候,會被作為一個slave掛到新的master上去,自己的數據會清空,重新從新的master復制數據
2、解決異步復制和腦裂導致的數據丟失
min-slaves-to-write 1 min-slaves-max-lag 10 要求至少有1個slave,數據復制和同步的延遲不能超過10秒 如果說一旦所有的slave,數據復制和同步的延遲都超過了10秒鍾,那么這個時候,master就不會再接收任何請求了 上面兩個配置可以減少異步復制和腦裂導致的數據丟失 (1)減少異步復制的數據丟失 有了min-slaves-max-lag這個配置,就可以確保說,一旦slave復制數據和ack延時太長,就認為可能master宕機后損失的數據太多了,那么就拒絕寫請求,這樣可以把master宕機時由於部分數據未同步到slave導致的數據丟失降低的可控范圍內 (2)減少腦裂的數據丟失 如果一個master出現了腦裂,跟其他slave丟了連接,那么上面兩個配置可以確保說,如果不能繼續給指定數量的slave發送數據,而且slave超過10秒沒有給自己ack消息,那么就直接拒絕客戶端的寫請求 這樣腦裂后的舊master就不會接受client的新數據,也就避免了數據丟失 上面的配置就確保了,如果跟任何一個slave丟了連接,在10秒后發現沒有slave給自己ack,那么就拒絕新的寫請求 因此在腦裂場景下,最多就丟失10秒的數據
三、redis哨兵的核心底層原理
1、sdown和odown轉換機制
sdown和odown兩種失敗狀態 sdown是主觀宕機,就一個哨兵如果自己覺得一個master宕機了,那么就是主觀宕機 odown是客觀宕機,如果quorum數量的哨兵都覺得一個master宕機了,那么就是客觀宕機 sdown達成的條件很簡單,如果一個哨兵ping一個master,超過了is-master-down-after-milliseconds指定的毫秒數之后,就主觀認為master宕機 sdown到odown轉換的條件很簡單,如果一個哨兵在指定時間內,收到了quorum指定數量的其他哨兵也認為那個master是sdown了,那么就認為是odown了,客觀認為master宕
2、哨兵集群的自動發現機制
哨兵互相之間的發現,是通過redis的pub/sub系統實現的,每個哨兵都會往__sentinel__:hello這個channel里發送一個消息,這時候所有其他哨兵都可以消費到這個消息,並感知到其他的哨兵的存在 每隔兩秒鍾,每個哨兵都會往自己監控的某個master+slaves對應的__sentinel__:hello channel里發送一個消息,內容是自己的host、ip和runid還有對這個master的監控配置 每個哨兵也會去監聽自己監控的每個master+slaves對應的__sentinel__:hello channel,然后去感知到同樣在監聽這個master+slaves的其他哨兵的存在 每個哨兵還會跟其他哨兵交換對master的監控配置,互相進行監控配置的同步
3、slave配置的自動糾正
哨兵會負責自動糾正slave的一些配置,比如slave如果要成為潛在的master候選人,哨兵會確保slave在復制現有master的數據; 如果slave連接到了一個錯誤的master上,比如故障轉移之后,那么哨兵會確保它們連接到正確的master上
4、slave->master選舉算法
如果一個master被認為odown了,而且majority哨兵都允許了主備切換,那么某個哨兵就會執行主備切換操作,此時首先要選舉一個slave來 會考慮slave的一些信息 (1)跟master斷開連接的時長 (2)slave優先級 (3)復制offset (4)run id 如果一個slave跟master斷開連接已經超過了down-after-milliseconds的10倍,外加master宕機的時長,那么slave就被認為不適合選舉為master (down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
接下來會對slave進行排序
(1)按照slave優先級進行排序,slave priority越低,優先級就越高
(2)如果slave priority相同,那么看replica offset,哪個slave復制了越多的數據,offset越靠后,優先級就越高
(3)如果上面兩個條件都相同,那么選擇一個run id比較小的那個slave
5、quorum和majority
每次一個哨兵要做主備切換,首先需要quorum數量的哨兵認為odown,然后選舉出一個哨兵來做切換,這個哨兵還得得到majority哨兵的授權,才能正式執行切換 如果quorum < majority,比如5個哨兵,majority就是3,quorum設置為2,那么就3個哨兵授權就可以執行切換 但是如果quorum >= majority,那么必須quorum數量的哨兵都授權,比如5個哨兵,quorum是5,那么必須5個哨兵都同意授權,才能執行切換
6、configuration epoch
哨兵會對一套redis master+slave進行監控,有相應的監控的配置 執行切換的那個哨兵,會從要切換到的新master(salve->master)那里得到一個configuration epoch,這就是一個version號,每次切換的version號都必須是唯一的 如果第一個選舉出的哨兵切換失敗了,那么其他哨兵,會等待failover-timeout時間,然后接替繼續執行切換,此時會重新獲取一個新的configuration epoch,作為新的version號
7、configuraiton傳播
哨兵完成切換之后,會在自己本地更新生成最新的master配置,然后同步給其他的哨兵,就是通過之前說的pub/sub消息機制
這里之前的version號就很重要了,因為各種消息都是通過一個channel去發布和監聽的,所以一個哨兵完成一次新的切換之后,新的master配置是跟着新的version號的
其他的哨兵都是根據版本號的大小來更新自己的master配置的
四、部署sentinel(三節點)
OS:centos7
redis:4.0.12
1、安裝redis並配置主從
參考https://www.cnblogs.com/panwenbin-logs/p/10242027.html
https://www.cnblogs.com/panwenbin-logs/p/10257741.html
2、配置sentinel(所有節點,注意bind地址)
[root@redis-master ~]# cd /usr/local/redis #redis解壓目錄 [root@redis-master redis]# cp sentinel.conf sentinel.conf-bak [root@redis-master redis]# mkdir /etc/sentinal [root@redis-master redis]# mkdir -p /var/sentinal/26379 [root@redis-master redis]# cp sentinel.conf /etc/sentinal/ [root@redis-master redis]# cd /etc/sentinal/ [root@redis-master sentinal]# cat sentinel.conf port 26379 bind 192.168.1.132 #本機地址 dir /var/sentinal/26379 sentinel monitor mymaster 192.168.1.132 6379 2 #mymaster是集群的名稱可自定義,IP地為集群中master的地址,注意與bind的區別 6379表示端口 2表示 需要多少哨兵同意才能執行故障轉移操作 sentinel down-after-milliseconds mymaster 30000 #超過多少毫秒跟一個redis實例斷了連接,哨兵就可能認為這個redis實例掛了 sentinel failover-timeout mymaster 60000 #failover轉移時間,超出此時間認為master轉移失效,重新開始轉移
sentinel parallel-syncs mymaster 1 #新的master別切換之后,同時有多少個slave被切換到去連接新master,重新做同步,數字越低,花費的時間越多
protected-mode no #關閉安全模式,否則會報錯
sentinel auth-pass mymaster redis-pass #如果集群設置了密碼,需要添加
daemonize yes #后台進程
logfile /var/log/sentinal/sentinal.log #日志路徑
啟動
[root@redis-master sentinal]# redis-sentinel /etc/sentinal/sentinel.conf #測試,生產應該將命令放入后台執行 22120:X 13 Jan 14:33:30.411 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 22120:X 13 Jan 14:33:30.411 # Redis version=4.0.12, bits=64, commit=00000000, modified=0, pid=22120, just started 22120:X 13 Jan 14:33:30.411 # Configuration loaded 22120:X 13 Jan 14:33:30.413 * Increased maximum number of open files to 10032 (it was originally set to 1024). _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 4.0.12 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in sentinel mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 26379 | `-._ `._ / _.-' | PID: 22120 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 22120:X 13 Jan 14:33:30.414 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 22120:X 13 Jan 14:33:30.414 # Sentinel ID is 704dddbb7700554d21df80d7be55830b2d4be0a7 22120:X 13 Jan 14:33:30.414 # +monitor master mymaster 192.168.1.132 6379 quorum 2 22120:X 13 Jan 14:33:30.416 * +slave slave 192.168.1.133:6379 192.168.1.133 6379 @ mymaster 192.168.1.132 6379 22120:X 13 Jan 14:33:37.775 * +sentinel sentinel d0864c9cddd5d9dd9165f8ac1c493a226189f093 192.168.1.133 26379 @ mymaster 192.168.1.132 6379 22120:X 13 Jan 14:33:42.751 * +sentinel sentinel 8ab3bf99d413a3810b7e0976fad94f88853b2445 192.168.1.134 26379 @ mymaster 192.168.1.132 6379
檢查
[root@redis-master 26379]# redis-cli -h 192.168.1.132 -p 26379 192.168.1.132:26379> sentinel master mymaster #查看master的信息 192.168.1.132:26379> SENTINEL slaves mymaster #查看slave的信息 192.168.1.132:26379> SENTINEL sentinels mymaster #查看sentinel的信息,本機sentinel信息不顯示 192.168.1.132:26379> SENTINEL get-master-addr-by-name mymaster #查看redis集群master的IP地址和端口
五、哨兵管理
1、哨兵節點的增加和刪除
1.增加sentinal,會自動發現 2.刪除sentinal的步驟 (1)停止sentinal進程 (2)SENTINEL RESET *,在所有sentinal上執行,清理所有的master狀態 (3)SENTINEL MASTER mastername,在所有sentinal上執行,查看所有sentinal對數量是否達成了一致
2、slave的永久下線
讓master摘除某個已經下線的slave:SENTINEL RESET mastername,在所有的哨兵上面執行
3、slave切換為Master的優先級
slave->master選舉優先級:slave-priority,值越小優先級越高
4、基於哨兵集群架構下的安全認證
每個slave都有可能切換成master,所以每個實例都要配置兩個指令
master上啟用安全認證,requirepass
master連接口令,masterauth
sentinal,sentinel auth-pass <master-group-name> <pass>
5、容災演練
通過哨兵看一下當前的master:SENTINEL get-master-addr-by-name mymaster 把master節點kill -9掉,pid文件也刪除掉 查看sentinal的日志,是否出現+sdown字樣,識別出了master的宕機問題; 然后出現+odown字樣,就是指定的quorum哨兵數量,都認為master宕機了 (1)三個哨兵進程都認為master是sdown了 (2)超過quorum指定的哨兵進程都認為sdown之后,就變為odown (3)哨兵1是被選舉為要執行后續的主備切換的那個哨兵 (4)哨兵1去新的master(slave)獲取了一個新的config version (5)嘗試執行failover (6)投票選舉出一個slave區切換成master,每隔哨兵都會執行一次投票 (7)讓salve,slaveof noone,不讓它去做任何節點的slave了; 把slave提拔成master; 舊的master認為不再是master了 (8)哨兵就自動認為之前的132:6379變成了slave了,133:6379變成了master了 (9)哨兵去探查了一下132:6379這個salve的狀態,認為它sdown了 所有哨兵選舉出了一個,來執行主備切換操作 如果哨兵的majority都存活着,那么就會執行主備切換操作 再通過哨兵看一下master:SENTINEL get-master-addr-by-name mymaster 嘗試連接一下新的master 故障恢復,再將舊的master重新啟動,查看是否被哨兵自動切換成slave節點 (1)手動殺掉master (2)哨兵能否執行主備切換,將slave切換為master (3)哨兵完成主備切換后,新的master能否使用 (4)故障恢復,將舊的master重新啟動 (5)哨兵能否自動將舊的master變為slave,掛接到新的master上面去,而且也是可以使用的
[root@redis-master 26379]# ps aux|grep redis [root@redis-master 26379]# kill -9 22065 查看sentinel日志 .... 22120:X 13 Jan 15:21:35.699 # +sdown master mymaster 192.168.1.132 6379 22120:X 13 Jan 15:21:35.832 # +new-epoch 1 22120:X 13 Jan 15:21:35.834 # +vote-for-leader d0864c9cddd5d9dd9165f8ac1c493a226189f093 1 22120:X 13 Jan 15:21:36.182 # +config-update-from sentinel d0864c9cddd5d9dd9165f8ac1c493a226189f093 192.168.1.133 26379 @ mymaster 192.168.1.132 6379 22120:X 13 Jan 15:21:36.182 # +switch-master mymaster 192.168.1.132 6379 192.168.1.133 6379 22120:X 13 Jan 15:21:36.182 * +slave slave 192.168.1.132:6379 192.168.1.132 6379 @ mymaster 192.168.1.133 6379 22120:X 13 Jan 15:22:06.243 # +sdown slave 192.168.1.132:6379 192.168.1.132 6379 @ mymaster 192.168.1.133 6379
[root@redis-master 26379]# systemctl start redis #啟動redis,在查看日志
22120:X 13 Jan 15:23:33.399 # -sdown slave 192.168.1.132:6379 192.168.1.132 6379 @ mymaster 192.168.1.133 6379
[root@redis-master 26379]# redis-cli -h 192.168.1.132 -p 26379
192.168.1.132:26379> SENTINEL get-master-addr-by-name mymaster #雖然舊的master恢復上線后,但是可以看到master並沒有進行切換
1) "192.168.1.133"
2) "6379"
#redis狀態改變在sentinel的配置文件中也會自動更改