1. 程式人生 > >《Redis官方文件》sentinel

《Redis官方文件》sentinel

原文連結

Redis Sentinel 文件

Redis Sentinel為Redis提供了高可用解決方案。實際上這意味著使用Sentinel可以部署一套Redis,在沒有人為干預的情況下去應付各種各樣的失敗事件。

Redis Sentinel同時提供了一些其他的功能,例如:監控、通知、併為client提供配置。

下面是Sentinel的功能列表:

  • 監控(Monitoring):Sentinel不斷的去檢查你的主從例項是否按照預期在工作。
  • 通知(Notification):Sentinel可以通過一個api來通知系統管理員或者另外的應用程式,被監控的Redis例項有一些問題。
  • 自動故障轉移(Automatic failover):如果一個主節點沒有按照預期工作,Sentinel會開始故障轉移過程,把一個從節點提升為主節點,並重新配置其他的從節點使用新的主節點,使用Redis服務的應用程式在連線的時候也被通知新的地址。
  • 配置提供者(Configuration provider):Sentinel給客戶端的服務發現提供來源:對於一個給定的服務,客戶端連線到Sentinels來尋找當前主節點的地址。當故障轉移發生的時候,Sentinels將報告新的地址。

Sentinel的分散式特性
Redis Sentinel是一個分散式系統,Sentinel執行在有許多Sentinel程序互相合作的環境下,它本身就是這樣被設計的。有許多Sentinel程序互相合作的優點如下:

  1. 當多個Sentinel同意一個master不再可用的時候,就執行故障檢測。這明顯降低了錯誤概率。
  2. 即使並非全部的Sentinel都在工作,Sentinel也可以正常工作,這種特性,讓系統非常的健康。

所有的Sentinels,Redis例項,連線到Sentinel和Redis的客戶端,本身就是一個有著特殊性質的大型分散式系統。在這篇文章中,我將逐步地介紹這些概念,最開始是一些基本的資訊來理解Sentinel的基本屬性,後面是更復雜的資訊來理解Sentinel是怎麼工作的。

快速開始

獲取 Sentinel

當前版本的Sentinel的被稱為 Sentinel 2 。它使用更強更簡單的預測演算法重寫了Sentinel的初始化實現(文章的後面將會解釋)。

Redis Sentinel 的一個穩定版本是隨著Redis2.8和3.0一起的。這兩個是Redis最新的穩定版。

新的進展在unstable

分支下進行,一旦新的特性是穩定的,就會被合併到2.8和3.0分支。

和Redis 2.6一起的Redis Sentinel版本1,是過時的。我們不該使用它。

執行Sentinel

如果你使用redis-sentinel可執行檔案,你可以使用下面的命令來執行Sentinel:

redis-sentinel /path/to/sentinel.conf

另外,你可以直接使用redis-server並以Sentinel模式來啟動:

redis-server /path/to/sentinel.conf --sentinel

兩種方式是一樣的。

不管咋樣,使用一個配置檔案來執行Sentinel是必須的,這個檔案被系統使用來儲存當前狀態,如果重啟,這些狀態會被重新載入。如果沒有配置檔案或者配置檔案的路徑不對,Sentinel將會拒絕啟動。

預設情況下,Sentinels監聽TCP埠26379,所以為了讓Sentinels執行,你的機器的26379埠必須是開啟的,用來接收其他Sentinel例項的連線,否則,Sentinels不能互相交流,也不知道該幹什麼,也不會執行故障轉移。

部署之前瞭解關於Sentinel的基本東西

  1. 一個健康的叢集部署,至少需要三個Sentinel例項
  2. 三個Sentinel例項應該被放在失敗獨立的電腦上或虛擬機器中,比如說不同的物理機或者在不同的可用區域上執行的虛擬機器。
  3. Sentinel + Redis 分散式系統在失敗期間並不確保寫入請求被儲存,因為Redis使用非同步拷貝。可是有很多部署Sentinel的 方式來讓視窗把丟失寫入限制在特定的時刻,當然也有另外的不安全的方式來部署。
  4. 如果你在開發環境中沒有經常測試,或者在生產環境中也沒有,那就沒有高可用的設定是安全的。你或許有一個錯誤的配置而僅僅只是在很晚的時候才出現(凌晨3點你的主節點宕掉了)。
  5. Sentinel,Docker ,其他的網路地址轉換表,埠對映 使用應該很小心的使用:Docker執行埠重新對映,破壞Sentinel自動發現另外的Sentinel程序和一個主節點的從節點列表。在文章的稍後部分檢視更過關於Sentinel和Docker的資訊。

Sentinel配置

Redis原始碼中包含一個名為sentinel.conf的檔案,是一個你可以用來配置Sentinel的示例配置檔案。一個典型的最小配置檔案像下面這樣:

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

你僅僅只需要指定要監控的主節點,並給每個單獨的主節點一個不同的名稱。不需要指定從節點,從節點會被自動發現。Sentinel將會根據從節點額外的資訊自動更新配置(為了在重啟時保留資訊)。在故障轉移中每當一個從節點被提升為主節點或者當一個新的Sentinel被發現的時候,配置資訊也被重新寫入。

示例配置在上面,監控兩個Redis例項集合,每個集合由一個主節點和不明確數量的從節點組成。一個集合叫做mymaster,另外一個叫做resque。

sentinel monitor引數的意思在下面

sentinel monitor <master-group-name> <ip> <port> <quorum>

為了更加清晰明瞭,讓我們一行一行來檢查配置選項的意思:

第一行用來告訴Redis監控一個叫做mymaster的主節點,地址是 127.0.0.1 埠號是6379,並且有2個仲裁機器。所有的意思都很明顯,但是除了這個quorum 引數:

  • quorum 是 需要同意主節點不可用的Sentinels的數量
  • 然而quorum 僅僅只是用來檢測失敗。為了實際的執行故障轉移,Sentinels中的一個需要被選定為leader並且被授權進行操作,這僅僅發生在大多數Sentinels進行投票的時候。

