Redisクラスタ構築とフェイルオーバー検証

# はじめに
Redis-Clusterにおけるフェイルオーバー時の挙動について挙動検証を行ったので、検証時の備忘メモとして環境構築と検証結果メモを記す。

# 1. Redisクラスタ構築
## 1.1 Redisインストール

今回はMac環境で検証を行うため、homebrewなどでインストールを行う。
インストール方法はググれば出てくるので省略。

インストール後の起動確認として下記を実行する。

```sh
# Redisサーバをバックグラウンドで起動させる
# オプションを指定しない場合、デフォルトの「」が参照される
# デフォルトのポート番号は「6379」
$ redis-server --daemonize yes
# RedisCLIを起動させpingコマンドを実行し、PONGが返ってくるのを確認する
$ redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> exit
# Redisサーバの停止
$ redis-cli shutdown
```

## 1.2 Redis-Cluster構築と起動
Redis-Clusterを構築する場合、最低6台のマシンが必要となる。
今回は1台のマシンでクラスタを構築するため、別々のポート番号でRedisを起動しクラスタを構築する。
構築する際にデフォルト(Mac Homebrewインストールの場合)の`/usr/local/etc/redis.conf`をコピーし`redis-[ポート番号].conf`の名前で6ファイル作成し、`/redis/conf/`に配置し設定の追加・変更を行った。

* ディレクトリ構成

```
/redis
  ├ log
  ├ conf
  ├ pid
  └ work
```

* configファイルの設定内容

```conf:/redis/conf/redis-[ポート番号].conf
# Working Directory
dir /redis/work/
# ポート番号（今回だと7000～7005をそれぞれに設定する）
port XXXX
# PIDファイル
pidfile /redis/work/pid/redis-[ポート番号].pid
# ログファイル
logfile "/redis/log/redis-[ポート番号].log"
# DBファイル
dbfilename dump-[ポート番号].rdb
# AOFモードOn/Off設定
appendonly yes
# AppendOnlyFile
appendfilename "appendonly-[ポート番号].aof"
# クラスターモードOn/Off設定
cluster-enabled yes
# クラスター設定ファイル名
cluster-config-file cluster-config-[ポート番号].conf
# ReidsClusterのnode間通信におけるタイムアウト設定 (ミリ秒)
cluster-node-timeout=1000
```

ファイル作成後、6台のRedisを起動させ、`redis-cli`のコマンドでクラスタを構築する。
Redis-Clusterの構築方法は`redis-cli`の`--cluster create`コマンドやredis-trib.rbを利用した方法※などがある。
redis-trib.rbはRedis3.0以降から利用可能であり、redis-trib.rbを使うのが一般的だが今回はフェイルオーバー検証が目的のため簡単に作れる`--cluster create`によりクラスタを構築する。

```sh
# 作成した6ファイルを指定し、Redisサーバを6台起動させる
$ redis-server /redis/conf/redis-7000.conf --daemonize yes
$ redis-server /redis/conf/redis-7001.conf --daemonize yes
$ redis-server /redis/conf/redis-7002.conf --daemonize yes
$ redis-server /redis/conf/redis-7003.conf --daemonize yes
$ redis-server /redis/conf/redis-7004.conf --daemonize yes
$ redis-server /redis/conf/redis-7005.conf --daemonize yes
# Redis-Clusterの構築
$ redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 \
127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1
# クラスタ作成時の標準出力は省略
```

クラスタの状態を確認し、master3台、slave3台が起動していればOK。
`-cluster create`コマンドでクラスタを構成した場合、Master/Slaveノードの割り振りはランダムで設定される
今回の場合は、下記構成でクラスターが構成された。

![Redisノード構成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177076/6fabe013-a5d4-4f48-e450-cf1d7ce2cc7c.png)


