tech.guitarrapc.cóm

Technical updates

Docker ComposeでローカルマシンにRedisClusterを構築する

ずっとRedis Cluster を使っているものの、ローカル環境を作るのが面倒で数年来避けてきました。 今回 Redis 7.0 の Shared Pub/Sub をしたくなったので、重い腰を上げてローカル環境に Docker Compose で Redis Cluster を構築したメモです。

tl;dr;

ローカルでも Redis Cluster が組めるのは、ElastiCache for Redis Cluster や MemoryDB が VPC外部からのアクセスが原則制約ある点を踏まえてもうれしいですね。

  • 従来めんどくさかった Redis Cluster 構築も Docker Compose で簡単に行えるようになった
  • 今後 Redis Cluster で Pub/Sub するなら、Global Pub/Sub から Shared Pub/Sub に変えていきたいところ

gist おいておきます。

https://gist.github.com/guitarrapc/2c6625acfab1c771f1543d917e3a6dd9

Redis Cluster を起動する Docker Compose

次の compose.yaml を用意して、 docker compose up -d 1 で起動すれば Redis Cluster がローカル環境で触れるようになります。 コツは、 bitnami/redis-cluster を使うことです。 bitnami の redis イメージは公式に追随しつつ扱いやすいように env 対応されていたりといい感じなのでよいです、おすすめ。

services:
  redis-node-0:
    image: docker.io/bitnami/redis-cluster:7.0
    environment:
      - 'REDIS_PASSWORD=bitnami'
      - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'

  redis-node-1:
    image: docker.io/bitnami/redis-cluster:7.0
    environment:
      - 'REDIS_PASSWORD=bitnami'
      - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'

  redis-node-2:
    image: docker.io/bitnami/redis-cluster:7.0
    environment:
      - 'REDIS_PASSWORD=bitnami'
      - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'

  redis-node-3:
    image: docker.io/bitnami/redis-cluster:7.0
    environment:
      - 'REDIS_PASSWORD=bitnami'
      - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'

  redis-node-4:
    image: docker.io/bitnami/redis-cluster:7.0
    environment:
      - 'REDIS_PASSWORD=bitnami'
      - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'

  redis-node-5:
    image: docker.io/bitnami/redis-cluster:7.0
    depends_on:
      - redis-node-0
      - redis-node-1
      - redis-node-2
      - redis-node-3
      - redis-node-4
    environment:
      - 'REDIS_PASSWORD=bitnami'
      - 'REDISCLI_AUTH=bitnami'
      - 'REDIS_CLUSTER_REPLICAS=1'
      - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'
      - 'REDIS_CLUSTER_CREATOR=yes'
    ports:
      - 6379:6379

  redis:
    image: bitnami/redis:7.0
    environment:
    - ALLOW_EMPTY_PASSWORD=yes

Redis Cluster のコマンドを試す

Redis 7.0 から、Redisの Pub/Sub にSharded Pub/Subを行うためのコマンド SSUBSCRIBESPUBLISH が追加されました。従来の グローバルPub/Sub に使う SUBSCRIBEPUBLISH には、Redis Cluster で Pub/Sub するとクラスター内部にメッセージが伝搬するためスケール問題を起こすことが知られています。この問題はShared Pub/Sub で改善されるので、Pub/Sub を使うRedis Cluster も水平スケール可能になります。

ということで、Shard Pub/Sub を先ほどDocker Compose で構築したRedis Cluster で試しましょう。

see: Redis 7 の Sharded Pub/Sub まとめ - Qiita

Pub/Subなので、まずは SSUBSCRIBE します。適当にマスターノードにつなぐと指定したキー sfoo を管理するシャードにredis-cliが自動的にリダイレクトされます。2

$ docker compose exec redis /bin/bash
$ redis-cli -h redis-node-5 -a bitnami -c
redis-node-5:6379> ssubscribe sfoo
Reading messages... (press Ctrl-C to quit)
-> Redirected to slot [13887] located at 172.18.0.3:6379
Reading messages... (press Ctrl-C to quit)
1) "ssubscribe"
2) "sfoo"
3) (integer) 1

別シェルで sfoo キーに SPUBLISH すると、自動的に同じシャードにリダイレクトされてメッセージが発行されます。

$ docker compose exec redis /bin/bash
$ redis-cli -h redis-node-5 -a bitnami -c
redis-node-5:6379> spublish sfoo 1
-> Redirected to slot [13887] located at 172.18.0.3:6379
(integer) 1
172.18.0.3:6379> spublish sfoo 2
(integer) 1

すると、先ほど SSUBSCRIBE した側で購読されます。いい感じですね。

1) "smessage"
2) "sfoo"
3) "1"
1) "smessage"
2) "sfoo"
3) "2"

別のキー sbar にしても同様です。キーを変更してSSUBSCRIBE します。

172.18.0.3:6379> ssubscribe sbar
Reading messages... (press Ctrl-C to quit)
-> Redirected to slot [2668] located at 172.18.0.2:6379
Reading messages... (press Ctrl-C to quit)
1) "ssubscribe"
2) "sbar"
3) (integer) 1

sbar キーに SPUBLISH します。

172.18.0.3:6379> spublish sbar hogemoge
-> Redirected to slot [2668] located at 172.18.0.2:6379
(integer) 1

意図通り、SSUBSCRIBE している側で購読されます。

1) "smessage"
2) "sbar"
3) "hogemoge"

以前、Redis Cluster にグローバルPub/Sub しかないときにPub/Subを組んだ時はノードを増やしても性能がスケールしなかったのですが、Shared Pub/Sub で改善しそうなので使っていけるか負荷かけたりして判断していきたいです。

ElastiCache for Redis は NLBを介することで外部からアクセスできます。しかし、Shared Pub/Sub はシャード管理しているノードへ接続がリダイレクトされるため、同手法は使えないと予想されます。開発環境の ElastiCache につなげないなら、代わりにローカルでRedis Cluster を使いたくなるでしょうから、今回のDocker Compose 手法が役立つでしょう。3

(余談) StackExchange.Redis の Shared Pub/Sub 対応

C# で Redis を扱う場合、StackExchange.Redis が事実上のデファクトスタンダードです。しかし、Missing Commands in StackExchange.Redis · Issue #2055 · StackExchange/StackExchange.Redis を見ると、SSUBSCRIBE/SPUBLISH のサポートはされていません。実装を探してもまだも見つからないので、使うには自前拡張がいる感じです。

image

(余談2) Grokzen/docker-redis-cluster は使うのは難しい

Redis Cluster の Docker イメージとしては Grokzen/docker-redis-cluster もあり、これを使うとRedis Cluster が1コンテナで起動してくれます。簡便ではあるものの構成上の副作用として、この Redis Cluster は外部から個別ノードへの接続を考慮しておらず4、同コンテナに exec してローカルホストからつな具必要があります。 コンテナの外から試せないので、適当に LinqPad や VS からRedis Cluster 試すのは難しく、利用ケースが限られてしまいますね。


  1. あるいは docker-compose up -d
  2. ElastiCache ならクラスターエンドポイントにつなげば同じことができます。
  3. できればDocker Compose 使わずもっと楽に起動したい気持ちがあります。現時点ではいい方法が思いつかないので仕方ない。
  4. 127.0.0.1 にすべてのノードが起動、通信しあっているので外部からノードにアクセスできない。