Redisクラスタ on Kubernetes のライフサイクル運用手順

次の図のような、Kubernetes上でのRedisクラスタの構築、増強、縮小、消去のライフサイクルについて、K8sとRedisの操作手順のメモです。

![Redis_Cluster_on_K8s.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42829/9596424d-07bc-ee61-8e98-d45a9e02ebc0.png)



# Redisクラスタのセットアップ 

Redisマスター x3、スレーブ x3 のRedisクラスタを構築した記録です。


## K8sクラスタのノードのリスト

Redisクラスタを作った環境は、IKS (IBM Cloud Kubernetes Service) の3ノード構成のK8sクラスタです。

~~~
$ kubectl get node
NAME           STATUS   ROLES    AGE   VERSION
10.193.10.14   Ready    <none>   22m   v1.13.8+IKS
10.193.10.20   Ready    <none>   21m   v1.13.8+IKS
10.193.10.58   Ready    <none>   22m   v1.13.8+IKS
~~~


## Redisクラスタを、マニフェストで起動する

ここで、適用するマニフェストは、https://github.com/sanderploegsma/redis-cluster を、そのまま利用したものです。このマニフェストは、IKSではありませんが、無修正で利用できました。

~~~
$ kubectl apply -f redis-cluster.yml
configmap/redis-cluster created
service/redis-cluster created
statefulset.apps/redis-cluster created
~~~

#### ポッドと永続ボリュームの作成完了状態

この時点で、Redisクラスタの各ポッドは、永続ボリュームをマウントしています。

~~~
$ kubectl get po -o wide
NAME              READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
redis-cluster-0   0/1     Pending   0          40s   <none>   <none>   <none>           <none>

$ kubectl get po -o wide
NAME              READY   STATUS    RESTARTS   AGE   IP               NODE        
redis-cluster-0   1/1     Running   0          28m   172.30.254.8     10.193.10.14
redis-cluster-1   1/1     Running   0          26m   172.30.103.132   10.193.10.58
redis-cluster-2   1/1     Running   0          24m   172.30.63.2      10.193.10.20
redis-cluster-3   1/1     Running   0          22m   172.30.254.9     10.193.10.14
redis-cluster-4   1/1     Running   0          20m   172.30.63.3      10.193.10.20
redis-cluster-5   1/1     Running   0          18m   172.30.103.133   10.193.10.58


$ kubectl get pvc
NAME                   STATUS   VOLUME         CAPACITY   ACCESS MODES   STORAGECLASS       AGE
data-redis-cluster-0   Bound    pvc-43b58162   20Gi       RWO            ibmc-file-bronze   28m
data-redis-cluster-1   Bound    pvc-86a09801   20Gi       RWO            ibmc-file-bronze   26m
data-redis-cluster-2   Bound    pvc-d3110510   20Gi       RWO            ibmc-file-bronze   24m
data-redis-cluster-3   Bound    pvc-19828267   20Gi       RWO            ibmc-file-bronze   22m
data-redis-cluster-4   Bound    pvc-6515149b   20Gi       RWO            ibmc-file-bronze   20m
data-redis-cluster-5   Bound    pvc-a8929ead   20Gi       RWO            ibmc-file-bronze   18m
~~~


## Redisクラスタの初期化

Redisクラスタのポッドの一つでコマンドを実行して、Redisクラスタを構築します。

~~~
$ kubectl exec -it redis-cluster-0 -- redis-cli --cluster create --cluster-replicas 1 \
> $(kubectl get pods -l app=redis-cluster -o jsonpath='{range.items[*]}{.status.podIP}:6379 ')
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.30.254.9:6379 to 172.30.254.8:6379
Adding replica 172.30.63.3:6379 to 172.30.103.132:6379
Adding replica 172.30.103.133:6379 to 172.30.63.2:6379
M: 901cdffd0f11f30e09ba2c22540ca2d31f1219e2 172.30.254.8:6379
   slots:[0-5460] (5461 slots) master
M: e3246d584d2390f7775ff168da4f8838939c6e53 172.30.103.132:6379
   slots:[5461-10922] (5462 slots) master
M: 9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 172.30.63.2:6379
   slots:[10923-16383] (5461 slots) master
S: 3309f5c2f345f4d4facd5af967588d794225fef1 172.30.254.9:6379
   replicates 901cdffd0f11f30e09ba2c22540ca2d31f1219e2
S: cfc0a7bb96305308cc35fd6af37b59b2e2b117ae 172.30.63.3:6379
   replicates e3246d584d2390f7775ff168da4f8838939c6e53
S: 2b4a9d44f3576c239101afc16a002db6cc19be2f 172.30.103.133:6379
   replicates 9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