比如如果你有五個Sentinel程序,對於一個主節點quorum被設定為2,下面是發生的事情:

  • 同時有兩個Sentinels同意主節點不可用,其中的一個將會嘗試開始故障轉移。
  • 如果至少有三個Sentinels是可用的,故障轉移將會被授權並且開始

實際中,這意味著在失敗時,如果大多數的Sentinel程序沒有同意,Sentinel永遠不會開始故障轉移。

其他的Sentinels選項

其他的選項幾乎都是如下形式:

sentinel <option_name> <master_name> <option_value>

用途如下:

down-after-milliseconds:當一個例項失去聯絡(要麼不回覆我們的請求,要麼回覆一個錯誤)超過了這個時間(毫秒為單位),Sentinel就開始認為這個例項掛掉了。

parallel-syncs:設定的從節點的數量,這些從節點在一次故障轉移過後可以使用新的主節點進行重新配置。數量越少,完成故障轉移過程將花費更多的時間,如果從節點為舊的資料提供服務,你或許不想所有的從節點使用主節點進行重新同步。複製程序對於從節點來說大部分是非阻塞的,還是有一個時刻它會停下來去從主節點載入資料。你或許想確保一次只有一個從節點是不可達的,可以通過設定這個選項的值為1來完成。

別的選項在文章的其他部分進行描述。

所有的配置引數都可以在執行時使用SENTINEL SET命令進行更改,檢視 Reconfiguring Sentinel at runtime章節獲取更多內容

Sentinel部署示例

現在你已經知道了Sentinel的基本資訊,你或許想知道哪裡放置你的Sentinel程序,需要多少個Sentinel程序等等。這個章節給出了幾個部署的例子。

為了以圖形(graphical )格式展示配置示例,我們使用ASCII藝術。下面是不同的符號的意思:

+--------------------+
| 這是一個獨立電腦   |
| 或者VM。我們稱它為 |
| “box”            |
+--------------------+

我們把我們想要執行的東西寫到boxes裡:

+-------------------+
| Redis master M1   |
| Redis Sentinel S1 |
+-------------------+

不同的box之間通過一條線連線,表示他們之間可以互相交流:

+-------------+               +-------------+
| Sentinel S1 |---------------| Sentinel S2 |
+-------------+               +-------------+

中斷的線條表示不同的網路分割槽:

+-------------+                +-------------+
| Sentinel S1 |------ // ------| Sentinel S2 |
+-------------+                +-------------+

同時還要注意:

  • 主節點稱為M1,M2,M3,…,Mn。
  • 從節點稱為R1,R2,R3,…,Rn。
  • Sentinels稱為S1,S2,S3,…,Sn。
  • 客戶端稱為C1,C2,C3,…,Cn。
  • 當一個例項因為Sentinels的行為轉換角色,我們把它放在方括號裡,所以[M1]表示一個例項現在是主節點。

注意永遠不要設定只有兩個Sentinels,因為開始一個故障轉移,Sentinels總是需要和大多數Sentinels交流。

示例1:僅僅只有兩個Sentinels,永遠不要這麼做

+----+         +----+
| M1 |---------| R1 |
| S1 |         | S2 |
+----+         +----+

Configuration: quorum = 1

在這個設定中,如果M1宕掉了,R1將會被提升至主節點,因為兩個Sentinels將會達成一致(顯然把quorum設定為1),並且授權開始一個故障轉移因為大多數是兩個。顯然,表面上可以工作,但是請檢查下一個點來看看為什麼這種設定是不可以的。

如果M1的box停止工作,M1也會停止。執行在另外一個box中的S2將不會被授權進行故障轉移,所以系統將不可用。

注意,需要大多數是為了應付不同的故障,最新的配置稍後會傳播給所有的Sentinels。同時注意在上述設定中單獨一邊的故障轉移能力,沒有任何協議,將是非常危險的:

+----+           +------+
| M1 |----//-----| [M1] |
| S1 |           | S2   |
+----+           +------+

在上面的配置中,我們完美對稱地建立了兩個主節點(假設S2在沒有授權的情況下可以進行故障轉移),客戶端或許會不確定寫往哪一邊,並且沒有辦法理解當分割槽治癒時候哪邊的配置是正確的。

所以請至少部署三個Sentinels在三個不同的box當中。

示例2:三個box的基本設定

這是一個非常簡單的設定,擁有更加安全的優點。它是基於三個boxes的,每個box執行一個Redis程序和Sentinel程序。

       +----+
       | M1 |
       | S1 |
       +----+
          |
+----+    |    +----+
| R2 |----+----| R3 |
| S2 |         | S3 |
+----+         +----+

Configuration: quorum = 2

如果M1掛掉,S2和S3將認同這次失敗,並且能授權開始一次故障轉移,這樣使客戶端可以繼續使用。

在每一個Sentinel設定中,Redis是非同步複製的,總是有丟失一些寫入資料的危險,因為當一個從節點被提升為主節點的時候一個寫入確認還沒有到達。然而在上面的設定中,還有一種更加危險的情況,由於客戶端和一個老的主節點在一個網路分割槽中,就像下面這樣:

         +----+
         | M1 |
         | S1 | <- C1 (writes will be lost)
         +----+
            |
            /
            /
+------+    |    +----+
| [M2] |----+----| R3 |
| S2   |         | S3 |
+------+         +----+

在這種情況下,網路分割槽把舊的主節點[M1]給孤立了,所以從節點R2被提升為主節點。然而,像客戶端C1,和舊的主節點在同一個網路分割槽中,或許繼續像舊的主節點寫入資料。當分割槽治癒,這些資料將永久丟失,這個舊得主節點將會被重新配置,作為新的主節點下的一個從節點,並丟棄它自己的資料。

可以使用下面的Redis複製特性減輕這個問題,如果一個主節點發現它不再能夠把它的寫入請求傳送給指定數量的從節點,它就停止接受寫入請求。

min-slaves-to-write 1
min-slaves-max-lag 10

當上面的配置應用於一個Redis例項。Redis發現它不能寫入至少一個1從節點,作為主節點的Reids將會停止接受寫入請求。由於複製是非同步,不能寫入也意味著從節點也是斷開的,或者超過了指定的max-lag秒數沒有傳送非同步迴應。

