Redis クラスタ

スポンサーリンク

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を算出します。

Redis Cluster 構成図01

その後、以下構成に示すように、tcp7006をReplicaとして追加する操作を示します。

Redis Cluster 構成図02

クラスタの構築

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> 
タイトルとURLをコピーしました