....
>>> Performing Cluster Check (using node 172.30.254.8:6379)
M: 901cdffd0f11f30e09ba2c22540ca2d31f1219e2 172.30.254.8:6379
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: 2b4a9d44f3576c239101afc16a002db6cc19be2f 172.30.103.133:6379
   slots: (0 slots) slave
   replicates 9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe
M: e3246d584d2390f7775ff168da4f8838939c6e53 172.30.103.132:6379
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
M: 9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 172.30.63.2:6379
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: cfc0a7bb96305308cc35fd6af37b59b2e2b117ae 172.30.63.3:6379
   slots: (0 slots) slave
   replicates e3246d584d2390f7775ff168da4f8838939c6e53
S: 3309f5c2f345f4d4facd5af967588d794225fef1 172.30.254.9:6379
   slots: (0 slots) slave
   replicates 901cdffd0f11f30e09ba2c22540ca2d31f1219e2
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
~~~

#### Redisクラスタのメンバーの確認

~~~
$ kubectl run -it redis-cli --rm --image redis --restart=Never -- bash
If you don't see a command prompt, try pressing enter.
root@redis-cli:/data# redis-cli -c -h 172.30.254.8 -p 6379 
172.30.254.8:6379> cluster nodes
2b4a9d44f3576c239101afc16a002db6cc19be2f 172.30.103.133:6379@16379 slave 9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 0 1564558966000 6 connected
e3246d584d2390f7775ff168da4f8838939c6e53 172.30.103.132:6379@16379 master - 0 1564558965000 2 connected 5461-10922
9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 172.30.63.2:6379@16379 master - 0 1564558966134 3 connected 10923-16383
f4631fb91f42024db54a829dfc00a12e9035761e 172.30.254.10:6379@16379 master - 0 1564558965000 0 connected
cfc0a7bb96305308cc35fd6af37b59b2e2b117ae 172.30.63.3:6379@16379 slave e3246d584d2390f7775ff168da4f8838939c6e53 0 1564558967137 5 connected
3309f5c2f345f4d4facd5af967588d794225fef1 172.30.254.9:6379@16379 slave 901cdffd0f11f30e09ba2c22540ca2d31f1219e2 0 1564558965128 4 connected
901cdffd0f11f30e09ba2c22540ca2d31f1219e2 172.30.254.8:6379@16379 myself,master - 0 1564558964000 1 connected 0-5460
~~~


#### NAME順のビューで分析
マスターから順番に起動して、スレーブを順番に起動している。 マスターとスレーブの関係が解る。

~~~
NAME              IP               NODE          ROLE     MASTER
redis-cluster-0   172.30.254.8     10.193.10.14  Master   -
redis-cluster-1   172.30.103.132   10.193.10.58  Master   -
redis-cluster-2   172.30.63.2      10.193.10.20  Master   -
redis-cluster-3   172.30.254.9     10.193.10.14  Slave    redis-cluster-0
redis-cluster-4   172.30.63.3      10.193.10.20  Slave    redis-cluster-1
redis-cluster-5   172.30.103.133   10.193.10.58  Slave    redis-cluster-2
~~~

上記の結果から、マスターとスレーブの対応表を作ると、次のようになる。

~~~
  マスター            スレーブ（レプリカ）
  redis-cluster-0 -> redis-cluster-3
  redis-cluster-1 -> redis-cluster-4
  redis-cluster-2 -> redis-cluster-5
~~~

#### ノード順のビューで分析

ノード順でソートすることで、K8sに共存するポッドが解る。その結果の解った事として、マスター redis-cluster-0 と スレーブ redis-cluster-3 が NODE *.14 の同じK8sノードに乗っている。そのため、このK8sノードが停止すると、ハッシュ・スロット slots:[0-5460] が失われる。そのため、マスターとスレーブが同時に失われ、スレーブが昇格して継続できない。

