カヤック流ソーシャルアプリの作り方 インフラ編

入社4年目にもなってtech.kayac初登場のせいです。

ブログ書けプレッシャーにとうとう屈する時がきました。 これで夢にkyo_agoが出てうなされなくてすみます。(彼はtech.kayacの尻たたき担当でした)

先々月「ぼくらの甲子園!熱闘編」というゲームをモバゲー内にてリリースしました。 これは去年リリースした「ぼくらの甲子園!」の続編です。 モバゲーユーザの方、是非遊んでみてください。

今回はこの「ぼくらの甲子園!熱闘編」がどういうインフラ構成になってるか紹介したいと思います。

注) 題名に「カヤック流」とはつけましたが、カヤックでは多様性を善としている風潮があり、 ゲームによってインフラの構成が違うどころか、利用しているプログラミング言語すら違います。 なので全てのゲームがこのような構成になってるわけではありません。

前提

今回のインフラ構成を決めるに至って考慮した点は「ラクに構築できるSPOFのない構成」です。

ソーシャルアプリはいまや群雄割拠の時代です。 どんなに面白いゲームを作ってもトラブルによりサービスが停止してしまったら、 ユーザーは不安を感じ、他のゲームに移ってしまいます。 反面、サーバーのハードウェア障害は避けられない運命にあるのでSPOFのない構成は必須です。

しかし、SPOFのない構成というと障害時の切り替えの仕組みを自動化するなど手間がかかるイメージがあります。 開発期間も潤沢にあるわけではないので、構築にそれほど時間も割けません。 そのため、できるだけラクに構築できてSPOFがない構成というのが理想でした。

また、ソーシャルアプリは、ヒットすれば膨大なリクエストがサーバーに押し寄せ、 逆に想定よりも格段にアクセス量が少なくなることもあります。 そのため、スケールアップ・ダウンが容易にとれることはもちろん必要です。

全体

Untitled.png

上図がインフラ構成図になります。各サーバーで利用しているミドルウェアは下記になります。

  • 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に登壇してそのサービスの裏側についてお話しします。

Perlで構築された中規模サイトのDC引っ越し記録

興味のある方は是非足をお運びください!

カヤックではfujiwaraと呑みたい技術者を大募集しています!