ISUCON7予選に出場してきました(インフラ編)

こんにちは。サーバエンジニアの@toritori0318です。
先日の10/21、弊社 @wanko / @ingtk120 と共に チームカリスマ としisucon7に出場しました。

結果は残念ながら
score:111899
にて予選敗退…
全体通して26位という結果でしたので上位には食い込めたのですが、あと一歩届かず…! 1

アプリ視点では既に別記事が公開されていますので、
自分からはインフラ視点でなにをやったかを振り返ってみようと思います。
なお上記記事と重複する箇所がいくつかありますがご了承頂ければと思います。


事前準備

練習環境としてAWS上に過去isuconのアプリ/ベンチマーク環境を構築しメンバーに提供しました。
また前週末にpixiv-private-isuconを用い、本番想定をしたリハーサル(10:00-18:00)を行いました。
メンバー間の役割や手順を確認しながら
「どのようにボトルネックに向き合っていくか」
を実践さながらにシミュレーションできたのはチームの底上げとしてはかなり効果があったと思っています。
pixivさんありがとうございます!


予選本番日開始前

開始時間が遅れるというアクシデントがありつつも、
空いた時間で methaneさんのisucon攻略をみんなで眺めたり基本リラックスモード。

また今回事前のレギュレーションから複数サーバでくるのはある程度予想がついたので
Nginxが必ずキーポイントになるであろう
と想定し 実践Nginx入門 を最終確認として読みふけっておりました。 2


予選: 開始序盤

開始直後にsshログインできないというアクシデントが発生していたため、その間みんなでレギュレーションを熟読。/fetchが得点にならないこと、/messagesのメッセージ数が得点になること、304はあまり得点にならないことなどを確認。

ログインしたあとは自分が各サーバのコア数/メモリ/MySQLのスキーマ毎のデータサイズなど一通り確認。 全てのサーバで CPU 1コア メモリ1G。DBサイズもそんなに大きくないのである程度メモリにはのりそうだし最終的には画像配信周りをどうにかするかんじだろなーという予想。

その間Appの2人には同時並行でアプリ周りの確認をしてもらっていて
「画像そのままDBに入れてて無いわー」
「インデックス貼ってないわー」
など明らかに駄目なとこは最優先に改善依頼。

自分は同時に

  • サーバ鍵を全サーバに配ってprivate githubにpush
  • 便利スクリプト配置
  • 各サーバにgoバイナリ配るスクリプト書く
  • alias配る
  • ミドルウェア/パフォーマンスツール周りセットアップ

と足回り整えたあと、一旦みんなで全体構成の整理。

この時点ではオーソドックスにapp x 2とdb x 1という構成で行くことに決定。
また進め方として
「まずはapp1台+db1台でボトルネック潰しつつキリキリにチューニングして行きましょう」
という方針を決定しました。
Appのボトルネック潰したらあとは複数台で適当にキャッシュかまして画像配信すればコンテンツ配信は問題ないだろーと考えてましたが、この考えがそもそもダメだったことに競技終了後気付くことに…。

実はそのとき @wanko
「/iconsをnginxで1台に集約できたりします?」
と依頼されてて
「もちろん出来るのですがもろもろ対応後に手が空いたらやりますねー」
という感じで後回しにしたまま結局最後まで着手できませんでした。
ただこれ今となって考えるとおそらく最終スコアに影響ありそうなとこだったんですよね…
(この時点では当然気づかず)

そして改善着手へ。

  • ベンチマーク回す
  • プロファイリング
  • アプリ側/ミドルウェア側の改善

のサイクルを繰り返しある程度基本的な改善が進んだ段階でdstat眺めていると明らかに帯域がサチってる感じになっていました。
※ちなみにこのくらいのときに一度暫定1位になってました (@wankoが記念スクショ取っていたw

んじゃーこの辺りでNginxにgzipとかexpire入れとくかー、ってなって入れたんですが、多少スコアが上がるもののベンチマークからは「静的コンテンツおせーよ」的な感じで怒られ続けててなんでー?ってなってました(おそらく他チームもみんな通った道)
どうやらicons周りのキャッシュが効いておらずぐぬぬ。

ちなみにこのあたりまではリハーサルの効果もあって割とチームの思惑通りに進んでたのと
それに合わせてスコアもグングンあがってたのでみんな「たーのしー!」と盛り上がってました :)


予選: 中盤

開始3時間が過ぎたあたりからスコアがなかなか上がらず停滞期。アプリやインフラいろいろ弄ったりするもベンチ通らなかったり逆に下がったりと苦しい時間帯…
自分の方はというと相変わらず帯域がなかなか下がらないのと格闘していました。たぶんここ突き抜けないと上位に行けない感があったのでだいぶプレッシャー感じてました。