```sh
# クラスタ構成の場合-pオプションで接続先ポートを指定しないと接続エラーとなるので接続先ポートを指定する
$ redis-cli -p 7000 cluster nodes
dd5f9cd86a1758e6d88a5e5d2496f8f295cfccbf 127.0.0.1:7001@17001 master - 0 1605445940617 2 connected 5461-10922
bb439ff10930780ad6cbd11a72136728f718dcf9 127.0.0.1:7000@17000 myself,master - 0 1605445940000 1 connected 0-5460
5a3976ef7bf2656714cbbd54d9f42e5da5c8747c 127.0.0.1:7005@17005 slave dd5f9cd86a1758e6d88a5e5d2496f8f295cfccbf 0 1605445940822 2 connected
09fbbd23ec43cb1189319c819111fbbf57f080cf 127.0.0.1:7003@17003 slave 130464b52347bb3ac76c090ec6ca7a4232451d65 0 1605445940822 3 connected
d2fd5916251ac934ede4a32d95e8c68cae741a42 127.0.0.1:7004@17004 slave bb439ff10930780ad6cbd11a72136728f718dcf9 0 1605445940617 1 connected
130464b52347bb3ac76c090ec6ca7a4232451d65 127.0.0.1:7002@17002 master - 0 1605445940720 3 connected 10923-16383
```

# 2. Redis-Clusterのフェイルオーバー検証
## 2.1 Masterノードの異常
Redis-Clusterのmasterに障害等が発生した場合、障害を検知しフェイルオーバー(masterとslaveを切り替え)が発生する。
フェイルオーバーにかかる時間は`redis.conf`の`cluster-node-timeout`パラメータの設定値に依存している。
各ノードの`cluster-node-timeout`を1秒、15秒、30秒に設定した状況で、擬似的な障害を発生させフェイルオーバーによるMaster/Slaveの切り替え完了までにかかる時間の計測を行った。

`redis-cli`の`cluster nodes`コマンドを1秒間隔で実行することで切り替え時間の計測を、擬似的な障害はでMasterノードのプロセスを落とすことで発生させた。

計測結果は下表のとおりである。

|cluster-node-timeoutの設定値|フェイルオーバー完了までの時間|
|:--|:----|
|1秒 | 3秒|
|15秒|21秒|
|30秒|35秒|

`cluster-node-timeout=15秒`設定時におけるcluster nodesコマンドの実行結果は以下の通りであり、Masterプロセスのkillから約15秒後にMasterノードの異常を検知し、その4秒後にMaster/Slaveの切り替わりが完了している。

```
# ポート7000/7004以外のノード情報は省略
==========================================
== 22:57:44
# 22:57:44に7000ポートのノードのプロセスをkill
Kill Process redis-server 127.0.0.1:7000
==========================================
== 22:57:44
bb439ff10930780ad6cbd11a72136728f718dcf9 127.0.0.1:7000@17000 master - 0 1605448662626 8 disconnected 0-5460
d2fd5916251ac934ede4a32d95e8c68cae741a42 127.0.0.1:7004@17004 slave bb439ff10930780ad6cbd11a72136728f718dcf9 0 1605448664658 8 connected
==========================================
== 22:58:00
bb439ff10930780ad6cbd11a72136728f718dcf9 127.0.0.1:7000@17000 master - 1605448665679 1605448662626 8 disconnected 0-5460
d2fd5916251ac934ede4a32d95e8c68cae741a42 127.0.0.1:7004@17004 slave bb439ff10930780ad6cbd11a72136728f718dcf9 0 1605448680033 8 connected
==========================================
== 22:58:01
# 7000ポートのノードの状態が「fail?」に変化
bb439ff10930780ad6cbd11a72136728f718dcf9 127.0.0.1:7000@17000 master,fail? - 1605448665679 1605448662626 8 disconnected 0-5460
d2fd5916251ac934ede4a32d95e8c68cae741a42 127.0.0.1:7004@17004 slave bb439ff10930780ad6cbd11a72136728f718dcf9 0 1605448680033 8 connected
==========================================
== 22:58:04
# 7000ポートのノードの状態が「fail」に変化
bb439ff10930780ad6cbd11a72136728f718dcf9 127.0.0.1:7000@17000 master,fail - 1605448665679 1605448662626 8 disconnected 0-5460
d2fd5916251ac934ede4a32d95e8c68cae741a42 127.0.0.1:7004@17004 slave bb439ff10930780ad6cbd11a72136728f718dcf9 0 1605448684155 8 connected
==========================================
== 22:58:05
# ポート7000/7004のMaster/Slaveが切り替わる
bb439ff10930780ad6cbd11a72136728f718dcf9 127.0.0.1:7000@17000 master,fail - 1605448665679 1605448662626 8 disconnected
d2fd5916251ac934ede4a32d95e8c68cae741a42 127.0.0.1:7004@17004 master - 0 1605448684155 9 connected 0-5460
==========================================
```



