2011年08月30日14:30にTech Blogにて公開された過去エントリです。
livedoor Techブログ : 自家製 #isucon のつくりかた

--
こんにちは、ISUCON というイベントのレギュレーションを考えたり環境の準備をやったりコード書いたりしてた tagomoris です。普段はライブドア開発本部のインフラサービス部というところで働いてます。
先日ISUCONは幸いにも大好評のうちに終了したのですが、へとへとになって疲れ切った状態で帰宅し、寝て起きてみると、公開しておいたソースコードをさっそく自分の手元で動かしている人がいました。説明とか何にもなかったのによくそこまで。どういうことなのと思わずにはいられません。

#isucon に参加してきました&isuconツールを試してみました - As a Futurist...
また翌日にはTwitterでも続々と動かしてみた報告が見られ、エンジニアのみなさんのバイタリティには感服するばかりです。


ざいりょう

で、せっかくだから本番と同じデータで同じように試せるようにしたいよね、ということで、ソースコード一式に加えて初期データも用意しました!

ソースコード一式についてですが、ISUCON本番から少しだけ変更を加えてあり、基本的には1台のPCでベンチ対象からベンチマークまで完結する設定・コードが最初から入っています。とりあえずお試しいただくにはこの状態からが多分やりやすいと思います。またベンチマークツールに多少のデバッグが行われています。

ISUCON本番と同じように多数サーバ構成・多数チーム参加で動作させるにはリリースタグの状態でやるのがいいと思いますが、サーバ構成をどうするか次第の部分もあり、なかなか面倒です。試したい人は頑張ってください。どうしてもという場合には @tagomoris 宛にお聞きいただけると答えられるかもしれません。
初期データファイルはISUCON本番のものと同じです。mysqldumpファイルなので、mysqlにそのまま食わせて使用できます。個人的なお勧めは、まずこのファイルを使わない状態でベンチマークを走らせ、その後にこのデータを入れてみることです。データの増大が(プログラムの構造によっては)性能にどれだけ影響を与えるかが実感できます。

したごしらえ

ISUCON環境を作成するPCについてですが、以下の環境のどれかを想定しています。
  • Linux系OSのどれか
    • ISUCONは CentOS 5.6 でしたが、CentOS 6 やDebian系のものでも問題ないと思います

  • Mac OSX
    • 10.6 Snow Leopard を手元では使用していますが、10.7 Lion でも多分大丈夫です

    • Xcode のインストールがおそらく必要です(入ってない環境が手元にないので確認できない……)

BSD系のOSやSolaris等でも気合いを入れれば動くような気がしますが、確認していません。基本的に以下のものが動くことが大前提です。
  • MySQL 5.5 (5.1でも多分動く)

  • Apache 2.2 (無くてもどうにかなる)

  • git

  • perl 5.8 以降

  • node.js 0.4.11 (0.4系の新しめのものなら大丈夫そうだが v0.4.11 おすすめ)

  • supervisord
    • easy_install 経由で入れる(後述)

特に node.js で環境が選ばれるかもしれません。node.js 0.4系はWindowsに対応していないので、WindowsだけでISUCON環境を作ることは残念ながら不可能です。(0.5を使ってもしかしたら動くかもしれませんが、全く試していません。試された方がいましたら教えてくださると嬉しいです。)
準備としてApacheとMySQLをなんとか入れます。ApacheはOSXは最初から入っていますし、MySQL 5.5はLinuxでもOSXでも公式のものが用意されていますので、自分の環境にあわせてインストールして下さい。このあたりはあちこちに案内があるので割愛します。

またperlも対象となる環境ではどれでも最初から使えるので省略します。perlのバージョンを変えて遊んだりしたい気持ちはよくわかりますが説明が大変なので、みなさまで各自頑張ってください。もしくは誰かがblogエントリを書いてくれると思います。
node.jsはちょっとレアなので書きます。nvm というnode.js自体のバージョンを管理するツール経由で入れるのが簡単です。naveという同種の目的のツールもありますので好み次第かなとも思いますが、とりあえず自分のとっている方法で。

このREADMEに従って以下のコマンドを叩きインストールします。ついでに必要なモジュールもインストールしてしまいましょう。
$ git clone git://github.com/creationix/nvm.git ~/.nvm
$ . ~/.nvm/nvm.sh
$ nvm install v0.4.11
$ nvm use v0.4.11
$ nvm alias default v0.4.11
$ npm install express jade mysql jsdom async

これでnodeおよびnode用モジュールのパスは以下のようになるはずです。
  • nodeのパス: ~/.nvm/v0.4.11/bin/node

  • モジュールのディレクトリ: ~/node_modules/

