Redis Clusterについて

前回のRedisの冗長化では、Redisの標準機能を用いてRedisの冗長化を行う方法についてご紹介しました。
今回はRedisでマルチマスター構成のクラスタを組むための機能であるRedis Clusterについてご紹介します。

Redis Cluster

Redis Cluster は Redis 3.0 で実装されました。Redis Cluster はマルチマスター構成をとり、データは複数のRedisサーバに自動的に分散されます(シャーディング)。これにより書き込み負荷を分散させることができます。Redis Cluster は一部のノードが故障しても動作を継続し、ある程度の可用性を提供しますが、多数のノードが利用不可となった場合は動作を停止します。各マスターノードにはスレーブを持たせ、可用性を向上することができます。クラスタの最小構成は3マスターノードとなります。

RedisのCluster構成

Clusterが使用するポートは通常のポートに加え、クラスタの通信用に+10000したポート番号を使用します(6379の場合は16739)。

シャーディング

Redis Cluster はクラスタ全体で16384個(0~16383)のhash slotをもちます。各ノードにhash slotの一部分が割り当てられます。例えば以下のようになります。

  • ノードA: 0~5460 の hash slot
  • ノードB: 5461~10922 の hash slot
  • ノードC: 10923~16383 の hash slot

クライアントがキーを登録するとき、キーごとにhash slotの値を計算し、対応するノードにデータが格納されます。キーからhash slotの値を求めるアルゴリズムは以下のようにシンプルなものとなります。

HASH_SLOT = CRC16(key) mod 16384

各ノードのhash slotは後から自由に移動することができるため、後からノードを追加したりノードを削除することが可能です。

Cluster の設定

本来は複数台のマシンで動作させますが、今回は1台のマシン上でポート番号を変えて6台の Cluster ノードを作成します。

名前 ホスト名 IPアドレス ポート クラスタポート
Node 1 server1 192.168.1.1 7000 17000
Node 2 server1 192.168.1.1 7001 17001
Node 3 server1 192.168.1.1 7002 17002
Node 4 server1 192.168.1.1 7003 17003
Node 5 server1 192.168.1.1 7004 17004
Node 6 server1 192.168.1.1 7005 17005

ノードごとに個別のディレクトリを作成し、既存の redis.conf をコピーして設定を行います。

# mkdir cluster-test
# cd cluster-test/
# mkdir 7000 7001 7002 7003 7004 7005
# cp /etc/redis.conf 7000
# vi 7000/redis.conf
# cp 7000/redis.conf 7001
# vi 7001/redis.conf
...
# vi 7005/redis.conf

同サーバ上で動かすため、ファイル名が重複しないように適切に設定を行います。

port 7000
pidfile "/var/run/redis_7000.pid"
logfile "/root/cluster-test/7000/redis.log"
dbfilename "dump-7000.rdb"
appendonly yes
appendfilename "appendonly-7000.aof"
cluster-enabled yes
cluster-config-file nodes-7000.conf

各ノードを起動します。

# redis-server 7000/redis.conf
# redis-server 7001/redis.conf
# redis-server 7002/redis.conf
# redis-server 7003/redis.conf
# redis-server 7004/redis.conf
# redis-server 7005/redis.conf

正常にプロセスが立ち上がり、 redis.log にエラーが出力されていないことを確認します。

# ps auxww|grep redis
root 2939 0.1 0.1 153952 2772 ? Ssl 14:23 0:00 redis-server 0.0.0.0:7000 [cluster]
root 2980 0.1 0.1 153952 2768 ? Ssl 14:24 0:00 redis-server 0.0.0.0:7001 [cluster]
root 2985 0.2 0.1 153952 2768 ? Ssl 14:24 0:00 redis-server 0.0.0.0:7002 [cluster]
root 2990 0.2 0.1 153952 2772 ? Ssl 14:24 0:00 redis-server 0.0.0.0:7003 [cluster]
root 2995 0.3 0.1 153952 2776 ? Ssl 14:25 0:00 redis-server 0.0.0.0:7004 [cluster]
root 3000 0.7 0.1 153952 2776 ? Ssl 14:25 0:00 redis-server 0.0.0.0:7005 [cluster]

