apatheia.info

ISUCON 3 の参加記録

Published on 11 Nov 2013 Tags: isucon performance

Web アプリケーションのパフォーマンスコンテスト ISUCON 3 に参加し、2 位の成績となった。どのような状態で当日を迎え、どのような作業を行ったのかをまとめる。

私自身はこれで三度目の ISUCON 参加となるが、今回チームを組むメンバーはみんな初めての参加ということもあり、事前の打ち合わせでは以下のようなことを話していた:

予選までに数回休日に集まっては過去の問題を解いたり、自分たちで考えた予想アプリに対してチューニングをしてみたり、もくもく自分なりのトレーニングをしたりする会を開いていた。

また、個人的には以下のようなことを行ってきた:

どんなアプリケーションがくるかわからない以上、不安がぬぐいさることはできない。この時点でできることは「当日できるだけ早くウィークポイントを見つけること」、「見つけたウィークポイントに対しての効果的な対応をとれるための引き出しを増やしておくこと」であり、そのための準備をひたすら積み重ねてきた。

予選

予選問題はチームメンバーの勤め先の会議室を借りて取り組んだ。何度も事前打ち合わせで利用していたので、ストレス無く作業に打ち込めた。ただ、興奮しすぎて当日 2 時間くらいしか眠れていない状態だったため、テンションが高いのに頭があんまり働かなくてだいぶつらかった。

題材はコードスニペット投稿アプリケーション、いわゆる Nopaste や Pastebin と呼ばれるたぐいで Gist を想像してもらうとわかりやすいと思う。ログインがありセッションが発生すること、公開/プライベートのフラグがあることがこれまでの題材との大きな違いであったが、ある程度予想の範囲内であったことと後述するとおりベンチマークの抜け道に気づけたので、すぐにスコアを伸ばすことができた。

主な施策は以下の通り:

事後の講評などでも抜け穴があったという話があったが、後日公開された予選のAMIを利用しても以下のような方法で簡単に確認できる。

  1. アプリを Perl から Ruby に変更 (※ これは単純に自分がRuby に慣れているからで、他の言語では確認していない)
  2. Memcached への接続先を MySQL Memcached plugin (ポート 11211) から本当の Memcached (ポート 11212)に変更しアプリケーション起動
  3. リバースプロキシの Apache を停止
  4. Varnish をインストール
  5. 以下の設定をして Varnish を起動

/etc/varnish/default.vcl

backend default { .host = "127.0.0.1"; .port = "5000"; }

sub vcl_recv {
  # POST リクエストやCookieがある(ログイン中)の場合は直接アプリを参照
  if (req.request != "GET" || req.http.Cookie) {
      return (pass);
  }
  return (lookup); # それ以外はキャッシュを探す
}

sub vcl_hash {
    hash_data(req.url);
    hash_data(req.http.host);
    return (hash);
}

sub vcl_fetch {
    set beresp.ttl = 1d; # キャッシュ期間は1日に設定。数字は適当で、とにかく長くしておけばいい
    return (deliver);
}

上記設定で初回 workload=1 で 9000 くらい、2 回目に workload=2 で24000くらいでベンチマークが Fail せずに完走することがわかる。Varnish じゃなくて他のコンテンツキャッシュを利用しても同様。

予選開始直後、Sinatra のアクション単位でリクエスト回数、平均実行時間、ワーストケースの実行時間などの情報がそろえ、いざ Varnish のチューニングを行いはじめたところ、どんなにキャッシュの TTL を伸ばしてもベンチマークが通ってしまうことに気づき思わず吹き出してしまった。

キャッシュで簡単にスコアが伸ばせることに気づいたのがたしかお昼前で、このあと夕方くらいまで 1 位だったと思う。その後トップは手放すこととなったが、最終的に予選5位で決勝進出を決めることができた。

予選後

どうにも簡単に勝てすぎたので本当の自分の実力がどの程度かの不安が残るかたちとなったが、穴を見つけられたのも実力のうちと考えて本戦へ向けての準備を進める。

