1. 程式人生 > >Redis: 2、Redis高可用原理,搭建與驗證

Redis: 2、Redis高可用原理,搭建與驗證

Redis高可用原理,搭建與驗證

 

一、redis-ha原理


1 原理


redis高可用採用的是哨兵(sentinel),多個redis-slave配備了多個哨兵程序,哨兵監控redis-master,一旦出現故障,將一臺slave提升為master。客戶端通過連線哨兵來獲取Redis的master地址,發生故障,哨兵會報告新的伺服器地址。


2 主備切換流程


2.1 一個哨兵認為master不可用,此時被仍為主觀不可用,當有指定個數的哨兵都認為master不可用,此時狀態進入客觀不可用,進入主備切換流程。
2.2 進入主備切換流程後,需要一定個數的哨兵都同意進行進行主備切換授權,此時才真正開始進行主備切換。
2.3 開始進行主備切換的時候,一個sentinel被授權, 獲得掛掉的master的最新配置版本號,主備切換後,該版本號用於最新配置。
2.4 一個sentinel成功對master進行主備切換,會把最新配置通過廣播形式高速其他sentinel,其他sentinel則更新對應master配置。
2.5 當將一個slave選舉為master併發送命令後,即使其他slave還沒有針對新master重新配置自己,主備切換也被認為是成功的,所有sentinels將會發布新的配置資訊。
一個相互通訊的sentinel叢集最終會採用版本號最高且相同的配置。

3 Sentine之間和Slaves之間自動發現機制


sentinel利用master的釋出/訂閱機制自動發現其他的sentinel節點
每個sentinel向每個master和slave釋出/訂閱頻道 __sentinel__:hello 每秒傳送一次訊息來宣佈存在,每個sentinel訂閱每個master和slave的頻道__sentinel__:hello的內容來發現未知sentinel,檢測新的sentinel,則加入自身維護的master列表。
每個sentinel傳送的訊息中包含其當前維護的最新master配置,如果某個sentinel發現自己配置版本低於接收到的配置版本,則用新配置更新自己的master配置。

4 Slave選舉與優先順序


slave選舉考慮以下幾方面:
1) 與master斷開連線的次數
2) Slave的優先順序
3)資料複製下標(評估slave當前擁有多少master資料)
4)程序id

候選人規則:
1) slaves優先順序越小排名越靠前
2)優先順序相同,看複製下標,哪個從master接收的複製資料多,就越靠前。
3)優先順序和下標相同,選擇程序ID較小的哪個。

5 獲取redis master地址的原理


步驟1:連線到第一個Sentinel
客戶端需要遍歷Sentinel列表的地址。對於每個地址,它需要使用一個較短的超時時間來嘗試連線到Sentinel。如果發生錯誤或者超時,下一個Sentinel地址將會被嘗試。
如果所有的Sentinel地址都嘗試了都不能成功的話,一個錯誤會被返回給客戶端。
第一個應答client的sentinel應該被放在列表的頭部,這樣在下一次重連的時候,我們將首先嚐試這個sentinel是否可達來最小化延遲。

步驟2: 請求master地址
一旦與Sentinel的連線建立後,客戶端應該在sentinel中嘗試去執行下面的命令:
SENTINEL get-master-addr-by-name master-name
注意: 請將上述master-name替換為真實的master名稱。

返回的結果是包含兩部分:
一個ip:port的二元組。
如果接收到一個 ip:port,該地址就是被用於連線到redis  master的地址。否則,如果接收到的是一個空的應答,客戶端需要嘗試列表中的下一個sentinel。

樣例結果如下:
bash-4.4$ redis-cli -p 26379
127.0.0.1:26379> sentinel get-master-addr-by-name mymaster
1) "10.233.65.74"
2) "6379"

步驟3: 在目標例項上呼叫ROLE命令
一旦客戶端發現了master的地址,它應該嘗試與master建立一次連線,並且呼叫ROLE命令來驗證該連線的例項是否真的是一個master。
如果該連線的例項不是master,客戶端應該等待一個較短的時間,然後繼續從步驟1開始執行。


處理重連
一旦服務名稱被解析為master的地址,一個與redis master的連線建立後,每次當需要重新連線時,客戶端應該使用sentinels從步驟1再次解析地址.
總結:
redis-ha解析獲取master 地址的原理:
步驟1: 獲取sentinel的ip:port組成的列表,遍歷該列表,
            1.1 如果當前sentinel的ip:port可以連線到該sentinel,