在上面的示例中,使用這個配置的舊的主節點M1,在10秒過後就不可用了。當分割槽治癒,Sentinel配置將會統一為新的,客戶端C1將獲取到一個有效的配置並且繼續。

然而天下沒有免費的午餐,在這種改進下,如果兩個從節點掛掉了,主節點將會停止接收寫入請求,這就是一個權衡。

示例3:Sentinel在客戶端所在的box中

有時候,我們只有兩個Redis box是可用的,一個給主節點,一個給從節點。在那種情況下,示例2中的配置是不可行的,我們可以採取下面的方法,Sentinels被放置在客戶端所在的地方:

            +----+         +----+
            | M1 |----+----| R1 |
            | S1 |    |    | S2 |
            +----+    |    +----+
                      |
         +------------+------------+
         |            |            |
         |            |            |
      +----+        +----+      +----+
      | C1 |        | C2 |      | C3 |
      | S1 |        | S2 |      | S3 |
      +----+        +----+      +----+

      Configuration: quorum = 2

在這種設定下,Sentinels的視角和客戶端是 一樣的:如果大部分的客戶端認為一個主節點是可用的,它就是可用的。這裡的C1,C2,C3是一般的客戶端, 並不意味著C1是連線到Redis的單個客戶端,它更像一個應用伺服器,一個Redis app,或者類似的東西。

如果M1和S1所在的box掛掉了,故障轉移將會進行,但是很明顯的看到不同的網路分割槽將導致不同的行為。比如說,如果客戶端和Redis服務斷開連線,Sentinel將不會被設定,因為Redis的主節點和從節點都是不可用的。

注意如果C3和M1在一個分割槽,我們有了一個和示例2中描述的類似的問題,不同的是,這裡我們沒有辦法打破對稱,因為只有一個主節點和從節點,所以主節點不會停止接收請求。

所以這是一個有效的設定,但是例項2中的設定更有優勢,比如Redis高可用系統,Redis執行在同一個box中,更容易被管理,並且可以限制在小部分的分割槽中主節點接收寫入請求的時間。

示例4:Sentinel 客戶端 這一邊少於三個客戶端

示例3描述的設定中,如果客戶端這一邊的box少於不夠三個,這個 設定就不能使用。在這種情況下,我們需要藉助混合設定,像下面這樣:

            +----+         +----+
            | M1 |----+----| R1 |
            | S1 |    |    | S2 |
            +----+    |    +----+
                      |
               +------+-----+
               |            |  
               |            |
            +----+        +----+
            | C1 |        | C2 |
            | S3 |        | S4 |
            +----+        +----+

      Configuration: quorum = 3

這和示例3中的設定非常相似,但是這裡我們在可用的四個box中運行了四個Sentinel。如果主節點M1變成不可用節點,其他三個Sentinel將執行故障轉移。

理論上,當移除S2和S4正在執行的box,這個設定可以工作,把quorum設定為2。然而,在應用層沒有高可用的系統,想在Redis這一邊得到高可用是不太可能的。

Sentinel,Docker,NAT 和可能的問題

Docker使用被稱為埠對映的技術:與一個程式認為他使用的埠相比,執行在Docker容器裡面的程式可能被暴露在不同的埠上。為了執行多個容器在相同的伺服器上同時使用同一個埠,這是非常有用的。

Docker不是唯一會發生這件事情的軟體系統,也有其他的網路地址轉換設定導致埠是被重對映,並且有時候沒有埠,只有IP地址。

埠和地址重對映在兩個方面製造了與Sentinel有關的問題:

  1. Sentinel的自動發現服務將停止工作,因為它使基於每個Sentinel 往它監聽的埠和IP地址廣播hello訊息來實現的。但是Sentinels沒有辦法來理解埠和IP地址被重映射了,所以他會宣佈它和其他的Sentinels的連線是不正常的。
  2. 在一個主節點的INFO輸出中,從節點 被列出來也是類似的方式:主節點檢查遠端對等的TCP連線來發現地址,在握手過程中,從節點自己廣告他的埠,然而由於相同的原因,埠或許是錯誤的。

因為Sentinels自動發現從節點使用主節點的INFO輸出資訊,發現的從節點是不可達的,並且Sentinel將永遠不會開始故障轉移,因為從系統的觀點來看,沒有好的從節點,所以目前沒有方式監控使用Docker部署的主節點和從節點例項,除非你通知Docker以1:1對映埠。

對於第一個問題,萬一你想使用Docker執行一堆Sentinel例項,你可以使用下面的兩個Sentinel配置,為了強迫Sentinel宣佈一個指定的埠和IP:

sentinel announce-ip <ip>
sentinel announce-port <port>

注意,Docker可以執行host networking模式。這就不會有問題因為埠不會被重新對映。

快速教程

在文章接下來的部分中,所有的說明都是關於Sentinel API,配置和語義。對於想盡快上手的人,這部分的教程展示了三個Sentinel怎麼配置和互動。

現在我假設三個例項分別在埠5000、5001、5002上。我也假設你在6379上有一個主節點Redis例項,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作為埠號。

上面的配置中需要注意的一些事情:

  • 主節點叢集稱為mymaster,它定義了主節點和它的從節點。因為每個master set 有一個不同的名稱,Sentinel能同時監控不同的主節點和從節點的集合。
  • quorum被設定為2。
  • down-after-milliseconds的值是5000毫秒,就是5秒鐘,所以在這個時間內一旦我們不能收到回覆,主節點將發現失敗。

一旦你啟動了三個Sentinels,可以看到他們列印的一些資訊:

+monitor master mymaster 127.0.0.1 6379 quorum 2

這是一個Sentinel事件,如果你SUBSCRIBE 了指定名稱的事件,你可以收到這種事件通過釋出/訂閱。

Sentinel在故障檢測和故障轉移中生成和列印不同的事件。

詢問Sentinel關於主節點的狀態

Sentinel開始啟動的時候,要做的事情是檢查主節點的監控是否正常:

$ 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"

