なまえは まだ ない

思いついたことをアウトプットします

ISUCON14延長戦の記録① 競技環境の構築

2024.01.10 追記

予定していた連載(?)が終わったので、元記事であるここにインデックスを作成しておくことにします。②以降の記事は以下のリンクから飛ぶことができます。

ISUCON14について

先日の記事で報告したとおり、12月に開催されたISUCON14に出場しました。 大変好評だったのか、本戦を終えた後も感想戦モードと称して再挑戦ができるようになっています。環境維持もタダではないでしょうに、運営の皆さまには感謝しかありません!

isucon.net

先日の結果がとても悔しかったので、また一から問題を解いてみてどこまで点を伸ばせるか挑戦してみました。延長戦だ!目指すは本戦の優勝スコアである58,153点!

延長戦のルール

  • 環境やルールは基本的にISUCON本戦のレギュレーションに準拠
  • ただし解説と講評は読んでOK

とりあえず当時の状況を再現しつつ進めてみます。が、私個人の技量・発想には限界があるので、作問者の解説を読んでヒントを得ていこうと思います。

なお、実際に改善したアプリケーションコードは以下のリポジトリで公開しています。

github.com

競技用の環境を整える

monitorサーバーのセットアップ

先日の記事でも書いたように、競技環境のモニタリングにpproteinを導入しました。これが大変便利だったので、簡単にセットアップするためのリポジトリを作りました。

github.com

今後の競技でも利用できるよう、テンプレートリポジトリ設定してあります。

この環境とISUOCNの競技環境をそれぞれデプロイし、monitorサーバーをセットアップしておきます。 その後、monitorサーバーから競技用サーバーに対してSSHポートフォワーディングを確立します。pproteinは双方向にHTTP通信をするので、双方向でトンネルできるよう次のように実行します。

ssh -L 19001:localhost:19000 -R 18080:localhost:80 isucon1

isucon1 はその名前まんまですが競技用サーバーの1台目のエイリアスです。次のような設定をmonitorサーバーの ~/.ssh/cofig に記述しておきます。

host isucon1
    HostName <競技用サーバーのグローバルIPアドレス>
    User isucon
    IdentityFile ~/.ssh/id_ed25519
    ServerAliveInterval 60

競技用サーバーはpproteinエージェントを19000ポートで起動します。 これをmonitorサーバーの19001ポートとリンクさせ、pproteinサーバーには localhost:19001 で各種ログを収集するように設定します。

また、pproteinサーバーには収集を開始するためのWebhookが存在します。 そのWebhookを競技用サーバーからリクエストするために、競技用サーバーの18080ポートをmonitorサーバーの80ポートにリンクしておきます。

複数台に展開したときはtmuxで複数セッションを作成して次のように設定します。ホスト毎にローカルポートが被らないようにだけ注意が必要です。 ただし、今回は結果的に3台目は使わずに終わってしまいました。

ssh -L 19002:localhost:19000 -R 18080:localhost:80 isucon2
ssh -L 19003:localhost:19000 -R 18080:localhost:80 isucon3

競技用サーバーのセットアップ

競技用サーバーにはpproteinエージェントでログ収集するため、Nginxアクセスログのフォーマット、MySQLのスロークエリログ設定を入れておきます。先に紹介したレシピに競技用サーバー用のレシピも用意してあるので、それを実行すればOKです。

git clone git@github.com:furusax0621/template-isucon-recipe.git ~/recipe
cd ./recipe
./install-app.sh

アプリケーションコードへの仕込み

pproteinでログ収集するため、アプリケーションコードに仕込みをしていきます。 完全な差分は以下のPull Requestを参照してください。

github.com

やることは2つです。

まず、pproteinエージェントをアプリケーションコード内で稼働させます。アプリケーションのサーバーが稼働するタイミングで次のコードを入れます。ゴルーチンで実行する必要があるので、その点に注意しましょう。

go standalone.Integrate(":19000")

先に述べたとおり、この19000ポートに対してmonitorサーバーからリクエストが飛んできます。

また、先述のWebhookにリクエストするコードを仕込みます。 Initializeエンドポイントの最後に入れておくことで、ベンチマーカーが動くのを契機にpproteinによる収集を開始できます。

if _, err := http.Get("http://localhost:18080/api/group/collect"); err != nil {
    writeError(w, http.StatusInternalServerError, err)
    return
}

まとめ・次回予告

というわけで競技を始めるための環境が整いました。 この状態でベンチマーカーを回して情報を収集し、そこからようやくチューニングの旅が始まります。つづく。