## 2.2 failoverコマンドによる手動フェイルオーバー
RedisのMasterノード障害だけでなく、redis-cliのコマンドにて手動で強制的にフェイルオーバーを起こすことも可能である。
手動フェイルオーバーを起こす場合、Slaveノードのマシンに対して、フェイルオーバーコマンドを実行することでMasterとSlaveの切り替えが発生する。

```sh
# クラスターフェイルオーバーコマンド
$ redis-cluster -p <Slaveノードのポート番号> cluster failover
```
今回は、手動フェイルオーバーを実行してから実際にフェイルオーバーが完了（Master/Slaveの切り替わり）するまでにかかる時間の検証を行う。検証方法は「2. Redis-Clusterのフェイルオーバー検証」と同じく1秒間隔で`redis-cli`の`cluster nodes`コマンドを実行している状態で、`culster failover`コマンドの実行するこでフェイルオーバーを発生させた。
検証結果は下記表のとおりで、`cluster-node-timeout`び設定値にかかわらず、即時に切り替わることが確認できている。

|cluster-node-timeoutの設定値|フェイルオーバー完了までの時間|
|:---|:----|
|1秒 |  1秒|
|15秒|  1秒|
|30秒|  1秒|

検証における`cluster nodes`コマンドの実行結果は以下の通りであり、フェイルオーバーコマンド実行から1秒以内のMastertとSlave切り替わりが確認できた。

```
==========================================
== 22:36:18
# 22:36:18にredis-cliのフェイルオーバーコマンドを実行
Redis Command Failover Eexecute.
==========================================
== 22:36:18
bb439ff10930780ad6cbd11a72136728f718dcf9 127.0.0.1:7000@17000 master - 0 1605447378364 1 connected 0-5460
d2fd5916251ac934ede4a32d95e8c68cae741a42 127.0.0.1:7004@17004 slave bb439ff10930780ad6cbd11a72136728f718dcf9 0 1605447378159 1 connected
==========================================
== 22:36:19
# ポート7000/7004のMaster/Slaveが切り替わる
bb439ff10930780ad6cbd11a72136728f718dcf9 127.0.0.1:7000@17000 slave d2fd5916251ac934ede4a32d95e8c68cae741a42 0 1605447379388 7 connected
d2fd5916251ac934ede4a32d95e8c68cae741a42 127.0.0.1:7004@17004 master - 0 1605447379696 7 connected 0-5460
==========================================
```

## 2.3 結果まとめ

* ノード障害発生時のフェイルオーバー完了までにかかる時間は`cluster-node-timeout`の設定値に依存する。
* `cluster failover`によるフェイルオーバーは即時切り替わりであり、`cluster-node-timeout`の設定値に依存しない。


# 参考資料
* https://www.sraoss.co.jp/tech-blog/redis/redis-cluster/
* https://runebook.dev/ja/docs/redis/cluster-forget
* https://www.sraoss.co.jp/tech-blog/redis/redis-persistdata/