読者です 読者をやめる 読者になる 読者になる

ISUCON6が終わったので出題した感想ポエム #isucon

ポエム 技術系

(注:このエントリーはポエムです。個人的な感想なのと、技術的な詳細は別の機会に詳しく書くと思います。多分)

ISUCON6が終わったので、自分がどう関わったのかと、その感想を書いてみたいと思います。あくまで感想なので技術的詳細はまた別の機会にしたいと思います。

社内ISUCON

自分の戦いは社内ISUCONの準備からでした。

github.com

catatsuy.hateblo.jp

inside.pixiv.net

社内ISUCONを作る際は、全くISUCONの問題の作り方が全く分からなかったので、過去のISUCONの予選問題(特にISUCON5とISUCON4)をかなり調べて問題を考えました。

ISUCONの問題作成に関して、過去に自分がやったことあるものしか問題にできないと聞いたのですが、本当にそうだと思います。社内ISUCONについて自分がテーマにしたのは何点かありますが、大きく以下のものでした。

  • MySQLに画像データをblobで入れたアプリケーションでの画像配信
  • 大量に存在するN+1クエリ
    • 時間内で全部解消するのは困難なので、優先度を見極める力を問われる
  • オンメモリに乗り切らないMySQLの扱い

どれくらいの人が気付いたのか分かりませんが、着想は自分が担当している社内広告管理サーバーから得ました。過去のISUCON予選を強く意識しているのでISUCONっぽさというのを感じられる問題になっていると思います。

公開した後に広く使われたようで、思惑通りに進んで良かったです。

inside.pixiv.net

社内ISUCONを公開した意図は広く使って欲しいという思いがあったからですが、実は他の思惑もありました。社内ISUCONを作っている時には既に、ISUCON本選問題のテーマや問題の方向性を @edvakf さんが中心に決めている最中でした。そこでその時に出たテーマとは全く関係のない問題を自分が作ることで、社内ISUCONからISUCON6本選問題の傾向を予測してくる人の期待を完全に裏切りたい、というものがありました。実際にISUCON6本選の懇親会で聞いたところ、一部の人から期待通りの反応を得られたので、とても満足度が高かったです。自分が中心に作った社内ISUCONとは違い、今回の本選問題は @edvakf さんが中心に作ったので似ているわけがなかった、というオチです。

ISUCONのベンチマーカーも全く作り方が分からなかったので、最初はかなり迷走していたのですが、他のメンバーの協力もあって、今後のISUCONにも流用できそうなものができました。実際にISUCON6予選では当然改造はされていますが、多くの部分を流用していました。ISUCON6本選では流用はしなかったものの、設計方針は踏襲して作られました。Go言語でリファクタリングも容易なので、適当に参考にしながらベンチマーカーを作ることは普通にできると思います。ベンチマーカーは今後も流用されることを考慮して、社内ISUCONだからと言って手を抜かずにしっかり作りきったことで、実際のISUCONに向けて何をすればいいのかだいぶ見えたので良かったと思います。

ベンチマーカーはアプリケーションが返すレスポンスをアプリケーションよりも速く捌かなければ、アプリケーションに対して負荷をかけきれません。それでいてベンチマーカーはレスポンスの正当性も確認する必要があります。しかも遅い初期実装でも、実装が進んだ高速な実装でも、正当なスコアを出す必要があります。Go言語だと標準の net/http パッケージにある http.Client がかなり高機能で、ブラウザの動きを再現しつつ、ちゃんとパフォーマンスを出す、ということがやりやすい言語です。非常にISUCON向けの言語だと思います。

社内ISUCONを振り返ってみると良かった点ばかりではなく 、反省すべき点も多くありました。

1つは参考実装を最初にRubyで作ったのですが、そのときに自分があまり他の言語への移植を考えずに書いたので、自分が行ったGo言語への移植で異常に苦労したということです。RubyはHashで適当なキーに好きな値を入れるという使い方をよくしますが、Go言語では構造体に定義されていないキーは使えません(DBから読み込んだデータのマッピングにはmap[string]stringみたいなのものは通常使わないし、決まった型しか入らないので不便)。Ruby実装であまりにもその辺りを自由に書きすぎて、Goに移植するときに悩みました。

またRuby実装ではテンプレートエンジンとしてERBを使用しました。ERBはテンプレートエンジンではありますが、中で普通にRubyのコードを書ける上に、includeで簡単に他のテンプレートを読み込めます。とても簡単にさまざまな機能を使うことができるので、普通に使う分にはいいのですが、他言語に移植するのは困難なコードになりました。特にテンプレートエンジンがあまり便利ではないGo言語ではどうすれば、このRuby実装を忠実に移植できるのか、かなり悩みました。Go実装を読むとテンプレート周りが異常なコードになっている事に気付くと思いますが、これはRuby実装を読んでもらえれば、なんでこのような実装になったのかが分かると思います。

実はこの時のテンプレートの移植への苦労が、ISUCON6本選のアプリケーションはJSON APIのみにした理由の1つでした。最大の理由は2016年のWebアプリケーションはHTMLを返さないだろう、という理由からですが。

