公式のDockerコンテナでRedis Clusterを構築する

はじめまして、@chaltosです。
今回はDockerコンテナでRedisクラスタを構築した際の手順について書きます。
set, getなどが問題なく実行できればよい」という状況での作業メモ的なものなので、レプリケーションやバックアップなどはまったく考慮してません。

環境

OS Mac OS X 10.9.2
Docker 1.8.2
DockerMachine 0.4.1

やってみる

Redis Cluster Tutorialによると、配布されているソースコードにはredis-tribやcreate-clusterというユーティリティが同梱されていて、以下のようなコマンドでノードの追加やスロットの割当てができるらしいです。便利ですね。

$ ./redis-trib.rb create 127.0.0.1:7000 127.0.0.1:7001 ...
$ create-cluster start
$ create-cluster create

ですが、公式のDockerコンテナではRedisのビルド後にソースコードを削除しているため、これらに頼ることはできません。
仕方ないので手動でやります。

設定ファイル

デフォルトの設定ではクラスタリングできないので、まずは設定ファイルを用意します。

cluster-enabled      yes
cluster-config-file  nodes.conf
cluster-node-timeout 5000

イメージの作成・起動

--volumeオプションで設定ファイルのあるディレクトリをマウントします。
上記の設定ファイルでredis-serverを実行すると、/data/nodes.confが生成されます。
このファイルの所有者はredis:redisです。
--volume $(pwd):/dataとすると/dataの所有者が1000:staffになってしまってnodes.confを生成できないので、--volume $(pwd):/data/confとしておきます。

$ docker create --name node_6379 -p 6379:6379 --volume $(pwd):/data/conf redis:3 redis-server /data/conf/redis.conf
$ docker create --name node_6380 -p 6380:6379 --volume $(pwd):/data/conf redis:3 redis-server /data/conf/redis.conf
$ docker create --name node_6381 -p 6381:6379 --volume $(pwd):/data/conf redis:3 redis-server /data/conf/redis.conf
$ docker start node_6379 node_6380 node_6381

クラスタリング

スロットの割当

まず各ノードにスロットを割当てます。

$ docker exec node_6379 redis-cli cluster addslots {0..5500}
OK
$ docker exec node_6380 redis-cli cluster addslots {5501..11000}
OK
$ docker exec node_6381 redis-cli cluster addslots {11001..16383}
OK

OKっぽいですね、確認しましょう。

$ docker exec node_6379 redis-cli cluster nodes
cf2e950621381be04ea1031ae80cb945f9743b83 :6379 myself,master - 0 0 0 connected 0-5500
$ docker exec node_6380 redis-cli cluster nodes
0969adb771d38a49e70f235b39665156ded5a43a :6379 myself,master - 0 0 0 connected 5501-11000
$ docker exec node_6381 redis-cli cluster nodes
d9c4125051222bbc36cd0d0172e406baa9d89579 :6379 myself,master - 0 0 0 connected 11001-16383

ちゃんと割当てできてるように見えます。

ノードの追加

クラスタにノードを追加していきます。

$ docker exec node_6380 redis-cli cluster meet `docker-machine ip $DOCKER_MACHINE_NAME` 6379
OK
$ docker exec node_6381 redis-cli cluster meet `docker-machine ip $DOCKER_MACHINE_NAME` 6379
OK

OKっぽいですね。

$ docker exec node_6379 redis-cli cluster nodes
cf2e950621381be04ea1031ae80cb945f9743b83 :6379 myself,master - 0 0 0 connected 0-5500
$ docker exec node_6380 redis-cli cluster nodes
0969adb771d38a49e70f235b39665156ded5a43a :6379 myself,master - 0 0 0 connected 5501-11000
$ docker exec node_6381 redis-cli cluster nodes
d9c4125051222bbc36cd0d0172e406baa9d89579 :6379 myself,master - 0 0 0 connected 11001-16383

変わってません。

やりなおす

