ISUCON3 予選を開催しました

出題担当なのですが正式名称が ISUCON3 なのか ISUCON 2013 なのか未だによく分かってない今日この頃です。

それはともかくとして、200名以上の皆様に参加していただいて ISUCON の予選を盛況のうちになんとか終えることができました。

  • スコアの算出方法が公表されていなかったり
    • 基本的に静的ファイル以外の1リクエストが1点 (ただしPOST→リダイレクト→GETは1点)、静的ファイルは0.02点、css扱いで で読まれるのは 0点、でした
    • Failについては合計3まで減点なし、それ以上は (Fails - 3)^2 * % を減点するので合計 Fails 13 で (10*10)% = 100% 減点でスコア0になります
  • 想定していない /recent/* 荒稼ぎポイントがあったり
    • 初日の競技中に気がついて、何チームぐらいここで稼いでくるかと思いましたがやはり60チーム以上いるとありますね

と、不備がいくつかありまして参加者の皆様には申し訳ないです。本選ではそのあたりしっかり準備します。

感想

予選を開催してみての感想をいくつか。

今回はフロントキャッシュ組と再実装オンメモリ組がどうしても強かったですね。

キャッシュ組が強かったのはチェッカーが甘かったからなのですが、オンメモリ実装組が強いのはある1インスタンスで予選をする以上ある程度仕方ないかな、という気がしています。

前回の ISUCON2 では4台のリソースをほぼすべて使い切った fujiwara 組とリバースプロクシのホスト1台にアプリ乗せて捌いた山形組が (1分計測では) 僅差でしたし、1台かつデータセットがメモリに保持できるという出題ではこれを覆すのはなかなか大変かなと。

本選では用意した複数台を使い切ることで性能が上がるような出題にしようと構想しています。

準備とか

準備はまあ、分かっていたことですが大変でした。

9/18〜20 の YAPC::Asia 前に出題のプロトタイプと Go でのベンチクライアントの仮実装ぐらいは何となくできていた状態で、YAPC 後の1週間でひとまず最初の版を仕上げて @acidlemon に解いてもらいはじめたのが 9/25(水)。

最初は /recent/* がなかったのですが、ちょっと簡単すぎるかも、ということで追加したのが 9/30(月)の昼。後付けだったから意図しない穴があったんですね。

そこから二人で Ruby, Python, Go, Node, PHP の移植をせっせとやって目処がついたのが10/2(水)〜3(木)。

開催前日10/4(金)にAMIの元になるインスタンスを用意して、結果登録管理サーバのAPI作成、ベンチツールもインターフェースを整備して管理サーバへの送信部分を作ったり……

ベンチツールにちょっと変更を入れると6実装分でチェックする必要があったのでなかなか時間は食われるし、想定外のFailを起こすことがあるとその原因を追及しないとリリースできないし、というのが面倒でしたね。

結局予選1日目用のAMIが仕上がったのが土曜日の午前3時過ぎでした。

直前の週は、(1日だけ子供を寝かしつけて寝落ちしましたが) ほぼ毎日3時ぐらいまでコードを書いていて、もうそろそろ不惑の身体にはなかなか辛かったです。

運営当日

当日は IRC で櫛井さんと @acidlemon と連絡を取りつつ、自宅でほぼ張り付き対応。土日を潰してしまって家族には申し訳ないことをしました。

AMI-id を提出してもらうことは決まっていたのですが、そのインターフェースがなかったので14時頃からせっせと管理画面を実装して18時にdeployしたり。

--workload の存在は気がつかない人多かったですね。途中でもうちょっとREADMEに親切に「負荷を変えることでスコアが伸びることがあります」とか書こうと思ったのですが、1日目と2日目で有利不利ができるとまずいのでそのままにしました。

1日目終了後、細かい不備が見つかったのでそれを直した AMI を作り直して、2日目用には別に用意することに。

2日目は終了後、とりあえず速報で暫定スコアと順位を出して終了。

予選後

週末を潰してしまったので、月曜日はさすがに有休を取って家族と買い物にいってました。

10/8(火) には公開用AMIの整備とか。

火曜日夜に提出 AMI が出そろったので、awscli を使って

  • チームごとにAMI-idを指定してインスタンス起動
  • user-data で指定されたベンチコマンドを実行する shell script を食わせて、結果を IRC チャンネルに通知

というスクリプトをぶん回してスコア確認をしました。中も見てみたかったので、2,3チームずつ run-instance して確認して terminate して、というかんじで合計3時間ぐらい。

user-dataから起動したからか、環境変数かカレントディレクトリの影響か何かでうまく動かないチームもいくつかあったので、その場合は SSH ログインして手動でベンチコマンド実行して確認しています。

10/9(水) に結果発表を行いまして、この記事を書いてなんとか予選の一連の作業をすべて終えられます。


……といってるまもなく一ヶ月後に本選がやってくるので、来週からはその準備ですね。

また本選で、よろしくお願いいたします。

YAPC::Asia 2013 で「社内ISUCONのつくりかた」を発表しました

blogを書くまでがYAPCということなので…

YAPC::Asia 2013 にて「社内ISUCONのつくりかた」を発表しました。朝一の同時間帯に魅力的なトークがひしめくなか、聴きにきていただいた皆様ありがとうございます。

毎回40分のトークで早口になるので今年はゆるふわにするつもりだったのですが、蓋を開けてみたら結局いつもの感じになってしまいました。ゆるふわ難しい。

資料はこちらです

技術評論社さんのレポートも書いていただきました。ありがとうございます。http://gihyo.jp/news/report/01/yapcasia2013/0002

YAPC::Asia は2006年の初回から8年連続参加していて、うち本編トークは4年連続でやらせていただいていて、あらためてこう数えてみると結構な歴史を感じますね。

来年からはまた新しい歴史が始まることに期待して、一年間自分のできることをやっていきたいなと思います。

【告知】
優勝賞金100万円の ISUCON 3、予選エントリー絶賛募集中ですのでぜひよろしくお願いします!

http://isucon.net/

[perl] Perl徹底攻略という本が出ました

本日、7月23日発売です。

Perl徹底攻略 (WEB+DB PRESS plus)

Perl徹底攻略 (WEB+DB PRESS plus)

昨年10月に発売された Web+DB Press vol.72 に寄稿した「Webアプリケーションのパフォーマンス改善」が収録されています。

内容はほぼ初出時のままです。再録にあたってコード部分の変更はなしでいけると思い込んでいてそのまま校了してしまった後に、Devel::KYTProf に執筆当時のバージョンと非互換な変更が入っているのを見つけてしまい、あわてて互換性を確保するpull reqを送って 発売までにリリースされて助かった、という裏話があったりなかったり。

PerlWebサービスを作るために必要な基礎から運用、娯楽まで幅広くカバーしている本ですので、是非お手にとっていただければと思います。

nginxでメソッドごとにリクエスト数制限を掛けたい

アプリケーションでどうしても捌けない量のリクエストが一時的に押し寄せてしまう場合、アプリケーションサーバが死ぬのを避けるために GET は制限を掛けたいが、POST はリトライが面倒なのでなるべく通してあげたい、というような要求を nginx で処理できるかどうか。

実装として一番望ましいのは

  • GET は 100 req/sec で制限 (超えたら503)
  • POST は無制限

のようにメソッドごとに別々の制限を掛けることだったのですが、とりあえず HttpLimitReqModule を使うことで、メソッドごとに同一の上限を設定することはできました。

http {
    limit_req_zone $request_method zone=method:1m rate=100r/s;
    server {
        listen 80;
        location / {
            limit_req zone=method;
            proxy_pass http://127.0.0.1:5000/;
        }
    }
}

これで $request_method をキーにして、100req/sec を超えた分を 503 にします。

この設定では GET も POST も同じ上限になりますが、たとえば仮に

  • リクエストの比率が GET : POST = 9 : 1
  • アプリケーションが処理できる上限が 150req/sec
  • そこに 300req/sec が到達する

という場合を考えると GET=270, POST=30 req/sec になるので、

  • GET は 100 req/sec を超えた分 (170 req/sec) が 503
  • POSTは 30 req/sec なのですべてアプリケーションの処理に回る
  • アプリケーションに到達するリクエストは合計 100 + 30 = 130 req/sec

となって、POSTを全部通しつつ限界を超えないような制限ができます。

chef-soloがcookbookから実行するscriptの無限ループで大量にメモリを食って死んだ件

とあるホストで初期設定をしようと思って chef-solo を実行していたところ、メモリを全部食い尽くして chef-solo (11.4.4) が死亡するという事案が発生。

追ってみたところ、どうやら原因はこんなかんじ。

  • cookbook から shell script を実行していて、その中で perl Makefile.PL && make && make install していた
  • CPAN.pm が初期設定を終えていない場合、対話モードに入る
  • 対話モードで標準入力が閉じられていると途中まではデフォルトの入力で進むが、地域を選択するところはデフォルトがないためここでメッセージを表示しながら無限ループする
  • 無限ループで大量に出力されたメッセージを、(詳細は確認していないですが) chef がメモリに乗せ続けて太る

ということで、cookbook 内で実行する script には注意しましょう。というか chef 自体そのあたりの防御機構を持っていた方がいい気もしますね。

# echo -n "" | perl Makefile.PL
*** Module::AutoInstall version 1.03
*** Checking for Perl dependencies...
We have to reconfigure CPAN.pm due to following uninitialized parameters:

cpan_home, keep_source_where, build_dir, build_cache, scan_cache, index_expire, gzip, tar, unzip, make, pager, makepl_arg, make_arg, make_install_arg, urllist, inhibit_startup_message, ftp_proxy, http_proxy, no_proxy, prerequisites_policy, cache_metadata

/usr/lib/perl5/5.8.8/CPAN/Config.pm initialized.

(略)

I'd use that as a database of CPAN sites. If that is OK for you,
please answer 'y', but if you want me to get a new database now,
please answer 'n' to the following question.

Shall I use the local database in /root/.cpan/sources/MIRRORED.BY? [y] y

(ここまではデフォルトがあるので進む)

Now we need to know where your favorite CPAN sites are located. Push
a few sites onto the array (just in case the first on the array won't
work). If you are mirroring CPAN to your local workstation, specify a
file: URL.

First, pick a nearby continent and country (you can pick several of
each, separated by spaces, or none if you just want to keep your
existing selections). Then, you will be presented with a list of URLs
of CPAN mirrors in the countries you selected, along with previously
selected URLs. Select some of those URLs, or just keep the old list.
Finally, you will be prompted for any extra URLs -- file:, ftp:, or
http: -- that host a CPAN mirror.

(1) Africa
(2) Asia
(3) Europe
(4) North America
(5) Oceania
(6) South America
Select your continent (or several nearby continents) []
Sorry! since you don't have any existing picks, you must make a
geographic selection.

(1) Africa
(2) Asia
(3) Europe

(以下無限ループ)

Provisioning Frameworks Casual Talks vol.1 で「新卒研修でserverspecとChefを使った話」を発表しました

主催の @studio3104 さん、登壇された方々、参加者の皆さんありがとうございました。
Provisioning Frameworks Casual Talks vol.1 にて、カヤックの今年の新卒研修でserverspecとChefを使った話、をしてきました。

スライドはこちら

このblogには書いていなかったので、2013年のカヤック技術部新卒研修について、参考資料のリンクも張っておきます。

Chefの実行結果に対するテストは書かないとなあ、とずっと思っていながら結局あまり有効な手を付けられていない状態だったのですが、ちょうどいいタイミングで serverspec が出てきたので試しがてら研修で取り入れてみたという状態です。

プロダクション環境のテストはこれから随時書いていきますが、なにしろ対象が大量に(台数的な意味ではなく cookbook のバリエーション的な意味で……)あるのでまず cookbook に変更を加えるところから順番ですかね。

先着順参加方式について

MySQL casual vol4 の時も今回も、人数がちょうどよく埋まっているのであまり不満は聞かれないようですが、定員に達した場合の受付締切のアナウンスは(どこでアナウンスするのかも含めて)しっかり告知する必要があるな、と思いました。

当日登録のatndを併用するのは、先に確保してしまって忘れてしまうような人をださない手としてよさそうですね。

MySQLでデータ領域をシステムと別diskにするならtmpdirも設定した方がいい

某所に300ホスト以上を2年ほど監視していたZabbixのMySQLがありまして、データが100GBぐらいになってメモリ8GBのホストではdisk IOが辛くなってきたので、移行することにしました。普段はそんなにでもないのですが、housekeeperが動作して古いデータを消しに行くとバッファプールに乗っていない部分に読みに行って重いのです。

この際折角なので Intel S3700 (サーバ用のSSD) をおごり、

  • Zabbix-1.8 から 2.0 にアップグレード
  • MySQL-5.0.77 から MySQL-5.6.11 に変更
  • システムは HDD で /dev/sda1
  • データは SSD で /dev/sdb1 を /data にマウント

という構成で移行の検証を行っていたところ…

MySQLのバージョンが大きく上がるので mysqldump を取得して restore 後、patch.sql (1.8→2.0 の migration SQL) を実行中に、妙に HDD への書き込みが多くてボトルネックになっている雰囲気。ALTERの一時ファイルは確かデータファイルと同じ所にできた記憶なんですが…

lsof で mysqld が開いているファイルを見ても、/data 以外には /tmp しかない。
ということで、Percona-Toolkit の pt-ioprofile を教えていただいたので試してみました。

日々の覚書: pt-ioprofileでMySQLのテンポラリテーブルのサイズを測る

結果。ばっちり /tmp を使っていました。

$ sudo pt-ioprofile --cell sizes --run-time 60
2013年  4月 24日 水曜日 11:06:23 JST
Tracing process ID 22049
     total      pread     pwrite filename
1309671424          0 1309671424 /tmp/ibQo90KP
1308622848 1308622848          0 /tmp/ibGwKRu3

/data/tmp を作成し、my.cnf で tmpdir を指定することで、MySQLにそちらを使用させるよう変更し ALTER 再実行したところ、無事 SSDのほうを使ってくれるようになりました。

# mkdir /data/tmp
# chmod 1777 /data/tmp
[mysqld]
tmpdir = /data/tmp

/tmp が十分に高速な場合はデータと別にしておくことでパフォーマンス向上が見込める可能性はあると思いますが、今回は /tmp が低速だったので SSD 上に置いてしまうほうがいいですね。

Twitterで教えていただいた @yoku0825 さん、 @kazuho さんありがとうございました。