2019年5月15日水曜日

docker + redis で Sentinel 環境を構築する

概要

docker 上で redis + sentinel を試してみました
最初は docker コマンドで master 1 台、slave 1 台、sentinel 1 台を構築しフェイルオーバーの挙動を確認します
その後で docker-compose 化しスケールなどに対応します

環境

  • macOS 10.14.4
  • docker 18.09.2
  • Redis 5.0.3

とりあえず master/slave 構成を作成する

master コンテンを起動します

  • docker run -d --name master redis

次に slave コンテナを起動します
master を参照できる必要があるので link で master コンテナの名前と紐付けます
そして salve として起動させるために起動コマンドに --slaveof を付与して起動します

  • docker run -d --link master --name slave1 redis redis-server --slaveof master 6379

これで slave の状態を確認しましょう
以下のようになっていれば OK です

  • docker exec slave1 redis-cli info replication
# Replication
role:slave
master_host:master
master_port:6379
master_link_status:up
master_last_io_seconds_ago:7
master_sync_in_progress:0
slave_repl_offset:98
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:b403efc891b46f6d9e1ef6b663f21c2ae43739dc
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:98
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:98

動作確認

master に書き込みをして slave 側にもデータが反映されているか確認します

  • docker exec master redis-cli set a a
  • docker exec master redis-cli get a

=> a

  • docker exec slave1 redis-cli get a

=> a

また slave 側に書き込みを行えないことも確認します

  • docker exec slave1 redis-cli set b b
READONLY You can't write against a read only replica.

sentinel を組み合わせる

redis の単純な master/slave 構成を構築したところで次に sentinel と組み合わせます
sentinel と組み合わせることで master がダウンした際に slave が自動的に master に昇格することができます

sentinel 用のイメージの作成から始めます
sentinel 用の設定ファイル sentinel.conf を作成します

  • vim sentinel.conf
port 26379
dir /tmp
sentinel monitor mymaster master 6379 1
sentinel down-after-milliseconds mymaster 5000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 5000

sentinel は 1 台のみ起動するので monitor は 1 にします
次に Dockerfile です
設定ファイルを元に redis-server を起動するようにすれば OK です

  • vim Dockerfile
FROM redis

ADD sentinel.conf /etc/redis/sentinel.conf
RUN chown redis:redis /etc/redis/sentinel.conf

CMD ["redis-server", "/etc/redis/sentinel.conf", "--sentinel"]

これでイメージをビルドしてコンテナを起動しましょう
コンテナを起動する際は link で master と slave を参照できるようにします

  • docker build -t sentinel .
  • docker run -d --link master --link slave1 --name sentinel1 sentinel

sentinel コンテナが起動したら sentinel API を使って状態を書くにします
まずは slave として登録されているコンテナを取得します

  • docker exec sentinel1 redis-cli -p 26379 sentinel slaves mymaster | grep -A 1 'name'

=> 172.17.0.3:6379

次に master として登録されているコンテナを確認します
それぞれ別のコンテナが登録されていることがわかります

  • docker exec sentinel1 redis-cli -p 26379 sentinel get-master-addr-by-name mymaster

=> 172.17.0.2

master を停止さて昇格するか確認

では本題のフェイルオーバーを試してみます
master コンテナを停止して slave が master になるか確認してみましょう

  • docker stop master

sentinel コンテナのログには以下のように表示されました

  • docker logs -f sentinel1
1:X 14 May 2019 00:26:27.421 # +sdown master mymaster 172.17.0.2 6379                                                            
1:X 14 May 2019 00:26:27.421 # +odown master mymaster 172.17.0.2 6379 #quorum 1/1                                                
1:X 14 May 2019 00:26:27.421 # +new-epoch 1
1:X 14 May 2019 00:26:27.421 # +try-failover master mymaster 172.17.0.2 6379                                                     
1:X 14 May 2019 00:26:27.428 # +vote-for-leader 6d8c18e7bbe1fe0c25c8fb8ea02d55795bc3aa11 1                                       
1:X 14 May 2019 00:26:27.428 # +elected-leader master mymaster 172.17.0.2 6379                                                   
1:X 14 May 2019 00:26:27.428 # +failover-state-select-slave master mymaster 172.17.0.2 6379                                      
1:X 14 May 2019 00:26:27.506 # +selected-slave slave 172.17.0.3:6379 172.17.0.3 6379 @ mymaster 172.17.0.2 6379                  
1:X 14 May 2019 00:26:27.506 * +failover-state-send-slaveof-noone slave 172.17.0.3:6379 172.17.0.3 6379 @ mymaster 172.17.0.2 6379
1:X 14 May 2019 00:26:27.584 * +failover-state-wait-promotion slave 172.17.0.3:6379 172.17.0.3 6379 @ mymaster 172.17.0.2 6379   
1:X 14 May 2019 00:26:28.438 # +promoted-slave slave 172.17.0.3:6379 172.17.0.3 6379 @ mymaster 172.17.0.2 6379                  
1:X 14 May 2019 00:26:28.438 # +failover-state-reconf-slaves master mymaster 172.17.0.2 6379                                     
1:X 14 May 2019 00:26:28.490 # +failover-end master mymaster 172.17.0.2 6379                                                     
1:X 14 May 2019 00:26:28.490 # +switch-master mymaster 172.17.0.2 6379 172.17.0.3 6379                                           
1:X 14 May 2019 00:26:28.491 * +slave slave 172.17.0.2:6379 172.17.0.2 6379 @ mymaster 172.17.0.3 6379                           
1:X 14 May 2019 00:26:33.506 # +sdown slave 172.17.0.2:6379 172.17.0.2 6379 @ mymaster 172.17.0.3 6379