像你所見的,它列印了主節點的一些資訊。有幾個是我們特別有興趣的:

  1. num-other-sentinels 是2,所以我們知道對於這個主節點Sentinel已經發現了兩個以上的Sentinels。如果你檢查日誌,你可以看到+sentinel事件發生。
  2. flags是master。如果主節點掛掉了,我們可以看到s_down或者o_down標誌。
  3. num-slaves現在是1,所以Sentinel發現有一個從節點。

為了探測關於這個例項更多的資訊,你可以嘗試下面的兩個命令:

SENTINEL slaves mymaster
SENTINEL sentinels mymaster

第一個將提供關於從節點類似的資訊,第二個是關於另外的Sentinels。

獲取當前主節點的地址

Sentinel也作為一個配置提供者,提供給客戶端它們想連線的主節點和從節點的叢集。因為可能的故障轉移和重配置,客戶端不知道一個叢集例項內當前的活著的主節點,所以Sentinel提供了一個API:

127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6379"

故障轉移測試

現在我們部署Sentinel可以被測試了。我們可以殺死主節點然後檢視配置變化。做我們可以做的:

redis-cli -p 6379 DEBUG sleep 30

這個命令讓我們的主節點變為不可達,睡眠30秒,它基本上模擬了主節點掛掉的一些原因。

如果你檢查Sentinel的日誌,你應該能看到許多動作:

  1. 每個Sentinel發現了主節點掛掉了並有一個+sdown事件
  2. 這個事件稍候升級到+odown,意味著大多數Sentinel已經同意了主節點是不可達的。
  3. Sentinels開始投票一個Sentinel開始並嘗試故障轉移
  4. 故障轉移開始

如果你重新詢問mymaster的當前主節點的地址,這次我們會得到一個不同的回覆:

127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6380"

目前為止一切都很順利,現在你可以建立你自己的Sentinel部署或者閱讀更多來理解Sentinel的命令和內部原理。

Sentinel API

Sentinel提供了一個API,可以用來檢查它的狀態,檢查主節點和從節點的健康,訂閱具體的通知並在執行時改變Sentinel的配置。

預設情況下Sentinel使用TCP埠號26379。Sentinels接收使用Redis的協議命令,所以你可以使用redis-cli或者其他未修改的Redis客戶端來和Sentinel交流。

直接查詢一個Sentinel來檢查所監控的Redis例項的狀態,看看另外的Sentinels所知道是可能的。有兩種方式,使用釋出/訂閱,每當一些事件發生,比如說一次故障轉移,或一個例項發生錯誤等,都可能接收到一個從Sentinels推送過來的通知。

Sentinel命令

下面是可以接收的命令列表,沒有覆蓋到那些用來改變Sentinel配置的命令:

  • PING 這個命令僅僅返回PONG。
  • SENTINEL masters  展示監控的主節點和它們的狀態列表
  • SENTINEL master <master name> 展示指定的主節點的資訊
  • SENTINEL salves <master name> 展示這個主節點的從節點,以及它們的狀態
  • SENTINEL  sentinels <master name> 展示這個主節點的sentinel例項,以及它們的狀態
  • SENTINEL  get-master-addr-by-name  <master name> 返回主節點的IP和埠號。如果這個主節點的一次故障轉移正在進行,就返回提升的從節點的IP和埠號
  • SENTINEL reset <pattern> 這個命令將會根據匹配的名稱重置主節點,pattern引數是萬用字元(glob-style)型別,重置程序清除主節點中之前的所有狀態,並且移除主節點發現和關聯的從節點和sentinel。
  • SENTINEL failover <master name> 如果主節點不可達,強制開始故障轉移,不需要另外的Sentinels同意。
  • SENTINEL ckquorum <master name> 檢查當前的Sentinel配置對於主節點的故障轉移是否能達到仲裁人數,並且大多數是需要的來授權故障轉移。這個命令應該在監控系統中使用來檢查一個Sentinel部署是否正常。
  • SENTINEL flushconfig  強制Sentinel重新寫入它的配置到磁碟上,包括當前Sentinel狀態。通常,每次當它狀態裡的一些東西改變,Sentinel就會重寫配置資訊。然而有時候配置檔案會丟失,由於錯誤的操作、磁碟故障、包升級指令碼、或配置管理。在那種情況下,強制Sentinel重寫它的配置檔案是容易的。甚至之前的配置檔案完全丟失,這個命令也能很好的工作。

執行時重新配置Sentinel

從Redis 2.8.4開始,Sentinel提供了一個API為了增加、移除或者改變一個給定的主節點的配置。注意如果你有多個sentinels,為了工作正常,你應該改變所有的Redis Sentinel 例項。這意味著改變單個Sentinel的配置不會把變化傳送給在網路中另外的Sentinels.

下面是SENTINEL自命令列表,用來更新一個Sentinel例項的配置:

  • SENTINEL MONITOR <name> <ip> <port> <quorum> 這個命令告訴Sentinel開始監控一個指定名稱、IP、埠號、quorum的主節點,它和sentinel.conf配置檔案中的sentinel monitor配置指令是完全相同的,不同的是這裡不能使用主機名作為IP,需要提供一個IPV4或IPV6地址。
  • SENTINEL REMOVE <name> 用來移除指定的主節點:主節點不再被監控,並且將被從Sentinel的內部狀態中被完全移除,所以不會被SENTINEL masters列出。
  • SENTINEL SET <name> <option> <value> SET命令和Reids的CONFIG SET指令非常相似,被用來改變一個指定主節點的配置引數。多個選項-值可以被指定。所有通過sentinel.conf配置的引數可以使用SET命令重新配置。

下面是SENTINEL SET命令的一個例子,為了修改一個名為objects-cache的主節點的down-after-milliseconds配置:

SENTINEL SET objects-cache-master down-after-milliseconds 1000

正如我們提到的,SENTINEL SET可以被用來設定所有的在啟動配置檔案中被設定的引數。而且,還可以僅僅改變主節點的quorum配置,而不需要使用SENTINEL REMOVE和SENTINEL MONITOR來刪除或者增加主節點,只需要使用:

SENTINEL SET objects-cache-master quorum 5

