RedisでClusterを構成する方法をまとめます。Redis Clusterは負荷分散と冗長化を同時に実現する構成です。一見すると、レプリケーションよりも良い事しかないように見えますが、Redis Serverをまたがるmgetができない等の制約に配慮した設計が必要になります。
前提
公式ドキュメント
参考になる公式ドキュメントを以下に示します。
動作確認済環境
- Rocky Linux 8.6
- Redis 5.0.3
構成図
操作簡略化のため、1つの仮想マシンで以下6つのRedis Serverプロセスを起動する構成を採用します。
まずは以下の6台構成のクラスタを構築します。Redis Clusterはkeyのハッシュ値を元にslotと呼ばれる値を算出し、slotに応じてデータの書き込み先を決定します。
正確に言えば「HASH_SLOT = CRC16(key) mod 16384」という計算でslotを算出します。
その後、以下構成に示すように、tcp7006をReplicaとして追加する操作を示します。
クラスタの構築
Redisサーバの構築
Redis Clusterを構成する最小限の設定は以下の通りです。
port 7000 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes
今回のシナリオは1つの仮想マシンで6つのRedis Serverを起動しますので、ファイルなどの各種リソースが競合しないように以下のような操作で6つの設定ファイルを作成します。
mkdir /etc/redis for port in {7000..7005} do mkdir /etc/redis/${port} cat << EOF > /etc/redis/${port}/redis.conf port ${port} cluster-enabled yes cluster-config-file ${port}-nodes.conf cluster-node-timeout 5000 appendonly yes logfile /var/log/redis/${port}-redis.log pidfile /var/run/${port}-redis.pid dbfilename "${port}-dump.rdb" EOF done
6つのRedis Serverプロセスを起動します。
redis-server /etc/redis/7000/redis.conf & redis-server /etc/redis/7001/redis.conf & redis-server /etc/redis/7002/redis.conf & redis-server /etc/redis/7003/redis.conf & redis-server /etc/redis/7004/redis.conf & redis-server /etc/redis/7005/redis.conf &
Redis Server起動後のログを少し観察してみましょう。「Node configuration loaded, I’m」というログがあります。このログに示される13a2c8b54de839292c3380898c51282726f0bc99という値はnode idと呼ばれる値でクラスタの設定変更をする時に指定する事もあります。
Redisクラスタ構成では、各Redis Serverにnode idと呼ばれる値が付与される事を覚えておきましょう。
[root@linux010 ~]# tail -f /var/log/redis/7000-redis.log <omitted> 2951:C 17 Nov 2022 21:15:00.792 # Redis version=5.0.3, bits=64, commit=00000000, modified=0, pid=2951, just started 2951:C 17 Nov 2022 21:15:00.792 # Configuration loaded 2951:M 17 Nov 2022 21:15:00.793 * Increased maximum number of open files to 10032 (it was originally set to 1024). 2951:M 17 Nov 2022 21:15:00.795 * Node configuration loaded, I'm 13a2c8b54de839292c3380898c51282726f0bc99 2951:M 17 Nov 2022 21:15:00.797 * Running mode=cluster, port=7000. 2951:M 17 Nov 2022 21:15:00.797 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 2951:M 17 Nov 2022 21:15:00.797 # Server initialized <omitted>
Redisクラスタの構築
以下のようなredis-cliコマンドでRedisクラスタを構築できます。オプションのcluster-replicasはMasterサーバに対して何個のReplicaを作成するかの指定です。以下コマンドの場合は、Redis Master 3台とRedis Replicaが3台の構成になります。
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
操作ログは以下のようになります。対話形式でクラスタを作成するかどうかを問われますので、”yes”と入力します。
[root@linux010 ~]# 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 >>> Performing hash slots allocation on 6 nodes... Master[0] -> Slots 0 - 5460 Master[1] -> Slots 5461 - 10922 Master[2] -> Slots 10923 - 16383 Adding replica 127.0.0.1:7003 to 127.0.0.1:7000 Adding replica 127.0.0.1:7004 to 127.0.0.1:7001 Adding replica 127.0.0.1:7005 to 127.0.0.1:7002 >>> Trying to optimize slaves allocation for anti-affinity [WARNING] Some slaves are in the same host as their master M: d4fdecb64209b66f7bdea31b51953051d4742047 127.0.0.1:7000 slots:[0-5460] (5461 slots) master M: b06d1d7cc6bc666c0238c8c8a268a9ef36277ccc 127.0.0.1:7001 slots:[5461-10922] (5462 slots) master M: ec272706e4f075f710e9aa001b7eb0fb6d003518 127.0.0.1:7002 slots:[10923-16383] (5461 slots) master S: 820dfbb1561f27248e5e261e4b22da93495c23e9 127.0.0.1:7003 replicates d4fdecb64209b66f7bdea31b51953051d4742047 S: 040df2dbffc53b7450c71bc60749db403a3d2fda 127.0.0.1:7004 replicates b06d1d7cc6bc666c0238c8c8a268a9ef36277ccc S: dc0a24345ebeb28b3eb1bc4806457aa84bb7d394 127.0.0.1:7005 replicates ec272706e4f075f710e9aa001b7eb0fb6d003518 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 127.0.0.1:7000) M: d4fdecb64209b66f7bdea31b51953051d4742047 127.0.0.1:7000 slots:[0-5460] (5461 slots) master 1 additional replica(s) S: dc0a24345ebeb28b3eb1bc4806457aa84bb7d394 127.0.0.1:7005 slots: (0 slots) slave replicates ec272706e4f075f710e9aa001b7eb0fb6d003518 S: 040df2dbffc53b7450c71bc60749db403a3d2fda 127.0.0.1:7004 slots: (0 slots) slave replicates b06d1d7cc6bc666c0238c8c8a268a9ef36277ccc M: ec272706e4f075f710e9aa001b7eb0fb6d003518 127.0.0.1:7002 slots:[10923-16383] (5461 slots) master 1 additional replica(s) M: b06d1d7cc6bc666c0238c8c8a268a9ef36277ccc 127.0.0.1:7001 slots:[5461-10922] (5462 slots) master 1 additional replica(s) S: 820dfbb1561f27248e5e261e4b22da93495c23e9 127.0.0.1:7003 slots: (0 slots) slave replicates d4fdecb64209b66f7bdea31b51953051d4742047 [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. [root@linux010 ~]#
cluster infoやcluster nodesコマンドを使用すると、クラスタの状態を確認する事ができます。前述のログで示したnode idは”cluster nodes”コマンドでも確認できる事を覚えておきましょう。
[root@linux010 ~]# redis-cli -p 7000 127.0.0.1:7000> CLUSTER INFO cluster_state:ok cluster_slots_assigned:16384 cluster_slots_ok:16384 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:6 cluster_size:3 cluster_current_epoch:6 cluster_my_epoch:1 cluster_stats_messages_ping_sent:330 cluster_stats_messages_pong_sent:332 cluster_stats_messages_sent:662 cluster_stats_messages_ping_received:327 cluster_stats_messages_pong_received:330 cluster_stats_messages_meet_received:5 cluster_stats_messages_received:662 127.0.0.1:7000> CLUSTER NODES dc0a24345ebeb28b3eb1bc4806457aa84bb7d394 127.0.0.1:7005@17005 slave ec272706e4f075f710e9aa001b7eb0fb6d003518 0 1668738188000 6 connected 040df2dbffc53b7450c71bc60749db403a3d2fda 127.0.0.1:7004@17004 slave b06d1d7cc6bc666c0238c8c8a268a9ef36277ccc 0 1668738188581 5 connected ec272706e4f075f710e9aa001b7eb0fb6d003518 127.0.0.1:7002@17002 master - 0 1668738188180 3 connected 10923-16383 b06d1d7cc6bc666c0238c8c8a268a9ef36277ccc 127.0.0.1:7001@17001 master - 0 1668738187177 2 connected 5461-10922 820dfbb1561f27248e5e261e4b22da93495c23e9 127.0.0.1:7003@17003 slave d4fdecb64209b66f7bdea31b51953051d4742047 0 1668738189182 4 connected d4fdecb64209b66f7bdea31b51953051d4742047 127.0.0.1:7000@17000 myself,master - 0 1668738186000 1 connected 0-5460 127.0.0.1:7000>
書き込みの動作確認
クラスタに対して接続する場合は、redis-cliコマンドに-cオプションを指定します。
[root@linux010 ~]# redis-cli -c -p 7000 127.0.0.1:7000>
以下出力に示すようにキーに応じて異なるRedisサーバに書き込まれている事が分かります。例えば、fooのハッシュ値は12182なので、キーfooはtcp7002のRedis Masterに書き込まれます。
127.0.0.1:7000> set foo bar -> Redirected to slot [12182] located at 127.0.0.1:7002 OK 127.0.0.1:7002> set hello world -> Redirected to slot [866] located at 127.0.0.1:7000 OK 127.0.0.1:7000> get foo -> Redirected to slot [12182] located at 127.0.0.1:7002 "bar" 127.0.0.1:7002> get hello -> Redirected to slot [866] located at 127.0.0.1:7000 "world" 127.0.0.1:7000>
障害発生時の挙動
まずは障害発生前の挙動を確認します。キーfooに対するget操作をすると、tcp7002に対して読み取り操作をしている事が分かります。
[root@linux010 ~]# redis-cli -c -p 7000 127.0.0.1:7000> GET foo -> Redirected to slot [12182] located at 127.0.0.1:7002 "bar" 127.0.0.1:7002>
この状態でtcp7002に対して疑似障害を発生させてみましょう。
[root@linux010 ~]# ps aux | grep redis root 2987 0.1 0.4 279052 16128 pts/2 Sl 21:18 0:06 redis-server *:7000 [cluster] root 2988 0.1 0.3 266252 12460 pts/2 Sl 21:18 0:06 redis-server *:7001 [cluster] root 2997 0.1 0.3 274444 14752 pts/2 Sl 21:18 0:06 redis-server *:7002 [cluster] root 2998 0.1 0.4 270348 16564 pts/2 Sl 21:18 0:06 redis-server *:7003 [cluster] root 2999 0.1 0.4 270348 16764 pts/2 Sl 21:18 0:06 redis-server *:7004 [cluster] root 3000 0.1 0.4 276492 18500 pts/2 Sl 21:18 0:06 redis-server *:7005 [cluster] root 3049 0.0 0.2 25512 8980 pts/2 S+ 21:23 0:00 redis-cli -p 7000 root 3099 0.0 0.2 25512 9040 pts/3 S+ 21:34 0:00 redis-cli -p 7000 root 3244 0.0 0.0 221936 1084 pts/6 S+ 22:23 0:00 grep --color=auto redis [root@linux010 ~]# kill 2997 [root@linux010 ~]#
キーfooに対するget操作が、tcp7002からtcp7005に切り替わった事が分かります。
[root@linux010 ~]# redis-cli -c -p 7000 127.0.0.1:7000> GET foo -> Redirected to slot [12182] located at 127.0.0.1:7005 "bar" 127.0.0.1:7005>
クラスタの構成変更
クラスタへのReplicaメンバ追加
クラスタのメンバ追加の操作例を示します。まずは動作確認用のtcp7006で起動するRedis Serverプロセスを立ち上げます。
mkdir /etc/redis/7006 cat << EOF > /etc/redis/7006/redis.conf port 7006 cluster-enabled yes cluster-config-file 7006-nodes.conf cluster-node-timeout 5000 appendonly yes logfile /var/log/redis/7006-redis.log pidfile /var/run/7006-redis.pid dbfilename "7006-dump.rdb" EOF redis-server /etc/redis/7006/redis.conf &
クラスタへレプリカメンバを追加する構文は以下のようになります。
redis-cli --cluster add-node \ <追加するRedis ServerのIPアドレスとポート番号> \ <既にクラスタを構成しているRedis ServerのIPアドレスとポート番号> \ --cluster-slave \ --cluster-master-id <マスターとするRedis Serverのnode id>
それでは実際に操作してみましょう。tcp7000のレプリカとしてtcp7006を追加する操作を例に挙げます。
まずはマスタとなるtcp7000のnode idを調査する必要があります。以下のようにcluster nodesコマンドを使用すると調査できます。
[root@linux010 ~]# redis-cli -p 7000 cluster nodes dc0a24345ebeb28b3eb1bc4806457aa84bb7d394 127.0.0.1:7005@17005 master - 0 1668742516697 7 connected 10923-16383 040df2dbffc53b7450c71bc60749db403a3d2fda 127.0.0.1:7004@17004 slave b06d1d7cc6bc666c0238c8c8a268a9ef36277ccc 0 1668742516597 5 connected ec272706e4f075f710e9aa001b7eb0fb6d003518 127.0.0.1:7002@17002 master,fail - 1668741844788 1668741844088 3 disconnected b06d1d7cc6bc666c0238c8c8a268a9ef36277ccc 127.0.0.1:7001@17001 master - 0 1668742516000 2 connected 5461-10922 820dfbb1561f27248e5e261e4b22da93495c23e9 127.0.0.1:7003@17003 slave d4fdecb64209b66f7bdea31b51953051d4742047 0 1668742516000 4 connected d4fdecb64209b66f7bdea31b51953051d4742047 127.0.0.1:7000@17000 myself,master - 0 1668742515000 1 connected 0-5460 [root@linux010 ~]#
以上の調査結果を踏まえると、tcp7000のレプリカとしてtcp7006を追加するコマンドは以下のようになる事が分かります。
redis-cli --cluster add-node \ 127.0.0.1:7006 127.0.0.1:7000 \ --cluster-slave \ --cluster-master-id d4fdecb64209b66f7bdea31b51953051d4742047
操作ログは以下の通りです。
[root@linux010 ~]# redis-cli --cluster add-node \ > 127.0.0.1:7006 127.0.0.1:7000 \ > --cluster-slave \ > --cluster-master-id d4fdecb64209b66f7bdea31b51953051d4742047 >>> Adding node 127.0.0.1:7006 to cluster 127.0.0.1:7000 Could not connect to Redis at 127.0.0.1:7002: Connection refused >>> Performing Cluster Check (using node 127.0.0.1:7000) M: d4fdecb64209b66f7bdea31b51953051d4742047 127.0.0.1:7000 slots:[0-5460] (5461 slots) master 1 additional replica(s) M: dc0a24345ebeb28b3eb1bc4806457aa84bb7d394 127.0.0.1:7005 slots:[10923-16383] (5461 slots) master S: 040df2dbffc53b7450c71bc60749db403a3d2fda 127.0.0.1:7004 slots: (0 slots) slave replicates b06d1d7cc6bc666c0238c8c8a268a9ef36277ccc M: b06d1d7cc6bc666c0238c8c8a268a9ef36277ccc 127.0.0.1:7001 slots:[5461-10922] (5462 slots) master 1 additional replica(s) S: 820dfbb1561f27248e5e261e4b22da93495c23e9 127.0.0.1:7003 slots: (0 slots) slave replicates d4fdecb64209b66f7bdea31b51953051d4742047 [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. >>> Send CLUSTER MEET to node 127.0.0.1:7006 to make it join the cluster. Waiting for the cluster to join >>> Configure node as replica of 127.0.0.1:7000. [OK] New node added correctly. [root@linux010 ~]#
cluster nodesコマンドにより確かにメンバが追加された事が分かります。
[root@linux010 ~]# redis-cli -p 7000 cluster nodes dc0a24345ebeb28b3eb1bc4806457aa84bb7d394 127.0.0.1:7005@17005 master - 0 1668742688372 7 connected 10923-16383 040df2dbffc53b7450c71bc60749db403a3d2fda 127.0.0.1:7004@17004 slave b06d1d7cc6bc666c0238c8c8a268a9ef36277ccc 0 1668742689000 5 connected ec272706e4f075f710e9aa001b7eb0fb6d003518 127.0.0.1:7002@17002 master,fail - 1668741844788 1668741844088 3 disconnected 155a00cc99fe333e3ae0776002cde135a51ba7c1 127.0.0.1:7006@17006 slave dc0a24345ebeb28b3eb1bc4806457aa84bb7d394 0 1668742689578 7 connected b06d1d7cc6bc666c0238c8c8a268a9ef36277ccc 127.0.0.1:7001@17001 master - 0 1668742689377 2 connected 5461-10922 820dfbb1561f27248e5e261e4b22da93495c23e9 127.0.0.1:7003@17003 slave d4fdecb64209b66f7bdea31b51953051d4742047 0 1668742688000 4 connected d4fdecb64209b66f7bdea31b51953051d4742047 127.0.0.1:7000@17000 myself,master - 0 1668742688000 1 connected 0-5460 [root@linux010 ~]#
クラスタへのメンバ削除
クラスタへメンバを削除する構文は以下のようになります。
redis-cli --cluster del-node \ <既にクラスタを構成しているRedis ServerのIPアドレスとポート番号> \ <削除するRedis Serverのnode id>
それではtcp7006を削除してみましょう。まずはtcp7006のnode idを調査します。
[root@linux010 ~]# redis-cli -p 7000 cluster nodes dc0a24345ebeb28b3eb1bc4806457aa84bb7d394 127.0.0.1:7005@17005 master - 0 1668742688372 7 connected 10923-16383 040df2dbffc53b7450c71bc60749db403a3d2fda 127.0.0.1:7004@17004 slave b06d1d7cc6bc666c0238c8c8a268a9ef36277ccc 0 1668742689000 5 connected ec272706e4f075f710e9aa001b7eb0fb6d003518 127.0.0.1:7002@17002 master,fail - 1668741844788 1668741844088 3 disconnected 155a00cc99fe333e3ae0776002cde135a51ba7c1 127.0.0.1:7006@17006 slave dc0a24345ebeb28b3eb1bc4806457aa84bb7d394 0 1668742689578 7 connected b06d1d7cc6bc666c0238c8c8a268a9ef36277ccc 127.0.0.1:7001@17001 master - 0 1668742689377 2 connected 5461-10922 820dfbb1561f27248e5e261e4b22da93495c23e9 127.0.0.1:7003@17003 slave d4fdecb64209b66f7bdea31b51953051d4742047 0 1668742688000 4 connected d4fdecb64209b66f7bdea31b51953051d4742047 127.0.0.1:7000@17000 myself,master - 0 1668742688000 1 connected 0-5460 [root@linux010 ~]#
操作例は以下の通りです。確かに、nodeが削除された事が分かります。
[root@linux010 ~]# redis-cli --cluster del-node 127.0.0.1:7000 155a00cc99fe333e3ae0776002cde135a51ba7c1 >>> Removing node 155a00cc99fe333e3ae0776002cde135a51ba7c1 from cluster 127.0.0.1:7000 Could not connect to Redis at 127.0.0.1:7002: Connection refused >>> Sending CLUSTER FORGET messages to the cluster... >>> SHUTDOWN the node. [1]+ 終了 redis-server /etc/redis/7006/redis.conf [root@linux010 ~]# [root@linux010 ~]# [root@linux010 ~]# redis-cli -p 7000 cluster nodes | grep 7006 [root@linux010 ~]# redis-cli -p 7000 cluster nodes dc0a24345ebeb28b3eb1bc4806457aa84bb7d394 127.0.0.1:7005@17005 master - 0 1668743013647 7 connected 10923-16383 040df2dbffc53b7450c71bc60749db403a3d2fda 127.0.0.1:7004@17004 slave b06d1d7cc6bc666c0238c8c8a268a9ef36277ccc 0 1668743013000 5 connected ec272706e4f075f710e9aa001b7eb0fb6d003518 127.0.0.1:7002@17002 master,fail - 1668741844788 1668741844088 3 disconnected b06d1d7cc6bc666c0238c8c8a268a9ef36277ccc 127.0.0.1:7001@17001 master - 0 1668743013546 2 connected 5461-10922 820dfbb1561f27248e5e261e4b22da93495c23e9 127.0.0.1:7003@17003 slave d4fdecb64209b66f7bdea31b51953051d4742047 0 1668743013000 4 connected d4fdecb64209b66f7bdea31b51953051d4742047 127.0.0.1:7000@17000 myself,master - 0 1668743013000 1 connected 0-5460 [root@linux010 ~]#
補足
ハッシュタグ
Redis Clusterはnodeをまたがる操作はできません。例えば、以下のようにmget操作を試みると、失敗する事が分かります。
[root@linux010 ~]# redis-cli -c -p 7000 127.0.0.1:7000> mget foo hello (error) CROSSSLOT Keys in request don't hash to the same slot 127.0.0.1:7000>
このような不都合はkey01{tag01}, key02{tag01}のようなハッシュタグと呼ばれる書式を使う事で回避できます。括弧で囲まれた値(tag01)のハッシュ値に応じたRedis Serverに格納されますので、意図したnodeノードへの格納ができます。
[root@linux010 ~]# redis-cli -c -p 7000 127.0.0.1:7000> SET key01{tag01} valu01 OK 127.0.0.1:7000> SET key02{tag01} valu02 OK 127.0.0.1:7000> 127.0.0.1:7000> MGET key01{tag01} key02{tag01} 1) "valu01" 2) "valu02" 127.0.0.1:7000>