然後就傳送命令:sentinel get-master-addr-by-name <master-name>
來獲取master的ip地址,並執行步驟2
        1.2 否則,繼續遍歷下一個sentinel的ip:port
步驟2: 建立與master的連線,並執行一個ROLE命令來判定連線的是否真的是master,
             2.1 如果是master,保持與master的連線。
              2.2 否則,轉步驟1
 如果現主備切換等需要重新連線的情況,會再次從步驟1開始執行,直到解析出master的地址。

二、redis-ha環境搭建


1 下載映象


下面是社群映象地址
https://quay.io/repository/smile/redis?tag=latest&tab=tags 
注意: 社群使用的映象是:
quay.io/smile/redis:4.0.11-r1


2  下載redis-ha的charts


具體地址:
https://github.com/helm/charts/tree/master/stable/redis-ha 
注意: 該程式碼必須是基於statefulset的redis-ha而不是原來基於deployment的redis-ha


3 修改redis-ha的charts


3.1 在redis-ha的values.yaml中redis.config下新增一行
protected-mode: "no"
具體修改後的樣例如下所示:
redis:
  port: 6379
  masterGroupName: mymaster
  config:
    ## Additional redis conf options can be added below
    ## For all available options see http://download.redis.io/redis-stable/redis.conf
    min-slaves-to-write: 1
    min-slaves-max-lag: 5   # Value in seconds
    maxmemory: "0"       # Max memory to use for each redis instance. Default is unlimited.
    maxmemory-policy: "volatile-lru"  # Max memory policy to use for each redis instance. Default is volatile-lru.
    # Determines if scheduled RDB backups are created. Default is false.
    # Please note that local (on-disk) RDBs will still be created when re-syncing with a new slave. The only way to prevent this is to enable diskless replication.
    save: "900 1"
    # When enabled, directly sends the RDB over the wire to slaves, without using the disk as intermediate storage. Default is false.
    repl-diskless-sync: "yes"
    rdbcompression: "yes"
    rdbchecksum: "yes"
    protected-mode: "no"

解釋:
redis沒有bind和密碼的情況下,保護模式開啟,拒絕其他sentinel連線,會導致主備切換不成功。所以要解除保護模式。即進行上述操作。
參考:
https://blog.csdn.net/csdn_ds/article/details/72550898

3.2 在redis-ha的redis-ha-configmap.yaml中
sentinel.conf的
dir "/data"
下新增如下內容:
{{- $protectedMode :=  index .Values.redis.config "protected-mode" }}
protected-mode {{ $protectedMode }}
具體修改後的樣例如下所示:
  sentinel.conf: |
{{- if .Values.sentinel.customConfig }}
{{ .Values.sentinel.customConfig | indent 4 }}
{{- else }}
    dir "/data"
    {{- $protectedMode :=  index .Values.redis.config "protected-mode" }}
    protected-mode {{ $protectedMode }}
    {{- $root := . -}}


3.3 根據需要是否修改persistentVolume的storageClass
預設是:

persistentVolume:
  enabled: true
  ## redis-ha data Persistent Volume Storage Class
  ## If defined, storageClassName: <storageClass>
  ## If set to "-", storageClassName: "", which disables dynamic provisioning
  ## If undefined (the default) or set to null, no storageClassName spec is
  ##   set, choosing the default provisioner.  (gp2 on AWS, standard on
  ##   GKE, AWS & OpenStack)
  ##
  # storageClass: "-"
可以檢視當前環境上支援的storageclass,然後設定可用的值。如果不設定,有可能導致PV掛不上,最終導致redis-ha安裝失敗。
例如:
[[email protected] 11_6_redis_ha]# kubectl get storageclass
NAME       PROVISIONER    AGE
ceph-ssd   ceph.com/rbd   4d
general    ceph.com/rbd   4d

解釋:
Persistent Volume:
含義:持久卷,是網路儲存,不屬於noe和pod,但每個Node上可以訪問模式: ReadWriteOnce,讀寫許可權,並且只能被單個Node掛載
適用: 需要先定義PersistentVolumeClaim(持久卷宣告,是一個申請)
StorageClass: 標記儲存資源的特性和效能,可將儲存資源定義為某種類別(Class)
參考: kubernetes權威指南

4 安裝redis-ha


