Redis Sentinelを運用してみたお話
こんにちは、宇津井です。
弊社ではゲソてんというゲームプラットフォームを運営しております。ゲソてんではサービスリリース当初から主にキャッシュ的な役割でRedisを導入していました。つい先日の事ですが訳あってRedis 2.6からサポートされたRedis Sentinelを採用しました。今回は、採用に至った経緯をご紹介します。
簡単なシステム構成
パフォーマンスは良いし、Memcachedには無い便利な機能を提供するRedisですが約一年間運用していくつかの問題が出てきました。
- Redisサーバーが突然高負荷に陥る
- するとWebサーバーも高負荷に陥る
- RedisのReplicationは張ってるけどアプリケーション側でRedis Slaveへフェイルオーバーしない(MySQLを直接参照する)
- Redis落ちると恐らくMySQLサーバーが耐えられない(落ちた事無いけど)
という事で対策を行ってきました。
まずは1,2番の問題です。WebサーバーとRedisサーバーの荒ぶりはRedisサーバーのUpgradeで事なきを得ました。当時のRedis Versionは2.4.15です。ReleaseNotes(2.4,2.6)を拝見すると色々あります。というか2.6が出てます。ReleaseNotesをきちんと確認してきちんとUpgradeしていきましょう。
検証して問題なかったのでさっさと2.6系にUpgradeし、一旦高負荷問題は落ち着きました。この際に色々とチューニングを施しましたがRedisは元々高速で面白みに欠けます。(後で解ったことですがF5アタックも受けてました。これは別途Nginxで対処してます)
尚、2.4系から2.6系はRedisの使われ方にも依りますが比較的すんなりUpgrade出来ます。今回行った手順はざっくり以下のような感じです。
1. Slaveは現状アプリから使ってないのでslaveを2.6にUpgradeしておく
2. アプリをSlave側に向ける
3. Slaveはslave_read_only:0にしておく(書込可能)
4. Masterにアクセスがない事を確認したらSlaveをMaster昇格
(redis-02) slaveof no one
5. Masterをシャットダウン
(redis-01) superviserctl stop redis
6. 旧Master(redis-01)のRedisを2.6にUpgrade & 旧Master(redis-01)のRedisを起動
7. 旧Master(redis-01)を新Master(redis-02)のSlaveに設定
(redis-01) slaveof redis-02 6379
8. Slaveは書込可能にしておく
(redis-01) slave_read_only:0
※この後に出てくるSentinelに対応するためreadonlyを解除してしまいます
さて、高負荷問題が落ち着きRedisは安定している。もう対策しなくていいんじゃね?という空気が漂う中DBAの田中さんが「いやいや、フェイルオーバーまでしないとダメでしょ!落ちたらどーすんの?」とケツを叩いてくれます。
やっぱりSentinelの対応することになりました。Sentinelですが簡単に説明すると以下のような感じです。
- MasterとSlaveの状態を監視
- 何か起きたら通知
- 何か起きたら自動フェイルオーバー
詳しくはドキュメントを読みましょう。(英語です)
加えて以下の特徴を持ちます。
- 一つのMasterに対して複数のSentinelプロセスを起動してお互いにMasterの情報を共有し合う
- Masterのダウンは複数個のSentinelプロセスが投票方式で検知する例:Sentinelを4プロセス立ち上げた場合、3個以上がダウン(SDOWN)と判定したらMasterダウン(ODOWN)と見なしフェイルオーバーする。※Masterダウン(ODOWN)を判定する条件は指定できます。
- 夢のようなクラスター機能ではない
- 通常はNoPreempt(フェイルバック機能は持たない)
- VIP、或はプロキシ的な機能は持たない
最後に「VIP、あるいはプロキシ的な機能は持たない」と書きましたがこれが面倒な点です。障害時にSlaveのMaster昇格は行ってくれますが、アプリケーションからの接続はRedis, Redis Sentinel以外の何かしらで制御する必要があります。
ということでアプリケーション側できちんとMater/Slave状態を追いかけるにはどうするのか考えました。(自社データセンター内ですのであまり制約がありません)
1. Load Balancer方式
- Slaveの負荷分散には良いけど、フェイルオーバーを追いかけるのには向かない
2. VIP方式(Keepalived / VRRPとか)
- Master:Slaveが1:1だったら良いけどSlaveが複数台ある要件に合わなくなってくる
- 上のLoad Balancer方式と組み合わせれば何とかなるかもしれないが、構成がちょっと複雑になってくる
3. DNS方式
- FuelPHP(実はフレームワークがFuelPHP)は redis-1-m:6379 に固定接続。TTLを短く(10秒とか)して障害を検知したらAレコードを書き換える。
- Sentinelプロセス監視してMasterを検知しつづけるチェックプログラムをバッチサーバーとかで動かす。
この方式のメリット
- アプリケーションの修正不要
- チェックプログラムは一つで大丈夫
この方式のデメリット
- DNSプロトコルに乗せるのでシステムがやや複雑になる
- DNSキャッシュの考慮を慎重に行う必要がある
- キャッシュサーバーの処理性能は20,000qpsぐらい
- ベンチマークを取ったところ1~20ms程度のオーバーヘッドが有る(それならlocalにDNSキャッシュ作った方が良い。)
想定切替秒数:最大85秒
4. hosts方式
- cronでSentinelを監視して、Masterが切り替わったら/etc/hostsを書き換える
- FuelPHPは redis-1-m:6379 に固定接続
この方式のメリット
- 単純
この方式のデメリット
- 各サーバーでチェックスクリプト動かす必要がある
- /etc/hostsの即時反映はnetwork restartとか必要
5. iptables方式
- cronでSentinelを監視して、Masterが切り替わったらiptablesのDestination Nat情報を書き換える
- FuelPHPは 192.168.2.10:6379 に固定接続
この方式のメリット
- キャッシュ関係なく確実にパケット転送が出来る
- 新たに追加されるミドルウェアとかが無い
この方式のデメリット
- 各サーバーでチェックスクリプト動かす必要がある
- 誤ってiptablesのDaemon落とすとredisに接続できなくなってしまう
- 想定切替秒数:最大25秒
6. FuelPHP改修方式
- Sentinelプロセス監視して自動failoverするようにフレームワークかアプリケーションを改修
この方式のメリット
- マージされるかライブラリを公開すれば世の役に立てる(かも)
この方式のデメリット
- 結構大変かも
7. twemproxy方式
- twitter社が開発したMemcached / redisのproxyサーバー
- https://github.com/twitter/twemproxy
- 主にシャーディング用途で使われる。今回の要件には合わなかった。
というわけで色々考えて検証したわけですが最終的に5番目のiptables方式を採用しました。
Sentinelを監視するスクリプトはこんな感じです。このスクリプト、最初は私がやっつけで作ったんですがDBAの田中さんに思いっきり修正されました。
参考までにredis.confとsentinel.conf(認証情報は適切に入れましょう)
このような対策で以下の事が出来るようになりました。
- Redisに接続できない時間は最大で60秒程度です。
- この時アプリケーション側できちんとエラー処理されてればMySQLに接続します。(元々実装済みでしたが改めてテストしたら抜けがあって503返しちゃうケースがありました)60秒くらいならMySQLも頑張れます。
- 安心して眠りにつけます。
Sentinelありがとう!
16 Notes/ Hide
- uokada reblogged this from gmomedia-engineer
- shinofara reblogged this from gmomedia-engineer
- cuon reblogged this from gmomedia-engineer
- act2012bl reblogged this from atm09td
- atm09td reblogged this from gmomedia-engineer
- kazupon reblogged this from gmomedia-engineer
- siso9to reblogged this from gmomedia-engineer
- yyuu-blog liked this
- paul-yamamoto liked this
- gmomedia-engineer posted this