去年に引き続き、ISUCONにLINEの選抜チーム「チーム生ハム原木」で出場して優勝することが出来ました!!!!
@tagomoris、@sugyan お疲れ様でした!!
最後の最後、残り15分でnginxの設定を行う場所を間違えていたということに気付き、ローカルのベンチマークでしか検証ができず、どの程度のスコアになるのか、またfailするのか分からない状況でしたが、結果的に良いスコアになってほっとしました。
自分でも何度も言いながら「nginxのrewriteはinternal redirect」の大原則を忘れていました。はい。1日100回唱えるようにします。
予選アプリケーションの復習
劇的なスコアは出ていませんが、地道に復習をしていて、
$ ~/benchmarker bench --workload 8
07:26:29 type:info message:launch benchmarker
07:26:29 type:warning message:Result not sent to server because API key is not set
07:26:29 type:info message:init environment
07:26:44 type:info message:run benchmark workload: 8
07:27:44 type:info message:finish benchmark workload: 8
07:27:49 type:info message:check banned ips and locked users report
07:27:52 type:report count:banned ips value:1048
07:27:52 type:report count:locked users value:5478
07:27:53 type:info message:Result not sent to server because API key is not set
07:27:53 type:score success:291580 fail:0 score:62985
これくらいの数字が出るようになってました。nginx、perl、redisの構成で、CSSの出力も変えず、GOGCもデフォルトのままです。CSSと画像を消すと、23万ぐらいだったかな。
具体的に、どんなことをやったのかは別のエントリに書こうと思いますが、新しく2つのモジュールを作りました。
- Redis::Jet - 高速に動作し、Redisサーバからの返事を待たない「noreplyモード」を備えるRedisクライアント
- Plack::Handler::Chobi - nginxのupstreamのサーバとして動作するという前提で書いたアプリケーションサーバ。HTTP/1.0のみサポート、KeepAliveもサポートしませんが、クライアントのコネクションとヘッダーのパースがすべてC言語で書かれている。環境にもよるがStarletの1.5倍から2倍ぐらい性能がでる
両方とも銀の弾丸ではないですが、高RPS勝負になった時に少しでも上に行く為の武器になるんじゃないかと考えてました。
復習したrepositoryはこちら
https://github.com/kazeburo/isucon4-elimination-myhack
準備
ISUCONは準備も大切です。
去年の本選や今年の予選と同じようにgithubのprivate repositoryを用意し、そこに最初に行うべきsshの鍵の配置やgitの設定、使いそうなソフトウェアのインストール方法をまとめました。
ソフトウェアはOpenRestyやmemcached、MySQL、cpanfileに追加するモジュールのリストなど、ほとんど去年と同じ内容ですが、今年はRedisが新しく加わっていました。
本選のアーキテクチャ
動画ファイルを配信するサービスということで、とりあえず動画ファイルを全てのサーバに配るという去年と同じようなアーキテクチャをとりました。これがその時に書いたメモ。
この図では1番でアップロードを受けて、2,3に配っていましたが、実際には1番のサーバにも動画ファイルを配りました。
nginxの設定をtagomoris、動画の配布とログをRedisに書きだすアプリケーションの改修をsugyan、サーバの足回りの設定を自分が担当して作業を行い、上の構成となったのが14時ぐらい。そこで1Gbpsの壁にあたってしまい、何をしてもスコアがあがらない時間がはじまりました。
スコアが上がらない中で、tagomorisが広告取得時のredirectをなくしたり、nginxから直接動画を返したり、sugyanが動画の中で一番サイズの小さいものを返すように改修を行っていましたが、どれも1Gbpsに壁にあたっている中では効果がでずに悩んでいました。
動画を返さない方法を探る
1Gbpsの壁を突き破る為には動画ファイルを返さない方法があるはずだ、ということであれこれ開始したのが17時過ぎ。gitのcommitをみると、どうやら「チームフリー素材」さんが33万点を出す前からやっていたようですが、33万点がでたことで、必ずその方法あるだろうということでいろいろ試しました。
例えばこんなのとかやってました。
location ~ /asset$ {
# /slots/{slot:[^/]+}/ads/{id:[0-9]+}/asset
#rewrite ^/slots/(.*)/ads/([0-9]+)/asset$ /data/$1-$2.mp4;
return 206;
}
他にもetagをonにしてみたりしましたが、どれも効果がなく、straceしてもIf-None-MatchやIf-Modified-Sinceを送って来ている形跡がみつからないので、もう半分諦めムードでしたが、最後に一つnginxの設定を自分が間違えていた事に気付きました。
location ~ /asset$ {
# /slots/{slot:[^/]+}/ads/{id:[0-9]+}/asset
rewrite ^/slots/(.*)/ads/([0-9]+)/asset$ /data/$1-$2.mp4;
+ expires 24h;
}
location /me {
これ、17:44のcommitです。はい、すみません、死にます。大変申し訳ございません。
「nginxのrewriteはinternal redirect」「nginxのrewriteはinternal redirect」「nginxのrewriteはinternal redirect」「nginxのrewriteはinternal redirect」もう皆さん分かりますよね。正解はこう書かなければなりません。
location /data {
root /data;
client_body_temp_path /data/client_temp;
dav_methods PUT DELETE MKCOL COPY MOVE;
create_full_put_path on;
dav_access all:rw;
expires 24h;
}
location ~ /asset$ {
# /slots/{slot:[^/]+}/ads/{id:[0-9]+}/asset
rewrite ^/slots/(.*)/ads/([0-9]+)/asset$ /data/$1-$2.mp4;
}
このcommitが18:43。いやぁ、厳しかった。。。
あれこれ
最終的に何が起きたか、確認はしてないのですが、たぶんこういう事でしょう
- expiers 24hがついたことで、レスポンスにCache-Contol: max-age=86400、Expiresヘッダが付くようになった
- ベンチマークツールがそれをみて、初めてIf-Modified-Sinceを送るようになった
長年画像配信野郎をやってますが、正直「おや。。」という感想をもちます。通常であれば
- Last-ModifiedヘッダがあればブラウザやCDN、Proxyサーバはコンテンツをとりあえずキャッシュする
- ソフトウェアによって異なるが、ブラウザやCDNは任意のタイミングでサーバにIf-Modified-Since付きでリクエストを送る。サーバはコンテンツが更新されてなければ304を返す事ができる
- サーバ側がCache-ControlやExpiresヘッダを返した場合は、そのヘッダで定められた期限までブラウザ、CDN、Proxyはキャッシュを行い、サーバには一切アクセスしない。 — ただし、CDNやProxyの設定で最大の有効期限が決まっていることもあるので、その場合はCache-Controlヘッダで決めた期限より速くアクセスがくることがある — 期限がすぎてアクセスした場合にIf-Modified-Sinceヘッダが付く(と思う)
もし、ベンチマークツールがLast-Modifiedを検出するような仕組みになっていれば、うちのチームでは14:26の時点で304が返せるようになって、もっとアプリケーションのチューニングに集中できていたのかなと思う。その上でCache-Controlを指定することでアクセスすら来なくなってさらに高得点がでるとかあったかもしれない。
んで、どうしたらこのあたりの動作が分かりやすくなるか、一つの案としてはベンチマークがLast-Modifiedをみて、If-Modified-Sinceを送り、さらに「Cache-Control: max-age=0」を送っていれば分かりやすいかなと思いました。これはブラウザが「リロード」を繰り返している状態、またはCDNでcacheの有効性を毎回確認する機能を使っている状態と同じです。
これだと、動画をファイルに落としてnginxから配信したチームから点数をかなり上げて、高RPS勝負になりやすかったのかなと思います。 ただ、簡単にそうはさせない罠を用意する必要があったかもしれませんね。そのネタはあります。
最後に
問題出題のクックパッドの@mirakuiさん、@rosylillyさん、@sora_hくん、大変おつかれさまでした。ぎりぎりでした!!wishlist公開お願いします!
テコラスの皆様、941さん、やぶたさんをはじめ運営スタッフのみなさまありがとうございました!あと、途中で応援に来てくれた奥さんと、息子と娘にも感謝!
最後に 2
今回、選抜チームをのぞき、弊社から本選に残ったチームがいなかった。これはどうにかしたい
最後に 3
頂いたドローン、家の中で動かしたら音が大きかったのか娘が号泣しました。次は外でやりたい