これでフェイルオーバーは完了しています
今回のようなそれぞれ 1 台構成だとすべてが起動していないと sentinel API で状況を確認できないのでダウンした master を再度起動します
次に起動する際は redis の世界では slave として起動します

  • docker start master
1:X 14 May 2019 00:28:17.289 # -sdown slave 172.17.0.2:6379 172.17.0.2 6379 @ mymaster 172.17.0.3 6379
1:X 14 May 2019 00:28:27.266 * +convert-to-slave slave 172.17.0.2:6379 172.17.0.2 6379 @ mymaster 172.17.0.3 6379

動作確認

ではフェイルオーバーの動作確認をします
まずは先程実行しように master/slave として登録されているコンテナを確認します

  • docker exec sentinel1 redis-cli -p 26379 sentinel slaves mymaster | grep -A 1 'name'

=> 172.17.0.2:6379

  • docker exec sentinel1 redis-cli -p 26379 sentinel get-master-addr-by-name mymaster

=> 172.17.0.3

ちゃんと入れ替わっていることが確認できると思います
次に master コンテナに対して set 命令をしてみます

  • docker exec master redis-cli set b b
READONLY You can't write against a read only replica.

このように master コンテナに対して set できなくなっています
これは redis の世界ではすでに master コンテナが slave として扱われているためです
これが docker 上で sentinel を使った場合のややこしい点でフェイルオーバー後は docker の name と redis 内部で管理されている master/slave に齟齬が発生するので注意が必要です

sentinel に対して read/write できるのか

念の為確認しましたがそれはできないようです

  • docker exec sentinel1 redis-cli -p 26379 get a
ERR unknown command `get`, with args beginning with: `a`,

つまりアプリケーション側では常に master 側に書き込みを行うような処理を入れなければならないことがわかります
もしくは sentinel 以外を使って常に master にバランシングするようなミドルウェアの導入が必要です

docker-compose 化する

最後に docker-compose 化しましょう

  • vim docker-compose.yml
version: '2'
services:
  master:
    image: redis
  slave:
    image: redis
    command: redis-server --slaveof master 6379
    links:
      - master
  sentinel:
    build: .
    links:
      - master
      - slave
  • vim Dockerfile
FROM redis

ADD sentinel.conf /data/sentinel.conf
RUN chown redis:redis /data/sentinel.conf

CMD ["redis-server", "/data/sentinel.conf", "--sentinel"]

sentinel.conf だけやや変えています
この後スケールさせるので monitor を 1 -> 2 に変更しています

  • vim sentinel.conf
port 26379
dir /tmp
sentinel monitor mymaster master 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 5000

あとはビルドして各コンテナを起動すれば OK です

  • docker-compose build
  • docker-compose up -d

動作確認

まだそれぞれ 1 台構成ですがとりあえず起動しているか確認してみましょう

  • docker-compose exec sentinel redis-cli -p 26379 sentinel get-master-addr-by-name mymaster

=> 172.21.0.2

  • docker-compose exec sentinel redis-cli -p 26379 sentinel slaves mymaster | grep -A 1 'name'

=> 172.21.0.3:6379

get/set の確認もしておきます

  • docker-compose exec master redis-cli set a a
  • docker-compose exec master redis-cli get a

=> a

  • docker-compose exec slave redis-cli get a

=> a

スケールさせて動作確認

最後に sentinel と slave コンテナをスケールさせて挙動を確認します
sentinel コンテナを 3 台、slave コンテナを 4 台にします

  • docker-compose scale sentinel=3
  • docker-compose scale slave=4

これで master コンテナを停止しましょう
フェイルオーバーが発生し salve の 4 台のうちどれかが master に昇格しています

  • docker-compose stop master
  • docker-compose exec sentinel redis-cli -p 26379 sentinel get-master-addr-by-name mymaster

=> 172.21.0.8

先程 master だったコンテナが slave として新たに登録されています

  • docker-compose exec sentinel redis-cli -p 26379 sentinel slaves mymaster | grep -A 1 'name'

=> 172.21.0.9:6379, 172.21.0.2:6379, 172.21.0.7:6379, 172.21.0.3:6379

この挙動自体は何ら問題ないのですがアプリケーションの観点から見ると以下のような問題点が発生します

問題点

docker の世界のコンテナ名で set を行おうとするとできなくなっています

  • docker-compose exec master redis-cli set a a
  • docker-compose exec master redis-cli set a a
(error) READONLY You can't write against a read only replica.

これは master だったものが slave になっているのが原因と slave が複数いるため本当の master を docker-compose exec では見つけられないため発生しています
もし docker コマンドで操作したい場合は各 slave コンテナの IP を調べて master に昇格しているコンテナを探した上でそのコンテナに対して docker exec すれば set 命令することができます

  • docker inspect sentinel_test_slave_4 | grep 'IPAddress'

=> "IPAddress": "172.21.0.8"

  • docker exec sentinel_test_slave_4 redis-cli set b b

=> OK

  • docker-compose exec master redis-cli get b

=> b

こんな感じです
ここから考察するに docker-compose を使って sentinel を構築した場合、アプリケーション側は link の名前ではなく IP ベースで master を追跡できるような仕組みを検討しないとダメかもしれません

最後に

docker 上で redis + sentinel 環境を構築し実際にフェイルオーバーやスケールなどをさせて簡単に挙動を確認してみました
一番問題なのは docker の世界の name と redis の世界の master/slave に齟齬が発生する点かなと思います
たぶん調べればバランシングするコンテナなどと組み合わせてアプリケーションは常にそこにアクセスするようにすれば解決できる方法はあると思います
ただ純正の sentinel の機能を使うだけだとそれは厳しいということがわかりました

正直なところ docker + sentinel は少し相性が悪いのかなといった印象を受けました

参考サイト

0 件のコメント:

コメントを投稿