helm install --name redis-ha redis-ha  --namespace openstack
注意: name後面的 redis-ha (即Release Name)請不要修改為其他名字,否則會導致gnocchi對接redis-ha失敗。
具體的原因是redis-ha中redis-ha/templates/redis-ha-service.yaml這個檔案中定義的service的名稱是一個變數,
具體如下:
apiVersion: v1
kind: Service
metadata:
  name: {{ template "redis-ha.fullname" . }}

而在redis-ha/templates/_helpers.tpl中定義的  redis-ha.fullname 這個模板如下:
{{- define "redis-ha.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
分析可知: 上述redis-ha-sentinel服務的真正名字是按照:
Release.Name-redis-ha
現在 Release.Name 是由  helm install 命令中 --name 後面指定的名稱來確定的,而gnocchi依賴這個服務,需要知道這個服務的具體名字,而不能是一個變數所以,這裡指定 :
Release.Name 
是 redis-ha
這樣gnocchi的charts中指定依賴服務的時候就可以指定具體redis-ha-sentinel的真正名字是
redis-ha-redis-ha
了,這樣兩邊保持一致,才能確保gnocchi和redis-ha的對接才能成功

5 驗證安裝成功


5.1 驗證redis-ha pod啟動成功
[[email protected] ark]# kubectl get pods -n openstack -o wide|grep redis-ha
redis-ha-redis-ha-server-0                      2/2       Running   0          3h        10.233.66.144   node-3
redis-ha-redis-ha-server-1                      2/2       Running   0          3h        10.233.65.74    node-1
redis-ha-redis-ha-server-2                      2/2       Running   0          3h 10.233.64.234   node-2

5.2 驗證PV建立成功
[[email protected] ark]# kubectl get pv|grep redis
pvc-7fc11ced-e236-11e8-a479-fa163e93c106   10Gi       RWO            Delete           Bound     openstack/data-redis-ha-redis-ha-server-0                  general                  5h
pvc-c080baa7-e194-11e8-a479-fa163e93c106   10Gi       RWO            Delete           Bound     openstack/data-redis-ha-redis-ha-server-2                  general                  1d
pvc-e79fcc8e-e193-11e8-a479-fa163e93c106   10Gi       RWO Delete Bound     openstack/data-redis-ha-redis-ha-server-1 general                  1d

5.3 驗證redis-ha的statefulset生成
[[email protected] ark]# kubectl get statefulset -n openstack|grep redis
redis-ha-redis-ha-server   3         3         5h

5.4 驗證redis-ha的service生成
[[email protected] ark]# kubectl get svc -n openstack|grep redis-ha
redis-ha-redis-ha         ClusterIP      None            <none>                                              6379/TCP,26379/TCP           5h

三、redis-ha驗證


1 判定主master


1.1進入任意一個redis的sentinel的容器中
即執行如下命令:
kubectl exec -it -n openstack redis-ha-redis-ha-server-0 -c sentinel /bin/bash
1.2 登入到redis-sentinel中
即執行如下命令:
redis-cli -p 26379
1.3 獲取master的地址
即只新如下命令:
sentinel get-master-addr-by-name mymaster
輸出樣例如下:
1) "10.233.65.74"
2) "6379"
 1.4 確定主master的地址對應的pod
即執行如下命令
kubectl get pods -n openstack -o wide|grep redis-ha|grep 10.233.65.74 
注意: 請將 10.233.65.74  替換為1.3中獲得的主master的ip地址
輸出樣例如下:
redis-ha-redis-ha-server-1                      2/2       Running   0          5h        10.233.65.74    node-1

2 主備切換驗證


2.1 進入到redis master對應的redis容器
根據上述步驟1.4中 確定了redis master對應的pod後,進入該pod中的redis容器,即執行如下命令:
kubectl exec -it -n openstack redis-ha-redis-ha-server-1 -c redis /bin/bash

2.2 模擬redis-ha hang住
即執行如下命令:
redis-cli -p 6379 DEBUG sleep 30

2.3 觀察redis-master對應的pod中的sentinel容器的日誌
即執行如下命令:
kubectl logs -n openstack  redis-ha-redis-ha-server-1 -c sentinel  --tail=300 -f --timestamps
可以觀察到有類似如下日誌輸出:

