ISUCON5予選 2位で通過しました

ISUCON5予選に@kazeburo@shmorimo@cubicdaiya(敬称略)の3人でチーム「GoBold」として参加してきました。

isucon.net

15時過ぎるくらいまではスコアが伸び悩んでいましたが、結果的に2位でフェニッシュすることができました。 以下はスコアの遷移をグラフ化したものです。

準備と方針

今回は予選に臨むにあたって事前に以下の準備を行いました。

  • Wikiで各種ミドルウェアの定石設定テンプレートを共有
  • Slackでプライベートグループを作成
  • 各人個別にGCE上でISUCON4予選問題(Ubuntu)の復習

次に事前に軽く打ち合わせして使用言語などの方針を固めました。

  • 基本はPerlで行く
  • 場合によってはGoやLua
  • デーモンはsupervisorで管理(systemdかもしれないけどそのときはそのとき)
  • 最初はアプリケーションのコードを読んだり、構成を把握するのに時間を使う

また、予選終了後にidobataを見てるとPHPだと思われていたようですが、最終的な構成はnginx + Perl + MySQL + memcachedでした。

まとめ

今回はお題がSNSで日記や足跡、コメント、友達リストなどデータベースを酷使する機能が多く、 ISUCON4の予選の時のようにミドルウェアのチューニングだけで大きくスコアを上げるのは難しそうでした。

また、ベンチマーク実行中にtopを眺めているとMySQLがCPUを3コア以上占有しており、SQLのチューニングが鍵だったことと 初期実装にバグがあるという説明があったのでGoを選択肢から外し、僕以外のメンバーはPerlが得意なので早い段階で以下のような役割分担になりました。

  • @cubicdaiya:インフラ周り。主に各種ミドルウェアの設定、ログからアクセスの傾向を探る、ngrepでベンチマーカーの挙動をパケットキャプチャして眺める等
  • @kazeburo, @shmorimo:言語はPerlSQLのチューニングをしていく。必要ならmemcachedにキャッシュする。

なのでこちらのエントリでは僕が主に担当したインフラ周りの話を書きます。アプリケーションやSQLのチューニングの方はあとで@kazeburo先生が書いてくれると思います。

追記:公開されました

blog.nomadscafe.jp

ngxtopでログからアクセスの傾向を掴む

ngxtopはnginxのアクセスログをパースして所謂topみたいなことができるツールです。

これを使ってトップページやログインページ、友達リストページへのアクセスが多いことと、 従来のISUCONと違って静的コンテンツへのアクセスがほとんどないことがわかりました。 また、nginxのアクセスログにリクエスト処理時間が出るようlog_format$request_timeを追加しています。

各種ミドルウェアの設定

あらかじめ用意していた各種ミドルウェアの定石設定を適用。supervisor入れたのはsystemdに慣れてなくてそっちにあまり時間を使いたくなかったためです。

あとは時折コード読んで「ここのループで複数回クエリ飛んでて重いので減らしましょう」とか「トップページでまだ100ms以上かかってるのでそっちを速くしましょう」とかPerl書けないのにエラソーに言いながら@kazeburo@shmorimoの二人を信じてMySQLの負荷が下がるのを待っていました。

nginxのパラメータチューニング

地道なSQLのチューニングが実を結んで15時を過ぎたあたりからスコアが一気に伸び始め、7000点台から一気に18000点台へジャンプすることに成功しました。 また、予選開始直後だとMySQLCPU使用率が300%を越えていましたが、MySQLCPU使用率は半分以下になっていました。

この時点でMySQLは大きなボトルネックではなくなったのでnginxのチューニングにも着手しました。

server_tokens off;
sendfile on;
tcp_nopush on;
open_file_cache max=100 inactive=60s;
client_body_temp_path /dev/shm/client_body_temp 1 2;
keepalive_requests 2000;

そしてappサーバへのリバースプロキシをUnixソケットで行うようにしました。

upstream app {
  server unix:/tmp/app.sock;
}

また、nginxをnginx-buildで最新の1.9.5をビルドして listenのパラメータにbacklog=Nfastopen=Mを指定。合わせてsysctl.confの設定も変更しています。(somaxconn)

listen 80 backlog=32768 fastopen=32768;

あまり効果が無いものも含まれていますが、トータルで2000くらいスコアが上がりました。20000点越えたのも確かこのあたりです。

入念に再起動試験

今回はOSが新しめのUbuntu15.04でデーモンの管理が慣れてないsystemdだったこともあり、いつもより入念に2時間前から再起動試験を行っていました。 もっとも運営の方が追試した際にはFAILしてしまっていたようですが(´・ω・`)

インスタンス放置してたら勝手にアップデートされるのつらい。。。

あと、何故か再起動を繰り返す度に(ウォームアップ済みでも)スコアが上下してたので インスタンスガチャかなと思ったのですが、どうやらガチャはないらしいです。

MySQLのウォームアップ

今回はデータベースの容量が大きいのでインスタンスを再起動した直後にそのままベンチマークをかけると Disk I/Oが刺さってスコアが出ないという問題がありました。(具体的にはウォームアップしてないと3000〜4000点ほど下がりました。)

このあたりは@kazeburo@shmorimoの二人がうまくやってくれてインスタンス起動時にウォームアップするスクリプトを仕込むことで解決しました。

ギリギリまで細かくチューニング

あとはアプリケーション側で地道なチューニングが続いて最終的に25861点でフィニッシュしました。

やり残したこと

アクセスログやtopを眺めていてわかったのですが、今回は例年と比べてベンチマーカーのワークロードが低めで、 スコアが20000点を越えたあたりでCPU1コアあたり30〜40%ほどidleしている状態でした。

また、静的ファイルへのアクセスが少なくベンチマーカーの並列数が少なめだったらしいので前段のnginxを外してすべてのリクエストをPerlアプリケーションサーバで受けるようにすればリバースプロキシのオーバーヘッドを回避できてもう少しスコア上がったかなと思います。