長文です。書きたいように書いているのでご容赦ください。
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>isuconお疲れさまでした会なう!
— 馬 (@Goryudyuma) 2016年10月21日
明日死ぬからね!!
…がんばろ…
@bgpat_ @fono09 @Goryudyuma チームkstm
明日よろしくお願いしまーす! pic.twitter.com/mnFHrcESs9
学生枠1位。めでたい。道義的に筆を執り何かしら記録を残すべきである。 しかし、今まで入賞とかしたのに色々書いてない。 ブログがない。 やっていたが高校より前の過去の話。今更消えている。下手すると黒歴史。 メモ帳でhtmlとか書いていた懐かしい時期の話だ。
世界公開する場合、Twitterアカウントと本名をダイレクトに繋げたくないという理由で、Facebookのエントリ公開という形にできない。そこでGistに書くこととした。それがこの文章である。
一昨年、サークルの先輩がニヤニヤしながら、何やらサーバでイメージを立ててISUCONなる大会の練習会を行った。馬くんと組んで歯向かったものの、アプリケーションに未熟で四苦八苦、特にスコアが伸びず、何も出来ない。壁の高さを知った。それがISUCONと私の出会いとなった。
そして、昨年の夏。ISUCONの講習会へ行くに至る。
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>ISUCON講習会、渋谷なのに東京まで乗るな(最大の反省)
— ふぉの (@fono09) 2015年8月19日
しかしながら、やはり経験不足。ISUCON5は苦い経験に。 Tweetがさながら即堕ち2コマの様相を呈した。
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>よろしくお願いします pic.twitter.com/268ydtDB0h
— ふぉの (@fono09) 2015年10月31日
昨年、あーちゃん、馬くんと共にISUCON5の本選へ行った。この本選ではPostgreSQLに腰を抜かしたり、実はAPIへの通信がボトルネックだったのに、 プロトコルの知識がチーム内でそこそこあり、原因に気づけるはずの私がそちらに全く介入できず、 いろいろ血迷った挙句、DBのレプリケーション設定を始め、 自分を含め学生枠出場チーム、全員Fail というなかなか香ばしい結果に終わった。
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>大コケした。死にたい。
— ふぉの (@fono09) 2015年10月31日
あーちゃんと、ICTSC5に行って散った後、半年ほど多忙を極めた後、サークルでICTSC6に押しかけ一定の雪辱を果たしたりと、相変わらずインフラをやっていた。
そして今年。あの ISUCON5から1年。個人的な小物スクリプトや、 研究室の課題(ここで使った技術が本戦で出るとは夢にも思っていなかった)以外では、 あまり本格的にWebアプリ作っていないので、今回のISUCON6もインフラ担当として参戦。
ただし、今回は本選出場してもオンプレではない。 そういう基礎的なところはかなーりオーソドックスな設定がなされていて、 変なことをしない限り壊れないし、壊れても2,3クリックで復旧できてしまう。 インフラは湯水のようにある。という状況になるわけだ。
とすると、私ができるのはカーネルチューンから上のレイヤでする仕事ひたすら、高速に。 チームメイトから要求が有れば、本人がコマンド打つより早く実行できるように。 各種パッケージ導入、環境整備、ロードバランサ、キャッシュ、シェル打ち担当になる覚悟をしていた。
起床成功しているのにも関わらず遅刻。 自己嫌悪と申し訳無さを抱えつつAzureのアカウント情報をチームメイトに投げて、雨のなか大学へ向かう。
gzip圧縮できところはやる、静的配信のリクエストがアプリケーション側からあれば、即それをnginxの設定に書く。午前中のかなり早い段階で基本的なチューニング終えた。
そうなると、バックエンドがより高い負荷を発生させ、フロントエンドからカーネルにかけての負荷を浮き彫りにしない限り何もない。
仕事がない!
午後はひたすらお茶汲み、炊事。実際、あーちゃんや馬くんのコップにお茶を注いだし お米を研いで炊いた。
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>お昼ご飯 #isucon pic.twitter.com/Q6ksjPT96g
— bgpat (@bgpat_) 2016年9月18日
予選後の空気からして、さすがに今年は無理だったかと思った。 が、学生枠で予選を突破していた。僥倖。 本選はもう少し活躍したいと思いつつ、本選に挑むことに。
ISUCON5のDockerの展開やdocker-networkの構成で、前々日の晩あーちゃんといろいろやった。 ISUCON5のあーちゃんお手製docker-compose.ymlを用いてネットワークもセットで一発展開、macvlan使ったりとか、IPアドレス自動付与とか、ゴニョゴニョしていてインフラインフラしていて楽しかった(小並感)
そして、前日。練習会しようといろいろやっていたら、 メインのサークル内のサーバーがコケた。ブートさえしない。
馬くんのノートPCで練習することに。何のための常設インフラじゃ
短い練習ながらISUCON5本戦のイメージでチームの動きを改めて洗い出せたのは大きな成果だと思う。 チームをどう動かすかという課題のために昨年からいろいろとツール(TrelloやSlack)を導入し、運用してきたのも、少なからず良い方に作用したと思う。
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>超適当なタスク管理表の様子 pic.twitter.com/H9LwKh89UN
— bgpat (@bgpat_) 2016年10月22日
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>メガネが見つからない
— ふぉの (@fono09) 2016年10月21日
起床事故ではなく、眼鏡を探していたら新幹線一本遅れた。 結局見つからず、眼鏡はバックアップの黒縁に。駅の横の道を自転車で飛ばしていたら、馬くんが乗っていると思われる新幹線が東京方へ走っていくのを見た。
人生で眼鏡を探して遅刻したのは初めてだった。 あーちゃんとホームで合流し、無事(ではないものの) 本戦そのものには遅刻せず会場に到着するに至った。
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>きっつ!!!!! #isucon
— 馬 (@Goryudyuma) 2016年10月22日
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>うそだろ…… #isucon
— ふぉの (@fono09) 2016年10月22日
本選のPVの段階、春に研究室の新人研修で作ったチャットアプリにて利用した、 Server-Sent Eventsがここで出ると知ったり、同時接続数に驚いたり。 少なくとも一気にハードルが上がったことを確信した。いままでのISUCONではない。
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>不幸にもデプロイに失敗してしまう #isucon
— ふぉの (@fono09) 2016年10月22日
そして例のデプロイで2回ほど失敗して肝を冷やした。 運が悪かったんだ。仕方あるまい。
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>なか田 おいしい…… #isucon
— ふぉの (@fono09) 2016年10月22日
デプロイが終わり大枠の方針が決まった段階でほぼお昼。 なか田おいしかったです。 旨い飯は焦る心を救います。世界を救います(飛躍)。 ツイート引用する前にお弁当思い出すぐらいには美味しかったです。
あとは、ひたすらやるべきことをやった。
API(Node)はCPUをよく食うので複数ホストに分散。ロードバランシング。 静的配信への振り分け、TLSはnginxだけに、gzip圧縮配信、HTTP/2配信に。
最後の2つは、対応しているクライアントにしか作用しないものの、スコアは上がっていった。 パケットキャプチャはしっかりしなかったので各々の詳細な効果は未検証。
また、トップページの画像列挙ページに関しては、削除はExpire任せの牧歌的なキャッシュを設置すると、Expireいくら短くしてもFailするか極端にスコアが落ちる。 そこで、キャッシュパージ(その時は知識がなくバックエンドからのリクエストによるキャッシュ削除とかヘボい言葉を発していた気がする)の実装を進言しつつ、実はLuaを書く腹積もりがあった。しかし、実装難度から諦めることとし通常の静的配信、ロードバランシングの設定に終始した。
尚、実装を進言した技術は作問者の方が言った通り、nginxのモジュールとして存在するのでお手元実装は悪手となる。 FRiCKLE/ngx_cache_purge: nginx module which adds ability to purge content from FastCGI, proxy, SCGI and uWSGI caches.
その裏であーちゃんと馬くんはアプリケーション側の改修を終えていて、 完全にスコアは19-20kで張り付いた。決定的なブレイクスルーがない。
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>辛い…… #isucon
— ふぉの (@fono09) 2016年10月22日
あとは、再起動テストで炙り出されて助かったが、設定で有り得ない凡ミスをかましていた。 このレベルのミスは只々タイムロスとなるので、チームメイトには謝ることしか無い。 あーちゃんが最後の改修として提案したDBのオンメモリ化の時間を失った気がする。
この段階で、3位ぐらいだった記憶しているが、順位がこのままというのは有り得ない。 最後に一気に浮上するチームは学生枠も含めて普通に居ると考え、受賞するとは思っていなかった。
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>submariner、浮上成功がどれぐらいでるか。 #isucon
— ふぉの (@fono09) 2016年10月22日
チームメイトがVPSの負荷ガチャしてベンチマークで辛うじて20k出してくれていたのを、 どん詰まりの数分、1回の誤クリックで無駄にしてしまったのをひどく後悔している。
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>凡ミスで胃が痛い #isucon
— ふぉの (@fono09) 2016年10月22日
そして、採点。学生1位、総合5位という結果に。
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>学生一位とりました! #ISUCON チームkstm
— 馬 (@Goryudyuma) 2016年10月22日
来年もくるぞ!
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>学生1位、全体5位!!
— bgpat (@bgpat_) 2016年10月22日
楽しいコンテストでした、運営の方々ありがとうございました。
また来年もよろしくお願いします。#isucon pic.twitter.com/eeEtig4AO8
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>やりました。#isucon pic.twitter.com/StlqO1S79b
— ふぉの (@fono09) 2016年10月22日
幸い素晴らしい結果を残せたことにほっとしつつ、このような機会(特にに今回は学生枠増枠によりギリギリ生き残った)を提供していただいたISUCONの運営様、この結果を共に導くことになったチームメイト、自分の好きなことをやり続けさせてくれる両親、学習できる環境を支えてくれる周囲の人々に感謝しつつ、筆を置くこととする。