K8sノード(仮想サーバー) *.14 が停止した場合の影響では、GCPの場合は、ただちに代替のK8sノード(仮想サーバー）が起動するので、短時間の停止で復旧する。一方、IKSでは、K8sノード（仮想サーバー）を短時間で起動できれば、短時間の停止で被害を抑えられる。

~~~
NAME              IP               NODE          ROLE     MASTER
redis-cluster-0   172.30.254.8     10.193.10.14  Master   -
redis-cluster-3   172.30.254.9     10.193.10.14  Slave    redis-cluster-0
redis-cluster-2   172.30.63.2      10.193.10.20  Master   -
redis-cluster-4   172.30.63.3      10.193.10.20  Slave    redis-cluster-1
redis-cluster-1   172.30.103.132   10.193.10.58  Master   -
redis-cluster-5   172.30.103.133   10.193.10.58  Slave    redis-cluster-2
~~~


以上で、Redisクラスタの構築は完了です。 K8sクラスタ内のクライアントから、内部DNS redis-cluster で Redisクラスタへアクセスできるようになります。 もちろん、Redisクラスタ対応のプログラム言語のライブラリが必須です。




# Redisクラスタの増強

前述で起動したRedisクラスタに、ポッドを追加して、Redisクラスタのマスターとスレーブのセットを増強します。 ただし、K8sクラスタのノード数を増強するところまでは、行っていません。作業の手順は以下の箇条書きの順番になります。

* ステートフルセット・コントローラに対して、レプリカセット数 8 をセット
* Redisマスターの追加
* Redisステーブの追加
* リバランスを実行して、追加したRedisマスターにデータを均一化



## レプリカセット数の増加 6 -> 8

ステートフルセット・コントローラーに、新しいレプリカ数をセットします。これによって、連番の最後にメンバーが追加されいます。この追加されたポッドには、既存のポッド同様に、永続ボリュームがマウントされています。

~~~
$ kubectl scale statefulset redis-cluster --replicas=8
statefulset.apps/redis-cluster scaled

$ kubectl get po -o wide
NAME              READY   STATUS    RESTARTS   AGE     IP               NODE
redis-cluster-0   1/1     Running   0          36m     172.30.254.8     10.193.10.14
redis-cluster-1   1/1     Running   0          35m     172.30.103.132   10.193.10.58
redis-cluster-2   1/1     Running   0          32m     172.30.63.2      10.193.10.20
redis-cluster-3   1/1     Running   0          30m     172.30.254.9     10.193.10.14
redis-cluster-4   1/1     Running   0          28m     172.30.63.3      10.193.10.20
redis-cluster-5   1/1     Running   0          26m     172.30.103.133   10.193.10.58
redis-cluster-6   1/1     Running   0          3m41s   172.30.254.10    10.193.10.14
redis-cluster-7   1/1     Running   0          112s    172.30.63.4      10.193.10.20
~~~

## Redisマスターの追加

ポッド redis-cluster-6 を Redisマスターとして追加することを、redis-cluster-0 へ通知します。この中で、「$(kubectl get pod redis-cluster-6 -o jsonpath='{.status.podIP}')」は、指定したポッドのIPアドレスを返します。IPアドレスを調べて、セットしても良いですが、この方法はスマートですね。

~~~
imac:redis-cluster maho$ kubectl exec redis-cluster-0 -- redis-cli --cluster add-node \
> $(kubectl get pod redis-cluster-6 -o jsonpath='{.status.podIP}'):6379 \
> $(kubectl get pod redis-cluster-0 -o jsonpath='{.status.podIP}'):6379
>>> Adding node 172.30.254.10:6379 to cluster 172.30.254.8:6379
>>> Performing Cluster Check (using node 172.30.254.8:6379)
M: 901cdffd0f11f30e09ba2c22540ca2d31f1219e2 172.30.254.8:6379
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: 2b4a9d44f3576c239101afc16a002db6cc19be2f 172.30.103.133:6379
   slots: (0 slots) slave
   replicates 9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe
M: e3246d584d2390f7775ff168da4f8838939c6e53 172.30.103.132:6379
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
M: 9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 172.30.63.2:6379
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: cfc0a7bb96305308cc35fd6af37b59b2e2b117ae 172.30.63.3:6379
   slots: (0 slots) slave
   replicates e3246d584d2390f7775ff168da4f8838939c6e53
S: 3309f5c2f345f4d4facd5af967588d794225fef1 172.30.254.9:6379
   slots: (0 slots) slave
   replicates 901cdffd0f11f30e09ba2c22540ca2d31f1219e2
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 172.30.254.10:6379 to make it join the cluster.
[OK] New node added correctly.
~~~

ポッドのIPアドレスを求める方法の補足です。 $() の中身だけを実行すると、ポッド redis-cluster-0 のIPアドレスを返していることが理解できます。

~~~
$ kubectl get pod redis-cluster-0 -o jsonpath='{.status.podIP}';echo
172.30.254.8
~~~



#### Redisノードのリスト表示

別の対話型ポッドを起動して、マスターが4つ、スレーブが３つの状態を確認できます。

~~~
$ kubectl run -it redis-cli --rm --image redis --restart=Never -- bash
If you don't see a command prompt, try pressing enter.
root@redis-cli:/data# redis-cli -c -h 172.30.254.8 -p 6379 
172.30.254.8:6379> cluster nodes
2b4a9d44f3576c239101afc16a002db6cc19be2f 172.30.103.133:6379@16379 slave 9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 0 1564558966000 6 connected
e3246d584d2390f7775ff168da4f8838939c6e53 172.30.103.132:6379@16379 master - 0 1564558965000 2 connected 5461-10922
9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 172.30.63.2:6379@16379 master - 0 1564558966134 3 connected 10923-16383
f4631fb91f42024db54a829dfc00a12e9035761e 172.30.254.10:6379@16379 master - 0 1564558965000 0 connected  <--- 追加
cfc0a7bb96305308cc35fd6af37b59b2e2b117ae 172.30.63.3:6379@16379 slave e3246d584d2390f7775ff168da4f8838939c6e53 0 1564558967137 5 connected
3309f5c2f345f4d4facd5af967588d794225fef1 172.30.254.9:6379@16379 slave 901cdffd0f11f30e09ba2c22540ca2d31f1219e2 0 1564558965128 4 connected
901cdffd0f11f30e09ba2c22540ca2d31f1219e2 172.30.254.8:6379@16379 myself,master - 0 1564558964000 1 connected 0-5460
~~~



## Redisスレーブを追加

Redisマスターが追加できたので、このマスターのデータをレプリカするRedisスレーブを追加します。これも ポッド redis-cluster-7 を追加することを、ポッドのIPアドレスとポート番号を指定して、ポッド　redis-cluster-0　へ通知します。 Redisクラスタのマスターは、どれも同格なので、他のRedisマスターのポッドでも良いと思います。

~~~
$ kubectl exec redis-cluster-0 -- redis-cli --cluster add-node --cluster-slave \
> $(kubectl get pod redis-cluster-7 -o jsonpath='{.status.podIP}'):6379 \
> $(kubectl get pod redis-cluster-0 -o jsonpath='{.status.podIP}'):6379
>>> Adding node 172.30.63.4:6379 to cluster 172.30.254.8:6379
>>> Performing Cluster Check (using node 172.30.254.8:6379)
M: 901cdffd0f11f30e09ba2c22540ca2d31f1219e2 172.30.254.8:6379
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: 2b4a9d44f3576c239101afc16a002db6cc19be2f 172.30.103.133:6379
   slots: (0 slots) slave
   replicates 9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe
M: e3246d584d2390f7775ff168da4f8838939c6e53 172.30.103.132:6379
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
M: 9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 172.30.63.2:6379
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
M: f4631fb91f42024db54a829dfc00a12e9035761e 172.30.254.10:6379
   slots: (0 slots) master
S: cfc0a7bb96305308cc35fd6af37b59b2e2b117ae 172.30.63.3:6379
   slots: (0 slots) slave
   replicates e3246d584d2390f7775ff168da4f8838939c6e53
S: 3309f5c2f345f4d4facd5af967588d794225fef1 172.30.254.9:6379
   slots: (0 slots) slave
   replicates 901cdffd0f11f30e09ba2c22540ca2d31f1219e2
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
Automatically selected master 172.30.254.10:6379
>>> Send CLUSTER MEET to node 172.30.63.4:6379 to make it join the cluster.
Waiting for the cluster to join

>>> Configure node as replica of 172.30.254.10:6379.
[OK] New node added correctly.
~~~

#### マスターとスレーブをリストして状態を確認

~~~
172.30.254.8:6379> cluster nodes
2b4a9d44f3576c239101afc16a002db6cc19be2f 172.30.103.133:6379@16379 slave 9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 0 1564559113000 6 connected
e3246d584d2390f7775ff168da4f8838939c6e53 172.30.103.132:6379@16379 master - 0 1564559115804 2 connected 5461-10922
9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 172.30.63.2:6379@16379 master - 0 1564559113000 3 connected 10923-16383
f4631fb91f42024db54a829dfc00a12e9035761e 172.30.254.10:6379@16379 master - 0 1564559114799 0 connected
63e279bbd0fd303240248dc530968ff8e0177d33 172.30.63.4:6379@16379 slave f4631fb91f42024db54a829dfc00a12e9035761e 0 1564559112789 0 connected
cfc0a7bb96305308cc35fd6af37b59b2e2b117ae 172.30.63.3:6379@16379 slave e3246d584d2390f7775ff168da4f8838939c6e53 0 1564559114000 5 connected
3309f5c2f345f4d4facd5af967588d794225fef1 172.30.254.9:6379@16379 slave 901cdffd0f11f30e09ba2c22540ca2d31f1219e2 0 1564559114000 4 connected
901cdffd0f11f30e09ba2c22540ca2d31f1219e2 172.30.254.8:6379@16379 myself,master - 0 1564559111000 1 connected 0-5460
~~~


## クラスタのマスターとスレーブのセットが追加が出来たのでリバランスを実行

Redisマスターとスレーブのポッドが追加できたので、既存のRedisマスターとの間で、保持しているデータをリバランス（再平均化）して、追加したRedisマスターとスレーブのノードが適切に機能するようにします。

~~~
$ kubectl exec redis-cluster-0 -- redis-cli --cluster rebalance --cluster-use-empty-masters \
> $(kubectl get pod redis-cluster-0 -o jsonpath='{.status.podIP}'):6379
>>> Performing Cluster Check (using node 172.30.254.8:6379)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Rebalancing across 4 nodes. Total weight = 4.00
Moving 1366 slots from 172.30.103.132:6379 to 172.30.254.10:6379
###### 省略
Moving 1365 slots from 172.30.63.2:6379 to 172.30.254.10:6379
###### 省略
Moving 1365 slots from 172.30.254.8:6379 to 172.30.254.10:6379
###### 省略
~~~

これで、マスターとスレーブの増設は作業は完了です。




# Redisクラスタの縮小  Redisマスターとスレーブのセットを削減

次は、Redisクラスタの縮小方法です。 こちらもデータを抱えているRedisクラスタを縮小するために、ステートフルセットのレプリカ数を減らすだけとは、いきません。 そのため、この手順を、以下の箇条書きにしました。


* 削除対象のRedisマスターのスレーブを削除
* 削除対象のRedisマスターからデータを他のマスターへ追い出し
* Redisクラスタから削除対象のRedisマスターを削除
* 残ったRedisマスター間でデータをリバランス
* ステートフルセットのポッド数を減らして、Redisクラスタの規模を縮小


ステートフルセットで、レプリカ数を減らすと、最後に追加されたものから、削除されるます。そのため、レプリカ数=6を通知すると、最後に追加された redis-cluster-7 と redis-cluster-6 を削除します。


## スレーブを削除

削除対象ポッド redis-cluster-7 のRedisクラスタIDをメモします。

~~~
$ kubectl exec redis-cluster-7 -- redis-cli cluster nodes | grep myself
63e279bbd0fd303240248dc530968ff8e0177d33 172.30.63.4:6379@16379 myself,slave f4631fb91f42024db54a829dfc00a12e9035761e 0 1564559920000 7 connected
~~~


Redisマスター redis-cluster-0 で、redis-cluster-7 を削除するコマンドを実行します。

~~~
$ kubectl exec redis-cluster-0 -- redis-cli --cluster del-node \
> $(kubectl get pod redis-cluster-0 -o jsonpath='{.status.podIP}'):6379 \
> 63e279bbd0fd303240248dc530968ff8e0177d33
>>> Removing node 63e279bbd0fd303240248dc530968ff8e0177d33 from cluster 172.30.254.8:6379
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
command terminated with exit code 1
~~~

どうして、exit code = 1 で終了するのか、解りませんでしたが、削除は成功していました。


スレーブが消えたことを確認します。 以下のように、削除後は、マスター x4、 スレーブ x3 になっていました。

~~~
## 消す前
172.30.254.8:6379> cluster nodes
2b4a9d44f3576c239101afc16a002db6cc19be2f 172.30.103.133:6379@16379 slave 9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 0 1564559113000 6 connected
e3246d584d2390f7775ff168da4f8838939c6e53 172.30.103.132:6379@16379 master - 0 1564559115804 2 connected 5461-10922
9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 172.30.63.2:6379@16379 master - 0 1564559113000 3 connected 10923-16383
f4631fb91f42024db54a829dfc00a12e9035761e 172.30.254.10:6379@16379 master - 0 1564559114799 0 connected
63e279bbd0fd303240248dc530968ff8e0177d33 172.30.63.4:6379@16379 slave f4631fb91f42024db54a829dfc00a12e9035761e 0 1564559112789 0 connected
cfc0a7bb96305308cc35fd6af37b59b2e2b117ae 172.30.63.3:6379@16379 slave e3246d584d2390f7775ff168da4f8838939c6e53 0 1564559114000 5 connected
3309f5c2f345f4d4facd5af967588d794225fef1 172.30.254.9:6379@16379 slave 901cdffd0f11f30e09ba2c22540ca2d31f1219e2 0 1564559114000 4 connected
901cdffd0f11f30e09ba2c22540ca2d31f1219e2 172.30.254.8:6379@16379 myself,master - 0 1564559111000 1 connected 0-5460

## 削除後
172.30.254.8:6379> cluster nodes
2b4a9d44f3576c239101afc16a002db6cc19be2f 172.30.103.133:6379@16379 slave 9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 0 1564560086177 6 connected
e3246d584d2390f7775ff168da4f8838939c6e53 172.30.103.132:6379@16379 master - 0 1564560085000 2 connected 6827-10922
9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 172.30.63.2:6379@16379 master - 0 1564560085176 3 connected 12288-16383
f4631fb91f42024db54a829dfc00a12e9035761e 172.30.254.10:6379@16379 master - 0 1564560085000 8 connected 0-1364 5461-6826 10923-12287
cfc0a7bb96305308cc35fd6af37b59b2e2b117ae 172.30.63.3:6379@16379 slave e3246d584d2390f7775ff168da4f8838939c6e53 0 1564560084171 5 connected
3309f5c2f345f4d4facd5af967588d794225fef1 172.30.254.9:6379@16379 slave 901cdffd0f11f30e09ba2c22540ca2d31f1219e2 0 1564560083000 4 connected
901cdffd0f11f30e09ba2c22540ca2d31f1219e2 172.30.254.8:6379@16379 myself,master - 0 1564560082000 1 connected 1365-5460
~~~

## Redisマスターの削除

削除対象のRedisマスターのIDをメモします。 コマンドで ポッド redis-cluster-6 のRedisクラスタ IDの取得方法は以下です。

~~~
$ kubectl exec redis-cluster-6 -- redis-cli cluster nodes | grep myself
f4631fb91f42024db54a829dfc00a12e9035761e 172.30.254.10:6379@16379 myself,master - 0 1564562825000 8 connected 0-1364 5461-6826 10923-12287

$ kubectl get pod -o wide
NAME              READY   STATUS    RESTARTS   AGE    IP               NODE
redis-cli         1/1     Running   0          65m    172.30.254.11    10.193.10.14
redis-cluster-0   1/1     Running   0          104m   172.30.254.8     10.193.10.14
redis-cluster-1   1/1     Running   0          102m   172.30.103.132   10.193.10.58
redis-cluster-2   1/1     Running   0          100m   172.30.63.2      10.193.10.20
redis-cluster-3   1/1     Running   0          98m    172.30.254.9     10.193.10.14
redis-cluster-4   1/1     Running   0          96m    172.30.63.3      10.193.10.20
redis-cluster-5   1/1     Running   0          94m    172.30.103.133   10.193.10.58
redis-cluster-6   1/1     Running   0          70m    172.30.254.10    10.193.10.14
redis-cluster-7   1/1     Running   1          69m    172.30.63.4      10.193.10.20
~~~



## データの移動 reshard (リシェアード) を実行

ポッド redis-cluster-6 の Redisマスター に保存されているデータを、他のRedisマスターへ移動します。
--cluster-from は、redis-cluster-6 のIDです。
--cluster-to は、redis-cluster-0 のIDです。

つまり、redis-cluster-6 のデータを redis-cluster-0 へ転送します。 あとで、もう一度リバランスするので、一時的退避となります。

~~~
$ kubectl exec redis-cluster-0 -- redis-cli --cluster reshard --cluster-yes ¥
--cluster-from f4631fb91f42024db54a829dfc00a12e9035761e ¥
--cluster-to 901cdffd0f11f30e09ba2c22540ca2d31f1219e2 ¥
--cluster-slots 16384 $(kubectl get pod redis-cluster-0 -o jsonpath='{.status.podIP}'):6379
>>> Performing Cluster Check (using node 172.30.254.8:6379)
M: 901cdffd0f11f30e09ba2c22540ca2d31f1219e2 172.30.254.8:6379
   slots:[1365-5460] (4096 slots) master
   1 additional replica(s)
S: 2b4a9d44f3576c239101afc16a002db6cc19be2f 172.30.103.133:6379
   slots: (0 slots) slave
   replicates 901cdffd0f11f30e09ba2c22540ca2d31f1219e2
M: e3246d584d2390f7775ff168da4f8838939c6e53 172.30.103.132:6379
   slots:[6827-10922] (4096 slots) master
   1 additional replica(s)
M: 9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 172.30.63.2:6379
   slots:[12288-16383] (4096 slots) master
   1 additional replica(s)
M: f4631fb91f42024db54a829dfc00a12e9035761e 172.30.254.10:6379
   slots:[0-1364],[5461-6826],[10923-12287] (4096 slots) master
S: cfc0a7bb96305308cc35fd6af37b59b2e2b117ae 172.30.63.3:6379
   slots: (0 slots) slave
   replicates e3246d584d2390f7775ff168da4f8838939c6e53
S: 3309f5c2f345f4d4facd5af967588d794225fef1 172.30.254.9:6379
   slots: (0 slots) slave
   replicates 9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

Ready to move 16384 slots.
  Source nodes:
    M: f4631fb91f42024db54a829dfc00a12e9035761e 172.30.254.10:6379
       slots:[0-1364],[5461-6826],[10923-12287] (4096 slots) master
  Destination node:
    M: 901cdffd0f11f30e09ba2c22540ca2d31f1219e2 172.30.254.8:6379
       slots:[1365-5460] (4096 slots) master
       1 additional replica(s)
  Resharding plan:
    Moving slot 0 from f4631fb91f42024db54a829dfc00a12e9035761e
    Moving slot 1 from f4631fb91f42024db54a829dfc00a12e9035761e
    Moving slot 2 from f4631fb91f42024db54a829dfc00a12e9035761e
~~~


ハッシュスロットの範囲が表示されなくなり、f4631fb91f42024db54a829dfc00a12e9035761e redis-cluster-6 からデータが無くなったことが、解ります。

~~~
$ kubectl exec redis-cluster-6 -- redis-cli cluster nodes | grep master
f4631fb91f42024db54a829dfc00a12e9035761e 172.30.254.10:6379@16379 myself,master - 0 1564574487000 8 connected
901cdffd0f11f30e09ba2c22540ca2d31f1219e2 172.30.254.8:6379@16379 master - 0 1564574488000 10 connected 0-6826 10923-12287
e3246d584d2390f7775ff168da4f8838939c6e53 172.30.103.132:6379@16379 master - 0 1564574490000 2 connected 6827-10922
9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 172.30.63.2:6379@16379 master - 0 1564574485735 9 connected 12288-16383
imac:redis-cluster maho$ kubectl get po -o wide
NAME              READY   STATUS    RESTARTS   AGE     IP               NODE
redis-cli         1/1     Running   0          4h19m   172.30.254.11    10.193.10.14
redis-cluster-0   1/1     Running   0          4h58m   172.30.254.8     10.193.10.14
redis-cluster-1   1/1     Running   0          4h56m   172.30.103.132   10.193.10.58
redis-cluster-2   1/1     Running   0          4h54m   172.30.63.2      10.193.10.20
redis-cluster-3   1/1     Running   0          4h52m   172.30.254.9     10.193.10.14
redis-cluster-4   1/1     Running   0          4h50m   172.30.63.3      10.193.10.20
redis-cluster-5   1/1     Running   0          4h48m   172.30.103.133   10.193.10.58
redis-cluster-6   1/1     Running   0          4h25m   172.30.254.10    10.193.10.14
redis-cluster-7   1/1     Running   1          4h23m   172.30.63.4      10.193.10.20
~~~


## Redisマスター redis-cluster-6 を Redisクラスタから削除

Redisマスター redis-cluster-6 からデータを追い出せたので、Redisクラスタから redis-cluster-6 を削除します。

~~~
kubectl exec redis-cluster-0 -- redis-cli --cluster del-node \
$(kubectl get pod redis-cluster-0 -o jsonpath='{.status.podIP}'):6379 \
f4631fb91f42024db54a829dfc00a12e9035761e
~~~

削除の確認結果です。 Redisマスターが3つに、減っていることが解ります。

~~~
172.30.103.132:6379> cluster nodes
cfc0a7bb96305308cc35fd6af37b59b2e2b117ae 172.30.63.3:6379@16379 slave e3246d584d2390f7775ff168da4f8838939c6e53 0 1564574917531 5 connected
2b4a9d44f3576c239101afc16a002db6cc19be2f 172.30.103.133:6379@16379 slave 901cdffd0f11f30e09ba2c22540ca2d31f1219e2 0 1564574919544 10 connected
9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 172.30.63.2:6379@16379 master - 0 1564574920548 9 connected 12288-16383
3309f5c2f345f4d4facd5af967588d794225fef1 172.30.254.9:6379@16379 slave 9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 0 1564574919000 9 connected
e3246d584d2390f7775ff168da4f8838939c6e53 172.30.103.132:6379@16379 myself,master - 0 1564574919000 2 connected 6827-10922
901cdffd0f11f30e09ba2c22540ca2d31f1219e2 172.30.254.8:6379@16379 master - 0 1564574920000 10 connected 0-6826 10923-12287
~~~

## 削除後に、データをリバランス

最後にもう一度、リバランスを実行して、平均化をはかります。

~~~
$ kubectl exec redis-cluster-0 -- redis-cli --cluster rebalance --cluster-use-empty-masters \
> $(kubectl get pod redis-cluster-0 -o jsonpath='{.status.podIP}'):6379
>>> Performing Cluster Check (using node 172.30.254.8:6379)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Rebalancing across 3 nodes. Total weight = 3.00
Moving 1366 slots from 172.30.254.8:6379 to 172.30.103.132:6379
###### 省略
Moving 1365 slots from 172.30.254.8:6379 to 172.30.63.2:6379
###### 省略
~~~

リバランス後の状態を確認です。

~~~
172.30.103.132:6379> cluster nodes
cfc0a7bb96305308cc35fd6af37b59b2e2b117ae 172.30.63.3:6379@16379 slave e3246d584d2390f7775ff168da4f8838939c6e53 0 1564575387676 11 connected
2b4a9d44f3576c239101afc16a002db6cc19be2f 172.30.103.133:6379@16379 slave 901cdffd0f11f30e09ba2c22540ca2d31f1219e2 0 1564575390000 10 connected
9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 172.30.63.2:6379@16379 master - 0 1564575389000 12 connected 1366-2730 12288-16383
3309f5c2f345f4d4facd5af967588d794225fef1 172.30.254.9:6379@16379 slave 9a3951b4d0fd3c78a927ccb6e43ff62fe7875fbe 0 1564575388000 12 connected
e3246d584d2390f7775ff168da4f8838939c6e53 172.30.103.132:6379@16379 myself,master - 0 1564575388000 11 connected 0-1365 6827-10922
901cdffd0f11f30e09ba2c22540ca2d31f1219e2 172.30.254.8:6379@16379 master - 0 1564575390694 10 connected 2731-6826 10923-12287
~~~

## ステートフルセットのポッド数を減らして、Redisクラスタの規模を縮小

最後に、ステートフルセットのレプリカ数を 6に減らして、redis-cluster-6 と redis-cluster-7 が削除されるのを観察します。

~~~
$ kubectl get po -o wide
NAME              READY   STATUS    RESTARTS   AGE     IP               NODE
redis-cli         1/1     Running   0          4h35m   172.30.254.11    10.193.10.14
redis-cluster-0   1/1     Running   0          5h14m   172.30.254.8     10.193.10.14
redis-cluster-1   1/1     Running   0          5h12m   172.30.103.132   10.193.10.58
redis-cluster-2   1/1     Running   0          5h10m   172.30.63.2      10.193.10.20
redis-cluster-3   1/1     Running   0          5h8m    172.30.254.9     10.193.10.14
redis-cluster-4   1/1     Running   0          5h6m    172.30.63.3      10.193.10.20
redis-cluster-5   1/1     Running   0          5h4m    172.30.103.133   10.193.10.58
redis-cluster-6   1/1     Running   1          4h41m   172.30.254.10    10.193.10.14
redis-cluster-7   1/1     Running   1          4h39m   172.30.63.4      10.193.10.20

$ kubectl scale statefulset redis-cluster --replicas=6
statefulset.apps/redis-cluster scaled

$ kubectl get po -o wide
NAME              READY   STATUS    RESTARTS   AGE     IP               NODE
redis-cli         1/1     Running   0          4h38m   172.30.254.11    10.193.10.14
redis-cluster-0   1/1     Running   0          5h17m   172.30.254.8     10.193.10.14
redis-cluster-1   1/1     Running   0          5h15m   172.30.103.132   10.193.10.58
redis-cluster-2   1/1     Running   0          5h13m   172.30.63.2      10.193.10.20
redis-cluster-3   1/1     Running   0          5h11m   172.30.254.9     10.193.10.14
redis-cluster-4   1/1     Running   0          5h9m    172.30.63.3      10.193.10.20
redis-cluster-5   1/1     Running   0          5h7m    172.30.103.133   10.193.10.58
~~~


以上で、Redisクラスタのスケールダウンの作業が完了です。


# クラスタのクリーンナップ

永続ボリュームごと、スレートフルセット・コントローラー、その管理下のポッド、サービスを削除する方法です。

~~~
$ kubectl delete statefulset,svc,configmap,pvc -l app=redis-cluster
statefulset.apps "redis-cluster" deleted
service "redis-cluster" deleted
configmap "redis-cluster" deleted
persistentvolumeclaim "data-redis-cluster-0" deleted
persistentvolumeclaim "data-redis-cluster-1" deleted
persistentvolumeclaim "data-redis-cluster-2" deleted
persistentvolumeclaim "data-redis-cluster-3" deleted
persistentvolumeclaim "data-redis-cluster-4" deleted
persistentvolumeclaim "data-redis-cluster-5" deleted
persistentvolumeclaim "data-redis-cluster-6" deleted
persistentvolumeclaim "data-redis-cluster-7" deleted
~~~


# あとがき

Red Hat が推進するオペレータでは、どれくらい手軽に作業できるようになるか、興味が湧いてきました。その前に、マルチゾーンに展開する方法を確認しないと受けないですね。



# 参考資料

1. https://github.com/sanderploegsma/redis-cluster