Redis Sentinel:叢集Failover解決方案

五柳-先生發表於2015-06-29

本文轉載自:http://shift-alt-ctrl.iteye.com/blog/1884370

文中的配置例子,還有failover過程中觸發的訂閱事件具有很好的參考價值。

   Redis sentinel(哨兵)模組已經被整合在redis2.4+的版本中,儘管目前不是release,不過可以嘗試去使用和了解,事實上sentinel還是有點複雜的.
   sentinel主要功能就是為Redis M-S(master,slaves)叢集提供了1)master存活檢測 2)叢集中M-S服務監控 3) 自動故障轉移,M-S角色轉換等能力,從一個方面說是提高了redis叢集的可用性.

    一般情況下,最小M-S單元各有一個maste和slave組成,當master失效後,sentinel可以幫助我們自動將slave提升為master;有了sentinel元件,可以減少系統管理員的人工切換slave的操作過程.

    sentinel的一些設計思路和zookeeper非常類似,事實上,你可以不使用sentinel,而是自己開發一個監控redis的zk客戶端也能夠完成相應的設計要求.

 

一.環境部署

    準備3個redis服務,簡單構建一個小的M-S環境;它們各自的redis.conf配置項中,除了port不同外,要求其他的配置完全一樣(包括aof/snap,memory,rename以及授權密碼等);原因是基於sentinel做故障轉移,所有的server執行機制都必須一樣,它們只不過在執行時"角色"不同,而且它們的角色可能在故障時會被轉換;slave在某些時刻也會成為master,儘管在一般情況下,slave的資料持久方式經常採取snapshot,而master為aof,不過基於sentinel之後,slave和master均要採取aof(通過bgsave,手動觸發snapshot備份).

 

   1)  redis.conf:

 

  1. ##redis.conf  
  2. ##redis-0,預設為master  
  3. port 6379  
  4. ##授權密碼,請各個配置保持一致  
  5. requirepass 012_345^678-90  
  6. masterauth 012_345^678-90  
  7. ##暫且禁用指令重新命名  
  8. ##rename-command  
  9. ##開啟AOF,禁用snapshot  
  10. appendonly yes  
  11. save “”  
  12. ##slaveof no one  
  13. slave-read-only yes  

 

  1. ##redis.conf  
  2. ##redis-1,通過啟動引數配置為slave,配置檔案保持獨立  
  3. port 6479  
  4. slaveof 127.0.0.1 6379  
  5. ##-----------其他配置和master保持一致-----------##  

 

  1. ##redis.conf  
  2. ##redis-1,通過啟動引數配置為slave,配置檔案保持獨立  
  3. port 6579  
  4. slaveof 127.0.0.1 6379  
  5. ##-----------其他配置和master保持一致-----------##  

 

    2) sentinel.conf

    請首先在各個redis服務中sentinel.conf同目錄下新建local-sentinel.conf,並將複製如下配置資訊.

  1. ##redis-0  
  2. ##sentinel例項之間的通訊埠  
  3. port 26379  
  4. sentinel monitor def_master 127.0.0.1 6379 2  
  5. sentinel auth-pass def_master 012_345^678-90  
  6. sentinel down-after-milliseconds def_master 30000  
  7. sentinel can-failover def_master yes  
  8. sentinel parallel-syncs def_master 1  
  9. sentinel failover-timeout def_master 900000  
 
  1. ##redis-1  
  2. port 26479  
  3. ##--------其他配置同上-------##  
 
  1. ##redis-2  
  2. port 26579  
  3. ##--------其他配置同上-------#   

    3) 啟動與檢測

   

  1. ##redis-0(預設為master)  
  2. > ./redis-server --include ../redis.conf  
  3. ##啟動sentinel元件  
  4. > ./redis-sentinel ../local-sentinel.conf  
    按照上述指令,依次啟動redis-0,redis-1,redis-2;在啟動redis-1和redis-2的時候,你會發現在redis-0的sentinel控制檯會輸出"+sentinel ..."字樣,表示有新的sentinel例項加入到監控.不過此處需要提醒,首次構建sentinel環境時,必須首先啟動master機器.

 

    此後你可以使用任意一個"redis-cli"視窗,輸入"INFO"命令,可以檢視當前server的狀態:

   

  1. > ./redis-cli -h 127.0.0.1 -p 6379  
  2. ##如下為列印資訊摘要:  
  3. #Replication  
  4. role:master  
  5. connected_salves:2  
  6. slave0:127.0.0.1,6479,online  
  7. slave1:127.0.0.1.6579,online  
    "INFO"指令將會列印完整的服務資訊,包括叢集,我們只需要關注"replication"部分,這部分資訊將會告訴我們"當前server的角色"以及指向它的所有的slave資訊.可以通過在任何一個slave上,使用"INFO"指令獲得當前slave所指向的master資訊.

 

    "INFO"指令不僅可以幫助我們獲得叢集的情況,當然sentinel元件也是使用"INFO"做同樣的事情.

    當上述部署環境穩定後,我們直接關閉redis-0,在等待"down-after-milliseconds"秒之後(30秒),redis-0/redis-1/redis-2的sentinel視窗會立即列印"+sdown""+odown""+failover""+selected-slave""+promoted-slave""+slave-reconf"等等一系列指令,這些指令標明當master失效後,sentinel元件進行failover的過程.

    當環境再次穩定後,我們發現,redis-1被提升("promoted")為master,且redis-2也通過"slave-reconf"過程之後跟隨了redis-1.

    如果此後想再次讓redis-0加入叢集,你需要首先通過"INFO"指令找到當前的masterip + port,並在啟動指令中明確指明slaveof引數:

  1. > ./redis-server --include ../redis.conf --slaveof 127.0.0.1 6479  

    sentinel例項需要全程處於啟動狀態,如果只啟動server而不啟動相應的sentinel,仍然不能確保server能夠正確的被監控和管理.

 

