Write and Run

it's a simple way, but the only way.

非公式ISUCON練習プラットフォームISUNARABEを公開した

あなたの予想に反して、この記事の大半はタイトルとおおよそ関係のない感想文である。感情の発露に任せて書いているのであって、読ませるために書いているわけではないため、読みづらくても耐えて欲しい。

表題のとおり、非公式 ISUCON 練習プラットフォーム「ISUNARABE」をリリースした。
これは、ISUCON の素振り*1をより簡単にすることを目指して開発されたシステムで、近年の ISUCON 予選のポータルと似たような機能を持っている。
具体的には、過去問環境の CloudFormation テンプレートの自動生成・チーム編成・ベンチマーク実行・ベンチマーク結果確認などである。自動生成された CloudFormation テンプレートでスタックを作成すると、GitHub から取得したチームメイトの公開鍵が焼かれた状態のインスタンスが上がってくる、というあたりが重要なポイントだと個人的には思っている。公開鍵をチームメイトから収集して数台のサーバーに撒くというのは結構ダルい作業である。

こだわりポイントという意味だと、ベンチマークの実行と結果確認の UX にはちょっとしたこだわりがある。
私はかなりせっかちな性格である。となりに階段があるなら、エレベーターを待つよりも階段を上ることを選ぶ。結果的にエレベーターの方が早かったとしても、何も手を下せずボタンを押して待つことしかできないという時間が嫌いなのだ。鉄の箱が扉の前に到着するまでの間、人間が自由を奪われているのだ。許せるわけがない。
したがって、ベンチマーカーの実行は即時に開始され、ベンチマーカーのログや結果は「直ちに、遅滞なく」UI に反映されなければならない。
Pull 型でポーリングをするアーキテクチャは負荷分散という点において優れているのは知っている。それが理由かはわからないが、AWS のサービスは極端にストリーミング API を避けている。なんでもかんでもポーリングである。まぁこれは課金単位のわかりやすさのためもあるかもしれない。すべて邪推である。 さて、本物の ISUCON ではベンチマーカーは共有資源である。他チームの利用で埋まっている場合には即座に実行開始できない。これは仕方がない。また参加するチームの数は数百のオーダーであり、競技の参加者は千を超える。さらには、100万円という賞金(よりもプライド)が懸かった真剣勝負であるため、競技時間中のシステムダウンは可能な限り避けたい。つまるところ、ベンチマーク実行リクエストはキューイングされるし、ベンチマーカーのログを厳密にストリーミングするのは難しい。
しかしながら、ISUNARABE は練習プラットフォームであり、安定性よりも快適性が優先される。なぜなら、快適でなければ人はどんどん素振りをしなくなるからである。したがって、ベンチマークの実行リクエストは即座に受理され、ログは完全にストリーミングされる。ベンチマーカーは CloudFormation テンプレートに含まれており、チームに1台ずつ用意される。共有リソースではないのでキューイングは不要である。

この体験を実現するアーキテクチャは、意外にも自明ではない。最もナイーブには、各ユーザーのベンチマーカーが HTTP API を露出させ、ポータルのフロントエンドがその API を呼び出す方法である。ストリーミングが必要なため、これは WebSocket になるかもしれない。
残念ながらこのアイデアはいくらかの問題がある。どうやってベンチマーカーの Public IP を発見するのか、HTTPS で通信するなら証明書はどのように発行するのか、そもそもベンチマーカーのポートを開放して安全なのか、認証はどうするのか、等である。
そこで私は Reverse HTTP Transport のようなプロキシがあったらよいのではないかと考えた。私が管理するサーバーにプロキシを立てて、プロキシとベンチマーカーの間にトンネルを掘り、ユーザーはそのプロキシ経由でベンチマーカーにリクエストを送るという方針である。
この場合、プロキシで認証と認可を行うチャンスがあり、またベンチマーカー側からプロキシ側に向けてセッションを開始するようにすればポート開放も要らず、サービス検出も達成できる。
Reverse HTTP Transport を真面目に実装してもよかったのだが、証明書の管理が面倒ということと、私のサーバーは Cloudflare の裏側にあり TLS が貫通しない(クライアント証明書による認証ができない)ということから、アイデアだけを拝借して独自のプロトコルを作ることにした。

独自に開発したトンネリングプロトコルの基本的なアイデアは以下の通りである。

  • HTTP/1.1 (HTTPS)の Connection: Upgrade を用いて、ベンチマーカーからプロキシに向かってコネクションを張る
  • HTTP/1.1 なので、Authorization ヘッダに含めたトークンを用いて認証・認可をする
  • 開いたコネクションの上には HTTP/2 のリクエストを流す
    • リクエストの向きは プロキシ→ベンチマーカー

ポイントは、生の TCP ではなく HTTP/1.1 を用いることで、認証情報を乗せるチャンスを得ることと、その上のプロトコルを HTTP/2 とすることで、1本のコネクション上に複数のリクエストを多重化することである。
ここまでくればあとは gRPC のリクエストをこのトンネルの中に通すだけである。gRPC の Server Streaming を使えばログをストリームするくらいはなんの工夫もなく達成できる。

かくして私が納得できる品質の ISUNARABE はリリースにこぎ着けたわけである。趣味で作ったウェブサービスを一般公開するという行為は大変久しぶりであり、ヘタすると10年ぶりとかそういうレベルである。

サービスをひとりで作り、またそれを公開するというのは私にとって特別な意味がある。これは、ワールドワイドウェブが、なんらかの力に独占されているわけではないという事実を噛みしめ、技術者ひとりの持つ力の潜在的な大きさを再確認するための行いである。メモ帳で <CENTER> を書き、DynDNS で半固定 IP に無料のドメイン名を割り当てていた頃と、ウェブは本質的にはなんにも変わっちゃいないんだと感じることで、ある種の全能感を得られる。

ウェブサービスを作ろうとすると、あらゆる難しいことが襲いかかってくる。AWS やら Docker やらといった技術的なこともそうであるが、法律的な部分や、炎上したらどうするのか、攻撃されたらどうするのかといった本当に考えたくないことが多すぎる。法律やセキュリティの知識が不要であるとは言わないが、無知が責められる光景を日常的に目の当たりにしすぎているが故に、過度に萎縮しているのではないかと感じることがある。

個人開発者にはビッグテックのような優秀な法務部もなければ、インシデントレスポンスチームもない。SNS で盛り上がってしまっているユーザーとうまくコミュニケーションをとってくれるカスタマーサポートもいない。でもそれらがなければウェブサービスを公開してはならないのかといえば、そうではないと思う。

私はウェブがこれほど堅苦しくなる以前にだいぶ自由にやらせてもらった側であり、それによって今のキャリアがある。言ってしまえばイタズラを繰り返していたらいつのまにか職をもらえていたという状況である。自身がイタズラ坊主出身であるからこそ、スーツの奴らが嫌いである。結局ギークとスーツという古典的な二項対立かよと思われるかもしれないが、そうではない。ギーク出身であっても、若いギークが生まれる土壌を維持しないやつは全員ダサいスーツであるということだ。大人の事情とかはどうでもいいのである。イタズラ坊主のイタズラを邪魔するスーツだけは絶対に許せないのだ。

この文章にオチはないので、必然的に綺麗にはまとまらないのだが、ひとりでサービスを開発して公開するという行いは、ウェブが未だにイタズラ可能な土地であるかを確かめることであり、ウェブがイタズラ可能でなくなりそうなときは立ち上がらねばならない。さもなくばスーツである。

*1:ISUCON の練習のことをこう呼ぶ