2018-11-07T03:08:00.877670363Z 1:X 07 Nov 03:08:00.877 # +switch-master mymaster 10.233.66.144 6379 10.233.65.74 6379
2018-11-07T03:08:00.877676242Z 1:X 07 Nov 03:08:00.877 * +slave slave 10.233.64.234:6379 10.233.64.234 6379 @ mymaster 10.233.65.74 6379
2018-11-07T03:08:00.877679125Z 1:X 07 Nov 03:08:00.877 * +slave slave 10.233.66.144:6379 10.233.66.144 6379 @ mymaster 10.233.65.74 6379
2018-11-07T03:08:10.896177687Z 1:X 07 Nov 03:08:10.895 # +sdown slave 10.233.66.144:6379 10.233.66.144 6379 @ mymaster 10.233.65.74 6379
2018-11-07T03:08:19.612363788Z 1:X 07 Nov 03:08:19.611 # -sdown slave 10.233.66.144:6379 10.233.66.144 6379 @ mymaster 10.233.65.74 6379
2018-11-07T08:19:18.049986807Z 1:X 07 Nov 08:19:18.049 # +sdown master mymaster 10.233.65.74 6379
2018-11-07T08:19:18.11869511Z 1:X 07 Nov 08:19:18.115 # +odown master mymaster 10.233.65.74 6379 #quorum 2/2
2018-11-07T08:19:18.118808386Z 1:X 07 Nov 08:19:18.115 # +new-epoch 2
2018-11-07T08:19:18.118816965Z 1:X 07 Nov 08:19:18.115 # +try-failover master mymaster 10.233.65.74 6379
2018-11-07T08:19:18.167892561Z 1:X 07 Nov 08:19:18.166 # +vote-for-leader 22cf8e8f535db85cd2f1a886991d325f64cfabf7 2
2018-11-07T08:19:18.35194575Z 1:X 07 Nov 08:19:18.351 # f96014b78eb3d4a95bf796a6098c1af001ceb03f voted for 22cf8e8f535db85cd2f1a886991d325f64cfabf7 2
2018-11-07T08:19:18.383625857Z 1:X 07 Nov 08:19:18.383 # f9c78c9fb4676bf6353040ea4bdeb23b36a542d8 voted for 22cf8e8f535db85cd2f1a886991d325f64cfabf7 2
2018-11-07T08:19:18.408188307Z 1:X 07 Nov 08:19:18.407 # +elected-leader master mymaster 10.233.65.74 6379
2018-11-07T08:19:18.408255617Z 1:X 07 Nov 08:19:18.407 # +failover-state-select-slave master mymaster 10.233.65.74 6379
2018-11-07T08:19:18.484098943Z 1:X 07 Nov 08:19:18.483 # +selected-slave slave 10.233.66.144:6379 10.233.66.144 6379 @ mymaster 10.233.65.74 6379
2018-11-07T08:19:18.48415193Z 1:X 07 Nov 08:19:18.483 * +failover-state-send-slaveof-noone slave 10.233.66.144:6379 10.233.66.144 6379 @ mymaster 10.233.65.74 63792018-11-07T03:08:00.877670363Z 1:X 07 Nov 03:08:00.877 # +switch-master mymaster 10.233.66.144 6379 10.233.65.74 6379
2018-11-07T03:08:00.877676242Z 1:X 07 Nov 03:08:00.877 * +slave slave 10.233.64.234:6379 10.233.64.234 6379 @ mymaster 10.233.65.74 6379
2018-11-07T03:08:00.877679125Z 1:X 07 Nov 03:08:00.877 * +slave slave 10.233.66.144:6379 10.233.66.144 6379 @ mymaster 10.233.65.74 6379
2018-11-07T03:08:10.896177687Z 1:X 07 Nov 03:08:10.895 # +sdown slave 10.233.66.144:6379 10.233.66.144 6379 @ mymaster 10.233.65.74 6379
2018-11-07T03:08:19.612363788Z 1:X 07 Nov 03:08:19.611 # -sdown slave 10.233.66.144:6379 10.233.66.144 6379 @ mymaster 10.233.65.74 6379
2018-11-07T08:19:18.049986807Z 1:X 07 Nov 08:19:18.049 # +sdown master mymaster 10.233.65.74 6379
2018-11-07T08:19:18.11869511Z 1:X 07 Nov 08:19:18.115 # +odown master mymaster 10.233.65.74 6379 #quorum 2/2
2018-11-07T08:19:18.118808386Z 1:X 07 Nov 08:19:18.115 # +new-epoch 2
2018-11-07T08:19:18.118816965Z 1:X 07 Nov 08:19:18.115 # +try-failover master mymaster 10.233.65.74 6379
2018-11-07T08:19:18.167892561Z 1:X 07 Nov 08:19:18.166 # +vote-for-leader 22cf8e8f535db85cd2f1a886991d325f64cfabf7 2
2018-11-07T08:19:18.35194575Z 1:X 07 Nov 08:19:18.351 # f96014b78eb3d4a95bf796a6098c1af001ceb03f voted for 22cf8e8f535db85cd2f1a886991d325f64cfabf7 2
2018-11-07T08:19:18.383625857Z 1:X 07 Nov 08:19:18.383 # f9c78c9fb4676bf6353040ea4bdeb23b36a542d8 voted for 22cf8e8f535db85cd2f1a886991d325f64cfabf7 2
2018-11-07T08:19:18.408188307Z 1:X 07 Nov 08:19:18.407 # +elected-leader master mymaster 10.233.65.74 6379
2018-11-07T08:19:18.408255617Z 1:X 07 Nov 08:19:18.407 # +failover-state-select-slave master mymaster 10.233.65.74 6379
2018-11-07T08:19:18.484098943Z 1:X 07 Nov 08:19:18.483 # +selected-slave slave 10.233.66.144:6379 10.233.66.144 6379 @ mymaster 10.233.65.74 6379
2018-11-07T08:19:18.48415193Z 1:X 07 Nov 08:19:18.483 * +failover-state-send-slaveof-noone slave 10.233.66.144:6379 10.233.66.144 6379 @ mymaster 10.233.65.74 6379
2018-11-07T08:19:18.561209047Z 1:X 07 Nov 08:19:18.560 * +failover-state-wait-promotion slave 10.233.66.144:6379 10.233.66.144 6379 @ mymaster 10.233.65.74 6379
2018-11-07T08:19:19.227931971Z 1:X 07 Nov 08:19:19.227 # +promoted-slave slave 10.233.66.144:6379 10.233.66.144 6379 @ mymaster 10.233.65.74 6379
2018-11-07T08:19:19.227989503Z 1:X 07 Nov 08:19:19.227 # +failover-state-reconf-slaves master mymaster 10.233.65.74 6379
2018-11-07T08:19:19.297924611Z 1:X 07 Nov 08:19:19.297 * +slave-reconf-sent slave 10.233.64.234:6379 10.233.64.234 6379 @ mymaster 10.233.65.74 6379
2018-11-07T08:19:19.49242546Z 1:X 07 Nov 08:19:19.492 # -odown master mymaster 10.233.65.74 6379
2018-11-07T08:19:20.311034165Z 1:X 07 Nov 08:19:20.310 * +slave-reconf-inprog slave 10.233.64.234:6379 10.233.64.234 6379 @ mymaster 10.233.65.74 6379
2018-11-07T08:19:20.311104628Z 1:X 07 Nov 08:19:20.310 * +slave-reconf-done slave 10.233.64.234:6379 10.233.64.234 6379 @ mymaster 10.233.65.74 6379
2018-11-07T08:19:20.381345989Z 1:X 07 Nov 08:19:20.380 # +failover-end master mymaster 10.233.65.74 6379
2018-11-07T08:19:20.381442475Z 1:X 07 Nov 08:19:20.380 # +switch-master mymaster 10.233.65.74 6379 10.233.66.144 6379
2018-11-07T08:19:20.38146031Z 1:X 07 Nov 08:19:20.381 * +slave slave 10.233.64.234:6379 10.233.64.234 6379 @ mymaster 10.233.66.144 6379
2018-11-07T08:19:20.381466083Z 1:X 07 Nov 08:19:20.381 * +slave slave 10.233.65.74:6379 10.233.65.74 6379 @ mymaster 10.233.66.144 6379
2018-11-07T08:19:30.427201725Z 1:X 07 Nov 08:19:30.426 # +sdown slave 10.233.65.74:6379 10.233.65.74 6379 @ mymaster 10.233.66.144 6379
2018-11-07T08:19:37.567222124Z 1:X 07 Nov 08:19:37.566 # -sdown slave 10.233.65.74:6379 10.233.65.74 6379 @ mymaster 10.233.66.144 6379
2018-11-07T08:19:18.561209047Z 1:X 07 Nov 08:19:18.560 * +failover-state-wait-promotion slave 10.233.66.144:6379 10.233.66.144 6379 @ mymaster 10.233.65.74 6379
2018-11-07T08:19:19.227931971Z 1:X 07 Nov 08:19:19.227 # +promoted-slave slave 10.233.66.144:6379 10.233.66.144 6379 @ mymaster 10.233.65.74 6379
2018-11-07T08:19:19.227989503Z 1:X 07 Nov 08:19:19.227 # +failover-state-reconf-slaves master mymaster 10.233.65.74 6379
2018-11-07T08:19:19.297924611Z 1:X 07 Nov 08:19:19.297 * +slave-reconf-sent slave 10.233.64.234:6379 10.233.64.234 6379 @ mymaster 10.233.65.74 6379
2018-11-07T08:19:19.49242546Z 1:X 07 Nov 08:19:19.492 # -odown master mymaster 10.233.65.74 6379
2018-11-07T08:19:20.311034165Z 1:X 07 Nov 08:19:20.310 * +slave-reconf-inprog slave 10.233.64.234:6379 10.233.64.234 6379 @ mymaster 10.233.65.74 6379
2018-11-07T08:19:20.311104628Z 1:X 07 Nov 08:19:20.310 * +slave-reconf-done slave 10.233.64.234:6379 10.233.64.234 6379 @ mymaster 10.233.65.74 6379
2018-11-07T08:19:20.381345989Z 1:X 07 Nov 08:19:20.380 # +failover-end master mymaster 10.233.65.74 6379
2018-11-07T08:19:20.381442475Z 1:X 07 Nov 08:19:20.380 # +switch-master mymaster 10.233.65.74 6379 10.233.66.144 6379
2018-11-07T08:19:20.38146031Z 1:X 07 Nov 08:19:20.381 * +slave slave 10.233.64.234:6379 10.233.64.234 6379 @ mymaster 10.233.66.144 6379
2018-11-07T08:19:20.381466083Z 1:X 07 Nov 08:19:20.381 * +slave slave 10.233.65.74:6379 10.233.65.74 6379 @ mymaster 10.233.66.144 6379
2018-11-07T08:19:30.427201725Z 1:X 07 Nov 08:19:30.426 # +sdown slave 10.233.65.74:6379 10.233.65.74 6379 @ mymaster 10.233.66.144 6379
2018-11-07T08:19:37.567222124Z 1:X 07 Nov 08:19:37.566 # -sdown slave 10.233.65.74:6379 10.233.65.74 6379 @ mymaster 10.233.66.144 6379

