言わずと知れた、Redisのフェイルオーバーツール、Redis Sentinel(
http://redis.io/topics/sentinel)をお手軽に使う方法を紹介します。
フェイルオーバー時に、アプリサーバからRedisへの接続先を変更する方法として、”アプリサーバへ /etc/hosts を rsync で配布する” といった方法をとります。
いままでは、RedisサーバのIPアドレスの付け替えや、HAProxyの設定変更を自動で行う、などといった方法がありましたが、これは、敷居低めで実装できるのが嬉しいです。
動作イメージは下のようになります。
redis1が壊れたとき、redis2が自動でマスタに昇格してサービスを継続します。
詳しく説明します。
app1、app2の /etc/hosts には、Redisマスタのホスト名 redis-master.myservice.com と redis1 のIPアドレスを紐づける記述しておきます。
app1、app2のアプリケーションは、このホスト名 redis-master.myservice.com 宛にRedis接続をします。
フェイルオーバー時には、redis-master.myservice.com の IPアドレスを redis2 のものに変更した /etc/hosts を rsync で差し替えます。
サーバIDとIPアドレスはこのようになっているとします。
app1 (100.100.100.1): アプリサーバ1 + Redis Sentinel
app2 (100.100.100.2): アプリサーバ2
redis1 (200.200.200.1): Redisマスタ
redis2 (200.200.200.2): Redisスレーブ
試験環境は、CentOS 6.5、Redis 2.8.7です。
Redisは、既にインストールされている状態ではじめます。
Redis Sentinelは、app1サーバにセットアップします。
iptablesなどのアクセスコントロールは便宜設定してください。
◆ 事前準備
1)app1からapp2サーバへrootユーザでログインできるようにしておく。
フェイルオーバーするときに、hostsファイルを転送できるようにしておきます。
2)Redisマスタ -> スレーブのレプリケーションを構築しておく。
redis2サーバで設定します。
Redisスレーブの設定ファイルに、マスタの情報を記述
# vim /etc/redis/redis.conf
---
slaveof 200.200.200.1 6379
---
Redisリスタート
# /etc/init.d/redis stop
# /etc/init.d/redis start
Starting Redis server...
infoを確認
# redis-cli -p 6379 info | grep role -3
latest_fork_usec:0
# Replication
role:slave
master_host:200.200.200.1
master_port:6379
master_link_status:up
-> リンクステータスがupなのでおkです。
ログを確認
# tail -f /var/log/redis.log
[3888] 24 Mar 13:05:36.291 # Server started, Redis version 2.8.7
[3888] 24 Mar 13:05:36.291 * DB loaded from disk: 0.000 seconds
[3888] 24 Mar 13:05:36.291 * The server is now ready to accept connections on port 6379
[3888] 24 Mar 13:05:36.293 * Connecting to MASTER 200.200.200.1:6379
[3888] 24 Mar 13:05:36.293 * MASTER <-> SLAVE sync started
[3888] 24 Mar 13:05:36.329 * Non blocking connect for SYNC fired the event.
[3888] 24 Mar 13:05:36.351 * Master replied to PING, replication can continue...
[3888] 24 Mar 13:05:36.375 * Partial resynchronization not possible (no cached master)
[3888] 24 Mar 13:05:36.376 * Full resync from master: 895edcc4655424517d5a9ddc25fc86a4aadb7f91:1
[3888] 24 Mar 13:05:36.465 * MASTER <-> SLAVE sync: receiving 18 bytes from master
[3888] 24 Mar 13:05:36.465 * MASTER <-> SLAVE sync: Flushing old data
[3888] 24 Mar 13:05:36.465 * MASTER <-> SLAVE sync: Loading DB in memory
[3888] 24 Mar 13:05:36.465 * MASTER <-> SLAVE sync: Finished with success
-> success と出ていますね。
マスタに何かsetして、スレーブでgetしてみましょう。
ちゃんと、レプリケーションできているでしょうか?
3) Redisマスタのホスト名を /etc/hosts に記述する。(app1, app2)
# vim /etc/hosts
---
200.200.200.1 redis-master.myservice.com
---
アプリケーションからは、redis-master.myservice.com 宛に接続するようにしておきましょう。
◆ Redis Sentinelのセットアップ
app1サーバでRedis Sentinelを設定します。
sentinel.confファイルを編集。
# vim /etc/redis/sentinel.conf
マスタのIPアドレスを設定、quorumは1とします。
---
sentinel monitor mymaster 127.0.0.1 6379 2
->
sentinel monitor mymaster 200.200.200.1 6379 1
---
以下を追記
---
daemonize yes
pidfile /var/run/redis_sentinel.pid
loglevel notice
logfile /var/log/redis_sentinel.log
sentinel client-reconfig-script mymaster /usr/share/redis_sentinel/master_ip_failover.sh
---
◆ フェイルオーバー時に実行するスクリプトを用意
app1サーバで用意します。
※ rootユーザによるapp2サーバへのssh疎通を要チェックしておきましょう。
rsyncで /etc/hosts を配布するスクリプトを設置します。
このスクリプトが、フェイルオーバー時に実行されます。
# mkdir -p /usr/share/redis_sentinel/
# vim /usr/share/redis_sentinel/master_ip_failover.sh
---
#!/bin/bash
NEW_HOSTS=/usr/share/redis_sentinel/hosts
# app1
cp ${NEW_HOSTS} /etc/hosts
# app2
rsync -avz ${NEW_HOSTS} 100.100.100.2:/etc/hosts
---
パーミッションを変更。
# chmod 755 /usr/share/redis_sentinel/master_ip_failover.sh
フェイルオーバー時に app1,app2 サーバへ配布する /etc/hosts ファイルを設置します。
mysql-master.myservice.com のIPアドレスを redis2 スレーブのモノにします。
# vim /usr/share/redis_sentinel/hosts
---
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
200.200.200.2 redis-master.myservice.com
---
◆ Redis Sentinelをスタート
app1サーバでRedis Sentinelを実行します。
# /usr/bin/redis-server /etc/redis/sentinel.conf --sentinel
プロセスを確認
# pgrep -fl 26379
3921 /usr/bin/redis-server *:26379
ログを確認
# cat /var/log/redis_sentinel.log
[3921] 24 Mar 13:47:15.111 * Max number of open files set to 10032
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 2.8.7 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 3921
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
[3921] 24 Mar 13:47:15.112 # Sentinel runid is 4b6ddc264813bafaf9142dcb1f53a7b446e47d75
[3921] 24 Mar 13:47:15.112 # +monitor master mymaster 200.200.200.1 6379 quorum 1
[3921] 24 Mar 13:47:15.114 * +slave slave 200.200.200.2:6379 200.200.200.2 6379 @ mymaster 200.200.200.1 6379
Redis Sentinel起動完了です。
◆ Redisフェイルオーバー実験
redis1サーバを落として、ちゃんとフェイルオーバーするか見てみましょう。
iptablesなどを使って接続できないようにするといいかもしれません。
1)redis1でiptablesを使ってポートを閉じる。
# /etc/init.d/iptables start
iptables: ファイアウォールルールを適用中: [ OK ]
2)app1でRedis Sentinelのログを確認。
15秒くらい待つと、フェイルオーバーが走る。
# tail -f /var/log/redis_sentinel.log
[4015] 24 Mar 14:30:19.127 # +sdown master mymaster 200.200.200.1 6379
[4015] 24 Mar 14:30:19.127 # +odown master mymaster 200.200.200.1 6379 #quorum 1/1
[4015] 24 Mar 14:30:19.127 # +new-epoch 3
[4015] 24 Mar 14:30:19.127 # +try-failover master mymaster 200.200.200.1 6379
[4015] 24 Mar 14:30:19.127 # +vote-for-leader 1aaecabcb27ff0f1491c64d98d4201162cb7746b 3
[4015] 24 Mar 14:30:19.128 # +elected-leader master mymaster 200.200.200.1 6379
[4015] 24 Mar 14:30:19.128 # +failover-state-select-slave master mymaster 200.200.200.1 6379
[4015] 24 Mar 14:30:19.194 # +selected-slave slave 200.200.200.2:6379 200.200.200.2 6379 @ mymaster 200.200.200.1 6379
[4015] 24 Mar 14:30:19.194 * +failover-state-send-slaveof-noone slave 200.200.200.2:6379 200.200.200.2 6379 @ mymaster 200.200.200.1 6379
[4015] 24 Mar 14:30:19.270 * +failover-state-wait-promotion slave 200.200.200.2:6379 200.200.200.2 6379 @ mymaster 200.200.200.1 6379
[4015] 24 Mar 14:30:20.187 # +promoted-slave slave 200.200.200.2:6379 200.200.200.2 6379 @ mymaster 200.200.200.1 6379
[4015] 24 Mar 14:30:20.187 # +failover-state-reconf-slaves master mymaster 200.200.200.1 6379
[4015] 24 Mar 14:30:20.264 # +failover-end master mymaster 200.200.200.1 6379
[4015] 24 Mar 14:30:20.264 # +switch-master mymaster 200.200.200.1 6379 200.200.200.2 6379
[4015] 24 Mar 14:30:20.264 * +slave slave 200.200.200.1:6379 200.200.200.1 6379 @ mymaster 200.200.200.2 6379
[4015] 24 Mar 14:30:50.354 # +sdown slave 200.200.200.1:6379 200.200.200.1 6379 @ mymaster 200.200.200.2 6379
3)redis2がマスタに昇格した事を確認
# redis-cli -p 6379 info | grep role -3
latest_fork_usec:0
# Replication
role:master
connected_slaves:0
master_repl_offset:1852
repl_backlog_active:0
4)app1のhostsが書き変わったことを確認
# cat /etc/hosts
200.200.200.2 redis-master.myservice.com
5)app2のhostsが書き変わったことを確認
# cat /etc/hosts
200.200.200.2 redis-master.myservice.com
フェイルオーバー完了です。
お疲れさまでした!
※ 注意
フェイルオーバーが実行されたあとに、redis1を復活させると、redis1の設定ファイルが書き変わり、スレーブとして起動するようになっています。
redis1
# cat /etc/redis/redis.conf
# Generated by CONFIG REWRITE
slaveof 200.200.200.2 6379
-> redis2がマスター、redis1がスレーブになっている。
redis2
# cat /etc/redis/redis.conf
-> slaveofの記述が削除されている
また、app1のRedis Sentinelの設定ファイルも書き変わっています。
案外、ハマりどころかもしれません。注意しましょう。
◆まとめ
Redis Sentinelをセットアップして、/etc/hostsや、rsyncなどの枯れたモノを使ってRedisをフェイルオーバできるようにしました。
インフラ構成が比較的ちいさなサービスなどで、”RedisのHA構成が欲しい”という要望があったときに、使えたりしそうです。
試してみてください。
参考