注意,沒有等價的GET命令,因為SENTINEL MASTER以一種易於解析的格式提供了所有的配置引數。

新增和移除sentinels

新增一個新的sentinel到你的部署中是很容易的一個過程,因為Sentinel有自動發現機制。所有的你需要做的事情是開啟一個新的Sentinel來監控當前的主節點。10秒過後,Sentinel將獲取到其他的Sentinels列表和當前主節點的從節點。

如果你想一次性增加多個Sentinels,建議你一個接一個的增加,等所有的Sentinels已經知道第一個再新增另一個。在新增的新的Sentinels過程中錯誤有可能發生,在這時候保證在一次網路分割槽內中大部分是可用是很有用的。

在沒有網路分割槽時,通過在30秒後增加每個新的節點,這是很容易實現的。

最後,可以使用SENTINEL MASTER mastername命令來檢查是否全部Sentinels都同意了監控主節點的Sentinels的總數。

移除一個Sentinel稍微複雜一點:Sentinels永遠不會忘記已經看到的Sentinels,甚至他們在相當長的一段時間內不可達,因為我們不想動態的改變授權一次故障轉移和建立新的配置所需要的大多數。在沒有網路分割槽的說話,需要執行下面的步驟來移除一個Sentinel:

  1. 停止你想要移除的Sentinel的程序
  2. 傳送一個SENTINEL RESET *命令到其他的Sentinel例項,相繼的,兩次傳送到例項之間至少等待30秒
  3. 檢查所有的Sentinels贊同的當前存活的Sentinels的數量,通過檢查每個SENTINEL MASTER mastername的輸出。

移除舊的主節點或不可達的從節點

Sentinels永遠不會忘記一個主節點的從節點,甚至當他們很長時間都不可達。這是很有用的,因為在一次網路分割槽或失敗事件發生後,Sentinels應該能正確地重新配置一個返回的從節點。

而且,在故障轉移發生之後,被故障轉移的主節點實際上被新增為新的主節點的從節點,一旦它可用的時候,這種方式將重新配置來複制新的主節點。

然而有時候你想從Sentinels監控的從節點列表中永久的移除一個從節點。

為了做這件事,你需要傳送一個SENTINEL RESET mastername命令給所有的Sentinels:它們將在十秒後重新整理從節點列表,只添加當前主節點的INFO輸出中正確的複製列表。

釋出/訂閱訊息

一個客戶端能使用一個Sentinel作為一個Redis相容的釋出/訂閱伺服器,為了SUBSCRIBE或者PSUBSCRIBE到指定頻道,獲取指定事件通知。

頻道的名稱和事件的名稱是一樣的。比如說名稱為+sdown的頻道將收到所有的關於例項進入SDOWN 條件的通知。

使用 PSUBSCRIBE * 訂閱來獲取所有的訊息。

下面是一個頻道列表,以及使用API,你可以接收到的訊息格式。第一個詞是頻道/事件名稱,剩餘部分是資料格式。

注意,指定instance details的地方意味著提供了下面的引數用於表示目標例項:

<instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>

標識主節點的部分(從@開始到結束)是可選的,只有例項本身不是主節點的時指定。

  • +reset-master <instance details> — 主節點被重置。
  • +slave <instance details> — 一個新的從節點被發現和關聯。
  • +failover-state-reconf-slaves <instance details> — 故障轉移狀態被轉換為reconf-slaves狀態。
  • +failover-detected <instance details> — 另一個Sentinel開始了故障轉移或者其他的外部實體被發現(一個關聯的從節點變為主節點)。
  • +slave-reconf-sent <instance details> — 為了給新的從節點重新配置,sentinel 中的leader傳送SLAVEOF命令到這個例項。
  • +slave-reconf-inprog <instance details> –從節點被重新配置展示一個主節點的從節點,但是同步過程尚未完成。
  • +slave-reconf-done <instance details> — 從節點現在和主節點是同步的。
  • -dup-sentinel <instance details> –指定的主節點,一個或者多個sentinels被 移除,因為是重複的。
  • +sentinel <instance details> — 這個主節點的一個新的sentinel被發現和關聯。
  • +sdown <instance details> — 指定的例項現在處於主觀下線狀態。
  • -sdown <instance details> — 指定的例項不再處於主觀下線狀態。
  • +odown <instance details> — 指定的例項現在處於客觀下線狀態。
  • -odown <instance details> — 指定的例項現在不處於客觀下線狀態。
  • +new-epoch <instance details> — 當前時間被更新。
  • +try-failover <instance details> — 準備新的故障轉移,等待大多數的選舉。
  • +elected-leader <instance details> — 贏得了選舉,開始故障轉移。
  • +failover-state-select-slave <instance details> — 新的故障轉移狀態是select-slave:我們 正在尋找合適提升為主節點的從節點。
  • no-good-slave <instance details> — 沒有合適進行提升的從節點。一般會在稍後重試,但是這或許會改變並且終止故障轉移。
  • selected-slave <instance details> — 我們找到了指定的從節點來進行提升。
  • failover-state-send-slaveof-noone <instance details> — 我們嘗試重新配置這個提升後的主節點,等待它切換。
  • failover-end-for-timeout <instance details> — 故障轉移由於超時而停止,無論如何從節點最後被配置為複製新的主節點。
  • failover-end <instance details> — 故障轉移由於成功而停止,所有的從節點被配置為複製新的主節點。
  • switch-master <master name> <oldip> <oldport> <newip> <newport> — 配置改變後,主節點新的IP和地址都是指定的。這是大多數外部使用者感興趣的訊息。
  • +tilt — 進入Tilt模式。
  • -tilt — 退出Tilt模式。

-BUSY狀態的處理

當一個Lua指令碼的執行時間超過了配置中指定的Lua指令碼時間限制,Redis例項將返回 -BUSY錯誤。當這個發生的時候,在觸發故障轉移之前 Redis Sentinel將嘗試傳送SCRIPT KILL命令,如果指令碼是隻讀的,就會成功。

如果在這個嘗試後,例項仍然處於失敗情況,它最後會開始故障轉移。

從節點優先