違ったなら nvm の動作が変わったのかもしれません。node.jsまわりにはよくあることなので、動揺せず適宜読み替えてください。
またアプリケーションやベンチマーク管理ツールなどをデーモンとして動作させるため supervisord をインストールします。

無くても試すだけは試せますが、スコアを求めてあれこれやる上ではあった方が便利です。easy_install コマンド経由でインストールするので、その準備をしておいてください。
$ sudo easy_install supervisor


つくりかた

なにはともあれ、ソースコードを取得します。適当なディレクトリを選んで git clone しましょう。isuconディレクトリの中に展開されます。
$ git clone git://github.com/tagomoris/isucon.git
$ cd isucon

まずWebアプリケーションから動作させることにしましょう。データベースにユーザを作成したりスキーマを定義したりします。
$ mysql -u root < webapp/config/database/isucon.sql

Perlでアプリケーションを動作させるには以下の手順でOKです。( webapp/perl/README に書いてある通りです。cpanmはインストール済みなら curl や chmod は不要です。)
$ cd webapp/perl
$ curl -k -L http://cpanmin.us/ > ./cpanm
$ chmod +x ./cpanm
$ ./cpanm -Lextlib -n --installdeps .
$ perl -Mlib=extlib/lib/perl5 extlib/bin/plackup -s Starman -E production --preload-app app.psgi

最後のコマンドを実行すると http://localhost:5000/ でWebアプリケーションにアクセスが可能になるはずです。ブラウザで開いてみてください。

最初の時点では記事が何もないので何も表示されませんが、ISUCONロゴをクリックすると記事投稿画面になり、そこから記事が投稿できます。ひとつ記事を投稿すればそれに対してコメントをつけたりもできるようになり、ブラウザ上にあれこれ表示されるようになってきます。

しばらくこのperlのプロセスは起動したままにしておきましょう。作業は別のターミナルを開いてやることにします。
最後にリバースプロキシとしてApacheの設定を行います。(なくてもとりあえず動くので、省略しても構いません。)

基本的には単純で、ApacheがListenしている port 80 に来たリクエストを、すべてlocalhost:5000に転送します。
ProxyRequests Off

<Proxy *>
Order deny,allow
Allow from all
</Proxy>

ProxyPass / http://127.0.0.1:5000/
ProxyPassReverse / http://127.0.0.1:5000/

mod_proxy (およびAPサーバを複数立てる場合には mod_proxy_balancer)を有効にしたApacheに上記の設定をします。OSXなら /etc/apache2/other/isu.conf などといったファイルを作成し、そこに書いておけば良いでしょう。Linuxの場合は /etc/httpd/conf.d/isu.conf に書く場合や /etc/apache2/site-enabled/default に追記すると良い場合があります。

書いたらApacheに設定を読み込ませます。
$ sudo apachectl restart

これで http://localhost/ でさっきのアプリケーションが見られるようになりました。なりましたよね?


ベンチマークの準備にもいくつかの手順が必要です。まず http_load をビルドします。
$ cd tools
$ tar xzf http_load/http_load-12mar2006.tar.gz
$ cd http_load-12mar2006
$ patch -p1 < ../http_load/http_load.patch
$ make

このとき、恐怖のkazeburoパッチをpatchコマンドで当てています。当てなくても動作はしますが、よりリアルなISUCON環境のためにはぜひとも当てるべきでしょう。

続けてperlのスクリプト用のモジュールの準備。
$ cd ../
$ ../webapp/perl/cpanm -Lextlib -n JSON Furl

もしリバースプロキシを立てない場合は、ベンチマークの設定ファイルの編集が必要です。tools/config.json を次のように書き換えましょう。(リバースプロキシのApacheを立てた場合はこの変更は不要です。)
--- config.json	2011-08-28 21:51:02.000000000 +0900
+++ config.json.2 2011-08-29 19:08:15.000000000 +0900
@@ -10,6 +10,6 @@
"team01"
],
"teams": {
- "team01": {"id":"team01", "name":"oreore", "pass":"pass", "bench":"bench01", "target":"127.0.0.1:80"}
+ "team01": {"id":"team01", "name":"oreore", "pass":"pass", "bench":"bench01", "target":"127.0.0.1:5000"}
}
}

target の指定のところでポート番号を変更していますね。ここだけで大丈夫です。
ここまでやれば、コマンドラインベースでのベンチマークは動作します(そのようなオプションをISUCON後に追加しました)。以下のコマンドを実行しましょう!
$ NODE_PATH=lib node bench.js team01 standalone