/var/lib/redis 以下に各ノードの AOF およびクラスタ設定ファイルが作成されます。

# ls -l /var/lib/redis/
合計 24
-rw-r--r--. 1 root root 0 6月 28 14:23 appendonly-7000.aof
-rw-r--r--. 1 root root 0 6月 28 14:24 appendonly-7001.aof
-rw-r--r--. 1 root root 0 6月 28 14:24 appendonly-7002.aof
-rw-r--r--. 1 root root 0 6月 28 14:25 appendonly-7003.aof
-rw-r--r--. 1 root root 0 6月 28 14:25 appendonly-7004.aof
-rw-r--r--. 1 root root 0 6月 28 14:25 appendonly-7005.aof
-rw-r--r--. 1 root root 114 6月 28 14:23 nodes-7000.conf
-rw-r--r--. 1 root root 114 6月 28 14:24 nodes-7001.conf
-rw-r--r--. 1 root root 114 6月 28 14:24 nodes-7002.conf
-rw-r--r--. 1 root root 114 6月 28 14:25 nodes-7003.conf
-rw-r--r--. 1 root root 114 6月 28 14:25 nodes-7004.conf
-rw-r--r--. 1 root root 114 6月 28 14:25 nodes-7005.conf

次に、クラスタを作成します。 –cluster-replicas 1 はマスターごとに1つのレプリカ(スレーブ)を配置することを意味します。
なお、 Redis 3.0、4.0 の場合は redis-cli の代わりに redis-trib.rb という外部ツールを使用する必要があります。

# 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 を入力します。

>>> 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:7004 to 127.0.0.1:7000
Adding replica 127.0.0.1:7005 to 127.0.0.1:7001
Adding replica 127.0.0.1:7003 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: 70ed8f0f72ee70d2b55adfa722fcf1f9eb0c8111 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
M: a5b82da0c32957954a3da1e16a3629083a8d5d8c 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
M: a18a55b7a4790c2bd1bd7fae057caa4144a03a52 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
S: 4d18eed5d6cdfc394f8cfa14a3655147393e3627 127.0.0.1:7003
replicates a5b82da0c32957954a3da1e16a3629083a8d5d8c
S: 04a2ad5e2b0ea06bc9d90c3ab3c4214b2ac5cce9 127.0.0.1:7004
replicates a18a55b7a4790c2bd1bd7fae057caa4144a03a52
S: 0a0dfeb8456c6bff470eddb24edae8743d6422ff 127.0.0.1:7005
replicates 70ed8f0f72ee70d2b55adfa722fcf1f9eb0c8111
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: 70ed8f0f72ee70d2b55adfa722fcf1f9eb0c8111 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 0a0dfeb8456c6bff470eddb24edae8743d6422ff 127.0.0.1:7005
slots: (0 slots) slave
replicates 70ed8f0f72ee70d2b55adfa722fcf1f9eb0c8111
S: 04a2ad5e2b0ea06bc9d90c3ab3c4214b2ac5cce9 127.0.0.1:7004
slots: (0 slots) slave
replicates a18a55b7a4790c2bd1bd7fae057caa4144a03a52
M: a5b82da0c32957954a3da1e16a3629083a8d5d8c 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
M: a18a55b7a4790c2bd1bd7fae057caa4144a03a52 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 4d18eed5d6cdfc394f8cfa14a3655147393e3627 127.0.0.1:7003
slots: (0 slots) slave
replicates a5b82da0c32957954a3da1e16a3629083a8d5d8c
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

クラスタの構成が正常に完了すると [OK] All 16384 slots covered. というメッセージが出力されます。
なお、マスターとレプリカの組み合わせを自分で決定したい場合は、
最初にマスターのみでクラスタを構成した後、マスターを指定してレプリカを1つずつ追加していきます。

# redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002

# redis-cli --cluster add-node 127.0.0.1:7003 127.0.0.1:7000 --cluster-slave --cluster-master-id 70ed8f0f72ee70d2b55adfa722fcf1f9eb0c8111
# redis-cli --cluster add-node 127.0.0.1:7004 127.0.0.1:7001 --cluster-slave --cluster-master-id a5b82da0c32957954a3da1e16a3629083a8d5d8c
# redis-cli --cluster add-node 127.0.0.1:7005 127.0.0.1:7002 --cluster-slave --cluster-master-id a18a55b7a4790c2bd1bd7fae057caa4144a03a52