予選が割とシンプルで簡単にキャッシュで返せるような作りだったので、本戦はよりアプリケーションよりの対応が求められるはず、そのためにはいままで以上にアプリの状況を把握する能力を高めておく必要がある。*stat 系のパフォーマンス調査ソフトに慣れておいたり、ログ解析のやり方探してみたり(GoAccess 使ったりだとか)、プロファイリング用のソフトウェアを把握しておいたり、調査用アクセスカウンタ を準備したり、とにかくすぐに分析できるようにしておいた。

また、予選の際は自分もアプリを改修し、アプリメインのメンバーもインフラ面を考慮してくれていたので、本戦で複数台構成になることにより作業がバッティングすることを懸念された。これについては事前にチーム内でそれぞれにメインとなる作業をお願いして競合しないよう取りはからった。

本戦

予選での経験を踏まえ、前日早めに就寝したお陰で当日は体調的には万全、先着で利用可能なミーティングスペースも確保でき順調な滑り出しだった。

題材は画像投稿版の Twitter で、アップロードした画像ファイルがパブリック/フォロワーのみ/プライベートといった公開範囲に応じて閲覧可能となり、関係するメンバーの投稿が画面に自動反映される SPA (Single Page Application) だった。「ファイルアップロード」「フォロワーのアクティビティがタイムラインに表示」などで、チーム内の感想としては「やっぱりきましたね」、といった感じだったのだが、なによりアプリケーションの出来がよくてびっくりした。

作業用サーバーとしては、アプリケーションが稼働している 1 台と自由に使える 4 台の計 5 台が与えられた。これまでの ISUCON では CentOS 5 系が使われており、データホテルの Web サイトでもホスティングしている VPS の OS CentOS 5.8 となっているので CentOS 5 系が来る可能性も考慮していたのだが、今回は CentOS 6.4 であった。

サーバー受け取り後、バックアップをとりつつアプリケーションのプロファイリングを進めるが、どう見ても画像変換のコストだけが突出していることがわかる。データベースへのアクセスはきわめて短時間で終わっており、ボトルネックではない以上手をかけるのは無駄だと判断した。

状況確認を踏まえ、以下のような作業を行っていった:

最終的に以下の構成で計測を迎えた。フロント 4 台に Nginx を配置のうえ try_files でローカルにファイルがあるかどうかをチェックし、なければバックエンドサーバーに処理を委譲するようになっている。

                +----------+        +----------------+
  Benchmark ->  | Web x 4  |  ----  | Web + App + DB |
                +----------+        +----------------+

時間がなくてこのようなかたちとなったが、今考えてもこれがよかったかどうかでいえばまったくもってよくなかった。完全にバックエンドのアプリサーバーが負荷でつぶれていたが、全サーバーでアプリを動かせばきれいに台数分スケールしていたはずだったのに、本当に悔やまれる。

タイムアップ時点ではそれほどの成績ではなかったが、本戦のベンチマークを無事乗り越え、気づいたら 2 位という成績で ISUCON 3 の幕を閉じた。

ふりかえって

正攻法ではない方法もいろいろ考えてはいたんだけど、結局のところ他のチームと比べても過激な改修は行わずに済ませた。直しやすそうなところ、目のつきやすいところではなく、手早く確実にウィークポイントを直すというのが目標だったので、それは実現できたんだと思う。

けどやっぱり優勝できなかったのは心残りで、試合終了後や帰宅後のチャット上でもチームメンバーと「非同期のワーカー作る時間の無駄だった」とか「どう考えてもアプリサーバーネックだったし、他の4台でもアプリ動かしてベンチマークのワーカー増やせばトップとれたんじゃないの」とか「Macbook Air でちまちま画像変換するの失敗だった」とか、いろいろ話をしていた。1位があまりに鮮烈で、それ以外の順位は空気みたいな存在だし、やはりこの世はトップ総取りなのだなぁ、と痛感した。

毎回このような場を用意していただいている LINE 社、データホテル社の方々には感謝の限りです。お弁当おいしゅうございました。出題のカヤックの皆さんもすばらしい問題をありがとうございました。