午後から→オーバークロック

駆け出しハッカー()によるプログラミング・サービス開発備忘録。

初心者二人でISUCON予選に突撃して予想通り撃沈したよ

f:id:nemupm:20140929061144p:plain

結局ISUCONの予選は惨敗の結果だったわけですが、当日の流れを書き起こしておきたいと思います。

ISUCONとはIikanjini Speed Up Contestの略で、

お題となるWebサービスを決められたレギュレーションの中で限界まで高速化を図るチューニングバトル

です。
最近友人(@__shiki49__)がYAPC::Asia 2014に行った際にその存在を知ったらしく、
「こんなコンテストがあるよ!」ということで紹介してくれました。

サーバはちょっと立ち上げた事があるくらいでほとんど大した知識もなく、
ましてやチューニングなんて出来る訳無いなどと最初は思ったのですが、
こういう機会を大切にしたいし、何事もチャレンジが大事かなと思い、
初心者二人で予選に参加する事にしました。

準備したこと

方針

まずWebアプリの言語について決めました。
友人が普段扱う言語はPHPで僕はPythonだったのですが、
「予選の突破も大事だけど、やっぱり勉強のためというモチベーションも大事にしたいから、
どうせなら新しい言語でやろう!」ということでNode.jsに決定しました。

情報収集

事前に色々な先人達のブログを読んでみました。

  • 最初にサービス全体を把握する事が大事そうだ。
  • ボトルネックを認識するのが鍵となりそう。
    • 一体どうやってするの?勘なのか、セオリーがあるのか。
  • データベースばりばり扱いそうだけど、SQLとかそんな詳しく無いぜ!
    • とりあえずインデックス作れば速くなるんでしょ、くらい
    • 扱うデータ容量が少なければオンメモリで扱う(Redis, Memcached?)のが良さそう
    • キャッシュ・キャッシュ・キャッシュ

過去問を解いてみる

去年のISUCON3の環境がAWSのAMIの形で提供されていたので、前日の夜から少しいじってみる。

  • まず二人ともそれぞれAWS立ち上げ。MySQLが立ち上がらなくて困る。
    • service mysql startじゃなくて、/etc/init.d/mysqld startだと立ち上がった。なんじゃそりゃ!(おかげでプロセスの監視とかのコマンドにちょっと詳しくなった)
  • ベンチマークを走らせる。1400くらい。
  • nginxというサーバが速いらしいので、Apacheからnginxに移行してみる。
    • そういれば、Webアプリケーションは初期状態でPerlが動いているらしいのだが、ApacheはどこでPerlに処理を渡してるんだ?
    • どうやらlocalhost:5000に投げているみたい。
    • nginx.confでルートに来たリクエストを全部5000に投げるように編集。ここでCSSが読み込まれない自体発生。
    • 要素を検証でリクエストヘッダ読んでみると、なんかCSSlocalhost:5000から読もうとしてる…。
    • 調べまくってproxy_set_header Host $host;書いたら直った。スコアは50くらいアップ。しょぼい…。
  • なんとなくNode.js周りを読んで理解してみる。
    • npmでパッケージ入れれるっぽい。
      • 最初自分だけなぜかパス通ってなくてはまった。
    • Node.jsに切り替えてみた結果、スコアが数百くらい下がった。お、おぅ…。

ここで予選当日の朝5時くらいだったのでもう寝ました。

予選詳細

とんだ失態だったのが、二人とも朝寝坊し、10:30くらいに起きたことでした(しかも友人のSkypeで起こされる)。
しかも、「まあ8時間もあるし大丈夫でしょ!」と、
今では目も当てられない、インフラ経験が少ない事によるあまりに浅はかな言葉を放っていました。

まず最初は予習を生かしてとりあえずnginx+Node.jsに環境を変えて動かしてみよう!ということで作業に入りました。 そしたら最初からまさかのnginxだったので、アプリをNode.jsに変えてサービスにアクセスしてみました。
そして動いているのを確認した後、ベンチマークを動かしました。
ここでスコア1400くらいで、ISUCONの予選専用サイトで順位を確かめると70位くらい。
49ersというチーム名(同じ高校の49期生)だったので、とりあえず49位目指すか、という感じで作業に取りかかり始めました。