Redis例項有個配置引數叫slave-priority。這個資訊在Redis從節點例項的INFO輸出中展示出來,並且Sentinel使用它來選擇一個從節點在一次故障轉移中:

  1. 如果從節點的優先順序被設定為0,這個從節點永遠不會被提升為主節點。
  2. Sentinel首選一個由更低( lower)優先順序的從節點。

比如在當前主節點的同一個資料中心有一個從節點S1,並且有另外的從節點S2在另外的資料中心,可以將S1優先順序設定為10,S2優先順序設定為100,如果主節點掛掉了並且S1和S2都是可用的,S1將是首選的。

檢視關於從節點選舉的更多資訊,請檢視本文章的slave selection and priority章節

Sentinel和Redis許可權

當主節點被配置為從客戶端需要密碼,作為一個安全措施,從節點也需要知道這個密碼為了主節點認證並且建立主-從連線用於非同步複製協議。

使用下列的配置選項來實現:

  • requirepass 在主節點中,為了設定認證密碼,並且確保例項不會處理來自沒有認證的客戶端的請求。
  • masterauth 在從節點中,為了取得主節點的認證,來從主節點正確的複製 資料。

當Sentinel使用的時候,沒有一個單獨的主節點,因為一次故障轉移過後,從節點將扮演主節點的角色,並且老的主節點被重新配置作為一個從節點,所以你要做的是在全部的例項中設定上面的選項,包括主節點和從節點。

這通常是一個理智的設定,因為你不想要僅僅在主節點中保護你的資料,在從節點中有同樣的資料。

然而,在罕見的情況下,你需要一個從節點是可進入的而不需要認證,你可以設定一個優先順序為0的從節點來實現,阻止這個從節點被提升為主節點,配置這個從節點的masterauth選項,不要使用requirepass選項,以便資料可以被讀在沒有認證的情況下。

Sentinel 客戶端實現

Sentinel需要顯式的客戶端支援,除非系統配置為執行指令碼來執行一個透明的重定向對於所有的主節點例項的請求(虛擬IP或類似的系統)。可以參考文件Sentinel clients guidelines

更高階的概念

下面的章節是關於Sentinel怎麼工作的一些細節,沒有付諸於實現的想法和演算法在文章的最後章節。

SDOWN和ODOWN失敗狀態

Redis Sentine有兩個不同概念的下線,一個被稱為主觀下線(Subjectively Down )條件(SDOWN),是一個本地Sentinel例項下線條件。另一個被稱為客觀下線(Objectively Down )條件(ODOWN),是當足夠的Sentinels具有SDOWN條件就滿足ODOWN,並且從其他的Sentinels使用SENTINEL is-master-down-by-addr命令得到反饋。

從一個Sentinel的角度來看,滿足一個SDOWN條件就是在指定的時間內對於PING請求不能收到有效的回覆,這個時間在配置檔案中是is-master-down-after-milliseconds引數。

一個PING請求可接受的回覆是下列之一:

  • 回覆+PONG。
  • 回覆 -LOADING錯誤。
  • 回覆-MASTERDOWN錯誤。

其他的回覆(或根本沒有回覆)被認為是無效的。注意一個合理的主節點在INFO輸出中通知他自己是一個從節點被認為是下線的。

注意SDOWN需要在配置中整個的時間間隔都沒有收到有效的回覆,因此對於例項如果時間間隔是30000毫秒,並且我們每隔29秒收到有效的回覆,這個例項就被認為在工作。

SDOWN還不夠觸發故障轉移:它僅僅意味著一個單獨的Sentinel相信一個Redis例項不可達。要觸發故障轉移,必須達到ODOWN狀態。

從SDOWN轉換到ODOWN,沒有使用強一致性演算法,而僅僅是gossip的形式:如果一個Sentinel在一個給定的時間範圍內從足夠的Sentinels 得到一個報告說一個主節點沒有在工作,SDOWN被提升為ODOWN。如果這個確認稍候消失,這個標識也會清除。

一個更加嚴格的授權是使用大多數需要為了真正的開始故障轉移,但是在達到ODOWN狀態之前不會觸發故障轉移。

ODOWN條件只適用於主節點。對於其他型別的例項,Sentinel不需要採取行動,所以對於從節點和其他的sentinels來說ODOWN狀態永遠不可能達到,而僅僅只有SDOWN狀態。

然而SDOWN也有語義的影響,比如一個從節點在SDOWN狀態不會被選舉來提升來執行一個故障轉移。

Sentinels和從節點自動發現

Sentinels和其他的Sentinels保持連線為了互相之間檢查是否可達和交換訊息。然而你不需要在每個執行的Sentinel 例項中配置其他的Sentinel地址列表,Sentinel使用Redis例項的釋出/訂閱能力來發現其他的監控相同的主節點和從節點的Sentinels。

通過往名稱為__sentinel__:hello的通道傳送hello訊息(hello messages)來實現這個特性。

同樣的,你不需要配置一個主節點關聯的從節點的列表,Sentinel也會自動發現這個列表通過問詢Redis:

  • 每隔兩秒,每個Sentinel向每個監控的主節點和從節點的釋出/訂閱通道__sentinel__:hello來公佈一個訊息,宣佈它自己的IP,埠,id。
  • 每個Sentinel都訂閱每個主節點和從節點的釋出/訂閱通道__sentinel__:hello,尋找未知的sentinels。當新的sentinels被檢測到,他們增加這個主節點的sentinels。
  • Hello訊息也包含主節點的全部配置資訊,如果接收的Sentinel有一個更舊的配置,它會立即更新它的配置。
  • 在增加一個主節點的新的sentinel之前,Sentinel總是要檢查是否已經有一個有相同的id、地址的sentinel。在這種情況下,所有匹配的sentinels被移除,新的被增加。

故障轉移之外重新配置

即使沒有故障轉移,Sentinels將嘗試設定當前的配置到監控的例項上面。

特別的:

  • 從節點聲稱為主節點,將被作為從節點配置來複制當前的主節點。
  • 從節點連線了一個錯誤的主節點,也會被重新配置來複制正確的主節點。

