今回は弊社が提供しているブログサービス「nowa」(ノワ http://nowa.jp)の仕組みをサーバ構成を中心に紹介したいと思います。
nowaでは一般的なブログサービス要素とSNS要素の機能を実装しています。弊社には先行して提供している「livedoor Blog」、「フレパ」といった大規模なサービスがありますので、そちらの開発・運用で問題になった点などを参考にしつつ開発を進めています。具体的にはアクセスによる負荷への対策、データベースの分散化、画像のストレージング、冗長性、スケーラビリティといった点になります。
- ポータル(nowa.jp)、CMS(cms.nowa.jp) のサーバ構成

ポータルページ(nowa.jp)とCMSページ(cms.nowa.jp)は、静的なファイルのリクエストを捌く+動的なコンテンツへのリクエストをプロキシするフロントサーバ(図、proxy)と、動的な処理を行うアプリケーションサーバ(図app)が立っています。フロント側の処理はRewriteRuleで静的ファイルへのリクエストをローカルへ、動的なコンテンツへのリクエストを裏側のアプリケーションサーバへReverseProxyしています。フロント側はApache2系を採用し、mod_proxy_balancerでアプリケーションサーバへのリクエストをバランシングしています。
- ユーザーブログページ(***.nowa.jp)の構成

ブログページ(**.nowa.jp)の構成は、前述のフロント(静的)->ReverseProxy->modperl(動的)という仕組みは同じですが、こちらは1台のサーバ内でProxy用のapache、modperl用のapacheをポートを変えて起動しています。CMS/ポータル側に比べると、アクセス量は多いのですが処理としては比較的軽いものが多いので、proxyとmodperlを1台に同居させ運用しています。ブログ側の処理は弊社のアプリケーションで採用されているフレームワークSledge(http://sl.edge.jp/)を使用せず、ハンドラを書いて実装しプロセスサイズを小さくしています。ブログページはシンプルな処理が多いので、フレームワークではオーバースペックでプロセスサイズも肥大化し効率が悪くなるため、このように実装しています。負荷の高い(高くなりそうな)ところ用に別途ハンドラを書いて、レスポンス速度を向上させたり、プロセスサイズを抑えてソケット数を増やす方法も有効かと思います。
- データベースの構成

データベースについてはmysqlのレプリケーション機能をベース使いつつ、アプリケーション側でクラスタリング機能を実装しています。ユーザ毎にそれぞれマスターサーバを選択し、そこにブログ記事などのデータを保持しています。ユーザの増加に伴うスケールはユーザー用のmasterサーバ(とslave)を追加していくだけなので、作業が比較的容易になるかと思います。アプリケーション側からのmasterサーバを選択するロジックに関しては実装が必要ですが、ユーザのデータベースに障害が発生しても影響範囲を一部に限定することが出来るというメリットもあります。
- 画像サーバの構成

画像サーバは、ユーザがモブログなどでアップロードした画像をストレージ、画像へのリクエスト/レスポンスを返す処理を行っています。実装はWebDAVを用いての分散配置を採用しています。ファイルごとに配置するサーバのグループを決定し、グループ内に登録されている複数のノードサーバにファイルを配置して冗長化しています。MogileFSのdevice id,mindevcountあたりの動作をイメージしてもらうと分かりやすいかと思います。
画像 abcdefg.jpg -> image01のグループに保持 -> image01.[01-02]からレスポンスを返す
画像 hijklmn.jpg -> image02のグループに保持 -> image02.[01-02]からレスポンスを返す

URLは以下のようになります。
http://image.nowa.jp/p/suehiro/0000001abcdefg.jpg
http://image.nowa.jp/p/suehiro/0000002hijklmn.jpg
画像の参照は各サーバのapacheでRewriteMapを使ってサーバ名、画像のパスの取得を行い、そこからファイルが配置されているサーバのmodperlへ問い合わせ、画像を返しています。認証の必要ない画像に関しては、キャッシュロジックを通して、modperlへの問い合わせの負荷を軽減しています。以下がリクエストからレスポンスまでの処理の流れになります。
1. リクエスト http://image.nowa.jp/p/suehiro/0000001abcdefg.jpg
2. RewriteMap サーバ image01、パス a/b/c/abcefg.jpg
3. RewriteMap キャッシュをチェック(見つかればそのままレスポンスを返す)
4. modperlへリクエスト(認証など)
5. レスポンス
#現状のRewriteMapの場合パフォーマンスの上限が見えていますので、リクエスト数の増加を見ながらApacheModule化への切り替えなどを考えています。
- キャッシュ
データベースへのクエリ数を減らすため、キャッシュロジックとしてmemcachedを採用しています。こちらは専用サーバはなくアプリケーションサーバなどメモリに余裕のあるいくつかのサーバで並列に動かしています。前述の画像ロジックもmemcachedを使ってキャッシュをしています。
- ジョブキュー
livejournal、sixapartで使われてるGearmanを採用しています。nowaでは「クラスターをまたいだ参照」(ポータル新着一覧など)が必要なため、「ユーザーマスタ(master01,master01)のマスタ(master)」としてテーブルを用意してユーザマスタと同時にそちらにもデータを書き込んでいます。この時の処理をGearmanでバックエンドで走らせて非同期に処理を行っています。gearmandや各workerなどのプロセスは、メモリに余裕がある他のサーバに同居する形で上がっています。
アプリケーションサーバ 10台
データベースサーバ 10台
画像サーバ 6台
その他バッチ処理サーバ 1台
ざっくりとした内容ですが、以上のような構成でnowaのサービスは動いています。nowaはサービスイン時から計30台弱のサーバで動いており、新規サービスとしては弊社の中でも規模が大きいものになります。上記内容はサービスに特化した部分もありますが、通常サービスであれば、静的ファイルを捌くプロキシサーバ、動的出力を行うアプリケーションサーバ 、データベースのマスターサーバ(WRITE) 、スレーブサーバ(READ)、といったあたりが後々サーバを増強していくときに切り分けやすいポイントになるかと思います。サーバ構成を決める時に「ここの負荷が上がるようならここを増強する」というポイントを意識しておくとスケールしやすい構成が組めるかと思います。またアプリケーション側も、「どのような構成のサーバ上にコードが置かれるのか」「実際に増強する場合にどんな作業が発生するか」ということをしっかりイメージしながらコードを書いておくと、サーバ側で作業が発生した場合にも、サービスの停止、作業手順の複雑化、不必要に危険な作業、といった場面も減っていくかと思います。
ということで以上です。