2019年5月21日火曜日

Redis Cluster を試してみた

概要

これまで Redis Sentinel を試してきました
Redis には Cluster の機能もありこれを使えばノードを増やすことでスケールアウトすることができます
今回は Mac 上に Redis Cluster 環境を構築しついでに Ruby からも操作してみました

環境

  • macOS 10.14.4
  • Redis 5.0.5
  • Ruby 2.6.2p47

各ノードの作成

プロセスであれば OK なので適当にディレクトリを作成して redis-server プロセスを起動します

  • mkdir 7000 7001 7002
  • touch 7000/redis.conf 7001/redis.conf 7002/redis.conf
    vim 7000/redis.conf
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes

redis.conf は 7001, 7002 ディレクトリ配下でも作成しましょう
また port の部分は 7001, 7002 に書き換えましょう

  • cd 7000
  • redis-server redis.conf

あとは各ディレクトリに移動して conf ファイルを指定して起動します

クラスタの構築

起動した各プロセスを元にクラスタを構築します
クラスタを構築するには redis-cli --cluster create コマンドを使います

  • redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002
>>> Performing hash slots allocation on 3 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
M: ad0cd036fa5cc5e87f3c46efeda4efa06ac866da 127.0.0.1:7000
   slots:[0-5460] (5461 slots) master
M: 7da858c6200f22ec2b8395a56e6f950e8c72685e 127.0.0.1:7001
   slots:[5461-10922] (5462 slots) master
M: c99ba957f8d81c4a38e77a275043730ab77aeb33 127.0.0.1:7002
   slots:[10923-16383] (5461 slots) master
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: ad0cd036fa5cc5e87f3c46efeda4efa06ac866da 127.0.0.1:7000
   slots:[0-5460] (5461 slots) master
M: c99ba957f8d81c4a38e77a275043730ab77aeb33 127.0.0.1:7002
   slots:[10923-16383] (5461 slots) master
