docker で redis-cluster を自動起動させる。ついでにPHP7で使う。

動くものをgithubに上げとります。

https://github.com/nanananamememe/redis-cluster-test

一般的な方法だと一度コンテナを立ち上げた後にコマンドで実行する必要がありますが、面倒くさいので自動化することに。
キモはネットワークの設定でipを割り当ててやることと、起動時にnode.confの指定をしてやることかと。

まずは docker-compose.yml の説明

```yml:./docker-compose.yml
version: '2'
services:
    #nginxの設定
    cluster-test-web:
        build: docker/web
        container_name: cluster-test-web
        ports:
            - 80:80
        links:
            - cluster-test-app:php
        volumes_from:
            - cluster-test-app
        #redisと同じネットワークに
        networks:
            app_net:
    #phpの設定
    cluster-test-app:
        build: docker/app
        container_name: cluster-test-app
        volumes:
            - ./:/var/www/html
        links:
            #redisと接続するのでその設定
            - cluster-test-redis:redis
        #redisと同じネットワークに
        networks:
            app_net:
    #redisの設定
    cluster-test-redis:
        image: redis:latest
        container_name: cluster-test-redis
        # ./docker/redis の内容を /data/conf にマウント
        volumes:
            - ./docker/redis/:/data/conf
        # コンテナが起動したら、
        # redis-serverとクラスター・レプリケーションの設定を行うshellスクリプトを実行。
        # その後、常時起動のコマンドを走らせる。
        command: bash -c "/bin/bash /data/conf/redis.sh && while true; do echo 1; sleep 1; done"
        ports:
            - 7000:7000
            - 7001:7001
            - 7002:7002
            - 7003:7003
            - 7004:7004
            - 7005:7005
        #shellスクリプト内でipを使用するのでここで設定。
        networks:
            app_net:
                ipv4_address: 172.16.239.10

networks:
    app_net:
        driver: bridge
        ipam:
            driver: default
            config:
                - subnet: 172.16.239.0/24
                  gateway: 172.16.239.1
```

redis-server の起動は docker-compose.yml の command で実行している redis.sh 内で行っている。

```shell:./docker/redis/redis.sh
#!/bin/bash

# 邪魔なファイルを削除。
rm -f \
    /data/conf/r7000i.log \
    /data/conf/r7001i.log \
    /data/conf/r7002i.log \
    /data/conf/r7003i.log \
    /data/conf/r7004i.log \
    /data/conf/r7005i.log \
    /data/conf/nodes.7000.conf \
    /data/conf/nodes.7001.conf \
    /data/conf/nodes.7002.conf \
    /data/conf/nodes.7003.conf \
    /data/conf/nodes.7004.conf \
    /data/conf/nodes.7005.conf ;

#redisを6台クラスターモードで(クラスターモードの設定はredis.conf)起動。
# nodes.****.conf はそれぞれ別々のファイルを指定する必要がある。
redis-server /data/conf/redis.conf --port 7000 --cluster-config-file /data/conf/nodes.7000.conf --daemonize yes ;
redis-server /data/conf/redis.conf --port 7001 --cluster-config-file /data/conf/nodes.7001.conf --daemonize yes ;
redis-server /data/conf/redis.conf --port 7002 --cluster-config-file /data/conf/nodes.7002.conf --daemonize yes ;
redis-server /data/conf/redis.conf --port 7003 --cluster-config-file /data/conf/nodes.7003.conf --daemonize yes ;
redis-server /data/conf/redis.conf --port 7004 --cluster-config-file /data/conf/nodes.7004.conf --daemonize yes ;
redis-server /data/conf/redis.conf --port 7005 --cluster-config-file /data/conf/nodes.7005.conf --daemonize yes ;

REDIS_LOAD_FLG=true;

#全てのredis-serverの起動が完了するまでループ。
while $REDIS_LOAD_FLG;
do
    sleep 1;
    redis-cli -p 7000 info 1> /data/conf/r7000i.log 2> /dev/null;
    if [ -s /data/conf/r7000i.log ]; then
        :
    else
        continue;
    fi
    redis-cli -p 7001 info 1> /data/conf/r7001i.log 2> /dev/null;
    if [ -s /data/conf/r7001i.log ]; then
        :
    else
        continue;
    fi
    redis-cli -p 7002 info 1> /data/conf/r7002i.log 2> /dev/null;
    if [ -s /data/conf/r7002i.log ]; then
        :
    else
        continue;
    fi
    redis-cli -p 7003 info 1> /data/conf/r7003i.log 2> /dev/null;
    if [ -s /data/conf/r7003i.log ]; then
        :
    else
        continue;
    fi
    redis-cli -p 7004 info 1> /data/conf/r7004i.log 2> /dev/null;
    if [ -s /data/conf/r7004i.log ]; then
        :
    else
        continue;
    fi
    redis-cli -p 7005 info 1> /data/conf/r7005i.log 2> /dev/null;
    if [ -s /data/conf/r7005i.log ]; then
        :
    else
        continue;
    fi
    #redis-serverの起動が終わったらクラスター・レプリカの割り当てる。
    #ipを127.0.0.1で割り当てるとphpで不具合が起こるのでpublic ipを指定。
    yes "yes" | redis-cli --cluster create 172.16.239.10:7000 172.16.239.10:7001 172.16.239.10:7002 172.16.239.10:7003 172.16.239.10:7004 172.16.239.10:7005 --cluster-replicas 1;
    REDIS_LOAD_FLG=false;
done
```

redis.conf の中身は↓

```config:./docker/redis/redis.conf
#クラスターモードON
cluster-enabled      yes
cluster-node-timeout 5000
#接続のip制限を解除
bind 0.0.0.0
```

動作確認用のphpを組む。

```php:./redis_test.php
<?php
#接続は docker-compose.yml の link の設定で繋がる
$rc = new \RedisCluster(null, [
    'redis:7000',
]);
# php7だと RedisCluster::FAILOVER_DISTRIBUTE_SLAVES が動かないので、ランダムで replica にアクセスの RedisCluster::FAILOVER_DISTRIBUTE を設定。
# https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#automatic-slave-failover--distribution
# https://github.com/phpredis/phpredis/issues/937
$rc->setOption(\RedisCluster::OPT_SLAVE_FAILOVER, \RedisCluster::FAILOVER_DISTRIBUTE);
echo "<pre>";
# master系のnodeのリストが表示できます
print_r($rc->_masters());
# replica(slave)も含めて全てのnodeを表示
print_r($rc->rawcommand(['172.16.239.10', 7000], 'cluster', 'nodes'));
$t = microtime();
$k = md5($t);
# 適当の値をセットして
$rc->set($k, $t);
# とりあえずゲットしてみる。ゲットできていれば万事上手く動いているんじゃないんですかね
print_r($rc->get($k));
```

`docker-compose up -d --build`で起動したなら`http://localhost/redis_test.php`をブラウザで表示してみる。

<img width="1135" alt="SS.png" src="https://qiita-image-store.s3.amazonaws.com/0/151490/fa0e11df-fd6d-d2f3-0349-b1d024a0be61.png">

こんな感じで master のリストと node のリストが表示されます。
ちゃんと均等にクラスターが割り当てられレプリケーションもできてますね。

以上。