入社4年目にもなってtech.kayac初登場のせいです。
ブログ書けプレッシャーにとうとう屈する時がきました。 これで夢にkyo_agoが出てうなされなくてすみます。(彼はtech.kayacの尻たたき担当でした)
先々月「ぼくらの甲子園!熱闘編」というゲームをモバゲー内にてリリースしました。 これは去年リリースした「ぼくらの甲子園!」の続編です。 モバゲーユーザの方、是非遊んでみてください。
今回はこの「ぼくらの甲子園!熱闘編」がどういうインフラ構成になってるか紹介したいと思います。
注) 題名に「カヤック流」とはつけましたが、カヤックでは多様性を善としている風潮があり、 ゲームによってインフラの構成が違うどころか、利用しているプログラミング言語すら違います。 なので全てのゲームがこのような構成になってるわけではありません。
前提
今回のインフラ構成を決めるに至って考慮した点は「ラクに構築できるSPOFのない構成」です。
ソーシャルアプリはいまや群雄割拠の時代です。 どんなに面白いゲームを作ってもトラブルによりサービスが停止してしまったら、 ユーザーは不安を感じ、他のゲームに移ってしまいます。 反面、サーバーのハードウェア障害は避けられない運命にあるのでSPOFのない構成は必須です。
しかし、SPOFのない構成というと障害時の切り替えの仕組みを自動化するなど手間がかかるイメージがあります。 開発期間も潤沢にあるわけではないので、構築にそれほど時間も割けません。 そのため、できるだけラクに構築できてSPOFがない構成というのが理想でした。
また、ソーシャルアプリは、ヒットすれば膨大なリクエストがサーバーに押し寄せ、 逆に想定よりも格段にアクセス量が少なくなることもあります。 そのため、スケールアップ・ダウンが容易にとれることはもちろん必要です。
全体
上図がインフラ構成図になります。各サーバーで利用しているミドルウェアは下記になります。
- Webサーバー - nginx
- アプリケーションサーバー - Starman
- キャッシュサーバー - memcached
- セッションストレージ ー - Kyoto Tycoon
- DBサーバー - MySQL
基本的にどのサーバーも冗長構成をとっていますが、 キャッシュサーバに関しては今回のゲームがキャッシュをそれほど活用しておらず、 キャッシュが仮になくても動作に支障がでないため、冗長構成はとってません。
アプリケーションサーバーでhaproxyを使う
アプリケーションからはDB・キャッシュ・セッションストレージへとそれぞれのサーバーへアクセスしますが、 このアクセス先をどう管理するかが問題になります。
アクセス先決め打ちでアクセスするとそのサーバーがダウンしたときに当然障害になります。
各サーバーの間にLVSなどロードバランサー用のサーバーを用意するという方法もありますが、 ロードバランサー自体を冗長化させる必要があり、その切替にも手間がかかります。
あとはクライアントライブラリに負荷分散機能が備わっていれば別ですが、 なかなか要件を満たすライブラリはなく、自作する必要があります。
そこで今回はhaproxyを各アプリケーションサーバー全てにインストールし、 アプリケーションサーバーから各サーバーへのアクセスは全てhaproxyを経由するようにしました。
haproxyの設定は以下のようになります。
listen mysql-master
bind 127.0.0.1:3306
mode tcp
option mysql-check user haproxy
server master k2-db01:3306 check
listen mysql-slave
bind 127.0.0.1:3307
mode tcp
option mysql-check user haproxy
balance roundrobin
server slave0 k2-db02:3306 check
server slave1 k2-db03:3306 check
....
listen kyototycoon
bind 127.0.0.1:11222
mode tcp
server master k2-cache02:11222 check
server backup k2-cache03:11222 check backup
これで各アプリケーションサーバーからはlocalhostの3306ポートがDBのmaster、 3307ポートがDBのslaveとして利用できるので、アプリケーションにはアクセス先を決め打ちにすることができます。
DBサーバーを1台余計に用意する
DBサーバーはよくあるmaster/slave構成です。
insert/updateはmaster、selectはslaveへアクセスしますが、 slaveへのアクセスは各アプリケーションサーバーのhaproxyを経由して分散されます。
仮にslaveがダウンした場合はそれをhaproxyが検知してそれ以外のslaveでサービスを継続します。 そのため、仮に1台ダウンして他のサーバーだけでサービスを続行しても、 支障が出ない程度にあらかじめスケーリングしておくことが必要です。
また、アプリケーションサーバーからアクセスがないslaveサーバーを一つさらに余計に用意しています。 これは主にはデイリーのバックアップ用途、スレーブ増設の際のコピー元になるためですが、 データの詳細な調査が必要になった際にSQLを手動実行出来る環境にも利用できます。
手動でSQLを発行して調査を行う際は重いクエリーを発行してしまうことが往々にして有ります。 それをサービスに影響なく実行するためにあらかじめ用意していると安心です。
参考: MySQLで参照の負荷分散を行うslaveは3台から構成するのがよいのでは
セッションストレージにKyoto Tycoon
当初はセッションデータを保持するためにキャッシュサーバーと同居して、memcachedを使うことを考えていましたが、 memcachedでは他のデータの容量次第でセッションデータがロストする可能性があります。 またレプリケーション機能が標準ではないため、冗長構成が取りにくいという欠点があります。
いつ消えてもサービスに影響がないキャッシュデータであればこれでも問題ないのですが、 セッションデータのロストはサービスの致命的な障害にはならないものの、 ユーザの利便性を大きく損なう可能性があるため、セッションストレージはKyoto Tyconnを利用しました。
他のKVSではなく、Kyoto Tycoonを採用した理由は主に以下です。
・Memcachedプロトコルをサポートしてること ・デュアルマスター構成が取れること
内部的な事情を言うと、当初memcachedを使うことを想定していたので、 memcached用のクライアントライブラリで開発をしていました。 そのためmemcachedプロトコルを使えるのは助かりました。
また、Kyoto Tycoonはデュアルマスタ構成が可能なので冗長化が容易です。 セッションストレージもDBのslave同様、各アプリケーションサーバーからhaproxyを経由してアクセスされるので、 仮に一方のサーバーがダウンしたとしても、もう一方のサーバーを利用してサービスを継続することができます。
Kyoto Tycoonの起動オプションはは下記のように設定してます。
ktserver -plsv /usr/local/libexec/ktplugservmemc.so \
-plex 'opts=f#port=11222' -ulog /var/lib/kyototycoon/master1-ulog -sid 1 -mhost k2-cache03 \
-rts /var/lib/kyototycoon/master1.rts \
'/var/lib/kyototycoon/master1.kch#opts=l#bnum=8000000#msiz=5g#dfunit=8'
最後に
以上で、ひと通りのサーバーに対して冗長構成がとれていますが、ひとつだけ冗長構成が取れてないサーバーがあります。
DBサーバーのmasterです。
DBサーバーの冗長化はまだこれといった解決策を見いだせていませんが、 仮に障害が起きた場合はslaveを昇格するなどの対応をとることにしています。 下記のようなツールの開発が行われていますので、要チェックです。
Announcing MySQL-MHA: “MySQL Master High Availability manager and tools”
また、実を言うと以上の構成は弊社の「こえ部」のインフラにインスパイア(!)されたものです。
そのインフラの立役者のfujiwaraがYAPC::Asiaに登壇してそのサービスの裏側についてお話しします。
興味のある方は是非足をお運びください!