現状分析

ボトルネックを把握しよう!ということで友人が調べて来たnewrelicというサービスを使ってサービスを解析してみました。
ただ、結局得た情報は「/loginへのリクエストが時間掛かってるな!」というだけ。そりゃそうかという感じ。

次に、色々いじりまくってベンチマーク通らなくなったら大変だから、gitで管理しようかー、ということで
gitで管理し、BitBucketにプライベートリポジトリつくってpushする形式にしました。
同一サーバのプロジェクトを複数人で直接編集する場合の方法は何が良いのかっていうのは、
普段の開発じゃなかなか無さそうなんでISUCON永遠の命題なのでは無かろうか。

それじゃあソースコードを読んでみようかということで、App.jsを読み始めました。
中で結構SQLクエリが実行されていたので、友人がslowqueryというものを導入して、
時間のかかったクエリを検知する様に、そのクエリを中心に解読していきました。

分かった事は、

  • ログインしてmypageに飛ぶ機能だけがあるフィッシング銀行サイト
  • 3・10回連続でログインミスったユーザ・IpはBANする
    • サーバでアクセスログを管理してて、最後に成功して以降のミスった回数とかカウントしててめっちゃ重そう
  • データベース自体の容量は数十MBと、そんなに大きくない。

ということ。そこで以下のような作戦を取る事にしました。

本当はNode.js上でずっとデータを保持しておけばいいんじゃないかと思いましたが、
リクエストを処理するプロセスは1つでは無いのでデータ共有が難しそうだし、
かといってプロセスを1つにするとマルチコアの恩恵が受けられなくなるのでmemcachedにしました。

実装

僕は初期化スクリプトPythonで書いて、友人はApp.jsの、現在SQLクエリを毎回発行して処理している部分を書き換える事にしました。
結果、init.pyの完成にめちゃくちゃ時間がかかり、その後App.jsとの連携をデバッグしながら毎回修正していったものの、
結局時間が無さ過ぎてベンチマークがfailするままに終わってしまいました(最後のスコアは2でした(エッ)。

予選終了後

時間内までに結果を出せなくて悔しかったですが、立てた作戦通りの実装をした場合にどれだけのスコアが出るか知りたかったので、
そのまま寝ずに実装してみました。
休憩をしたりサッカーを見たりしながら朝まで頑張った結果、failしないようになって、最終スコアが5500くらいでした(nemupm/isocun4_pre・GitHub)。

実装でつまづいてた点は、

  • データがmemcachedのメモリサイズ上限値を超えてて一部が消えてた。
  • memcachedとのやり取りの際にJSONシリアライズとかしないといけないことに後で気付いた。
  • リクエストのうちカバーできていないパターンが結構あった

という感じでした。

また、後で見直してみて、結局毎回シリアライズとかに時間掛かってるだろうから、
初期化もjsでやってシリアライズ不要にしたほうが良かったんだろうなと思いました。

まとめ

以下感想です。

  • デバッグがすごく難しかったです。
    • 他の方のブログとか読んでると、nginxのアクセスログを独自に分析したりして必要な情報を集めていて、感心した。
  • 方針は割と本質とずれてなかったと思うけど、実装力なさ過ぎぃ!
  • インフラ知識が無さすぎて、「こういう時はこうする」という常識が無くて時間がかかったり、SQL以外のボトルネックの把握が出来なかった。

ISUCONに参加してみて今回本当に良かったなと思ったのは、僕に足りない知識の部分ってのが具体的に見えたことです。
しかも、それが勉強って感じではなく、与えられた課題を楽しんで解いていって、
終わった後もidobataでチャットを眺めたりブログを読んで色々考えて、というように、
すごく楽しく取り組めたのが最高でした。
みなさん本当にありがとうございます!

是非、次は予選突破を現実的に狙えるだけの知識をつけて、再度挑戦したいです!