二. sentinel原理

    首先解釋2個名詞:SDOWN和ODOWN.

  • SDOWN:subjectively down,直接翻譯的為"主觀"失效,即當前sentinel例項認為某個redis服務為"不可用"狀態.
  • ODOWN:objectively down,直接翻譯為"客觀"失效,即多個sentinel例項都認為master處於"SDOWN"狀態,那麼此時master將處於ODOWN,ODOWN可以簡單理解為master已經被叢集確定為"不可用",將會開啟failover.

    SDOWN適合於master和slave,但是ODOWN只會使用於master;當slave失效超過"down-after-milliseconds"後,那麼所有sentinel例項都會將其標記為"SDOWN".

   

    1) SDOWN與ODOWN轉換過程:

  • 每個sentinel例項在啟動後,都會和已知的slaves/master以及其他sentinels建立TCP連線,並週期性傳送PING(預設為1秒)
  • 在互動中,如果redis-server無法在"down-after-milliseconds"時間內響應或者響應錯誤資訊,都會被認為此redis-server處於SDOWN狀態.
  • 如果2)中SDOWN的server為master,那麼此時sentinel例項將會向其他sentinel間歇性(一秒)傳送"is-master-down-by-addr <ip> <port>"指令並獲取響應資訊,如果足夠多的sentinel例項檢測到master處於SDOWN,那麼此時當前sentinel例項標記master為ODOWN...其他sentinel例項做同樣的互動操作.配置項"sentinel monitor <mastername> <masterip> <masterport> <quorum>",如果檢測到master處於SDOWN狀態的slave個數達到<quorum>,那麼此時此sentinel例項將會認為master處於ODOWN.
  • 每個sentinel例項將會間歇性(10秒)向master和slaves傳送"INFO"指令,如果master失效且沒有新master選出時,每1秒傳送一次"INFO";"INFO"的主要目的就是獲取並確認當前叢集環境中slaves和master的存活情況.
  • 經過上述過程後,所有的sentinel對master失效達成一致後,開始failover.

    2) Sentinel與slaves"自動發現"機制:

    在sentinel的配置檔案中(local-sentinel.conf),都指定了port,此port就是sentinel例項偵聽其他sentinel例項建立連結的埠.在叢集穩定後,最終會每個sentinel例項之間都會建立一個tcp連結,此連結中傳送"PING"以及類似於"is-master-down-by-addr"指令集,可用用來檢測其他sentinel例項的有效性以及"ODOWN"和"failover"過程中資訊的互動.
    在sentinel之間建立連線之前,sentinel將會盡力和配置檔案中指定的master建立連線.sentinel與master的連線中的通訊主要是基於pub/sub來發布和接收資訊,釋出的資訊內容包括當前sentinel例項的偵聽埠:

  1. +sentinel sentinel 127.0.0.1:26579 127.0.0.1 26579 ....  

    釋出的主題名稱為"__sentinel__:hello";同時sentinel例項也是"訂閱"此主題,以獲得其他sentinel例項的資訊.由此可見,環境首次構建時,在預設master存活的情況下,所有的sentinel例項可以通過pub/sub即可獲得所有的sentinel資訊,此後每個sentinel例項即可以根據+sentinel資訊中的"ip+port"和其他sentinel逐個建立tcp連線即可.不過需要提醒的是,每個sentinel例項均會間歇性(5秒)向"__sentinel__:hello"主題中釋出自己的ip+port,目的就是讓後續加入叢集的sentinel例項也能或得到自己的資訊.
    根據上文,我們知道在master有效的情況下,即可通過"INFO"指令獲得當前master中已有的slave列表;此後任何slave加入叢集,master都會向"主題中"釋出"+slave 127.0.0.1:6579 ..",那麼所有的sentinel也將立即獲得slave資訊,並和slave建立連結並通過PING檢測其存活性.

    補充一下,每個sentinel例項都會儲存其他sentinel例項的列表以及現存的master/slaves列表,各自的列表中不會有重複的資訊(不可能出現多個tcp連線),對於sentinel將使用ip+port做唯一性標記,對於master/slaver將使用runid做唯一性標記,其中redis-server的runid在每次啟動時都不同.

 

   3) Leader選舉:

    其實在sentinels故障轉移中,仍然需要一個“Leader”來排程整個過程:master的選舉以及slave的重配置和同步。當叢集中有多個sentinel例項時,如何選舉其中一個sentinel為leader呢?

    在配置檔案中“can-failover”“quorum”引數,以及“is-master-down-by-addr”指令配合來完成整個過程。

    A) “can-failover”用來表明當前sentinel是否可以參與“failover”過程,如果為“YES”則表明它將有能力參與“Leader”的選舉,否則它將作為“Observer”,observer參與leader選舉投票但不能被選舉;

    B) “quorum”不僅用來控制master ODOWN狀態確認,同時還用來選舉leader時最小“贊同票”數;

    C) “is-master-down-by-addr”,在上文中以及提到,它可以用來檢測“ip + port”的master是否已經處於SDOWN狀態,不過此指令不僅能夠獲得master是否處於SDOWN,同時它還額外的返回當前sentinel本地“投票選舉”的Leader資訊(runid);

    每個sentinel例項都持有其他的sentinels資訊,在Leader選舉過程中(當為leader的sentinel例項失效時,有可能master server並沒失效,注意分開理解),sentinel例項將從所有的sentinels集合中去除“can-failover = no”和狀態為SDOWN的sentinels,在剩餘的sentinels列表中按照runid按照“字典”順序排序後,取出runid最小的sentinel例項,並將它“投票選舉”為Leader,並在其他sentinel傳送的“is-master-down-by-addr”指令時將推選的runid追加到響應中。每個sentinel例項都會檢測“is-master-down-by-addr”的響應結果,如果“投票選舉”的leader為自己,且狀態正常的sentinels例項中,“贊同者”的自己的sentinel個數不小於(>=) 50% + 1,且不小與<quorum>,那麼此sentinel就會認為選舉成功且leader為自己。

    在sentinel.conf檔案中,我們期望有足夠多的sentinel例項配置“can-failover yes”,這樣能夠確保當leader失效時,能夠選舉某個sentinel為leader,以便進行failover。如果leader無法產生,比如較少的sentinels例項有效,那麼failover過程將無法繼續.

 

    4) failover過程:

    在Leader觸發failover之前,首先wait數秒(隨即0~5),以便讓其他sentinel例項準備和調整(有可能多個leader??),如果一切正常,那麼leader就需要開始將一個salve提升為master,此slave必須為狀態良好(不能處於SDOWN/ODOWN狀態)且權重值最低(redis.conf中)的,當master身份被確認後,開始failover

    A)“+failover-triggered”: Leader開始進行failover,此後緊跟著“+failover-state-wait-start”,wait數秒。

    B)“+failover-state-select-slave”: Leader開始查詢合適的slave

    C)“+selected-slave”: 已經找到合適的slave

    D) “+failover-state-sen-slaveof-noone”: Leader向slave傳送“slaveof no one”指令,此時slave已經完成角色轉換,此slave即為master

    E) “+failover-state-wait-promotition”: 等待其他sentinel確認slave

    F)“+promoted-slave”:確認成功

    G)“+failover-state-reconf-slaves”: 開始對slaves進行reconfig操作。

    H)“+slave-reconf-sent”:向指定的slave傳送“slaveof”指令,告知此slave跟隨新的master

    I)“+slave-reconf-inprog”: 此slave正在執行slaveof + SYNC過程,如過slave收到“+slave-reconf-sent”之後將會執行slaveof操作。

    J)“+slave-reconf-done”: 此slave同步完成,此後leader可以繼續下一個slave的reconfig操作。迴圈G)

    K)“+failover-end”: 故障轉移結束

    L)“+switch-master”:故障轉移成功後,各個sentinel例項開始監控新的master。