2.4 驗證master已經修改
1)進入任意一個redis的sentinel的容器中
即執行如下命令:
kubectl exec -it -n openstack redis-ha-redis-ha-server-0 -c sentinel /bin/bash
2)登入到redis-sentinel中
即執行如下命令:
redis-cli -p 26379
3) 獲取master的地址
即只新如下命令:
sentinel get-master-addr-by-name mymaster
輸出樣例如下:
127.0.0.1:26379> sentinel get-master-addr-by-name mymaster
1) "10.233.66.144"
2) "6379"
而原來的master地址是
127.0.0.1:26379> sentinel get-master-addr-by-name mymaster
1) "10.233.65.74"
2) "6379"
證明主備切換已經成功

測試主備參考:
https://redis.io/topics/sentinel


四、redis-ha對接


1 gnocchi和redis-ha對接原理


由於gnocchi在對接redis的程式碼時,主要時根據redis_url進行解析的,具體的程式碼在:
gnocchi/storage/common/redis.py中裡面定義瞭如何例項化redis的客戶端。具體程式碼如下:
def get_client(conf):
    if redis is None:
        raise RuntimeError("python-redis unavailable")
    parsed_url = parse.urlparse(conf.redis_url)
    options = parse.parse_qs(parsed_url.query)

    kwargs = {}
    if parsed_url.hostname:
        kwargs['host'] = parsed_url.hostname
        if parsed_url.port:
            kwargs['port'] = parsed_url.port
    else:
        if not parsed_url.path:
            raise ValueError("Expected socket path in parsed urls path")
        kwargs['unix_socket_path'] = parsed_url.path
    if parsed_url.password:
        kwargs['password'] = parsed_url.password

    for a in CLIENT_ARGS:
        if a not in options:
            continue
        if a in CLIENT_BOOL_ARGS:
            v = strutils.bool_from_string(options[a][-1])
        elif a in CLIENT_LIST_ARGS:
            v = options[a][-1]
        elif a in CLIENT_INT_ARGS:
            v = int(options[a][-1])
        else:
            v = options[a][-1]
        kwargs[a] = v
    if 'socket_timeout' not in kwargs:
        kwargs['socket_timeout'] = CLIENT_DEFAULT_SOCKET_TO

    # Ask the sentinel for the current master if there is a
    # sentinel arg.
    if 'sentinel' in kwargs:
        sentinel_hosts = [
            tuple(fallback.split(':'))
            for fallback in kwargs.get('sentinel_fallback', [])
        ]
        sentinel_hosts.insert(0, (kwargs['host'], kwargs['port']))
        sentinel_server = sentinel.Sentinel(
            sentinel_hosts,


            socket_timeout=kwargs['socket_timeout'])
        sentinel_name = kwargs['sentinel']
        del kwargs['sentinel']
        if 'sentinel_fallback' in kwargs:
            del kwargs['sentinel_fallback']
        master_client = sentinel_server.master_for(sentinel_name, **kwargs)
        # The master_client is a redis.StrictRedis using a
        # Sentinel managed connection pool.
        return master_client
    return redis.StrictRedis(**kwargs)