Sentinels重新配置從節點,錯誤的配置在一段時間內應該被觀察到,比在廣播新的配置的時候要好得多。

這個阻止了有一個過時配置(比如說從一個分割槽中重新加入)的Sentinels 在收到更新之前去交換從節點的配置。

同樣注意:

  • 主節點的故障轉移被重新配置作為從節點當他們返回可用的時候
  • 在一個網路分割槽中,從節點一旦可達,被重新配置。

本章最重要的教訓就是:Sentinels是每個程序總是嘗試去把最後的配置施加到監控的例項上的一個系統。

從節點選舉和優先順序

當一個Sentinel例項準備執行故障轉移,因為主節點在ODOWN狀態下並且Sentinel從大多數已知的Sentinel例項中收到了授權開始故障轉移,一個合適的從節點要被選舉出來。

從節點選舉過程評估從節點的下列資訊:

  1. 與主節點斷開的時間
  2. 從節點優先順序
  3. 複製偏移處理
  4. 執行ID

一個從節點被發現從主節點斷開超過主節點配置超時(down-after-milliseconds 選項)時間十倍以上,加上從正在執行故障轉移的Sentinel的角度看主節點不可用的時間,將被認為是不合適的並且會被跳過。

在更嚴格的條件下,一個從節點的INFO輸出建議了從主節點斷開超過:

(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state

被認為是不可靠的並且會被無視。

從節點選舉只會考慮通過了上述測試的從節點,並根據上面的條件進行排序,以下列順序:

  1. 根據Redis例項中的redis.conf檔案中配置的slave-priority進行排序,更低的優先順序會被優先。
  2. 如果優先順序相同,檢查複製偏移處理,從主節點收到更加新的資料的從節點會被選擇。
  3. 如果多個從節點有相同的優先順序和資料偏移,執行進一步檢查,選擇有著更小執行ID的從節點。有一個更小的ID並不是具有正真的優點,但是對於從節點選舉來說更確定,而不是隨機選擇一個從節點。

如果有機器是首選,Redis主節點、從節點必須被配置一個slave-priority。否則,所有的例項都有一個預設的ID。

一個Redis例項可以被配置指定slave-priority為0為了永遠不被Sentinels 選擇為新的主節點。然而一個這樣配置的從節點會被Sentinels重新配置,為了在一次故障轉移後複製新的主節點,唯一不同的是,它永遠不會成為主節點。

演算法和內部結構

下面的章節,我們將會探索Sentinel特性的細節。對於使用者來說,並不需要知道全部的細節,但是一個更深入的理解可能會幫助部署和操作Sentinel以一個更加有效的方式。

Quorum

前面的章節展示了每個被Sentinel監控的主節點和一個配置的quorum相關聯。它指定了需要同意主節點是不可達或者錯誤的Sentinel程序的數量為了觸發一次故障轉移。

可是,在故障轉移觸發後,為了真正地執行故障轉移,至少大多數的Sentinels必須授權一個Sentinel開始故障轉移。當只有小部分的Sentinels存在的一個網路分割槽中,故障轉移永遠不會執行。

我們嘗試讓這件事更加清晰:

  • Quorum:為了把一個主節點標記成ODOWN,需要的Sentinel程序數量來發現錯誤條件。
  • ODOWN狀態觸發故障轉移。
  • 一旦故障轉移被觸發,Sentinel嘗試向大多數的Sentinels請求授權。

不同之處看起來很微妙,但是實際上很簡單地理解和使用。如果你又5個Sentinel例項,quorum被設定為2,一旦2個Sentinel認為主節點不可達,故障轉移就會被觸發。然而2個Sentinels中的一個得到3個Sentinels的授權才開始故障轉移。

把quorum設定為5,必須所有的Sentinels同意主節點失敗,併為了開始故障轉移,需要得到所有Sentinels的授權。

這意味著quorum在兩方面可以被用來調整Sentinel:

  1. 如果quorum被設定為小於我們部署的Sentinels大多數,我們使Sentinel對主節點失敗更加敏感,並一旦少數的Sentinels不再和主節點交流就會觸發故障轉移。
  2. 如果quorum被設定為大於我們部署的Sentinels大多數,僅僅當大多數連線良好的Sentinels同意主節點掛掉的時候,Sentinel才能開始故障轉移。

配置epochs

為了開始故障轉移,Sentinels需要從大多數得到授權,有下面幾個重要的原因:

當一個Sentinel被授權,它為故障轉移的主節點獲得一個獨一無二的配置epoch。這將用來標識新的配置的版本在故障轉移完成之後。因為大多數同意一個版本被分配給指定的Sentinel,沒有其他的Sentinel可以使用它。這意味著,每次故障轉移的配置都有一個獨一無二的版本號。我們將看到這為什麼是很重要的。

此外Sentinels有一個規則:如果一個Sentinel投票給其他的Sentinel在一次故障轉移中,它將等待一段時間再次嘗試故障轉移這個主節點,你可以在sentinel.conf中配置這個延遲時間failover-timeout。這意味著Sentinels在相同的時間內不會嘗試故障轉移相同的主節點,第一次請求授權的將會嘗試,如果失敗了,另一個將會在一段時間後嘗試,等等。

Redis Sentinel保證了活性(liveness)性質,如果大多數Sentinels 能夠交流。如果主節點掛了,最後將有一個會被授權開始故障轉移。

Redis Sentinel同樣也保證了安全(safety )性質,每個Sentinel將使用不同的配置epochconfiguration epoch)來故障轉移同一個主節點。

配置傳播

一旦一個Sentinel能成功的故障轉移一個主節點,它將開始廣播新的配置以便其他的Sentinels更新他們關於主節點的資訊。

為了認定一次故障轉移是成功的,它需要Sentinel能傳送SLAVEOF NO ONE指令給選舉的從節點,並且切換為主節點,稍後能在主節點的INFO輸出中觀察到。

這時候,即使從節點的重新配置正在進行,故障轉移也被認為是成功的,並且所有的Sentinels需要開始報告新的配置。

一個新的配置廣播的方式,就是為什麼我們需要每次Sentinel被授權故障轉移時有一個不同的版本號的原因。