どうもNAT越しだとうまくいかない(?)ようです。

--net hostを追加して、ブリッジしないようにしてみます。
ついでに各ノードで異なるポートを使用するようにしておきます。

$ for n in `seq 6379 6381`; do mkdir $n && echo "port $n" > $n/redis.conf && cat redis.conf >> &n/redis.conf; done
$ docker stop node_6379 node_6380 node_6381
$ docker rm node_6379 node_6380 node_6381
$ docker create --name node_6379 --volume $(pwd)/6379:/data/conf --net host redis:3 redis-server /data/conf/redis.conf
$ docker create --name node_6380 --volume $(pwd)/6380:/data/conf --net host redis:3 redis-server /data/conf/redis.conf
$ docker create --name node_6381 --volume $(pwd)/6381:/data/conf --net host redis:3 redis-server /data/conf/redis.conf
$ docker start node_6379 node_6380 node_6381
$ docker exec node_6379 redis-cli -p 6379 cluster addslots {0..5500}
$ docker exec node_6380 redis-cli -p 6380 cluster addslots {5501..11000}
$ docker exec node_6381 redis-cli -p 6381 cluster addslots {11001..16383}
$ docker exec node_6380 redis-cli -p 6380 cluster meet `docker-machine ip $DOCKER_MACHINE_NAME` 6379
$ docker exec node_6381 redis-cli -p 6381 cluster meet `docker-machine ip $DOCKER_MACHINE_NAME` 6379
$ docker exec node_6379 redis-cli -p 6379 cluster nodes
ca0a4f11069103e902ea83de9023b34a7aa01d06 192.168.99.100:6379 myself,master - 0 0 0 connected 0-5500
465ab243a99efec4e7cfa00491304b8dcffd0701 192.168.99.100:6381 master - 0 1444015783288 2 connected 11001-16383
c185ffd045e2ce324dce43ae5374253df8da2af2 192.168.99.100:6380 master - 0 1444015784307 1 connected 5501-11000
$ docker exec node_6380 redis-cli -p 6380 cluster nodes
465ab243a99efec4e7cfa00491304b8dcffd0701 192.168.99.100:6381 master - 0 1444015806729 2 connected 11001-16383
ca0a4f11069103e902ea83de9023b34a7aa01d06 192.168.99.100:6379 master - 0 1444015805713 0 connected 0-5500
c185ffd045e2ce324dce43ae5374253df8da2af2 192.168.99.100:6380 myself,master - 0 0 1 connected 5501-11000
$ docker exec node_6381 redis-cli -p 6381 cluster nodes
c185ffd045e2ce324dce43ae5374253df8da2af2 192.168.99.100:6380 master - 0 1444015823353 1 connected 5501-11000
465ab243a99efec4e7cfa00491304b8dcffd0701 192.168.99.100:6381 myself,master - 0 0 2 connected 11001-16383
ca0a4f11069103e902ea83de9023b34a7aa01d06 192.168.99.100:6379 master - 0 1444015824374 0 connected 0-5500

先ほどとは違い、どのノードも他のノードを認識できてるようです。

実際にsetとかgetしてみる

-cは必須です。忘れるとリダイレクトせずにエラーを返します。

$ docker exec -it node_6379 redis-cli -c -p 6379
127.0.0.1:6379> set hoge 1
OK
127.0.0.1:6379> set hoge2 2
-> Redirected to slot [11000] located at 192.168.99.100:6380
OK
192.168.99.100:6380> set hoge3 3
-> Redirected to slot [15065] located at 192.168.99.100:6381
OK
192.168.99.100:6381> get hoge
-> Redirected to slot [1525] located at 192.168.99.100:6379
"1"
192.168.99.100:6379> get hoge2
-> Redirected to slot [11000] located at 192.168.99.100:6380
"2"
192.168.99.100:6380> get hoge3
-> Redirected to slot [15065] located at 192.168.99.100:6381
"3"

きちんと他のノードにリダイレクトしてくれてますね。