三.Sentinel.conf詳解

 

  1. ##sentinel例項之間的通訊埠  
  2. ##redis-0  
  3. port 26379  
  4. ##sentinel需要監控的master資訊:<mastername> <masterIP> <masterPort> <quorum>  
  5. ##<quorum>應該小於叢集中slave的個數,只有當至少<quorum>個sentinel例項提交"master失效"  
  6. ##才會認為master為O_DWON("客觀"失效)  
  7. sentinel monitor def_master 127.0.0.1 6379 2  
  8.   
  9. sentinel auth-pass def_master 012_345^678-90  
  10.   
  11. ##master被當前sentinel例項認定為“失效”的間隔時間  
  12. ##如果當前sentinel與master直接的通訊中,在指定時間內沒有響應或者響應錯誤程式碼,那麼  
  13. ##當前sentinel就認為master失效(SDOWN,“主觀”失效)  
  14. ##<mastername> <millseconds>  
  15. ##預設為30秒  
  16. sentinel down-after-milliseconds def_master 30000  
  17.   
  18. ##當前sentinel例項是否允許實施“failover”(故障轉移)  
  19. ##no表示當前sentinel為“觀察者”(只參與"投票".不參與實施failover),  
  20. ##全域性中至少有一個為yes  
  21. sentinel can-failover def_master yes  
  22.   
  23. ##當新master產生時,同時進行“slaveof”到新master並進行“SYNC”的slave個數。  
  24. ##預設為1,建議保持預設值  
  25. ##在salve執行salveof與同步時,將會終止客戶端請求。  
  26. ##此值較大,意味著“叢集”終止客戶端請求的時間總和和較大。  
  27. ##此值較小,意味著“叢集”在故障轉移期間,多個salve向客戶端提供服務時仍然使用舊資料。  
  28. sentinel parallel-syncs def_master 1  
  29.   
  30. ##failover過期時間,當failover開始後,在此時間內仍然沒有觸發任何failover操作,  
  31. ##當前sentinel將會認為此次failoer失敗。  
  32. sentinel failover-timeout def_master 900000  
  33.   
  34. ##當failover時,可以指定一個“通知”指令碼用來告知系統管理員,當前叢集的情況。  
  35. ##指令碼被允許執行的最大時間為60秒,如果超時,指令碼將會被終止(KILL)  
  36. ##指令碼執行的結果:  
  37. ## 1    -> 稍後重試,最大重試次數為10;   
  38. ## 2    -> 執行結束,無需重試  
  39. ##sentinel notification-script mymaster /var/redis/notify.sh  
  40.   
  41. ##failover之後重配置客戶端,執行指令碼時會傳遞大量引數,請參考相關文件  
  42. # sentinel client-reconfig-script <master-name> <script-path>  
    更詳細資訊,請參考src/sentinel.c原始碼 .配置檔案載入過程參見方法:sentinelHandlerConfiguration(..)

相關文章