Redis Sentinel叢集部署
什麼是Redis Sentinel
Redis Sentinel是用來實現Redis高可用的一套解決方案。Redis Sentinel由兩個部分組成:由一個或者多個Sentinel例項組成Sentinel系統;由一個主Redis伺服器(Master Redis)和多個從Redis伺服器(Slave Redis)組成主從備份的Redis系統。
Sentinel系統本身是一個分散式的系統,它的作用是監視Redis伺服器,在Master Redis下線時,自動將某個Slave Redis提升為新的主伺服器。Redis系統由Master Redis處理客戶端的命令請求,Slave Redis作為主伺服器的備份而存在。
Redis Sentinel主要作用
- 監控(Monitoring):Sentinel 會不斷地檢查你的主伺服器和從伺服器是否運作正常。
- 提醒(Notification):當被監控的某個Redis伺服器出現問題時, Sentinel可以通過API向管理員或者其他應用程式傳送通知。
- 自動故障遷移(Automatic failover):當一個主伺服器不能正常工作時, Sentinel會開始一次自動故障遷移操作,它會將失效主伺服器的其中一個從伺服器升級為新的主伺服器,並讓失效主伺服器的其他從伺服器改為複製新的主伺服器;當客戶端試圖連線失效的主伺服器時,叢集也會向客戶端返回新主伺服器的地址,使得叢集可以使用新主伺服器代替失效伺服器。
Redis Sentinel系統架構圖
Sentinel的原理並不複雜:
- 啟動N個Sentinel例項,這些Sentinel例項會去監控你指定的Redis Master/Slaves。
- 當Redis Master節點掛掉後,Sentinel例項通過ping檢測失敗發現這種情況就認為該節點進入 SDOWN狀態,也就是檢測的Sentinel例項主觀地(Subjectively)認為該Redis Master節點掛掉。
- 當一定數目(Quorum引數設定的Sentinel例項都認為該Master掛掉的情況下,該節點將轉換進入ODOWN狀態,也就是客觀地(Objectively)掛掉的狀態。
- 接下來Sentinel例項之間發起選舉,選擇其中一個Sentinel例項發起failover過程:從Slave中選擇一臺作為新的Master,讓其它Slave從新的Master複製資料,並通過Pub/Sub釋出事件。
- 使用者客戶端從任意Sentinel例項獲取Redis配置資訊,並監聽(可選)Sentinel發出的事件: SDOWN, ODOWN以及failover等,並做相應主從切換,Sentinel還扮演了服務發現的角色。
- Sentinel的Leader選舉採用的是Raft協議。
構建Redis Sentinel叢集
Redis Sentinel環境準備
一個一主多從的Redis系統中,可以使用多個Sentinel進行監控任務以保證系統足夠穩健。此時,不僅Sentinel會同時監控主資料庫和從資料庫,Sentinel之間也會相互監控。在這裡,建議大家Sentinel至少部署三個,並且使用奇數個Sentinel。
安裝Redis和Sentinel
在三臺伺服器上分別安裝Redis和Sentinel。需要注意的是,如果要給Redis設定密碼,需要在三個Redis的配置檔案中設定相同的密碼。
安裝的Redis版本必須在2.8版本以上。
$ apt-get install redis-server redis-sentinel
配置Redis和Sentinel
配置Redis
三臺Redis主機配置類似,只是初次配置時角色不同。這裡以主機dev-master-01
為例,其它兩臺按實際情況修改就行了。
Redis預設會繫結到127.0.0.1
,這裡要在多臺機器間通訊,我們將它繫結到主機IP上。
$ vim /etc/redis/redis.conf bind 192.168.2.210
如果要給Redis設定密碼,需要在三個Redis的配置檔案中設定相同的密碼。
$ vim /etc/redis/redis.conf requirepass "000000"
設定主從複製
在兩個Slave Redis的配置檔案中宣告所從屬的主資料庫。
$ vim /etc/redis/redis.conf slaveof 192.168.2.210 6379
這裡需要注意一點:當一個Master配置需要密碼才能連線時,客戶端和Slave在連線時都需要提供密碼。Master通過requirepass
設定自身的密碼,不提供密碼無法連線到這個Master。Slave通過masterauth
來設定訪問Master時的密碼。
Sentinel可以切換主從資料庫,主資料庫可能會變成從資料庫,所以三臺機器上都需要同時設定requirepass
和masterauth
配置項。
$ vim /etc/redis/redis.conf requirepass "000000" masterauth "000000"
配置Sentinel
redis-sentinel軟體包中預設包含了一個名為sentinel.conf
的檔案,預設在/etc/redis/sentinel.conf
。這裡以主機dev-master-01
為例,其它兩臺配置類似,按實際情況修改就行了。
執行一個Sentinel所需的最少配置如下所示:
$ vim /etc/redis/sentinel.conf daemonize yes port 26379 bind 192.168.2.210 sentinel monitor redis-master 192.168.2.210 6379 2 sentinel down-after-milliseconds redis-master 5000 sentinel failover-timeout redis-master 180000 sentinel parallel-syncs redis-master 2 sentinel auth-pass redis-master 000000 sentinel notification-script redis-master /etc/redis/notify.sh sentinel client-reconfig-script redis-master /etc/redis/failover.sh logfile /var/log/redis/redis-sentinel.log
以上配置項說明:
daemonize yes
以後臺程序模式執行。
port 26379
Sentinel例項之間的通訊埠,該埠號預設為26379。
bind 192.168.2.210
Sentinel預設會繫結到127.0.0.1
,這裡要在多臺機器間通訊,我們將它繫結到主機IP上。
sentinel monitor redis-master 192.168.2.210 6379 2
Sentinel去監視一個名為redis-master的主伺服器,這個主伺服器的IP地址為192.168.2.210 ,埠號為6379。將這個主伺服器判斷為失效至少需要2個Sentinel同意,一般設定為N/2+1(N為Sentinel總數)。只要同意Sentinel的數量不達標,自動故障遷移就不會執行。
不過要注意,無論你設定要多少個Sentinel同意才能判斷一個伺服器失效, 一個Sentinel都需要獲得系統中多數Sentinel的支援,才能發起一次自動故障遷移,並預留一個給定的配置紀元。(configuration Epoch ,一個配置紀元就是一個新主伺服器配置的版本號)。
sentinel down-after-milliseconds redis-master 5000
down-after-milliseconds
選項指定了Sentinel認為伺服器已經斷線所需的毫秒數。如果伺服器在給定的毫秒數之內,沒有返回Sentinel傳送的PING命令的回覆,或者返回一個錯誤,那麼Sentinel將這個伺服器標記為主觀下線(subjectively down,簡稱SDOWN)。
不過只有一個Sentinel將伺服器標記為主觀下線並不一定會引起伺服器的自動故障遷移,只有在足夠數量的Sentinel都將一個伺服器標記為主觀下線之後,伺服器才會被標記為客觀下線(objectively down,簡稱ODOWN), 這時自動故障遷移才會執行。將伺服器標記為客觀下線所需的Sentinel數量由對主伺服器的配置(sentinel monitor引數)決定。
sentinel failover-timeout redis-master 180000
如果在多少毫秒內沒有把宕掉的那臺Master恢復,那Sentinel認為這是一次真正的宕機。在下一次選取時排除該宕掉的Master作為可用的節點,然後等待一定的設定值的毫秒數後再來探測該節點是否恢復,如果恢復就把它作為一臺Slave加入Sentinel監測節點群,並在下一次切換時為他分配一個”選取號”。
sentinel parallel-syncs redis-master 2
parallel-syncs
選項指定了在執行故障轉移時,最多可以有多少個從伺服器同時對新的主伺服器進行同步。這個數字越小,完成故障轉移所需的時間就越長。
如果從伺服器被設定為允許使用過期資料集(slave-serve-stale-data
選項), 那麼你可能不希望所有從伺服器都在同一時間向新的主伺服器傳送同步請求。因為儘管複製過程的絕大部分步驟都不會阻塞從伺服器,但從伺服器在載入主伺服器發來的RDB檔案時,仍然會造成從伺服器在一段時間內不能處理命令請求。
如果全部從伺服器一起對新的主伺服器進行同步,那麼就可能會造成所有從伺服器在短時間內全部不可用的情況出現。你可以通過將這個值設為1來保證每次只有一個從伺服器處於不能處理命令請求的狀態。
sentinel auth-pass redis-master 000000
當Master設定了密碼時,Sentinel連線Master和Slave時需要通過設定引數auth-pass
配置相應密碼。
sentinel notification-script redis-master /etc/redis/notify.sh
指定Sentinel檢測到該監控的Redis例項failover時呼叫的報警指令碼。指令碼被允許執行的最大時間為60秒,超過這個時間指令碼會被kill。該配置項可選,但線上系統建議配置。這裡的通知指令碼簡單的記錄一下failover事件。
# 建立通知指令碼 $ vim /etc/redis/notify.sh #! /bin/bash echo "master failovered at `date`" > /var/log/redis/redis_issues.log
# 給指令碼增加執行許可權 $ chmod +x /etc/redis/notify.sh
sentinel client-reconfig-script redis-master /etc/redis/failover.sh
指定Sentinel failover之後重配置客戶端時執行的指令碼,該配置項可選,但線上系統建議配置。
logfile /var/log/redis/redis-sentinel.log
日誌檔案所在位置,預設在/var/log/redis/redis-sentinel.log。
執行Sentinel
配置完Redis和Sentinel之後,按順序啟動各個角色。啟動順序如下:Master->Slave->Sentinel
,要確保按照這個順序依次啟動。
- 啟動Redis
$ systemctl start redis
- 啟動Sentinel
執行Sentinel有兩種方式:
雖然Redis Sentinel有單獨的軟體安裝包,但實際上它只是一個執行在特殊模式下的Redis例項。當前Redis Stable版已經自帶了redis-sentinel這個工具。你可以在啟動一個普通Redis例項時通過給定--sentinel
選項來啟動Redis Sentinel。
第一種:用單獨的可執行檔案redis-sentinel
$ redis-sentinel /etc/redis/sentinel.conf
第二種:使用redis-server的—sentinel選項
$ redis-server /etc/redis/sentinel.conf --sentinel
以上兩種方式,都必須指定一個Sentinel的配置檔案sentinel.conf。需要確保配置檔案是可寫的,因為Sentinel會往配置檔案裡新增很多資訊作為狀態持久化,這是為了重啟等情況下可以正確地恢復 Sentinel的狀態。
當配置檔案無法寫入時,Sentinel會啟動失敗。Sentinel預設監聽26379埠,所以執行前必須確定該埠沒有被別的程序佔用。
我們這裡用redis-sentinel
方式來啟動:
$ systemctl start redis-sentinel
- 測試Sentinel
啟動成功後可以通過redis客戶端工具檢視當前Sentinel的資訊,終端輸入:
$ redis-cli -p 26379 -h 192.168.2.210 INFO Sentinel # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:2 sentinel_scripts_queue_length:1 master0:name=redis-master,status=ok,address=192.168.2.210:6379,slaves=2,sentinels=1
Sentinel啟動後會輸出類似的日誌:
$ tail -f /var/log/redis/redis-sentinel.log 6207:X 06 Jun 10:40:05.904 # Sentinel runid is 2b2446b24a2bd01b9f54a6b2ca4f945a3480dd7e 6207:X 06 Jun 10:40:05.904 # +monitor master redis-master 192.168.2.210 6379 quorum 2 6207:X 06 Jun 10:40:05.910 * +slave slave 192.168.2.211:6379 192.168.2.211 6379 @ redis-master 192.168.2.210 6379 6207:X 06 Jun 10:40:05.915 * +slave slave 192.168.2.212:6379 192.168.2.212 6379 @ redis-master 192.168.2.210 6379 6207:X 06 Jun 12:01:33.098 * +sentinel sentinel 192.168.2.211:26379 192.168.2.211 26379 @ redis-master 192.168.2.210 6379 6207:X 06 Jun 12:02:07.922 * +sentinel sentinel 192.168.2.212:26379 192.168.2.212 26379 @ redis-master 192.168.2.210 637968.2.212:6379 192.168.2.212 6379 @ redis-master 192.168.2.210 6379
輸出的結果表示開始監控redis-master叢集,並輸出叢集的基本資訊。+slave
和+sentinel
分別代表成功發現了從資料庫和其他Sentinel。
- 檢視sentinel.conf
重新開啟sentinel.conf檔案,發現Sentinel自動生成了一些資訊,記錄了監控過程中的狀態變化。
$ cat /etc/redis/sentinel.conf # Generated by CONFIG REWRITE maxclients 4064 sentinel leader-epoch redis-master 0 sentinel known-slave redis-master 192.168.2.212 6379 sentinel known-slave redis-master 192.168.2.211 6379 sentinel current-epoch 0
- 檢視Sentinel監控的主從伺服器
列出所有被監視的主伺服器,以及這些主伺服器的當前狀態。
$ redis-cli -p 26379 -h 192.168.2.210 192.168.2.210:26379> sentinel master redis-master 1) "name" 2) "redis-master" 3) "ip" 4) "192.168.2.210" 5) "port" 6) "6379" 7) "runid" 8) "02d88a6105ce3277745c1fc65b695887f165f302" 9) "flags" 10) "master" 11) "pending-commands" 12) "0" 13) "last-ping-sent" 14) "0" 15) "last-ok-ping-reply" 16) "1096" 17) "last-ping-reply" 18) "1096" 19) "down-after-milliseconds" 20) "5000" 21) "info-refresh" 22) "4732" 23) "role-reported" 24) "master" 25) "role-reported-time" 26) "667578" 27) "config-epoch" 28) "0" 29) "num-slaves" 30) "2" 31) "num-other-sentinels" 32) "0" 33) "quorum" 34) "2" 35) "failover-timeout" 36) "180000" 37) "parallel-syncs" 38) "2" 39) "notification-script" 40) "/etc/redis/notify.sh"
Sentinel驗證
測試Failover
我們讓dev-master-01主機上的redis-master主動休眠30秒來觀察failover過程:
$ redis-cli -p 6379 -h 192.168.2.210 -a 000000 DEBUG sleep 30
我們可以看到每個Sentinel程序都監控到Master掛掉,從sdown狀態進入odown,然後選舉了一個leader來進行failover,最終192.168.2.212
成為新的Master, Sentinel的日誌輸出:
日誌的幾個主要事件
在Redis Master角色上主要有以下幾個事件:
+sdown master redis-master 192.168.2.210 6379
,發現Master檢測失敗,主觀認為該節點掛掉,進入sdown狀態。+odown master redis-master 192.168.2.210 6379 #quorum 2/2
,有兩個Sentinel節點認為Master 6379掛掉,達到配置的quorum
值2,因此認為Master已經客觀掛掉,進入odown狀態。+try-failover master redis-master 192.168.2.210 6379
,表示Sentine開始進行故障恢復。+vote-for-leader 2b2446b24a2bd01b9f54a6b2ca4f945a3480dd7e 1
,準備選舉一個 Sentinel Leader來開始failover。+failover-end master redis-master 192.168.2.210 6379
,表示Sentinel完成故障修復,其中包括了Sentinel Leader的選舉、備選從資料庫的選擇等較為複雜的過程。+switch-master redis-master 192.168.2.210 6379 192.168.2.212 6379
, 切換Master節點,failover完成。-sdown slave 192.168.2.210:6379 192.168.2.210 6379 @ redis-master 192.168.2.212 6379
,192.168.2.210休眠完成後,作為Slave掛載到192.168.2.212後面,可見Sentinel確實同時在監控Slave狀態,並且掛掉的節點不會自動移除,而是繼續監控。
在Redis Slave角色上主要有以下幾個事件:
Redis Slave上大多數都和Redis Master上的事件類似,不同的是Redis Slave還多了一步配置更新。
+config-update-from sentinel 192.168.2.210:26379 192.168.2.210 26379 @ redis-master 192.168.2.210 6379
檢視Sentinel配置檔案變更
經過一次failover後,會發現Sentinel配置檔案變更了以下一些內容。可以看到Sentinel將最新的叢集狀態寫入了配置檔案。
$ cat /etc/redis/sentinel.conf # Generated by CONFIG REWRITE maxclients 4064 sentinel leader-epoch redis-master 1 sentinel known-slave redis-master 192.168.2.211 6379 sentinel known-slave redis-master 192.168.2.210 6379 sentinel known-sentinel redis-master 192.168.2.212 26379 bbf85fae74692d9527e77c5b0bb83a2b5db40dd2 sentinel known-sentinel redis-master 192.168.2.210 26379 2b2446b24a2bd01b9f54a6b2ca4f945a3480dd7e sentinel current-epoch 1
驗證下三臺主機上的角色
- dev-node-02
以下輸出資訊,表明192.168.2.212上的Redis是Master角色。
$ redis-cli -p 6379 -h 192.168.2.212 -a 000000 info Replication # Replication role:master connected_slaves:2 slave0:ip=192.168.2.211,port=6379,state=online,offset=2312008,lag=0 slave1:ip=192.168.2.210,port=6379,state=online,offset=2312008,lag=0 master_repl_offset:2312008 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1639828 repl_backlog_histlen:672181
- dev-master-01
以下輸出資訊,表明192.168.2.210上的Redis是Slave角色。
$ redis-cli -p 6379 -h 192.168.2.210 -a 000000 info Replication # Replication role:slave master_host:192.168.2.212 master_port:6379 master_link_status:up master_last_io_seconds_ago:0 master_sync_in_progress:0 slave_repl_offset:2222825 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:593455 repl_backlog_histlen:1048576
- dev-node-01
以下輸出資訊,表明192.168.2.211上的Redis是Slave角色。
$ redis-cli -p 6379 -h 192.168.2.211 -a 000000 info Replication # Replication role:slave master_host:192.168.2.212 master_port:6379 master_link_status:up master_last_io_seconds_ago:0 master_sync_in_progress:0 slave_repl_offset:2283392 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0
Sentinel日常運維
Sentinel常用命令
以下列出的是Sentinel接受的命令:
PING
:返回PONG。SENTINEL masters
:列出所有被監視的主伺服器,以及這些主伺服器的當前狀態。SENTINEL master <master name>
:用於檢視監控的某個Redis Master資訊,包括配置和狀態等。SENTINEL slaves <master name>
:列出給定主伺服器的所有從伺服器,以及這些從伺服器的當前狀態。SENTINEL sentinels <master name>
:檢視給定主伺服器的Sentinel例項列表及其狀態。SENTINEL get-master-addr-by-name <master name>
:返回給定名字的主伺服器的IP地址和埠號。 如果這個主伺服器正在執行故障轉移操作,或者針對這個主伺服器的故障轉移操作已經完成,那麼這個命令返回新的主伺服器的IP地址和埠號。SENTINEL reset <pattern>
:重置所有名字和給定模式pattern相匹配的主伺服器。pattern 引數是一個Glob風格的模式。重置操作清除主伺服器目前的所有狀態,包括正在執行中的故障轉移,並移除目前已經發現和關聯的,主伺服器的所有從伺服器和Sentinel。SENTINEL failover <master name>
:當主伺服器失效時, 在不詢問其他Sentinel意見的情況下, 強制開始一次自動故障遷移(不過發起故障轉移的Sentinel會向其他Sentinel傳送一個新的配置,其他Sentinel會根據這個配置進行相應的更新)。SENTINEL reset <pattern>
:強制重設所有監控的Master狀態,清除已知的Slave和Sentinel例項資訊,重新獲取並生成配置檔案。SENTINEL failover <master name>
:強制發起一次某個Master的failover,如果該Master不可訪問的話。SENTINEL ckquorum <master name>
:檢測Sentinel配置是否合理,failover的條件是否可能滿足,主要用來檢測你的Sentinel配置是否正常。SENTINEL flushconfig
:強制Sentinel重寫所有配置資訊到配置檔案。SENTINEL is-master-down-by-addr <ip> <port>
:一個Sentinel可以通過向另一個Sentinel傳送SENTINEL is-master-down-by-addr
命令來詢問對方是否認為給定的伺服器已下線。
增加和移除監控以及修改配置引數
SENTINEL MONITOR <name> <ip> <port> <quorum> SENTINEL REMOVE <name> SENTINEL SET <name> <option> <value>
增加和移除Sentinel
增加新的Sentinel例項非常簡單,修改好配置檔案,啟動即可,其他Sentinel會自動發現該例項並加入叢集。如果要批量啟動一批Sentinel節點,最好以30秒的間隔一個一個啟動為好,這樣能確保整個 Sentinel叢集的大多數能夠及時感知到新節點,滿足當時可能發生的選舉條件。
移除一個Sentinel例項會相對麻煩一些,因為Sentinel不會忘記已經感知到的Sentinel例項,所以最好按照下列步驟來處理:
- 停止將要移除的sentinel程序。
- 給其餘的sentinel程序傳送
SENTINEL RESET *
命令來重置狀態,忘記將要移除的sentinel,每個程序之間間隔30秒。 - 確保所有sentinel對於當前存貨的sentinel數量達成一致,可以通過
SENTINEL MASTER <mastername>
命令來觀察,或者檢視配置檔案。
生產環境推薦
對於一個最小叢集,Redis應該是一個Master帶上兩個Slave,並且開啟下列選項:
min-slaves-to-write 1 min-slaves-max-lag 10
這樣能保證寫入Master的同時至少寫入一個Slave,如果出現網路分割槽阻隔併發生failover的時候,可以保證寫入的資料最終一致而不是丟失,寫入老的Master會直接失敗。
Slave可以適當設定優先順序,除了0之外(0表示永遠不提升為Master),越小的優先順序,越有可能被提示為Master。如果Slave分佈在多個機房,可以考慮將和Master同一個機房的Slave的優先順序設定的更低以提升他被選為新的Master的可能性。
考慮到可用性和選舉的需要,Sentinel程序至少為3個,推薦為5個。如果有網路分割槽,應當適當分佈(比如2個在A機房, 2個在B機房,一個在C機房)等。
客戶端實現
客戶端從過去直接連線Redis ,變成:
- 先連線一個Sentinel例項
- 使用
SENTINEL get-master-addr-by-name master-name
獲取Redis地址資訊。 - 連線返回的Redis地址資訊,通過
ROLE
命令查詢是否是Master。如果是,連線進入正常的服務環節。否則應該斷開重新查詢。 - (可選)客戶端可以通過
SENTINEL sentinels <master-name>
來更新自己的Sentinel例項列表。
當Sentinel發起failover後,切換了新的Master,Sentinel會發送 CLIENT KILL TYPE normal
命令給客戶端,客戶端需要主動斷開對老的Master的連結,然後重新查詢新的Master地址,再重複走上面的流程。這樣的方式仍然相對不夠實時,可以通過Sentinel
提供的Pub/Sub
來更快地監聽到failover事件,加快重連。
如果需要實現讀寫分離,讀走Slave,那可以走SENTINEL slaves <master name>
來查詢Slave列表並連線。
其他
由於Redis是非同步複製,所以Sentinel其實無法達到強一致性,它承諾的是最終一致性:最後一次failover的Redis Master贏者通吃,其他Slave的資料將被丟棄,重新從新的Master複製資料。此外還有前面提到的分割槽帶來的一致性問題。
其次,Sentinel的選舉演算法依賴時間,因此要確保所有機器的時間同步,如果發現時間不一致,Sentinel實現了一個TITL模式來保護系統的可用性。
文章來自微信公眾號:運維之美