その間は同時にアプリのUnix Domain Socket化やってもらってたんですが、これをNginxでサーブした時にとあるリクエストで必ずNginxがおちるという現象に悩まされ、最終的には結局TCP versionに戻しました。
でTCPに戻した段階でTCP TIME WAITがゴッソリ現れるいつものやつが出てきたので
「はっはっはっ、またお前か。こんなんすぐ直るわー」
とタカをくくってアプリにconnectionヘッダ付けてもらってnginxでhttp1.1でプロキシするようにしてすぐ直る…はずだったのですが
なぜかTIME WAIT消えず「なんで?」とヘッダーとかsysctl周りいろいろ弄ってなんとか思い通りに動作するようになりました :(
他にもこのあたりの時間帯、よくわからない現象でOpenRestyが動作しなかったりと、おそらく1時間以上無駄な作業で大きなタイムロスをしてしまいました。ここはだいぶ痛かった。


予選: 終盤

60000超え程度で停滞していた頃、上位チームは10万超えもゾロゾロ出てきていてさすがに焦りモードに。
自分はその間も上位チームのスコア的に「Nginxのキャッシュ制御でどうにかなるはず!」と信じていろいろ設定を試してはベンチマークをする、ということを繰り返していました 4

そしてついにCache-Control: publicにたどり着き11万超え!この時点でtop8に食い込めました。

実はこの時点で

[app1(webapp)] - [app2(未使用)]  - [app3(mysql / redis)]  

という構成で、app2が遊んでいる状態だったので
「これでapp1とapp2をwebappにしてベンチかければ倍push行けるのでは!!!???」
とメンバーの期待も高まりました。
しかし、2台にベンチかけると なぜかスコアが下がる という結果に… 😵

うーん、理由はわからないけど他のボトルネックにぶつかったんだろうか?とパニクってすぐに諦めてしまいました。5

ここでの判断は、残り時間も少ないしリスクの少ない

[app1(webapp)] - [app2(redis)] - [app3(mysql)]  

という構成にしよう、と決定しました。

このあとはアプリ側の微調整やNginxの微調整を繰り返し、結局スコアはこれ以上延びず頭打ちのまま競技終了〜

1日目の中ではTop8くらいだったし、これはまだワンチャンあるか?と希望を持っていましたが
2日目が強すぎてぬか喜びでした ;q


足りなかったことについて考えてみた

  1. チームとしての総合力
  2. 判断の迅速さ
  3. 攻めの足りなさ

1.に関しては、特に今回は総合的な理解力が求められる問題だったと思います。
今回のチームカリスマの場合、メンバー全員が業務的にメインがAPIサーバ担当のため、サーバアプリケーションの改善については全く心配しておりませんでした。
ですが全員静的コンテンツ配信は得意分野ではなく、チームとしての得意分野が偏っていたのは否めなかったと思っています。
過去に ハイパフォーマンスWeb は読んでいるのでキャッシュ周りも目を通してはいるはずなのですが publicやif-modified-sinceの挙動にすぐ気付くことができませんでした。
この辺りもう少し自分が補填出来ればよかったのですがここは完全に自分の力不足でした。大変申し訳ございません。。今じっくり本を読み直しております。
また、こういった時にチーム内の複数メンバーがインフラ/アプリ含めてカバーできるチームが本戦に出場できるチームなのではないかと思います。

2.に関しては自分も含めメンバーもハマってウンウン唸る時間帯が多かったので、5分くらいやって解決しなければ捨てて戻して別のことをやる、くらいのスピード感があってもよかったかなとおもいました。

3.に関しては最後の11万超え辺りの判断です。
あとから考えると 何故すぐに複数コンテンツ配信を諦めちゃったんだろうか? と思い返してみました。

おそらくこの時点でtop10圏内にいたし残り時間も多くはないし、よくわからない原因でスコア落ちちゃったし、ここは攻めずにリスクを取らない方向に行こう、と心理的に考えてしまったような気がします。

今考えるとベストスコアって結果的に サーバ2台のみでのスコア なんですよね。
Redisを別サーバに置いても全くスコアアップしなかったのは明白ですし、この時点ですぐにサーバ増やしてもスコアが上がらない原因を探るべきでした。
落ち着いてNginxのログを確認すれば原因に気づけたはずです。
ここに関してはあとからだいぶ後悔しました…
ただ、実際あの状況で落ち着いて対応できたかというと自信がないですね。isucon難しい。


謝辞

仕事が忙しい中isucon練習の許可を頂いたHAROiD社、 @muddydixon ありがとうございました。
またこのような大規模なインフラ環境を構築していただいたさくらインターネット様ありがとうございました。本当この規模のプロビジョニングは大変だったと思います。
また素晴らしい予選問題を作成/運用していただいたisucon運営のみなさまありがとうございました。

まとめ

足りなかったことなどつらつら書いたのですが、チームとしてはやれることはやれましたし、最終的に上位に食い込めたのもこのメンバーあってのものでした。 @wanko / @ingtk120 ありがとうございました!

総じてとっっっても楽しかったのですが、とっっっても悔しいので来年も開催されることがありましたら絶対に5本の指(カリスマ)に入って本戦行けるように今から精進します!!!


お約束

HAROiDではTV、あるいはTVとWebを使った何かを支えるエンジニアを募集中です。
領域的にはなかなか触れることの出来ないところをやれますし、 とにかく面白いことしたいエンジニアがいらっしゃいましたら雑談レベルでも良いのでお話しましょう!
よろしくお願い致します!

HAROiD - Wantedly


  1. 1日目組の中では上位8チーム内なのに…2日目組強すぎ!

  2. 想定はある程度当たっていたのですが、問題は遥かその先を行っていたという…

  3. これ結局原因わからず。あとで同じ環境で検証したい

  4. ちなみにこの時、app1をgoapp専用に、app2をnginx専用にしてコンフリクトしないようにしていました

  5. ここに関しては複数サーバで違うif-modified-sinceを返してしまっていたことが原因かと思われます。終わってから気づいた…