ISUCON5予選 2位で通過しました
ISUCON5予選に@kazeburo、@shmorimo、@cubicdaiya(敬称略)の3人でチーム「GoBold」として参加してきました。
15時過ぎるくらいまではスコアが伸び悩んでいましたが、結果的に2位でフェニッシュすることができました。 以下はスコアの遷移をグラフ化したものです。
GoBoldスコア遷移のグラフ #isucon pic.twitter.com/JKkfjiVJnS
— Shigeki Morimoto (@shmorimo) September 28, 2015
準備と方針
今回は予選に臨むにあたって事前に以下の準備を行いました。
次に事前に軽く打ち合わせして使用言語などの方針を固めました。
- 基本はPerlで行く
- 場合によってはGoやLuaで
- デーモンはsupervisorで管理(systemdかもしれないけどそのときはそのとき)
- 最初はアプリケーションのコードを読んだり、構成を把握するのに時間を使う
また、予選終了後にidobataを見てるとPHPだと思われていたようですが、最終的な構成はnginx + Perl + MySQL + memcachedでした。
まとめ
今回はお題がSNSで日記や足跡、コメント、友達リストなどデータベースを酷使する機能が多く、 ISUCON4の予選の時のようにミドルウェアのチューニングだけで大きくスコアを上げるのは難しそうでした。
また、ベンチマーク実行中にtopを眺めているとMySQLがCPUを3コア以上占有しており、SQLのチューニングが鍵だったことと 初期実装にバグがあるという説明があったのでGoを選択肢から外し、僕以外のメンバーはPerlが得意なので早い段階で以下のような役割分担になりました。
- @cubicdaiya:インフラ周り。主に各種ミドルウェアの設定、ログからアクセスの傾向を探る、ngrepでベンチマーカーの挙動をパケットキャプチャして眺める等
- @kazeburo, @shmorimo:言語はPerl。SQLのチューニングをしていく。必要ならmemcachedにキャッシュする。
なのでこちらのエントリでは僕が主に担当したインフラ周りの話を書きます。アプリケーションやSQLのチューニングの方はあとで@kazeburo先生が書いてくれると思います。
追記:公開されました
ngxtopでログからアクセスの傾向を掴む
ngxtopはnginxのアクセスログをパースして所謂top
みたいなことができるツールです。
これを使ってトップページやログインページ、友達リストページへのアクセスが多いことと、
従来のISUCONと違って静的コンテンツへのアクセスがほとんどないことがわかりました。
また、nginxのアクセスログにリクエスト処理時間が出るようlog_format
に$request_time
を追加しています。
各種ミドルウェアの設定
あらかじめ用意していた各種ミドルウェアの定石設定を適用。supervisor入れたのはsystemdに慣れてなくてそっちにあまり時間を使いたくなかったためです。
あとは時折コード読んで「ここのループで複数回クエリ飛んでて重いので減らしましょう」とか「トップページでまだ100ms以上かかってるのでそっちを速くしましょう」とかPerl書けないのにエラソーに言いながら@kazeburo、@shmorimoの二人を信じてMySQLの負荷が下がるのを待っていました。
nginxのパラメータチューニング
地道なSQLのチューニングが実を結んで15時を過ぎたあたりからスコアが一気に伸び始め、7000点台から一気に18000点台へジャンプすることに成功しました。 また、予選開始直後だとMySQLがCPU使用率が300%を越えていましたが、MySQLのCPU使用率は半分以下になっていました。
この時点で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=N
とfastopen=M
を指定。合わせてsysctl.conf
の設定も変更しています。(somaxconn
)
listen 80 backlog=32768 fastopen=32768;
あまり効果が無いものも含まれていますが、トータルで2000くらいスコアが上がりました。20000点越えたのも確かこのあたりです。
入念に再起動試験
今回はOSが新しめのUbuntu15.04でデーモンの管理が慣れてないsystemdだったこともあり、いつもより入念に2時間前から再起動試験を行っていました。 もっとも運営の方が追試した際にはFAILしてしまっていたようですが(´・ω・`)
いやー、最初にチェックに通した1日目の上位2チームが続けてチェックに失敗したときはどうしてくれようかと思ったね! (その後いくらなんでもおかしいと詳細を調査した #isucon
— tagomoris (@tagomoris) September 27, 2015
インスタンス放置してたら勝手にアップデートされるのつらい。。。
競技後インスタンス起動してそのままにしておいたら自動updateでkernelの新しいのが入って、それだと起動失敗して/ がread onlyになる。つらい #isucon
— fujiwara (@fujiwara) September 28, 2015
あと、何故か再起動を繰り返す度に(ウォームアップ済みでも)スコアが上下してたので インスタンスガチャかなと思ったのですが、どうやらガチャはないらしいです。
GCEインスタンスはBorgできっちりCPUリソース管理されてるので、ガチャ発生ってたぶんないです #isucon
— Kazunori Sato (@kazunori_279) September 27, 2015
MySQLのウォームアップ
今回はデータベースの容量が大きいのでインスタンスを再起動した直後にそのままベンチマークをかけると Disk I/Oが刺さってスコアが出ないという問題がありました。(具体的にはウォームアップしてないと3000〜4000点ほど下がりました。)
このあたりは@kazeburo、@shmorimoの二人がうまくやってくれてインスタンス起動時にウォームアップするスクリプトを仕込むことで解決しました。
ギリギリまで細かくチューニング
あとはアプリケーション側で地道なチューニングが続いて最終的に25861点でフィニッシュしました。
やり残したこと
アクセスログやtopを眺めていてわかったのですが、今回は例年と比べてベンチマーカーのワークロードが低めで、 スコアが20000点を越えたあたりでCPU1コアあたり30〜40%ほどidleしている状態でした。
また、静的ファイルへのアクセスが少なくベンチマーカーの並列数が少なめだったらしいので前段のnginxを外してすべてのリクエストをPerlのアプリケーションサーバで受けるようにすればリバースプロキシのオーバーヘッドを回避できてもう少しスコア上がったかなと思います。