任意のノードに redis-cli で接続し、 cluster nodes コマンドを実行するとノードの一覧を取得することができます。これは nodes-7000.conf とほぼ同じ内容になります。

# redis-cli -p 7000 cluster nodes
0a0dfeb8456c6bff470eddb24edae8743d6422ff 127.0.0.1:7005@17005 slave 70ed8f0f72ee70d2b55adfa722fcf1f9eb0c8111 0 1561702297172 6 connected
70ed8f0f72ee70d2b55adfa722fcf1f9eb0c8111 127.0.0.1:7000@17000 myself,master - 0 1561702298000 1 connected 0-5460
04a2ad5e2b0ea06bc9d90c3ab3c4214b2ac5cce9 127.0.0.1:7004@17004 slave a18a55b7a4790c2bd1bd7fae057caa4144a03a52 0 1561702299186 5 connected
a5b82da0c32957954a3da1e16a3629083a8d5d8c 127.0.0.1:7001@17001 master - 0 1561702296161 2 connected 5461-10922
a18a55b7a4790c2bd1bd7fae057caa4144a03a52 127.0.0.1:7002@17002 master - 0 1561702298000 3 connected 10923-16383
4d18eed5d6cdfc394f8cfa14a3655147393e3627 127.0.0.1:7003@17003 slave a5b82da0c32957954a3da1e16a3629083a8d5d8c 0 1561702298181 4 connected

Clusterの動作確認

redis-cli に -c を付けるとクラスタモードになります。これで動作を確認します。

# redis-cli -c -p 7000
127.0.0.1:7000> set foo 1
-> Redirected to slot [12182] located at 127.0.0.1:7002
OK
127.0.0.1:7002> get foo
"1"
127.0.0.1:7002> set bar 2
-> Redirected to slot [5061] located at 127.0.0.1:7000
OK
127.0.0.1:7000> get bar
"2"
127.0.0.1:7000> get foo
-> Redirected to slot [12182] located at 127.0.0.1:7002
"1"
127.0.0.1:7002> get hoge
-> Redirected to slot [1525] located at 127.0.0.1:7000
(nil)
127.0.0.1:7000> incr foo
-> Redirected to slot [12182] located at 127.0.0.1:7002
(integer) 2
127.0.0.1:7002> get foo
"2"

キーに応じてslotが変化し、対応するノードに自動的に接続を切り替えていることがわかります。

Clusterのクエリの制限

Clusterではキーごとに格納されるノードが異なるため、クライアント側でキーに対応するノードを知る必要があります(対応するノードはサーバから通知されます)。また、キーが複数のノードにまたがっている場合、複数のキーをまとめて取得する mget のようなコマンドは失敗します。

複数のキーを同じノードに割り当てたい場合、ハッシュタグ({ } で囲まれた文字列)を利用することができます。キーの一部に {…} という文字列を含めることで、 { } 内の文字列のみが hash slot の計算に使用されるようになります。以下の2つのキーは同一の hash slot となり、同じノードに格納されます。

{foo}hoge
{foo}fuga

フェイルオーバー

7000番ポートのmasterに障害を発生させます。

# kill 2939
# ps auxww|grep redis
root 2980 0.1 0.1 156512 3256 ? Ssl 14:24 0:07 redis-server 0.0.0.0:7001 [cluster]
root 2985 0.1 0.1 158560 3284 ? Ssl 14:24 0:07 redis-server 0.0.0.0:7002 [cluster]
root 2990 0.2 0.2 167264 4144 ? Ssl 14:24 0:07 redis-server 0.0.0.0:7003 [cluster]
root 2995 0.2 0.1 160608 3372 ? Ssl 14:25 0:07 redis-server 0.0.0.0:7004 [cluster]
root 3000 0.2 0.1 160608 3376 ? Ssl 14:25 0:07 redis-server 0.0.0.0:7005 [cluster]

cluster nodes の表示が fail となります。