実行すると1分間、すごい勢いでアプリケーションにリクエストが飛びます。1分経過後、ベンチマークおよびWebアプリケーション動作チェックの結果が次のように表示されればOKです。(これはtagomorisのMacBookAirで実行した結果。)
tools tagomoris$ NODE_PATH=lib node bench.js team01 standalone
{ teamid: 'team01',
resulttime: Mon, 29 Aug 2011 10:02:48 GMT,
test: true,
score: 7102,
bench:
{ fetches: 7102,
'max parallel': 10,
bytes: 245187000,
seconds: 60.0002,
'mean bytes/connection': 34523.6,
'msecs/connect': { mean: 0.378283, max: 36.007, min: 0.081 },
'msecs/first-response': { mean: 12.754, max: 1254.57, min: 0.906 },
response: { success: 7102, error: 0 },
responseCode: { '200': 7102 } },
checker:
{ checker: { summary: 'success' },
poster: { summary: 'success' } } }

ここに出ている "test:true" が動作チェックにパスしたということ、そして "score: 7102" がベンチマークの結果のスコアです。当日はみんなでこの数値を競っていたわけですね。これであなたもISUCON参加者!

なおここに表示されたデータ(およびこれから後に書く方法で実行されたベンチマークのデータ)はすべて tools/data 下に保存され、後から確認できます。

もりつけ


実質的にはここまでで動くのですが、このままではちょっと見た目が残念ですし、コードや設定に変更を加えた場合にもいろいろと面倒です。なので、まず各種ツールが常時起動した状態になるようにします。

これが完了すると、ISUCONのように21チーム準備した場合にはこんな画面が見られるわけですね!
master

このために supervisord を使用します。他にも daemontools など同種のツールがありますが、supervisordのシンプルさにひかれて今回はこれを使いました。

以下の作業を始める前に、前の方で perl コマンドを叩いて起動しっぱなしにしていたプロセスは ctrl-C して停止させておきましょう。
スコアはデータベースに格納されますので、そのためのスキーマをMySQLに導入します。以下のコマンドで一発です。
$ mysql -u root < tools/etc/master.sql

で、おひとり様用のsupervisord.conf全部入りを作りましたので、これを使うのが楽です。が、その前に各種パス用の設定ファイルを修正します。
$ cd isucon
$ cat standalone/env.sh
#!/bin/bash

USERNAME=tagomoris
USER_HOME=/Users/tagomoris
NODE_VERSION=v0.4.11

export PATH=$PATH:$USER_HOME/.nvm/$NODE_VERSION/bin
export NODE_PATH=$NODE_PATH:$USER_HOME/node_modules/

ここで USERNAME および USER_HOME はnvmのインストールなどをしたユーザ名およびそのホームディレクトリを入れておきます。このあたりは普通 $HOME を使えばいいのですが supervisord を使う場合は root から呼ばれる場合などもあるので、このようにしてあります。

また NODE_VERSION や PATH や NODE_PATH には nvm 関連のパス等をセットしています。これはインストール後に確認したご自分の環境に合わせてください。
これが終わったら、次に standalone/supervisord.conf を /etc/ 以下にコピーし、自分の環境にあわせて書き換えます。
$ sudo cp standalone/supervisord.conf /etc/
$ sudo vi /etc/supervisord.conf

ログファイルの場所や細かいパラメータなどいろいろありますが、とりあえず動作させるために書き換えるのは以下の7ヶ所に埋まっている、isuconリポジトリの場所、およびユーザ名だけです。
[program:master]
command=/Users/tagomoris/Documents/isucon/tools/etc/master.sh
process_name=isucon bench master
user=tagomoris
...

[program:agent]
command=/Users/tagomoris/Documents/isucon/tools/etc/agent.sh
process_name=isucon bench agent
user=tagomoris
...

[program:isucon_perl]
directory = /Users/tagomoris/Documents/isucon/webapp/perl
command=perl -Mlib=/Users/tagomoris/Documents/isucon/webapp/perl/extlib/lib/perl5 /Users/tagomoris/Documents/isucon/webapp/perl/extlib/bin/plackup -s Starman -E production --preload-app --disable-keepalive --workers 10 /Users/tagomoris/Documents/isucon/webapp/perl/app.psgi
user=tagomoris

ここに /Users/tagomoris/Documents/isucon とあるのが自分がOSX上でisuconのリポジトリを置いた場所ですね。これをみなさんのPC上のパスにあわせて変更してください。 program:isucon_perl の command 行には何ヶ所かに埋まっているので修正漏れがないようにしてください。

