こんにちは、開発部の高田です。
先日の 2017/10/21(土) に、ISUCON7に「D2C dot」チームとして参加してきました。
ISUCONとは Iikanjini Speed Up Contest の略で、お題として提供されたWebサービスを、レギュレーションの範囲内で出来るだけ高速化しようぜ!っていう大会です。
ISUCONは、ブログを書くまでがISUCON なので、予選体験のレポートを書きとどめます。
参加者
今回は、普段チームで働いているメンバーの「きのさん@がんばらない」と、「だらかた」さんと一緒に参加しました。
ところで今回のISUCON7では、
- 参加者:1132名
- 参加チーム数:407組
だったそうです。すごい・・・。
今回使用した言語はperlとなります。
集合
さて当日の朝、9時前に会社に到着しました。
普段こんなに早くこないのですが、電気がついてて驚きました。
・・・が、誰もいなかったので消し忘れですね(笑)
その後、メンバーも順次到着。
10時からやるぞ~って意気込んでたら(予選は10時~18時の8時間勝負)、サーバ構築の関係で12時開始となりました。
その後、色々トラブルが合ったようで、最終的に13:12開始の21:12終了となりました。
運営様、本当にお疲れ様でしたm(_ _)m
その間は、Discordで他参加者のチャットをみたり、ラジオをかけてご飯を食べながらまったりと優雅に待機です。テンションだけが上がって行きます。
コンテスト開始
開始後、まずは今回のレギュレーションと、サーバの確認です。
初参加なので前のレギュレーションはわからないのですが、今回は3台のサーバが与えられており、Web+appサーバが2台、DBサーバが1台と言う構成でした。
ただこれは別に変えてもおっけーで、今回のお題に対してどう構成していくかも腕の見せ所です。
まずは事前に用意していたメモに応じて、各種ツールを導入します。
sudo apt-get install tmux htop dstat glances unzip lv vim-nox wget https://github.com/tkuchiki/alp/releases/download/v0.3.1/alp_linux_amd64.zip unzip alp_linux_amd64.zip sudo cp alp /usr/local/bin/
alp用にnginxの設定を入れます。
log_format ltsv "time:$time_local" "\thost:$remote_addr" "\tforwardedfor:$http_x_forwarded_for" "\treq:$request" "\tstatus:$status" "\tmethod:$request_method" "\turi:$request_uri" "\tsize:$body_bytes_sent" "\treferer:$http_referer" "\tua:$http_user_agent" "\treqtime:$request_time" "\tcache:$upstream_http_x_cache" "\truntime:$upstream_http_x_runtime" "\tapptime:$upstream_response_time" "\tvhost:$host"; access_log /var/log/nginx/access.log ltsv;
あとは、access.logをベンチごとにmvし、systemctl reload nginxしつつ
alp --sum -r -f /var/log/nginx/access.log --aggregates='/keywords/.*'
でアクセスログを解析します。
(予習しておいてよかった)
この結果、明らかにアイコンファイルが遅いことが判明しました。
iconはDB管理になっていたので、これをファイルに書き出します。
my $icons = $dbh->select_all(" SELECT * FROM image "); my $dir = dir("$FindBin::RealBin")->subdir("..")->subdir("public")->subdir("icons"); $dir->mkpath; for my $row (@$icons) { say $row->{ name }; $dir->file($row->{ name })->spew($row->{ data }); }
これをnginxで配信するようにします。
ここまでで、多分1万点はoverしていたかと思います。
ボトルネック
今回のコンテストのキモはiconファイルをいかに返さないか、だったようです。
public側の回線が100Mbps(実測125Mbps?)しかなかったため、一瞬で上限に達しちゃってレスポンスが遅延→ベンチマーカーが負荷を下げる→スコアでない
という感じです。
画像ファイルについては、ベンチマーカーに対して一度取得したものを302で返すアプリケーションコードで対応しようとしたものの、そもそもベンチマーカーが条件付きアクセスを返してくれない、何故だ・・・・ となり、結局そこは解決できずでした。
キモは
Cache-Control: public, max-age=秒数
だったようですね。
nginxデフォルトだとCache-Controlヘッダを出さないため、明示的に
location /icons/ { expires 60; add_header Cache-Control "public"; }
としないといけませんでした。
ベンチマーカーはブラウザ的挙動をするのかなぁ、と思っていたのですが(つまり、Last-Modifiedやetagがあればキャッシュするだろうという想定)、どちらかというとフォワードプロキシやCDNエッジのような挙動みたいですね。
対応策
ということで、弊チームでは、仕方がないので下記のようにrate_limitを書ける作戦を取りました。
limit_req_zone $binary_remote_addr zone=limit:1m rate=20r/s; location /icons/ { limit_req zone=limit burst=15; }
リミット値はglancesで状況を見ながら調整し、最終的には上の値としました。
また、帯域が足りないなら増やせばいいじゃないか!ということで、全サーバをWebサーバ化しました。
また、これとは別に下記のような対応もとりました。
- message.channel_idにindex追加
- dbをmaster -> slave*2構成に変更し、参照系をlocal DBへ
- 一部DBアクセスをmemcachedへキャッシュ
誤算
今回大きな誤算だったのが、Devel::NYTProf によるPlack appのプロファイリング結果が、何故か行ごとに出てくれない、という事象が発生してしまい、perlアプリのプロファイルが待ったくできなかったことでした。
昨年の問題を予習した際には、こちらの方法でうまく取れたのですが、今回は肝心のWeb.pmのプロファイリングが全然とれず・・・暗中模索状態になってしまいました。
プロファイリング取らずしてボトルネックを推測するのは時間の無駄、です、が・・・。
本件は次回までの課題です。
結果
我々のチームの最高スコアは 81218, 最終スコアは 74110 となりました。

予選突破の最低ラインが 210,472 でしたので、まだまだまだまだ・・・ですね。
感想
ものすごく楽しかったです!!
当日の朝は、起きたと思ったらまだ午前2時で、そっから寝て次起きたら午前4時で、そっから寝てまた起きたら午前5時で・・・ 楽しみすぎて朝までが長かったです(笑)
結果としては本戦にいけずで、アプリのチューニングもあまりやれず、悔いは残りますが、これを糧として来年は本戦出場を目指して精進したいと思います!!!
運営の皆様、楽しい大会を開催して頂き、本当に有難うございます。
来年度も絶対参加します!