分析上述程式碼可知:
    步驟1: gnocchi解析配置的redis url, 提取所有sentinel資訊
    步驟2: 將上述列表用於Sentinel的初始化
    步驟3: 將步驟2獲取的Sentinel物件向主redis名稱進行請求來獲得主redis客戶端,並返回
因此: 原來redis_url是:
redis://redis.openstack.svc.cluster.local:6379/
如果要和redis-ha對接,經過修改之後,最後對接的redis_url是:
redis://redis-ha-redis-ha.openstack.svc.cluster.local:26379?sentinel=mymaster

解釋:
之所以需要sentinel=mymaster的原因是:
參考gnocchi的文件:
https://gnocchi.xyz/stable_4.2/install.html#configuration-file
#       redis://<sentinel host>:<sentinel port>?sentinel=<master name>&
#         sentinel_fallback=<other sentinel host>:<sentinel port>&
#         sentinel_fallback=<other sentinel host>:<sentinel port>&
#         sentinel_fallback=<other sentinel host>:<sentinel port>
結合gnocchi上述程式碼,需要知道redis-ha中主redis的名字,而主redis的名字是mymaster,所以需要上述配置。

2 更新gnoccchi的redis_url


修改gnocchi的charts中的values.yaml
endpoints:
redis:
  name: redis-ha-redis-ha
  hosts:
    default: redis-ha-redis-ha
    public: redis-ha-redis-ha
  host_fqdn_override:
    default: null
  path:
    default: null
  scheme:
    default: 'redis'
  port:
    api:
      default: 26379
      public: 80