また user も何ヶ所かにありますが、これはあなたのユーザ名に変更してください。
変更が終わったら sudo supervisord -n とすると、その端末内で supervisord が立ち上がり master.js と agent.js および perl のアプリケーションをすべて起動します。何か修正ミスなどがあればこのときにエラーになるので、端末内に表示される supervisord のエラーログ、あるいは /tmp/isucon.perl.log や /tmp/isucon.master.log などとして作成される各ツールのログを確認してください。

さらっと書いてきましたが、master/agentとは以下のような役割のプログラムです。
  • master
    • ベンチマークのスコアを各チームについて表示したり、ベンチマークの起動を指示したりするためのツール

    • 起動していれば http://localhost:3080/ でアクセスできる

  • agent
    • masterからのベンチマーク起動の指示を受け取って bench.js を実行したり、ベンチマークの走行状況がどのようになっているかを報告したりするためのツール

    • 特にベンチマークをかける側を複数台のサーバに分散したりする場合にこのようなものが必要

    • 人が直接見られる画面はなし

supervisord を起動して問題なく各プロセスが上がるようになれば、あとはもう sudo supervisord だけで起動してバックグラウンドにいってもらっても大丈夫です。
supervisord 経由で各プロセスが無事に起動していれば、以下のように「記録なし!」な画面が出てきますね。チームがひとつなのはちょっとさびしいですが、おひとり様用なのでしょうがありません。

"idle" をクリックするとベンチマークを起動できますので、初期パスワード "pass" を入力して "START" をクリックすれば状態が "running" となります。無事ベンチマークが完走したらチーム名の部分が青くなると同時にスコアが表示され、さあチューニングのはじまりだ!

123

かくしあじ

ここまでで初期データのないISUCON環境ができて、成績の数値も出てきました。6000とかすごいスコアですねISUCON上位に入れるじゃん! と思ったあなた、初期データの導入をお忘れですね。

ページ先頭のリンクから初期データ isucon_db.sql.gz をダウンロードし、適当な場所に置いておきます。次のようなコマンドで MySQL に読み込ませましょう。
$ gzcat isucon_db.sql.gz | mysql -uroot

完了後にそのままもういちどベンチマークを回してみると……
d1

スコアなんとたったの58・・・! 俺たちのISUCON坂はまだはじまったばかりだ! という気分になりますね! (サーバでの動作とはいえ)ISUCON優勝チームが1分だと90,000のスコアを叩き出していたことを考えるとどれだけ先が長いかがわかります。
またPCでの実行で、かつベンチマーク対象もベンチマーク元も同じPCになっていると、デフォルトの状態でも "FAILED" となることがあります。その表示をクリックしてみるとこんなダイアログが出るので確認してください。
2

理由としては負荷をかけたときにサーバが重過ぎて、表示内容のチェックを行うためのHTTPリクエストがぜんぜん通ってないんですね。HTMLの内容を確認することができず、レギュレーション上のチェックが通らないため、そのまま FAILED となっています。

根本的な原因はアプリケーションが重過ぎることですが、特にリバースプロキシにApacheを立てていて、かつプロセス数が少ないような場合にこういうことが起こります。お使いの環境で以下の項目をチェックしてみてください。
  • Apache のプロセス数が少なすぎる数になっていないか

  • Apache の設定で keepalive が有効になっていないか

  • そもそも非力すぎるマシンで実行していないか

どうしてもFAILEDが消せない場合、以下の対策を複合すればとりあえず通るようになるかもしれません。
  1. リバースプロキシのApacheを使うのをやめてベンチマークをアプリケーションに直接向ける
    • tools/config.json の変更を行うこと

  2. Perl版アプリケーションをやめてNode.js版を使う
    • アプリケーション単体での高負荷耐性はNode.js版の方が良いです

    • supervisord.conf でPerl版アプリケーションの設定セクションをコメントアウトし、Node.js版の側のコメントアウトを外してから supervisord を起動します

    • 各種パスの修正に気をつけてやってあれば、そのまま起動するはずです

さて、実はこの「おひとり様用」の設定は http_load の並列度をわざと2に落としています。これは初期状態でのベンチマークが通りやすくするためですね。パフォーマンスが十分に高い環境では並列度はもっと上げた方がスコアは上がります。

ISUCON本戦では http_load の並列度は 10 という設定に固定してありました。あなたのアプリケーションが高スコアを叩き出すようになってきたら、この数値に戻してみてもいいでしょう。tools/bench.js の9行目を変更してお試しください。

できあがり

これで環境は完成です。思う存分にあなたのISUCONをお楽しみください! 高得点をゲットできたらblogなどに書いていただけると我々もとても嬉しいです。
ライブドアではISUCONと楽しく激しく格闘できるエンジニアを募集しています!