Redis哨兵-實現Redis高可用
轉載自Redis中文網 - Redis哨兵-實現Redis高可用
概述
Redis哨兵爲Redis提供了高可用性。實際上這意味着你可以使用哨兵模式創建一個可以不用人爲干預而應對各種故障的Redis部署。
哨兵模式還提供了其他的附加功能,如監控,通知,爲客戶端提供配置。
下面是在宏觀層面上哨兵模式的功能列表:
- 監控:哨兵不斷的檢查master和slave是否正常的運行。
- 通知:當監控的某臺Redis實例發生問題時,可以通過API通知系統管理員和其他的應用程序。
- 自動故障轉移:如果一個master不正常運行了,哨兵可以啓動一個故障轉移進程,將一個slave升級成爲master,其他的slave被重新配置使用新的master,並且應用程序使用Redis服務端通知的新地址。
- 配置提供者:哨兵作爲Redis客戶端發現的權威來源:客戶端連接到哨兵請求當前可靠的master的地址。如果發生故障,哨兵將報告新地址。
哨兵的分佈式特性
Redis哨兵是一個分佈式系統:
哨兵自身被設計成和多個哨兵進程一起合作運行。有多個哨兵進程合作的好處有:
- 當多個哨兵對一個master不再可用達成一致時執行故障檢測。這會降低錯誤判斷的概率。
- 即使在不是所有的哨兵都工作時哨兵也會工作,使系統健壯的抵抗故障。畢竟在故障系統裏單點故障沒有什麼意義。
Redis的哨兵、Redis實例(master和slave)、和客戶端是一個有特種功能的大型分佈式系統。在這個文檔裏將逐步從爲了理解哨兵基本性質需要的基礎信息,到爲了理解怎樣正確的使用哨兵工作的更復雜的信息(這是可選的)進行介紹。
快速入門
獲得哨兵
當前的哨兵版本是sentinel 2。它是基於最初哨兵的實現,使用更健壯的和更簡單的預算算法(在這個文檔裏有解釋)重寫的。
Redis2.8和Redis3.0附帶穩定的哨兵版本。他們是Redis的兩個最新穩定版本。
在不穩定版本的分支上執行新的改進,且有時一些新特性一旦被認爲是穩定的就會被移植到Redis2.8和Redis3.0分支中。
Redis2.6附帶Redis sentinel 1,它是棄用的不建議使用。
運行哨兵
如果你使用可執行的 redis-sentinel(或者你有可執行的redis-server),你可以使用下面的命令行運行哨兵:
redis-sentinel /path/to/sentinel.conf
另外你可以直接使用可執行的redis-server在哨兵模式下啓動。
redis-server /path/to/sentinel.conf --sentinel
兩種方式效果都是一樣的。
然而在啓動哨兵時必須使用一個配置文件,因爲這個配置文件將用於系統保存當前狀態和在重啓時重新加載。哨兵會在沒有指定配置文件或指定的配置文件不可寫的時候拒絕啓動。
Redis 哨兵默認監聽26379 TCP端口,所以爲了哨兵的正常工作,你的26379端口必須開放接收其他哨兵實例的IP地址的連接。否則哨兵不能通信和商定做什麼,故障轉移將永不會執行。
部署哨兵之前需要了解的基本事情
- 一個健壯的部署至少需要三個哨兵實例。
- 三個哨兵實例應該放置在客戶使用獨立方式確認故障的計算機或虛擬機中。例如不同的物理機或不同可用區域的虛擬機。
- sentinel + Redis實例不保證在故障期間保留確認的寫入,因爲Redis使用異步複製。然而有方式部署哨兵使丟失數據限制在特定時刻,雖然有更安全的方式部署它。
- 你的客戶端要支持哨兵,流行的客戶端都支持哨兵,但不是全部。
- 沒有HA設置是安全的,如果你不經常的在開發環境測試,在生產環境他們會更好。你可能會有一個明顯的錯誤配置只是當太晚的時候。
- Sentinel,Docker,或者其他形式的網絡地址交換或端口映射需要加倍小心:Docker執行端口重新映射,破壞Sentinel自動發現其他的哨兵進程和master的slave列表。稍後在這個文檔裏檢查關於Sentinel和Docker的部分,瞭解更多信息。
配置哨兵
Redis源碼發佈包包含一個sentinel.conf的文件,它是一個自含的配置文件示例,你可以使用它配置哨兵,一個典型的最小的配置文件就像下面的配置:
sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 60000 sentinel failover-timeout mymaster 180000 sentinel parallel-syncs mymaster 1 sentinel monitor resque 192.168.1.3 6380 4 sentinel down-after-milliseconds resque 10000 sentinel failover-timeout resque 180000 sentinel parallel-syncs resque 5
你只需要指定masters監控,給予每個分開的master不同的名字。不需要指定slaves,它們是自動發現的。Sentinel會自動的更新關於slaves的附加信息(爲了保留信息以防重啓)。每次slave晉升爲一個master或者每次發現新的Sentinel都會重寫配置。
上面的示例配置中,主要監控兩組Redis示例,每個由一個master和若干個slave組成。一組實例叫mymaster,另一組叫 resque。
sentinel monitor的參數聲明的含義如下所示:
sentinel monitor [master-group-name] [ip] [port] [quorum]
爲了更清晰,我們逐行的解釋每個選項的含義:
第一行用於告訴Redis監控一個master叫做mymaster,它的地址在127.0.0.1,端口爲6379,法定人數是2。每個參數都很容易理解,但是quorum需要解釋一下:
- quorum是Sentinel需要協商同意master是否可到達的數量。爲了真正的標記slave爲失敗,並最終是否需要啓動一個故障轉移進程。
- 無論怎樣,quorum只用於檢測故障。爲了實際執行故障轉移,Sentinel需要選舉leader並進行授權。這隻發生在大多數Sentinel進程的選舉。
例如你有5個哨兵進程,並且給定的master的quorum的值設置爲2,這是將發生的事情:
- 如果兩個哨兵進程同時同意master是不可到達的,這兩個哨兵的其中一個將啓動一個故障轉移。
- 如果至少三個哨兵可獲得,故障轉移將會被授權並真實的啓動。
實際上這意味着在故障轉移期間如果大多數的Sentinel進程不能通信,Sentinel將會永不啓動故障轉移(即在少數分區沒有故障轉移)。
其他Sentinel選項
其他的選項通常是這種形式:
sentinel [option_name] [master_name] [option_value]
並且用於下面的目的:
- down-after-milliseconds - 一個實例不可到達(不能ping通或者有錯誤),Sentinel開始認爲它是down的毫秒數。
- parallel-syncs - 設置在故障轉移之後同時可以重新配置使用新master的slave的數量。數字越低,更多的時間將會用故障轉移完成,但是如果slaves配置爲服務舊數據,你可能不希望所有的slave同時重新同步master。因爲主從複製對於slave是非阻塞的,當停止從master加載批量數據時有一個片刻延遲。通過設置選項爲1,確信每次只有一個slave是不可到達的。
其他的選項在本文檔其他部分有描述,並且在示例sentinel.conf的備有文件裏有附帶說明。
所有的Sentinel參數在運行時可以使用SENTINEL SET命令更改。查看在運行時重新配置Sentinel部分了解更多。
Sentinel部署示例
既然你知道了sentinel的基本信息,你可以很想知道應該將Sentinel放置在哪裏,需要多少Sentinel進程等等。這個章節展示了幾個部署示例。
我們爲了圖像化展示配置示例使用字符藝術,這是不同符號的意思:
+--------------------+ | This is a computer | | or VM that fails | | independently. We | | call it a "box" | +--------------------+
我們寫在盒子裏表示他們正在運行什麼:
+-------------------+ | Redis master M1 | | Redis Sentinel S1 | +-------------------+
不同的盒子之間通過線條連接,表示他們可以相互通信:
+-------------+ +-------------+ | Sentinel S1 |---------------| Sentinel S2 | +-------------+ +-------------+
使用斜槓展示網絡斷開:
+-------------+ +-------------+ | Sentinel S1 |------ // ------| Sentinel S2 | +-------------+ +-------------+
還要注意:
- Master 被叫做 M1,M2,M3 ... Mn。
- Slave 被叫做 R1,R2,R3 ... Rn(replica的首字母)
- Sentinels 被叫做 S1,S2,S3 ... Sn
- Clients 被叫做 C1,C2,C3 ... Cn
- 當一個實例因爲Sentinel的行爲改變了角色,我們把它放在方括號裏,所以[M1]表示因爲Sentinel的介入,M1現在是一個master。
注意永遠不會顯示的設置只是使用了兩個哨兵,因爲爲了啓動故障轉移,Sentinel總是需要和其他大多數的Sentinel通信。
實例1,只有兩個Sentinel,不要這樣做
+----+ +----+ | M1 |---------| R1 | | S1 | | S2 | +----+ +----+ Configuration: quorum = 1
- 在這個設置中,如果master M1故障,R1將被晉升因爲兩個Sentinel可以達成協議並且還可以授權一個故障轉移因爲多數就是兩個。所以他表面上看起來是可以工作的,然而檢查下一個點了解爲什麼這個設置是不行的。
- 如果運行M1的盒子停止工作了,S1也停止工作。運行在其他盒子上的S2將不能授權故障轉移,所以系統將變成不可用。
注意爲了排列不同的故障轉移需要少數服從多數,並且稍後向所有的Sentinel傳播最新的配置。還要注意上面配置的故障轉移的能力,沒有任何協定,非常危險:
+----+ +------+ | M1 |----//-----| [M1] | | S1 | | S2 | +----+ +------+
在上面的配置中我們使用完美的對稱方式創建了兩個master(假定S2可以在未授權的情況下進行故障轉移)。客戶端可能會不確定往哪邊寫,並且沒有途徑知道什麼時候分區配置是正確的,爲了預防一個永久的斷裂狀態。
所有請永遠部署至少三個Sentinel在三個不同的盒子裏。
例2:使用三個盒子的基本設置
這是個非常簡單的設置,它有簡單調整安全的優勢。它基於三個盒子,每個盒子同時運行一個Redis實例和一個Sentinel實例。
+----+ | M1 | | S1 | +----+ | +----+ | +----+ | R2 |----+----| R3 | | S2 | | S3 | +----+ +----+ Configuration: quorum = 2
如果M1故障,S2和S3將會商定故障並授權故障轉移,使客戶端可以繼續。
在每個Sentinel設置裏,Redis是異步主從複製,總會有丟失數據的風險,因爲有可能當它成爲master的時候,一個確認的寫入操作還沒有同步到slave。然後在上面的設置中有一個更高的風險由於客戶端分區一直是老的master,就像下面的圖像所示:
+----+ | M1 | | S1 | [- C1 (writes will be lost) +----+ | / / +------+ | +----+ | [M2] |----+----| R3 | | S2 | | S3 | +------+ +----+
在這個案例中網絡分區隔離老的master M1,所以slave R2晉升爲master。然而客戶端,比如C1,還在原來的老的master的分區,可能繼續往老master寫數據。這個數據將會永久丟失,因爲分區恢復時,master將會重新配置爲新master的slave,丟棄它的數據集。
這個問題可以使用下面的Redis主從複製特性減輕,它可在master檢查到它不再能傳輸它的寫入操作到指定數量的slave的時候停止接收寫入操作。
min-slaves-to-write 1 min-slaves-max-lag 10
使用上面的配置(請查看自帶的redis.conf示例瞭解更多信息)一個Redis實例,當作爲一個master,如果它不能寫入至少1個slave將停止接收寫入操作。(N個Slave以上不通就停止接收)
由於主從複製是異步的不能真實的寫入,意味着slave斷開連接,或者不再向我們發送異步確認的指定的max-lag秒數。(判定連接不通的超時時間)
在上面的示例中使用這個配置,老master M1將會在10秒鐘後變爲不可用。當分區恢復時,Sentinel配置將指向新的一個,客戶端C1將能夠獲取到有效的配置並且將使用新master繼續工作。
然而天下沒有免費的午餐,這種改進,如果兩個slave掛掉,master將會停止接收寫入操作。這是個權衡。
例三:Sentinel在客戶端盒子裏
有時我們只有兩個Redis盒子可用,一個master和一個slave。在例二中的配置在那樣的情況下是不可行的,所謂我們可以藉助下面的,Sentinel放置在客戶端:
+----+ +----+ | M1 |----+----| R1 | | S1 | | | S2 | +----+ | +----+ | +------------+------------+ | | | | | | +----+ +----+ +----+ | C1 | | C2 | | C3 | | S1 | | S2 | | S3 | +----+ +----+ +----+ Configuration: quorum = 2
在這個設置裏,Sentinel的視角和客戶端的視角相同:如果大多數的客戶端認爲master是可以到達的,它就是好的。C1,C2,C3是一般的客戶端,這不意味着C1識別單獨的客戶端連接到Redis。它更像一些如應用服務,Rails應用之類的。
如果運行M1和S1的盒子故障,故障轉移將會發生,然而很容看到不同的網絡分區將導致不同的行爲。例如如果客戶端和Redis服務之間的斷開連接,Sentinel將不能設置,因爲master和slave將都不可用。
注意如果使用M1獲取分區,我們有一個和例二中描述的相似的問題,不同的是這裏我們沒有辦法打破對稱,由於只有一個slave和master,所以當它的master斷開連接時master不能停止接收查詢,否則在slave故障期間master將永不可用。
所以這是個有效的設置但是在例二中的設置有像更容易管理HA系統的優點, 並有能力限制master接收寫入的時間。
例4:少於3個客戶端的Sentinel客戶端
在例3中如果客戶端少於3個就不能使用。在這個案例中我們使用一個混合的設置:
+----+ +----+ | M1 |----+----| R1 | | S1 | | | S2 | +----+ | +----+ | +------+-----+ | | | | +----+ +----+ | C1 | | C2 | | S3 | | S4 | +----+ +----+ Configuration: quorum = 3
這裏和例3非常類似,但是這裏我們在4個盒子裏運行四個哨兵。如果M1故障其他的三個哨兵可以執行故障轉移。
Sentinel,Docker,NAT,和可能的問題
Docker使用一個叫端口映射的技術:程序運行在Docker容器可以暴露一個不同端口。這非常有助於在相同的服務器上相同的時間使用相同的端口運行多個容器。
Docker不僅僅可以實現這個功能,還有其他的設置可以重新映射端口,有時是端口,有時是IP地址。
重新映射端口和地址有兩個問題:
- Sentinel自動發現其他不再工作的Sentinel,因爲它基於每個Sentinel監聽連接的端口和IP的問候消息。然而不知道地址和端口是重新映射的,所以它會宣告不正確的配置信息。
- Slaves以相同的方式列在master的Info輸出裏:通過master檢測每個TCP連接檢測地址,因爲端口是通過slave自己在握手期間廣告的,然而端口就像第一點裏說的是錯誤的。
快速教程
本文接下來的章節裏,將逐步覆蓋關於Sentinel API,配置,和語義的所有細節。然而對於那些想盡快使用系統的人,這個章節是一個教程,展示怎麼使用三個Sentinel實例配置。
這裏我們假定實例已經運行在5000,5001,5002。還假定Redis master運行在6379端口,slave運行在6380端口。在這個教程裏我們使用IPv4迴路地址127.0.0.1,假定運行在你的個人電腦裏。
三個Sentinel配置文件應該是這樣的:
port 5000 sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 60000 sentinel parallel-syncs mymaster 1
另外的兩個配置文件除了使用5001和5002端口外,其他全部相同。
上面的配置中需要注意一些事情:
- master 叫做 mymaster。它標識master和它的slave。因爲每個master設置有個不同的名字,Sentinel可以同時監控不同組的master和slave。
- quorum設置爲2(sentinel monitor指令的最後一個參數)。
- down-after-milliseconds 的值爲5000毫秒,也就是5秒,所以一旦我們在這個時間範圍內不能接收響應,master將會被標記爲故障。
一旦你啓動三個Sentinel,你將會看到一些消息日誌,像:
+monitor master mymaster 127.0.0.1 6379 quorum 2
這是Sentinel事件,如果你SUBSCRIBE事件名稱,你可以通過Pub/Sub接收到各種事件。
Sentinel在故障檢測和轉移期間生產並記錄不同的事件。
訪問Sentinel master的狀態
開始使用Sentinel最明顯的事情是,檢查master是否正常監控:
$ redis-cli -p 5000 127.0.0.1:5000] sentinel master mymaster 1) "name" 2) "mymaster" 3) "ip" 4) "127.0.0.1" 5) "port" 6) "6379" 7) "runid" 8) "953ae6a589449c13ddefaee3538d356d287f509b" 9) "flags" 10) "master" 11) "link-pending-commands" 12) "0" 13) "link-refcount" 14) "1" 15) "last-ping-sent" 16) "0" 17) "last-ok-ping-reply" 18) "735" 19) "last-ping-reply" 20) "735" 21) "down-after-milliseconds" 22) "5000" 23) "info-refresh" 24) "126" 25) "role-reported" 26) "master" 27) "role-reported-time" 28) "532439" 29) "config-epoch" 30) "1" 31) "num-slaves" 32) "1" 33) "num-other-sentinels" 34) "2" 35) "quorum" 36) "2" 37) "failover-timeout" 38) "60000" 39) "parallel-syncs" 40) "1"
就像你看到的,它打印了大量的關於master的信息。有一些是對於我們非常有用的:
- num-other-sentinels 是2,所以我們知道Sentinel已經檢測到這個master的其他兩個Sentinel。如果你檢查日誌,你會看到生成了 +sentinel 事件。
- flags 是 master。如果master down了,我們在這裏希望看到 s_down 或者 o_down。
- num-slaves這裏是1,所以Sentinel還檢測到master有一個slave。
爲了更多的探索這個實例,你可能想嘗試下面的兩個命令:
SENTINEL slaves mymaster SENTINEL sentinels mymaster
第一個會打印連接到master的slave的信息,第二個是關於其他Sentinel的信息。
獲取當前master地址
我們已經說明,Sentinel還可以作爲客戶端的配置提供者,連接到一組master和slave。因爲故障轉移和重新配置,客戶端沒有辦法指定誰是當前活動master,所以Sentinel提供了一個API解決這個問題:
127.0.0.1:5000] SENTINEL get-master-addr-by-name mymaster 1) "127.0.0.1" 2) "6379"
測試故障轉移
這時候我們部署的sentinel已經準備好了測試。我們只需要kill掉master檢查配置是否改變。所以只需要做如下的事情:
redis-cli -p 6379 DEBUG sleep 30
這個命令將使master休眠30秒不能訪問。這主要是模仿master由於一些原因掛掉。
如果你檢查Sentinel日誌,以應該能看到很多動作:
- 每個Sentinel檢查到master掛掉,有一個+sdown事件。
- 這個時間稍後升級到 +odown,這意味着多個Sentinel商定了master不可到達的事實。
- Sentinels投票選舉一個Sentinel啓動第一次故障轉移。
- 發生故障轉移。
如果你再次訪問 mymaster 當前master的地址,最終你這次應該得到一個不一樣的答案:
127.0.0.1:5000] SENTINEL get-master-addr-by-name mymaster 1) "127.0.0.1" 2) "6380"
到目前位置一切都順利,這個時候你可以跳轉到創建Sentinel部署或者可以閱讀更多信息瞭解所有的Sentinel命令和內部構建。
Sentinel API
Sentinel 提供了一個AP以便於檢查它的狀態,檢查監控的master和slave的健康,訂閱接收特定的通知,和在運行時改變Sentinel配置。
Sentinel默認使用TCP端口26379(注意6379是標準的Redis端口)。Sentinels使用Redis協議接收命令,所以你可以使用 redis-cli 或任何其他的Redis客戶端和Sentinel通信。
可以直接查詢Sentinel檢查監控的Redis實例的狀態,查看它知道的其它Sentinel,等等。或者,使用Pub/Sub,可以從Sentinel接收推送類型的通知,每次有事件發生,例如故障轉移,或者實例進入錯誤條件,等等。
Sentinel 命令
下面是一系列的接收命令,而不是全部的命令,用於修改Sentinel配置。
- PING - 這個命令簡單的返回PONE。
- SENTINEL masters - 展示監控的master清單和它們的狀態。
- SENTINEL master [master name] - 展示指定master的狀態和信息。
- SENTINEL slaves [master name] - 展示master的slave清單和它們的狀態。
- SENTINEL sentinels [master name] - 展示master的sentinel實例的清單和它們的狀態。
- SENTINEL get-master-addr-by-name [master name] - 返回master的IP和端口。如果故障轉移在處理中或成功終止,返回晉升的slave的IP和端口。
- SENTINEL reset [pattern] - 這個命令將重置所有匹配名字的masters。參數是blog風格的。重置的過程清空master的所有狀態,並移除已經發現和關聯master的所有slave和sentinel。
- SENTINEL failover [master name] - 如果master不可到達,強制執行一個故障轉移,而不徵求其他Sentinel的同意。
- SENTINEL ckquorum [master name] - 檢查當前的Sentinel配置是否能夠到達故障轉移需要的法定人數,並且需要授權故障轉移的多數。這個命令應該用於監控系統檢查部署是否正確。
- SENTINEL flushconfig - 強制Sentinel在磁盤上重寫它的配置,包括當前的Sentinel狀態。通常Sentinel每次重寫配置改變它的狀態。然而有時由於操作錯誤、硬盤故障、包升級腳本或配置管理器可能導致配置文件丟失。在這種情況下收到強制Sentinel重寫配置文件。這個命令即使上面的配置文件完全不見了。
運行時配置Sentinel
從Redis 2.8.4開始,Sentinel提供了API以便於添加、刪除、或者修改指定master的配置。注意如果有多個Sentinel,應該申請更改所有的Redis Sentinel實例。這意味這修改一臺Sentinel不會在網絡上自動傳播到其他的Sentinel實例。
下面是SENTINEL的子命令清單,用於修改Sentinel實例的配置。
- SENTINEL MONITOR [name] [ip] [port] [quorum] - 這個命令告訴Sentinal開始使用指定的name、ip、port、quorum監控新master。這個sentinel.conf配置文件裏的指令完全相同,區別是這裏不能使用主機名作爲IP,你需要提供一個IPv4或IPv6地址。
- SENTINEL REMOVE [name] - 用於刪除指定的master:master將不再受監控,並從Sentinel內部狀態完全刪除,所以通過SENTINEL masters命令不會再列處理,等等。
- SENTINEL SET [name] [option] [value] - SET命令和Redis的CONFIG SET 非常相似,用於修改指定master的配置參數。可以指定多組option / value。所有通過sentinel.conf配置的參數都可以通過SET命令設置。
下面是一個SENTINEL SET 命令的例子,用於修改名叫objects-cache的master的down-after-milliseconds。
SENTINEL SET objects-cache-master down-after-milliseconds 1000
正如前面提到的,SENTINEL SET 可以用於配置所有可在啓動配置文件裏設置的參數。此外還可以值更改quorum配置而不用刪除和重新添加master,只是使用:
SENTINEL SET objects-cache-master quorum 5
注意沒有對應的GET命令因爲SENTINEL MASTER以簡單的格式提供了所有的配置參數。
添加或刪除Sentinel
添加新的Sentinel非常簡單因爲Sentinel實現了自動檢測的機制。你需要做的就是啓動新的Sentinel配置監控當前活動的master。10秒之內Sentinel就會獲得其他的Sentinel列表和master的slave。
如果你需要一次添加多個Sentinel,建議添加一個之後再添加另一個,在添加下一個之前等待其他的Sentinel已經知道了第一個Sentinel。這有助於始終保證可以在一個分區獲得多數,在添加Sentinel過程中發生故障的機會。
通過每添加一個Sentinel有一個30秒的延遲非常容易獲得。
在這個過程的最後要使用命令SENTINEL MASTER mastername一邊檢查是否所有Sentinel同意監控master的Sentinel的總數。
刪除Sentinel有點複雜:Sentinel永遠不會忘記見到過的Sentinel,即使它們在很長時間內不能到達,因爲我們不希望動態的改變需要授權的多數並創建一個新值。所以爲了刪除一個Sentinel應該在網絡分區裏執行以下步驟:
- 停止想要刪除的Sentinel進程。
- 發送一個SENTINEL RESET * 命令到所有的Sentinel實例。在執行實例之間每個至少等待30秒鐘。
- 檢查所有Sentinel協商的當前活動的Sentinel數量,通過檢查每個Sentinel的SENTINEL MASTER mastername輸出。
刪除老master或不可及的slave
Sentinels從不會忘記指定master的slave,即使他們有很長時間不可及。這非常有用,因爲Sentinels應該能夠正確的重新配置網絡分區或故障事件之後返回的slave。
此外,故障轉移之後,轉移故障的master實質上添加作爲新master的slave,這個方式一旦它再次可用就會重新配置從新master複製。
然而有時候你可能想從哨兵監控的slave列表裏永久的刪除一個salve(也可能是老master)。
爲了做到這個事情,你需要向所有的哨兵發送一個SENTINEL RESET mastername命令:他們將會在10秒內刷新slave列表,只添加一個列表複製當前master的信息輸出。
發佈/訂閱消息
客戶端可以使用Sentinel作爲Redis兼容的發佈/訂閱服務器以便於SUBSCRIBE或PSUBSCRIBE通道並獲得關於特殊事件的通知。
通道的名字和事件的名字相同。比如通道的名字是 +sdown 將會接收所有實例進入 SDOWN 狀態的通知。(SDWON意思是實例從Sentinel的視角不再可及)
訂閱所有的消息只需要使用 PSUBSCRIBE *。
下面是你可以使用的API通道和消息格式列表。第一個單詞是通道/事件名稱,其餘的部分是數據格式。
注意:指定的 instance details部分意思是提供下面的參數確定目標實例。
[instance-type] [name] [ip] [port] @ [master-name] [master-ip] [master-port]
表示master的部分(從@到最後)是可選的並只在實例不是master自己的時候指定。
- +reset-master [instance details] -- master被重置。
- +slave [instance details] -- 發現附加一個新slave。
- +failover-state-reconf-slave [instance details] -- 故障轉移狀態更改爲 reconf-slaves 狀態。
- +failover-detected [instance details] -- 有另一個Sentinel啓動一個故障轉移或者任何其他的外部實體被檢測到(附加的salve變成master)。
- +slave-reconf-sent [instance details] -- leader sentinel向實體發送 SALVEOF命令以便於重新配置新Slave。
- +slave-reconf-inprog [instance details] -- slave正在被從新配置爲新master的slave,但是同步過程還沒有完成。
- +slave-reconf-done [instance details] -- slave現在已經同步到新master。
- -dup-sentinel [instance details] -- 指定master的一個或多個Sentinel被移除。(當Sentinel實例重啓時會發生)
- +sentinel [instance details] -- 發現添加一個sentinel。
- +sdown [instance details] -- 指定的實例現在在主觀上是Down狀態。
- -sdown [instance details] -- 指定的實例不再是主觀上的Down狀態。
- +odown [instance details] -- 指定的實例現在在客觀上是Down狀態。
- -odown [instance details] -- 指定的實例在客觀上不再是Down狀態。
- +new-epoch [instance details] -- 當前時代被更新。
- +try-failover [instance details] -- 新故障轉移過程中,等待其他多數決定。
- +elected-leader [instance details] -- 指定的時代贏得了選舉,可以執行故障轉移。
- +failover-state-select-slave [instance details] -- 新的故障轉移狀態是 select-slave:嘗試查找適當晉升的slave。
- no-good-slave [instance details] -- 沒有適當晉升的slave。稍後重試,但是這可能會發生改變並終止故障轉移。
- selected-slave [instance details] -- 找到適合晉升的slave。
- failover-state-send-slaveof-noone [instance details] -- 嘗試重新配置晉升的slave作爲master,等待轉換。
- failover-end-for-timeout [instance details] -- 由於超時終止故障轉移,不管怎樣,slave最後將被配置爲複製新master。
- failover-end [instance details] -- 故障轉移成功完成。所有slaves被重新配置爲新master的從。
- switch-master [master name] [oldip] [oldport] [newip] [newport] -- 在改變配置之後指定master新IP和地址。這是大多數外部用戶感興趣的消息。
- +tilt -- 進入 Tilt模式。
- -tilt -- 退出 Tilt模式
處理 -BUSY狀態
當Lua腳本運行時間超過配置的Lua腳本時間限制時,Redis實例返回-BUSY錯誤。在觸發故障轉移之前發生這種情況時,Sentinel將嘗試發送一個 SCRIPT KILL命令,它只在腳本只讀的時候成功。
如果嘗試過這個之後實例仍是一個錯誤狀態,最後就會啓動故障轉移。
Slaves優先級
Redis實例有一個salve-priority的配置參數。這個信息暴露在Redis slave的 INFO 輸出裏,Sentinel使用它以便於在可用的salves中獲取slave進行故障轉移:
- 如果Slave優先級設置爲0.slave永遠不會晉升爲master。
- 優先級數字越低越被Sentinel優先選擇。
例如如果有一個slave S1在當前master的同一個數據中心,並且另一個salve S2在另一個數據中心,可以設置S1的優先級爲10,S2的優先級爲100,所以如果master故障並且S1和S2都可用,S1將會被優先選擇。
瞭解關於slave選擇的更多信息,請參考 本文檔的 slave選擇和優先級部分。
Sentinel和Redis身份驗證
當master配置要求從客戶端提供密碼作爲安全措施時,slave也需要知道這個密碼以便於master的身份驗證並建立master-slave之間的連接用於異步複製協議。
通過以下配置指令可以實現:
- requirepass 在master裏,設置身份驗證密碼,並確定實例不處理沒有身份驗證的客戶端請求。
- masterauth 在slave裏,爲了slave驗證master以便於從master正確的複製數據。
當使用Sentinel時,沒有單獨的master,因爲在故障轉移之後slave可以改變master的角色,並且老master可以被重新配置爲slave,所以你要做的是在master和slaves的所有實例裏都配置上上面的指令。
這通常也是一個理智的設置因爲你不只是想保護master裏的數據,在slave裏也是一樣。
然而在一些罕見的情況下你需要slave不進行身份驗證就可以訪問,你可以將slave的優先級設置爲0,放置這個slave升級爲master,並在這個slave裏只配置masterauth指令,不用requirepass指令,以便於沒有授權的客戶端也可以訪問。
Sentinel客戶端實現
Sentinel需要顯示的客戶端支持,除非系統被配置爲執行腳本對所有請求執行重定向到新master實例。客戶端類庫的實現話題在文檔Sentinel clients guidelines有覆蓋。
更多高級概念
接下來的部分我們將會涉及Sentinel怎麼工作的一些細節,沒有依靠實現細節和算法並會在本文檔的其他部分涉及。
SDOWN和ODOWN故障狀態
Redis Sentinel對於正在down的狀態有兩個不同的概念,一個叫做主管的DOWN(SDOWN),它是一個本地Sentinel實例的狀態。另一個叫客戶端的Down(ODOWN),當足夠的Sentinel SDOWN狀態是可達的,並且從其他的Sentinel使用SENTINEL is-master-down-by-addr命令得到反饋,就進入ODOWN狀態。
從Sentinel的視角,當它在配置文件is-master-down-after-milliseconds參數指定的時間之內沒有收到有效的PING響應就會進入一個SDOWN條件。
PING的響應是下面的其中之一:
- PONE
- -LOADING 錯誤
- -MASTERDOWN 錯誤
任何其他的響應都視爲無效。但是注意一個合理的master通知自己在INFO輸出裏作爲salve被視爲down。
注意SDOWN要求不可接受的請求是對整個配置間隔,所以例如如果間隔是30000毫秒,且我們每29秒接受的一個ping請求,實例被認爲是正常運行的。
SDOWN不能觸發故障轉移:這只是意味着一個單獨Sentinel認爲Redis實例不可及。觸發一個故障轉移,必須到達ODWN狀態。
從SDOWN轉換到ODOWN沒有共識的算法,僅僅是形式上的:如果一個指定的Sentinel從足夠多的Sentinel獲得報告在指定的時間範圍內master沒有工作,SDOWN就升級爲ODOWN。如果確認在稍後丟失,標記就被清空。
更嚴格的身份驗證是需要大多數確認以便於真正的啓動故障轉移,但是沒有到ODOWN狀態不會觸發故障轉移。
ODOWN只適用於master。其他的實例Sentinel不要求,所以SLAVE和其他sentinel永遠不會到達ODOWN狀態,僅僅是SDOWN。
SDOWN還有其他含義。例如Sentinel執行故障轉移時slave在SDOWN狀態不會被選中晉級。
自動發現Sentinels和Slaves
Sentinel保持與其他Sentinel的連接以便於相互的檢查可用性,並傳輸消息。然而你不需要在每個Sentinel實例裏配置其他Sentinel的地址,因爲Sentinel使用Redis實例的發佈/訂閱功能用於發現監控相同master和slave的其他Sentinel。
這個功能由向_sentinel_:hello通道發送問候消息實現。
同樣你不需要配置master附加的slave列表,因爲Sentinel會自動發現。
- Sentinel每兩秒發佈一個消息到每個監控的master和slave 發佈/訂閱通道_sentinel_hello,消息內容包括ip,port,runid宣佈他的存在。
- Sentinel訂閱每個master和slave的_sentinel_:hello通道,查找不知道的sentinels。一旦檢測到新的sentinel,他們就添加爲這個master的sentinel。
- 問候消息包括當前master完整的配置。如果Sentinel有一個配置比接收的老,它會立刻更新配置。
- 在添加sentinel到master之前,sentinel總是檢測是否已經有相同runid或相同地址的sentinel。在這種情況下會刪除所有匹配的sentinel,並添加新的。
故障轉移過程之外的重新配置
即使不進行故障轉移,Sentinel也會一直嘗試設置當前配置。特別的:
- Slaves成爲master,將會配置爲當前master的slave。
- Slave連接到錯誤的master,會重新配置爲正確的master。
Sentinels重新配置slaves,有時必須監控錯誤配置,比週期性的廣播新配置要好。
Sentinel使用狀態配置信息在接收更新之前嘗試改變slaves配置。
還要注意怎樣嘗試利用當前配置防止故障分區:
- Master在可用的時候被重新配置爲slave。
- Slaves分區在重新配置分區期間一旦可及就會被重新配置。
在這個部分要記住的重要的是:Sentinel是一個系統,每個進程一直嘗試利用最後一個合理的配置設置監控的實例。
Slave選擇和優先級
當Sentinel實例準備執行故障轉移時,由於master在ODOWN狀態且從多數的Sentinel實例接收到授權,需要選擇一個適當的slave。
選擇過程會評估slave下面的信息:
- 從master斷開的次數。
- Slave優先級。
- 複製偏移量。
- Run ID。
發現Slave從master斷開超過10倍的超時時間,加上Sentinel認爲master不可用的時間,就認爲不適合故障轉移並跳過。
更嚴格的條件,slave的INFO輸出信息顯示從master斷開超過:
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
被認爲是不適合的並完全忽略。
Slave選擇只認爲通過上面的測試,並在上面的標準基礎上排序:
- Slave通過Redis實例的redis.conf文件配置的slave-priority排序。優先級越低越被優先考慮。
- 如果優先級相同,檢查slave的複製偏移量,並選擇接收更多數據的slave。
- 如果多個slave有相同的優先級和同樣的處理數據過程,就會執行一個更進一步的驗證,選擇一個有較短run ID的slave。run ID 對於 slave沒太大用,但是非常有助於選擇slave的過程,而不是隨機選擇slave。
如果要強烈的匹配,Redis master和slaves都要配置 salve-priority。否則所有的實例可以使用默認的run ID運行(建議這樣設置,因爲這比通過複製偏移量選擇slave更感興趣)。
Redis實例可以配置特定的slave-priority爲0用於永遠不讓sentinel選舉爲master。但是用這種方式配置slave會在故障轉移之後重新配置以便於複製新master的數據,不同的只是他永遠不會變成一個master。
算法和內部構建
在接下來的部分我們將會探究Sentinel特性的細節。用戶不需要關注所有的細節,但是深度理解可以幫你更有效的開發和操作Sentinel。
Quorum
上面的章節中展示了每個監控的master都關聯一個quorum配置。它指定需要商定master不可及或錯誤條件Sentinel進程數量以便於觸發故障轉移。
然而,觸發故障轉移之後,爲了真正的執行故障轉移,至少大多數的Sentinel必須授權Sentinel進行故障轉移。如果只有少數的Sentinel存在,Sentinel將永遠不會執行故障轉移。
讓我們試着使事情更清晰:
- Quorum:需要檢測錯誤條件以便於標記master爲ODOWN的Sentinel進程數量。
- 通過ODOWN狀態觸發故障轉移。
- 一旦觸發了故障轉移,Sentinel嘗試請求大多數的Sentinel授權故障轉移(如果quorun設置的數量大於多數,則需要大於等於)。
不同之處似乎很小,但是很容易理解和使用。例如如果你有5個Sentinel實例,quorum設置爲2,一旦2個Sentinel認爲master不可及就會觸發故障轉移,但是隻有其中一個Sentinel得到至少3個同意的時候纔可以故障轉移。
如果把quorum配置爲5,所有的Sentinel都必須統一master的錯誤條件,且從所有的Sentinel得到授權纔可以故障轉移。
這意味着quorum可以通過兩個方式用於調整Sentinel:
- 如果quorum設置的值小於我們部署的大多數Sentinel,這基本上是合理的,即使少數的Sentinel不可通信業可以觸發故障轉移。
- 如果quorum設置的值大於多數的Sentinels,只有當大多數的Sentinel連接正常並同意纔可以故障轉移。
配置 epochs
Sentinel要求從多數獲得授權以便於啓動故障轉移有幾個重要原因:
當一個Sentinel被授權,它得到一個唯一的配置epoch控制故障轉移。這是一個數字它將用於在故障轉移之後新配置的版本。因爲多數同意授權指定的版本到指定的Sentinel,沒有其他的Sentinel能夠使用它。這意味着每個故障轉移的配置都有一個唯一的版本。我們就知道了這爲什麼這麼重要。
此外Sentinel有個規則:如果Sentinel選舉其他的Sentinel啓動指定master的故障轉移,它將等待一些時間再次嘗試相同的master。這個延遲時間你可以在sentinel.conf文件裏配置failover-time。
這意味着Sentinel在相同的時間不會嘗試相同的master,先嚐試第一個授權,如果失敗在段時間後嘗試另一個,以此類推。
Redis Sentinel保證liveness屬性如果多數Sentinels可以通信,最後就會被授權故障轉移如果master是down。
Redis Sentinel還保證safety屬性每個Sentinel將使用不同的configuration epoch轉移相同的master。
配置傳播
一旦Sentinel能夠成功的故障轉移master,它將啓動傳播新的配置以便於其他的Sentinel更新指定master的信息。
一個故障轉移被認爲是成功的,它需要Sentinel能夠發送SLAVEOF NO ONE命令到選擇的slave,並且切換到master稍後會在master的INFO輸出裏觀察。
這時候,即使在slave的重新配置過程中,故障轉移也被認爲是成功的,且所有的Sentinel被要求報告新配置。
新配置方式的傳播方式就是爲什麼我們需要用不同的版本號(epoch)授權每個Sentiel故障轉移的原因。
每個Sentinel使用發佈/訂閱消息的方式連續不斷的傳播它的配置版本,在相同的時間所有的Sentinel等待消息查看其他Sentinel傳播的配置是什麼。
配置信息在_sentinel_:hello發佈/訂閱通道里廣播。
因爲每個配置有不同的版本號,高版本總是勝過低版本。
所以比如所有的Sentinel認爲master在192.168.1.53:6379。這個配置的版本是1.一些時間之後被授權的故障轉移版本是2.如果故障轉移成功,它會開始廣播新配置,比如192.168.1.50:9000.所有的其他實例會看到這個版本號是2的配置並更新他們的配置,因爲新的配置有更高的版本號。
這意味着Sentinel保證一個第二leveness屬性:一組Sentinel能夠通信並彙集更高保本號更新相同的配置。
基本上如果網絡分區,每個分區將彙集更高的本地配置。在特殊的案例中沒有分區,有一個單獨的分區且每個Sentinel會商定配置。
分區下的一致性
Redis Sentinel配置最終是一致的,所以配個分區將彙集更高可用的配置。然而在現實系統中使用Sentinel有三個不同的角色:
- Redis Instances。
- Sentinel instances。
- Clients。
爲了定義系統特性我們必須考慮這三個角色。
下面是一個簡單的有三個節點的網絡,每個節點運行一個Redis實例和一個Sentinel實例:
+-------------+ | Sentinel 1 |----- Client A | Redis 1 (M) | +-------------+ | | +-------------+ | +------------+ | Sentinel 2 |-----+-- // ----| Sentinel 3 |----- Client B | Redis 2 (S) | | Redis 3 (M)| +-------------+ +------------+
這個系統中初始狀態是Redis3是master,Redis1和2是slaves。發生了一個分區隔離了老master,Sentnel1和2啓動一個故障轉移將Sentinel 1升級爲master。
Sentinel屬性保證Sentinel1和2現在有master的新配置。然而3始終是老配置因爲它在不同的分區裏。
我們知道當網絡分區修復之後Sentinel3會獲取更新的配置,然而在分區期間如果有客戶端連接老master分區會發生什麼?
客戶端會始終能寫到Redis 3,老的master。當分區重新接入,Redis 3將重新變爲Redis 1的salve,並且所有在分區期間寫入的數據都將會丟失。
根據你的配置你可能不想讓這個事情發生:
- 如果你正在使用Redis作爲緩存,它可以使客戶端B能夠寫入到老master,即使數據丟失。
- 如果你正在作爲存儲,這就不好了而且你需要配置系統以便於部分的防止這個問題。
因爲Redis是異步複製,沒有辦法完全的防止數據丟失,然而你可以使用下面的配置選項綁定Redis3和Redis1之間的鬆散度。
min-slaves-to-write 1 min-slaves-max-lag 10
使用上面的配置,當切換master時,如果有1臺以上slave不可寫入就會停止接收寫入操作。
因爲主從複製是異步的,slave斷開連接或者不發送異步確認大於指定的max-lag的秒數意味着不能真正的寫入。
在上面的例子中使用這個配置,Redis3會在10秒之後變爲不可用。當分區恢復時,Sentinel 3配置將彙集新的,且Client B能夠檢查有效的配置並繼續。
通常Redis+Sentinel作爲一個整體是一個最終一致的系統,且數據丟棄老的複製當前master的數據,所以始終有一個丟失確認寫入的窗口。這是因爲Redis異步複製。注意這是Sentinel自身的限制,如果你想完整一致的故障轉移,相同的屬性將仍然適用。有兩個方法避免丟失確認的寫入:
- 使用同步複製
- 使用一致性系統。
Redis現在不能使用上面的任何系統,並且這在開發目標之外。但是有代理實現的方案2,例如SoundCoud Roshi,或者 Netfix Dynomite。
Sentinel持久化狀態
Sentinel狀態持久化到Sentinel配置文件裏。例如每次接收一個新配置,或者創建,配置文件會和配置epoch一起持久化到硬盤上。
這意味着在停止和重啓Sentinel進程的時候是安全的。
TILT 模式
Redis Sentinel嚴重依賴於計算機時間:例如爲了理解一個實例是否可用,它記錄上次成功響應PING命令的時間,並和當前時間對比分析已經多久了。
然而如果計算機時間意外的改變了,或者計算機非常忙,或者進程由於某些原因損壞,Sentinel可能會有一個意想不到的行爲。
TILT模式是一個特殊的 "protection" 模式,當檢測到可以降低系統可靠性怪異現象時,Sentinel可以進入 "protection" 模式。Sentinel時間計數器一般每秒調用10次,所以我們期望大約100毫秒在兩個調用之間中斷。
什麼哨兵調用中斷定時器,並和當前調用對比:如果時常是負數或出乎意料的大(2秒或更大)就進入TILT模式。
當進入TILT模式,Sentinel將繼續監控每件事,除了:
- 停止代理。
- 開始否定的回答SENTINEL is-master-down-by-addr請求,不再可信。
一切恢復正常30秒之後,就退出TILT模式。
注意在某些方面TILT模式可以使用很多內核支持的單調時鐘API替換。然而仍不清楚這是否是一個好的解決方案,因爲當前系統避免問題的過程只是在很長時間暫停或不執行調度。