主要是將redis修改為redis-ha-redis-ha
這個redis-ha-redis-ha是redis-ha的服務名稱,可以通過執行如下命令檢視:
[[email protected] ark]# kubectl get svc -n openstack|grep redis-ha
redis-ha-redis-ha         ClusterIP      None            <none>                                              6379/TCP,26379/TCP               5h

更新上述gnocchi中的redis_url為:
redis://redis-ha-redis-ha.openstack.svc.cluster.local:26379?sentinel=mymaster
最後升級gnocchi。

3 驗證redis-ha對接成功


1)先建立一臺虛機
即執行如下命令
nova flavor-create min10 1308 64 1 1
nova boot --image b1ffcdc5-efe5-4db8-8771-b7d538f07e50 --flavor 1308 --nic net-id=7ec650d5-82ca-4128-a3a9-d75625d7bec9 chen
注意:
上述: 
b1ffcdc5-efe5-4db8-8771-b7d538f07e50 是一個image id,可以通過 glance image-list獲取
7ec650d5-82ca-4128-a3a9-d75625d7bec9 是一個net id,可以通過 neutron net-list獲取
請替換為真實的id

2) 檢視gnocchi對應的虛機資源資訊
gnocchi resource show 1dc3d157-e1d0-4f6a-8fc9-fcc7ab64bf8e
+-----------------------+-------------------------------------------------------------------+
| Field                 | Value                                                             |
+-----------------------+-------------------------------------------------------------------+
| created_by_project_id | f43f4dd82c2040ca99778fe730f2b933                                  |
| created_by_user_id    | 3ff16e7409c94265b97c0594aca5d228                                  |
| creator               | 3ff16e7409c94265b97c0594aca5d228:f43f4dd82c2040ca99778fe730f2b933 |
| ended_at              | None                                                              |
| id                    | 1dc3d157-e1d0-4f6a-8fc9-fcc7ab64bf8e                              |
| metrics               | cpu.delta: 42353e7b-ca36-427b-893e-aba26eb8292c                   |
|                       | cpu_util: 6399da04-75ec-42a2-bf91-40380da86bef                    |
|                       | disk.read.bytes.rate: 73c49751-01a2-4e9a-8ab9-7ec978b344e6        |
|                       | disk.read.requests.rate: 10417fe9-b230-45f6-8086-2ad098495ebb     |
|                       | disk.write.bytes.rate: 3ddc7ed5-619b-4afe-8543-b218756ff878       |
|                       | disk.write.requests.rate: c79ff681-acaa-45af-90bf-d1f7a9a70d69    |
|                       | disks.total: 2295f8a8-771e-4c1a-9f08-28f38f3098e0                 |
|                       | disks.used: 6f2b4305-1d03-455a-99b2-b899e2f351b0                  |
|                       | disks.util: 63994336-39fe-48b7-bab8-f5159d84f0fd                  |
|                       | memory.usage: 3395574a-d03c-4983-97ed-e9e84982b36a                |
|                       | memory.util: 50206a11-d857-4013-a9e6-633f30ae1e81                 |
| original_resource_id  | 1dc3d157-e1d0-4f6a-8fc9-fcc7ab64bf8e                              |
| project_id            | d460d9ea2b2744c38e1077d32588650d                                  |
| revision_end          | None                                                              |
| revision_start        | 2018-10-25T12:54:21.879170+00:00                                  |
| started_at            | 2018-10-25T12:32:30.361941+00:00                                  |
| type                  | instance                                                          |
| user_id               | 6b1f0a4d2e3541008bb511d9dd2018bd                                  |
+-----------------------+-------------------------------------------------------------------+