M: 7da858c6200f22ec2b8395a56e6f950e8c72685e 127.0.0.1:7001
   slots:[5461-10922] (5462 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

16384 スロットが均等に割り振られれば OK です

localhost で指定すると ERR Invalid node address specified: localhost:7000 になるので IP で指定します
また --cluster-replicas 1 を指定すると各 master に対する slave ノードを作成することができます
今回は master 用の 3 台しかプロセスを用意していないので指定できません

動作確認

クラスタが構築できたら動作確認してみましょう

  • redis-cli -c -p 7000 set a a

=> OK

  • redis-cli -c -p 7001 get a

=> "a"

  • redis-cli -c -p 7002 set b b

=> OK

  • redis-cli -c -p 7000 get b

=> "b"

どこからも set/get ができることを確認しましょう
またノードの状態も確認できます

  • redis-cli -p 7000 cluster nodes

新しいノードの追加

ノードを 4 台に増やしてみます
reshard する点がポイントです

  • mkdir 7003
  • touch 7003/redis.conf
  • vim 7003/redis.conf
port 7003
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
  • cd 7003
  • redis-server redis.conf

プロセスを起動したら追加します

  • redis-cli --cluster add-node 127.0.0.1:7003 127.0.0.1:7000

コマンドは redis-cli --cluster add-node を使います
新ノードのIP:ポート既存ノードのIP:ポート のフォーマットで指定します

>>> Adding node 127.0.0.1:7003 to cluster 127.0.0.1:7000
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: ad0cd036fa5cc5e87f3c46efeda4efa06ac866da 127.0.0.1:7000
   slots:[0-5460] (5461 slots) master
M: c99ba957f8d81c4a38e77a275043730ab77aeb33 127.0.0.1:7002
   slots:[10923-16383] (5461 slots) master
M: 7da858c6200f22ec2b8395a56e6f950e8c72685e 127.0.0.1:7001
   slots:[5461-10922] (5462 slots) master
[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:7003 to make it join the cluster.
[OK] New node added correctly.

これでノードの追加ができました
が、このままではデータが保存されません
reshard することで新しいノードにもデータが分散されるようにします

  • redis-cli --cluster reshard 127.0.0.1:7000

いろいろと聞かれます
まずは「4096」を入力しましょう
これは 16384 / 4 の値になります
4 は 4 台で分散しているためです
次に「0eb468ab4a1766f2e9c31ba75a9ae880563ae462」を入力します
これは新規に追加した redis-server の ID になります
各ノードの ID は redis-cli -p 7000 cluster nodes などで確認できます
3 つ目に「all」を入力します
これはデータをノード全体で reshard することを意味します
最後に「yes」で reshard が始まります
reshard が始まるとずらーっとログが流れ始めるので待ちます

完了すると 4096 スロットずつに分散されていることが確認できます

  • redis-cli --cluster info 127.0.0.1:7000
127.0.0.1:7000 (ad0cd036...) -> 1 keys | 4096 slots | 0 slaves.
127.0.0.1:7002 (c99ba957...) -> 1 keys | 4096 slots | 0 slaves.
127.0.0.1:7003 (0eb468ab...) -> 0 keys | 4096 slots | 0 slaves.
127.0.0.1:7001 (7da858c6...) -> 0 keys | 4096 slots | 0 slaves.
[OK] 2 keys in 4 masters.
0.00 keys per slot on average.

ノードの削除

削除する際も reshard が必要です
コマンドは redis-cli --cluster del-node を使います

  • redis-cli --cluster del-node 127.0.0.1:7003 0eb468ab4a1766f2e9c31ba75a9ae880563ae462

フォーマットは 削除するノードのIP:ポート 削除するノードID という感じで指定します
おそらく以下のようなエラーが出ると思います
[ERR] Node 172.17.0.5:6379 is not empty! Reshard data away and try again.
ようするにデータがあるから指定のノードをクラスタから削除できないよというエラーになります
削除したいノードからデータを移すのも reshard を使います

  • redis-cli --cluster reshard 127.0.0.1:7000

入力する情報が異なります

まずは「4096」を入力します
これは移したいノードが持つスロット数になります
そして「ad0cd036fa5cc5e87f3c46efeda4efa06ac866da」を入力します
これはデータの移し先のノードになるので削除するノードではなく残すノードの ID を指定しましょう
そして「0eb468ab4a1766f2e9c31ba75a9ae880563ae462」->「done」を入力します
ここに削除するノードの ID を入力します
今回は 1 台だけなので 1 ID 分入力したら done で終了します
そして「yes」で reshard を開始します

完了するとノードが削除されて redis-server プロセスが停止します

  • redis-cli --cluster info 127.0.0.1:7000
127.0.0.1:7000 (ad0cd036...) -> 1 keys | 8192 slots | 0 slaves.
127.0.0.1:7002 (c99ba957...) -> 1 keys | 4096 slots | 0 slaves.
127.0.0.1:7001 (7da858c6...) -> 1 keys | 4096 slots | 0 slaves.
[OK] 3 keys in 3 masters.
0.00 keys per slot on average.

スロットのバランスがかなり悪いので再度 5461reshard してあげると良いかなと思います

クラスタ解体

--cluster create コマンドはありますが delete コマンドはないようです
もし丁寧にやるのであれば最後の 1 台になるまで reshard して最後に 1 台のプロセスを kill する感じかなと思います
もうすでにアクセスがなくデータが飛んでもいいのであればいきなり kill でも良いと思います

Ruby から操作する

せっかくなので Ruby から操作してみました
redis-rb を使います

  • bundle init
  • vim Gemfile
gem "redis"
  • bundle install --path vendor
  • vim app.rb
require 'redis'

nodes = (7000..7002).map { |port| "redis://127.0.0.1:#{port}" }
redis = Redis.new(cluster: nodes)

puts redis.get('c')
  • bundle exec ruby app.rb

=> c

こんな感じです
Redis オブジェクトを初期化する際にクラスタ内に存在するノードを指定する必要があります
なので add-nodedel-node でノード数が変わる場合はアプリケーションも変更する必要がありそうです

最後に

Redis Cluster を試してみました
redis-server プロセスを立ち上げてあとは redis-cli --cluster create で構築すれば使えるといった感じです
基本は master で上がって来ます
必要であれば slave を用意することもできます

Sentinel に比べてスケーラビリティは高いと思います
サーバのスペックが足りなくなったら新規で redis-server プロセスを追加すれば良いだけだからです
ただ reshard など管理する項目も増えるので運用は大変になりそうなイメージです
master が一定数存在する必要もあります
例えば今回は 3 台構成を紹介しましたが最低 3 台必要になります (1, 2 台でもクラスタは組めますが公式でも3 台以上を推奨しています)
なので 1 台ダウンするとクラスタが壊れてしまうためうまく読み書きできなくなってしまいます
slave を用意すれば Cluster でも failover させることができるので可用性を担保することはできますが管理する台数は増えます

この辺りは用途に合わせた使い分けかなと思うのでどっちが良い悪いという話ではないかなと思います

参考サイト

0 件のコメント:

コメントを投稿