# redis-cli -p 7001 cluster nodes
70ed8f0f72ee70d2b55adfa722fcf1f9eb0c8111 127.0.0.1:7000@17000 master,fail - 1561703162227 1561703160916 1 disconnected 0-5460
a5b82da0c32957954a3da1e16a3629083a8d5d8c 127.0.0.1:7001@17001 myself,master - 0 1561703190000 2 connected 5461-10922
a18a55b7a4790c2bd1bd7fae057caa4144a03a52 127.0.0.1:7002@17002 master - 0 1561703189000 3 connected 10923-16383
04a2ad5e2b0ea06bc9d90c3ab3c4214b2ac5cce9 127.0.0.1:7004@17004 slave a18a55b7a4790c2bd1bd7fae057caa4144a03a52 0 1561703190193 3 connected
0a0dfeb8456c6bff470eddb24edae8743d6422ff 127.0.0.1:7005@17005 slave 70ed8f0f72ee70d2b55adfa722fcf1f9eb0c8111 0 1561703189185 1 connected
4d18eed5d6cdfc394f8cfa14a3655147393e3627 127.0.0.1:7003@17003 slave a5b82da0c32957954a3da1e16a3629083a8d5d8c 0 1561703188000 4 connected

しばらくすると自動的に7000番ポートのmasterのslaveである7005番ポートのノードがmasterに昇格し、フェイルオーバーが完了します。

# redis-cli -p 7001 cluster nodes
70ed8f0f72ee70d2b55adfa722fcf1f9eb0c8111 127.0.0.1:7000@17000 master,fail - 1561703162227 1561703160916 1 disconnected
a5b82da0c32957954a3da1e16a3629083a8d5d8c 127.0.0.1:7001@17001 myself,master - 0 1561703238000 2 connected 5461-10922
a18a55b7a4790c2bd1bd7fae057caa4144a03a52 127.0.0.1:7002@17002 master - 0 1561703240648 3 connected 10923-16383
04a2ad5e2b0ea06bc9d90c3ab3c4214b2ac5cce9 127.0.0.1:7004@17004 slave a18a55b7a4790c2bd1bd7fae057caa4144a03a52 0 1561703242665 3 connected
0a0dfeb8456c6bff470eddb24edae8743d6422ff 127.0.0.1:7005@17005 master - 0 1561703242000 8 connected 0-5460
4d18eed5d6cdfc394f8cfa14a3655147393e3627 127.0.0.1:7003@17003 slave a5b82da0c32957954a3da1e16a3629083a8d5d8c 0 1561703240000 4 connected

7000番ポートのRedisを再度起動すると、7005番ポートのmasterのslaveとして立ち上がり、再同期が行われます。

# redis-server 7000/redis.conf
# redis-cli -p 7001 cluster nodes
70ed8f0f72ee70d2b55adfa722fcf1f9eb0c8111 127.0.0.1:7000@17000 slave 0a0dfeb8456c6bff470eddb24edae8743d6422ff 0 1561703557658 8 connected
a5b82da0c32957954a3da1e16a3629083a8d5d8c 127.0.0.1:7001@17001 myself,master - 0 1561703558000 2 connected 5461-10922
a18a55b7a4790c2bd1bd7fae057caa4144a03a52 127.0.0.1:7002@17002 master - 0 1561703559675 3 connected 10923-16383
04a2ad5e2b0ea06bc9d90c3ab3c4214b2ac5cce9 127.0.0.1:7004@17004 slave a18a55b7a4790c2bd1bd7fae057caa4144a03a52 0 1561703556649 3 connected
0a0dfeb8456c6bff470eddb24edae8743d6422ff 127.0.0.1:7005@17005 master - 0 1561703558000 8 connected 0-5460
4d18eed5d6cdfc394f8cfa14a3655147393e3627 127.0.0.1:7003@17003 slave a5b82da0c32957954a3da1e16a3629083a8d5d8c 0 1561703558667 4 connected

おわりに

今回はRedisの標準機能の一つであるRedis Clusterについて紹介しました。Redis Clusterは必要なサーバ台数が多くなるものの、負荷分散と可用性を両立させることができます。

次回はPacemakerを用いたRedisの冗長化について紹介します。