注意: 請將1dc3d157-e1d0-4f6a-8fc9-fcc7ab64bf8e 替換為真實的虛機id

3) 檢視gnocchi虛機資源對應的監控項的監控資料
gnocchi measures show 6399da04-75ec-42a2-bf91-40380da86bef
+---------------------------+-------------+-------+
| timestamp                 | granularity | value |
+---------------------------+-------------+-------+
| 2018-10-25T00:00:00+00:00 |     86400.0 |   0.0 |
| 2018-10-25T12:00:00+00:00 |      7200.0 |   0.0 |
| 2018-10-25T12:45:00+00:00 |       900.0 |   0.0 |
| 2018-10-25T12:55:00+00:00 |       300.0 |   0.0 |
+---------------------------+-------------+-------+

注意: 請將 6399da04-75ec-42a2-bf91-40380da86bef 替換為監控項的id

上述有監控資料,表明redis-ha對接成功

4 驗證主備切換後應用正常


先執行主備切換,然後驗證上述2中是否可以獲取新的監控資料

五、redis client使用


1 非redis-ha client使用


如果是直接初始化redis的客戶端,那麼樣例程式碼如下:
import redis
r = redis.StrictRedis(host='localhost', port=6379, db=0)
r.set('foo', 'bar')
r.get('foo')

2 redis-ha client使用


 關於sentinel的支援
redis-py可以用於發現redis的節點。需要至少保證有一個sentinel守護程序執行。可以從sentinel例項中建立redis 客戶端連線,可以連線到master。樣例程式碼如下:
from redis.sentinel import Sentinel
sentinel = Sentinel([('localhost', 26379)], socket_timeout=0.1)
master = sentinel.master_for('mymaster', socket_timeout=0.1)
master.set('foo', 'bar')
這個master物件是普通的StrictRedis例項,該例項綁定了連線池到sentinel例項上。當一個sentinel後端客戶端嘗試去建立連線,它先查詢sentinel伺服器來確定一個連線的正確的主機。如果沒有找到server,一個MasterNotFoundError將會丟擲。

參考:
[1] https://github.com/helm/charts/tree/master/stable/redis-ha 
[2] https://blog.csdn.net/csdn_ds/article/details/72550898
[3] kubernetes權威指南
[4] https://redis.io/topics/sentinel
[5] https://segmentfault.com/a/1190000002680804
[6] https://segmentfault.com/a/1190000002685515
[7] https://www.kubernetes.org.cn/3974.html
[8] http://www.cnblogs.com/S-tec-songjian/p/9354828.html
[9] https://www.cnblogs.com/S-tec-songjian/p/9365921.html
[10] https://pypi.org/project/redis/#description