ISUCON6予選

予選に関してははてな@songmu さんが中心に作っていたので、自分がやったことは事前回答と当日のサポートを行ったという感じでした。予選が始まる前も本選問題を作っていたのですが、AzureにAMIのような作成したイメージを色んな人に共有する仕組みがない、ということに本選問題を作る上でも悩まされていたので、予選でどう切り抜けるのか、とても心配でした。結局 @y_uuk1 さんが、tagomoris/xbuildでビルド済みのものをAzure Storage(S3的なものです)に入れておいて、セットアップ時はダウンロードをして配置するだけにしてコンパイル時間を削る、という荒技を使うなどして回避していたのが、自分もまさに悩んでいたので印象に残っています。

またAzureに関して誰よりも先回りして情報を発信し続けていた @matsuu さんには本当に助けられました。予選の後に、本選の名誉運営として招こうという話になり、運営に入ってもらえたのですが、Azureの使い方で悩んで数日潰す、というようなことを繰り返していた自分にとっては救世主でした。matsuuさん抜きではここまでスムーズに本選問題を作成できませんでした。

ISUCON6本選

本選では多くの部分を担当しました。以下はその中でも大きなものです。

  • Go言語の参考実装の実装
  • ベンチマーカーの一部機能実装と技術検証
  • 競技者のインスタンスのセットアップ
  • ポータル・ベンチマーカーのセットアップとクラスタ群の構築(後述)
  • レギュレーションの記述
    • 前日に寝ぼけながら書いたので、ミスが多くて大変申し訳ございませんでした…

これらについて、技術的な詳しい話は長くなるのでまたの機会にします。

本選に関して、edvakfさんから以下の提案がありました。

これに応えるためにサーバーのセットアップ手法や技術検証などを、matsuuさんに協力してもらいながら、構築していきました。個人的に面白いと思っていることは、ベンチマーカーが6万コネクションの壁を越えるために、ポータルとベンチマーカーの他にproxyというクラスタ群を作って、proxyの設定更新のためにconsulを使ってクラスタ群を組んだところです。

最初のプレゼンで10万コネクションと発表したときにざわっとなった時に「この反応が欲しかった!」と心の底から思いました。これについては近いうちにどこかで書きたいと思っています。

また競技者のアプリケーションはhttpsで提供するようにして、http2も解釈できるようにしようということになったので技術検証を行いました。するとGo言語の http.Client には以下の問題があることが判明しました。

  • HTTP2のコネクションは違うhttp.Client{}間でも共有されるので、http2にした瞬間にコネクションが激減してしまう
    • 秘孔になってしまう可能性がある
  • 証明書のエラーを無視するようにするとHTTP2が使えない

これはGo言語がおかしいのではなく、完全に自分がやろうとしていることがおかしいのですが、今回のISUCONはオレオレ証明書を使う(ベンチマーカーに証明書をインストールして正当な証明書にする案などもありましたが、開発のしにくさや、公開した後のことを考え、却下しました)のと、問題の性質の関係上、この問題を突破する必要がありました。

色々調べた結果、net/httpの実装をいじるしかない事が判明して、net/httpの実装をコピーして一部を書き換えることで対応しました。標準パッケージのソースコードをコピーしてパッチを当てることに関しては賛否があると思いますが、私の観測範囲ではそんなに珍しくない印象です。具体例を挙げると puma/puma-dev などがあります。

ただnet/httpClientのHTTP2実装には2016/10/23現在のGoの最新バージョンの1.7.3で解決されたバグがありました。これを知って直前に慌ててバージョンアップをしたので、コピーするのはおすすめできないのは間違いないと思います。

github.com

そして今までISUCONのポータルはhttpで提供していましたが、今回の問題の性質上、httpsで提供しないのはないだろうということでhttpsで提供しました。実はポータルサイトはhttp2に対応していて、問題のヒントになっていました。httpsのために今回初めてLet's Encryptを使ったのですが、あまりにも一瞬でhttps化ができて驚きました。みんなも使うといいと思います。

またISUCON6本選前日にDDoS攻撃があり、GitHubが使えなくなったのは本当につらかったです。しかもGitHubが落ちるとPHPやGoなどの参考実装はそもそもセットアップができないので、ISUCON開催も不可能になる可能性がありました。もしDNSのみの障害ならば、競技者のインスタンスセットアップ時にGitHubのIPを書いた/etc/hostsをデプロイしようと真剣に考えていました。結局やらなかったですが、ISUCONの主催となると心配事が多かったです。

というわけで、他にも話したいことがあるのですが、長くなるのでまた別の機会にします。ISUCON6に参加した皆様は本当にありがとうございました。来年もしISUCONがあれば、絶対に出場して優勝したいと思っているので来年もISUCONがあって欲しいな、と願っています。

ISUCONについては問題を公開するなどなど、まだ作業がいくつか残っています。これからも作業をしていくのでよろしくお願いします。