每個Sentinel使用Redis釋出/訂閱訊息來連續不斷的廣播它的一個主節點的配置的版本號,所有的從節點和主節點。同時,所有的Sentinels等待訊息來檢視其他的Sentinels廣播的配置。

配置在__sentinel__:hello釋出/訂閱頻道中被廣播。

因為每個配置都有一個不同的版本號,大的版本號總是贏得小的版本號。

例如 一開始所有的Sentinels認為主節點mymaster的配置為192.168.1.50:6379。這個配置的版本號為1。一段時間後,一個被授權開始故障轉移有版本號2,如果故障轉移成功,它將廣播新的配置,它說是192.168.1.50:9000,版本號為2,。所有其他的例項將看到這個配置並更新他們的配置,因為新的配置有更高的版本號。

這意味著Sentinel保證第二個活性屬性:一個Sentinels集合能互相交流並且把配置資訊收斂到一個更高的版本號。

基本上,如果網路是分割槽的,每個分割槽將收斂到一個更高的本地配置。沒有網路分割槽的特殊性情況下,只有一個分割槽並且每個Sentinel將同意配置。

分割槽下的一致性

Redis Sentinel 配置是最終一致的,所以每個分割槽將收斂到更高的可用的配置。然而在使用Sentinel的真實世界的系統中,有三個不同的角色:

  1. Redis例項
  2. Sentinel例項
  3. 客戶端

為了定義系統的行為,我們考慮所有的三種。

下面是一個簡單的有三個節點的網路,每個都執行一個Redis例項和一個Sentinel例項:

            +-------------+
            | Sentinel 1  |----- Client A
            | Redis 1 (M) |
            +-------------+
                    |
                    |
+-------------+     |          +------------+
| Sentinel 2  |-----+-- // ----| Sentinel 3 |----- Client B
| Redis 2 (S) |                | Redis 3 (M)|
+-------------+                +------------+

在這個系統中,原始狀態是Redis3是主節點,Redis1和Redis2是從節點。一個網路分割槽發生隔離了舊的主節點。Sentinels1 和 2開始一個故障轉移過程提升Sentinel 1為新的主節點。

Sentinel的屬性保證Sentinel 1和 2 現在有了一個主節點的新的配置。可是Sentinel 3依然有舊的配置因為它在一個不同的分割槽中存活。

我們知道Sentinel 3將得到他的配置更新當網路分割槽治癒的時候,但是如果有客戶端和舊的主節點在一起,分割槽時會發生什麼呢?

客戶端仍然可以向Redis 3寫入資料。當網路分割槽治癒,Redis 3變成Reids 1的一個從節點,在分割槽期間寫入的資料都會丟失。

根據你的配置,你可以想或不想讓這種情況發生:

  • 如果你使用Redis作為快取,客戶端B仍然可以向舊的主節點寫入資料是很方便的,即使資料將會丟失。
  • 如果你使用Redis作為儲存,這是不好的,你需要配置系統為了部分的阻止這個問題。

因為Redis是非同步複製的,這種情況下,沒有辦法完全阻止資料丟失,但是你可以使用下面的Redis配置選項來限制Redis 3 和 Redis 1之間的分歧:

min-slaves-to-write 1
min-slaves-max-lag 10

當一個Redis有上面的配置,當作為主節點的時候,如果他不能向至少一個從節點寫入資料,將會停止接受寫入請求。因為複製是非同步的,不能寫(not being able to write )意味著從節點都是分離的,或者沒有傳送非同步確認超過了指定的max-lag的時間。

使用這個配置,上面的例子中的Redis 3將會在10秒之後變得不可用。當分割槽治癒,Sentinel 3的配置將會是新的,Client B能獲取到一個有效的配置並繼續工作。

總之, Redis + Sentinel是一個最終一致性系統( eventually consistent system),功能是最後一個故障轉移獲勝( last failover  wins)。舊節點中的資料會被丟棄,從當前主節點複製資料,所以總有一個丟失確認寫的視窗。這是由於Redis的非同步複製和系統的“虛擬”合併功能的丟棄性質。注意,Sentinel本身沒有限制,如果你把故障轉移編排起來,相同的屬性仍然適用,僅僅有兩種方式來避免丟失寫入確認:

  1. 使用同步複製
  2. 使用一個最終一致的系統,相同物體的不同版本能被合併

Redis現在不能使用上面的任何系統,是目前的發展目標。可是有一個代理實現解決方案2在Redis儲存之上,比如說SoundCloud  Roshi,或者Netflix  Dynomite

Sentinel持久化狀態

Sentinel狀態儲存在sentinel配置檔案中。例如,每次一個收到一個新的配置,主節點,配置和配置epoch一起被儲存在磁碟上。這意味著停止和重啟Sentinel程序是很安全的。

TILT模式

Redis Sentinel嚴重依賴電腦時間:例如為了推斷一個例項是否可達,它會記住最後一次成功回覆PING命令的時間,並和當前時間比較來推斷哪個是舊的。

可是,如果電腦時間意外改變了,或者電腦非常繁忙,或程序由於某些原因阻塞。Sentinel或許開始表現意外的行為。

TILT模式是一個特殊的“保護”模式,當發現奇怪的事情可能降低系統的可靠性,一個Sentinel可以進入這個模式。Sentinel定時中斷呼叫每秒10次,所以我們期待定時中斷呼叫之間間隔100毫米左右。

Sentinel所做的就是登記之前的中斷呼叫時間,並和當前的呼叫時間比較:如果結果是負數或意外的數,將會進入TILT模式。

當處於Sentinel模式Sentinel將會繼續監控每件事,但是:

  • 停止一切動作
  • 開始回覆負數給SENTINEL is-master-down-by-addr請求讓檢測失敗不再有信

如果30秒內每件事都表現正常,將退出TILT模式。

注意某些情況下,使用許多核心提供的單調時鐘API代替TILT模式。可是它仍然是不清晰的如果這是一個很好的解決方案,因為在程序只是僅僅掛起或排程很長時間沒有執行的情況下,當前的系統會避免這個問題。