ISUCON 12の予選に参加したよ!

はじめに

ブログではお久しぶりです。@kakira9618 です。前の記事が約7年前ですが、Twitterでは元気に飯テロしてます。

さて、今回、ISUCON 12 の予選にチーム kakipippi として参加してきました。
結果は、、、スコア0(泣)でしたが、色々収穫があったので忘れる前に記事にします。

超長い(19000文字以上あります)ので注意。

目次

メンバーについて

Twitterのフォローワーの @tran0826 (かいと; 敬称略) と @ui_mtc (スギノキ; 敬称略) と一緒にISUCONに参加してきました。 全員Web系の会社勤務で、私以外のふたりは初めての参加です。また、みんな競プロ経験勢(特に、@tran0826 は橙コーダー)です。

私は今まで4回ほどISUCONに参加してきましたが、サークルの先輩と一緒にチームを組むことが多く、おんぶにだっこという形で参加させていただいていました。 しかし、今回は自分が一番先輩ということで、おんぶにだっこではマズい、引っ張っていく側にならなきゃという意識で臨みました。

Web・インフラの経験について

私のWebの経験は、小学生くらいのときに自鯖(ftp, http, ssh)建てたりCGIを改造してみたり〜から始まって、はてなインターンperl/Swiftを使った(当時の)モダンな開発を経験したり、研究室のサーバーを管理したり、会社の研修で、ミニアプリ(AtCoder Replay)などを作ったりしてた、という感じでした。業務(4年目)ではセキュリティということで、開発のレイヤーをガッツリと触ることは少なかったですが、AWSGCPを使う案件も多く、それなりにクラウド技術などは触っていたりしました。プログラミング経験全体からみるとWeb専門、というわけでは無いです(ゲームのほうが長いです)が、一応ISUCONが改善の対象とするレイヤーの経験は一通りある、という感じです。

@ui_mtcと@tran0826のふたりは、業務で初めてWebに触れていて、それぞれ1年目、2年目でした。@tran0826は、ちょうどISUCON練習が始まる前にwebで動くフーリエ級数展開ゲーム(!?)を趣味で作っていて、すごい。

@ui_mtc は現在進行系で会社のwebの研修を受けていて、クライアント側の開発(Reactなど)を勉強しているところ、とのことでした。

事前準備について

時系列で書きます。(ここから常体)

6/6以前 Twitterにて

@ui_mtc と @trans0826 が Twitter でチームメイトの募集をしているのを見かけて、申し込む。

6/6 爆速登録

@tran0826 が爆速でISUCONの登録をこなす。

1分10秒で締め切りって何ていうISUCON?

6/13 kickoff MTG

kickoff MTGがあった。軽くISUCONやWeb・インフラ歴などを共有したり、使用言語(go)を決めたりした。あと、定期的に勉強会をしようという話になり、各自勉強することを決めたりした。

6/22, 6/29, 7/6 練習前半(Web全体像講座)

勉強会。@ui_mtc がちゃんとGoの自習(Go tour)を進めていて、わからないところは質問したりしてて偉すぎる。また、ISUCON12の事前講習とかの情報を@tran0826に共有してもらう。

自分は何をしたかというと、ISUCONの技術領域はとんでもなく広く、web開発に関する全体像(概要)がつかめていないと勉強するハードルがかなり上がってしまう&チーム内の意思疎通が難しくなってしまいがちだと思ったので、ISUCONの理解に必要な事項に絞った、Web全体像を把握する講座(?)をメンバー向けに開いた。資料が用意できなかったので、jamboardで手書きで色々書いたのだが、今板書を見てみるとあまりにもカオスで笑った。書くのも練習が必要ですねこれは()

Web全体像講座の一部。その場のノリでどんどん書いていったため、カオス。(できればiPadとかを使って手書きしたかったけど、jamboardのレスポンスが悪くて結局マウスで書くことに。。何か良い手書き共有サービスがあったら教えて下さい!というか作るか・・・)

内容は、

  • クライアント、サーバー、HTTPリクエスト/レスポンス(厚め)
  • 静的なページを配信するシステム
  • 原始的な方法(CGI、ファイルによるDB管理)で動的なページを配信する方法の説明
  • DBMSSQL、placeholder、ORM
  • MVCフレームワークを使ったWebアプリの開発
  • ネットワーク(特にIPアドレスとポート番号が何を識別するのかについて)
  • プロセス
  • リバースプロキシ・LBの導入、DBの分割
  • 公開鍵暗号方式sshd
  • linuxのコマンド(随時)、ファイルパーミッション、所有者

という感じです。ISUCON向けに絞った(例えば、Reactなどフロントエンドの開発については割愛している)ものの分量的にはかなりあって、これを2日に分けて合計5時間くらいで説明したのでだいぶ急ぎ足の説明になってしまった。 本当はセッション管理とかgit、Docker、CIとかも説明したかったけど、時間の都合上、割愛。

効果がちゃんとあったかどうかは不明。だけど、これ以降、少なくとも意思疎通で困ることはなかった。

7/9, 7/17, 7/20 練習後半(ISUCON 10予選による実践練習)

このあたりで、実践練習をしていた。過去問が色々ある中で、練習の対象にしたのは ISUCON 10 予選 だった。これは

  • そこそこ新しい
  • ISUCON 11 予選 は直前練習とか用に取っておきたい
  • さくらクラウドのクーポン1を使って、無料で練習でき、ISUCON10予選用の環境構築方法が丁寧に記述されたマニュアルがあった

といった理由で選んだ。結局時間無くて ISUCON 11 予選の練習はできなかった…(泣

実際にインスタンスを建てて、環境をセットアップして練習した。やはりスムーズに行く感じではなく、sshログインからベンチ走行まで、計測ツールのインストール&設定から実行まで、個別の改善x2まで、と一つの問題を4回くらいに分けて解く感じになってしまった2ssh-agent の挙動が Win と Mac で違ったり3、コード管理の方法の方針を決めたり4でかなり時間がかかってしまった。

また、作業をしている中で、ツールのインストール・設定方法や、躓いた点、参考文献などをドキュメントにしておきたかったので、github に isucon12-memo というプライベートリポジトリを建てて、チームに共有した。 memo のつもりでちょっと雑に書いてあるため、特に@ui_mtc を混乱させてしまった面もあり、反省5

isucon12-memo はこちら。間違いとかもあると思うので、ご利用は自己責任で。 github.com todoがまだある状態で本番を迎えてしまったため、来年までになんとか詰めておきたい。

本番前の最後の練習日に、役割と本番の立ち回り6を決めて、いざ本番……!!!という感じだった。

本番・・・の前に (計測くんについて)

いざ本番!って感じだったのだが、ここでちょっと天啓が・・・。

@kakira9618「Discord で計測結果見れたら便利なんじゃね?」

残り2日くらいしかなかった&計測の役割を@ui_mtcに振ってからの発想だったので申し訳なくなりつつも、かなり便利だと思ったので、急いで実装。 言語はpythonで、discord.pyを使えば結構カンタンに Discord bot を作れるということがわかった7ので、コードの質とかはガン無視してとにかく動くものを作った。出来上がったのはこちら。

Discord bot 『計測くん』の動作例。benchと打つと、ベンチマークを走らせて、各種計測を行い、Discordに結果を投稿してくれる。

これ、すごく便利でした。チームメイトの2人にもたくさん使ってもらえました。Twitterでも反響があり、やっぱり手続きを簡易化したり可視化したりするのって大切なんだなぁという感触を得ました。

ちなみに、以下に示すとおりbenchコマンド以外にも色々実装しましたが、本番で使われることはありませんでした。。。8

  • ベンチ中に計測コマンドを発行する機能…ベンチマーカーの出力に特定の文字列が現れたら、そのタイミングで計測コマンドを発行できるようにした9
  • 計測だけをする機能…3台のサーバーを組み合わせた実装になったときに、ベンチをリクエストするのは1台だけのはずなので実装。
  • alp コマンド機能…alpはデフォルトのオプション以外にも使う状況がありそうなので実装。
  • 任意のコマンド実行機能…RCEです。普通にsshで打てば良さそうだけど、ログを取ったほうが良いことはあると思うので、ログ機能付きの簡易シェルとして実装。

具体的に良かった点を言語化すると、

  1. ベンチ実行 -> 計測 -> 投稿までDiscord上の簡単なコマンドで行えるため、効率が上がる。ベンチマークを回す回数を増やせるので、それだけ改善に時間を割くことができる。("4人目"ができるようなもの、だと思ってます)
  2. コマンド操作に慣れていなくても、誰でも簡単に計測を行うことができる。
  3. Discord上に計測結果が集約されるので、見やすい。ログが確実に残るので、あとから見直したりできる。
  4. ツイッターで少しバズる(?)

など。悪かった点は反省(後述)で。特にコマンドの扱いに慣れていないメンバーもいたので、2.のメリットはかなり大きかった。また、3. は本来コンテスト中に役立つかなぁと思ったが、まさに今、ブログを書くときに役立っている。git commit のように、そのベンチで何を改善したのか、というのをコマンドの引数として入れる実装にすれば、あとになって振り返るときにもっと楽なのかもしれない。

コードはこちら。RCE機能が入っているので、セキュリティにはお気をつけください。

github.com

ギリギリまでコード修正などをして、なんとか作り上げられた。しかし、作り上げられたという達成感と、本番への期待感が入り混じってしまい、本番前日は結局一睡もできなかった。前日に作業するの禁止したほうが良さそう。

本番

そして、いざ本番! コミュニケーションコストや家の広さ等を考え、3人とも @tran0826 の家に集合することになった。

ちゃんとしたイス+デスク+デュアルモニタ環境が2セットもあってすごかった10。めっちゃ作業しやすかったです。ありがとうございます。

6:00 頃 起床(?)

眠れないので「計測くん」を改善する。起床技術者試験、突破(?)

7:45 頃 出発

家を出る。深夜テンション。

9:40 頃 到着

みんな家到着。特に@ui_mtcと会うのは久しぶり(2年ぶりくらいかな)。ISUCON生放送を聞く。

9:55 頃 問題説明

問題の説明が始まる。動画がめちゃくちゃ豪華・・・! なるほど、SaaSか。なんか複雑そう? でも機能はそこまで無いのかな?

10:00 頃 開始

開始!ポータルから選手用サイトにログイン。 事前の打ち合わせ通りに、自分はまっ先に当日マニュアルを開き、手順に沿って環境をセットアップ。cloudformation で yml 選択してポチるだけ。簡単。

10:10 頃 ssh接続で躓く

インスタンスが立ち上がったのを確認する あれ、なんかsshしても deny されてしまう。なぜ…(2分くらい考える) あ、そうだ!事前準備のenv_checkしたのだいぶ前(1ヶ月くらい前)で、そのときに入っていなかった公開鍵(私用PC用)を後からgithubに追加したんだった。多分事前準備で env_check したときの公開鍵が自動的に ~/.ssh/authorized_keys に記述される設定になっているのだろう、と思い、急遽 @tran0826 にsshログイン&自分の公開鍵を追加してもらうことにした。すまん。でも無事ログインできた。

おっと今回はサイトにHTTPアクセスするのに /etc/hosts の編集が必要らしい。なるほどね。設定をDiscordに投下。 初回のベンチを回す。3000点くらい。

10:20 頃 構成把握

sshログインして、どんな構成か軽く見て回った11

練習(ISUCON 10)では接続ユーザはubuntu、webアプリが置いてあるユーザはisuconとなっていたので、まずisuconでログインできるようにするところから始めていたが、今回は必要なさそう。

nginxとかmysqlはsystemdにぶら下がっているのか。あれ、でも docker-compose-go.yaml があるぞ?あ、アプリはdockerで管理して、dockerdをsystemdで管理してるのか。なるほど。

goのコードを眺める。うわ長そう(そっ閉じ)。

メモリ量などスペック把握。OK。

10:30 頃 リポジトリ用意

計測ツールを入れるか・・・!となったが、その前にリポジトリの準備が必要だったので、githubリポジトリを作り、webappをpush。

また、設定ファイルも管理したいので、/etc/nginxの中身を/webapp/etc/nginxに移して、/etc/nginx -> /webapp/etc/nginx などのシンボリックリンクを貼った。権限周りで何か起こると面倒なので脳死sudo chmod -R 777 /webapp/etc/nginx などした。mysqlも同じように設定12シンボリックリンクのコマンドは混乱しやすいので、前日に練習していた。順調順調。

このへんでチームメイトが重要そうなことメモを作ってDiscordに投稿してくれた。ありがたや

10:40 頃 計測ツール導入

ツールを入れはじめる。 予め用意してあった isucon12-memo を見ながら設定した。 まずalp。/etc/nginxシンボリックリンクの設定はうまく出来てるかな? -> 設定変更、反映確認。できてそう! dstat, netdata はインストールするだけ。OK。ちゃんと動作確認済みの資料用意しておいてよかった〜。

10:45 頃 pprof が導入できない

このあたりから雲行きがおかしくなる。pprofがうまく導入できない。導入するとアプリが落ちるんだけど・・・。と思い、journalctl でログを確認。

なんか WAF (Web Application Flamework) にecho じゃなくて echo/v4 を使っているらしい。memoは echo用にラップされたモジュール github.com/sevenNt/echo-pprof を使って設定する手順を書いていた。しかし、これは echo/v4用ではなく echo 用であり、ラップする関数を読んでいるところで引数の型が整合しなくてエラー落ちしてた。github見に行くとv4対応されているようなので、go get で最新のコミットを指定してインストールしなおすもうまく行かず。ラッパーを使うの諦めて自分で全部書くのも視野に入れるか、、、なんて思っていたところに、github.com/hiko1129/echo-pprof が引っかかり、導入してみたらうまく行った。ここで30分くらいロスト。

他のチームメイトはこのあたりでアプリを触ってもらっていた。セキュリティがガバガバである。

11:15 頃 my.cnfが反映されない

なんとかpprof動いたので、次は pt-query-digest。まずは slow-query の設定をしなきゃ。。。ということで my.cnfを設定するも、実際に mysql にログインして確認すると設定値が変わってなさそう。え?シンボリックリンクミスった?と思ったが、問題はなさそう。journalctl 読むと、/webapp/etc/mysqlパーミッションがあまりにもおかしいので my.cnf の読み込みを ignore した、と出てて、なぁんだ、となる。パーミッションを戻す。

しかしそれでも設定値が反映されない。 というかデフォルト設定の slow_query_log_file = ほげ/ip-192-168-0-12-slow.log ってなんなんだよ。となり、mysql のバージョンを確認してみると 8.0 ということがわかる。今まで5.7で練習していたので、これが原因か orz となった。8.0をつけて設定をググるがなかなか設定値を反映させることができない。mysql の help から my.cnf を読み込む先を取得できるらしいということをググって見つける。色んな場所にmy.cnfを置いてみるがやはり反映されない。/root/.my.cnf とかも試してみるが、やはりだめ。えっ・・・mysql 5.7に戻すか、、、と思ったが、アプリケーションに影響が出るのが怖いため、それもしたくない。(この地点で12:00くらい)

八方塞がりになってしまったので、いろいろごちゃごちゃやったあと、一旦 /etc/mysql の状態をもとに戻した。すると、slow_query_log=1 の設定だけは反映されていることがわかる。え?マジ?どうやらシンボリックリンクにするとうまく動かないようなので、一つのリポジトリにするのは諦めて、/etc/mysql 用のリポジトリを作った。しかし、/etc/mysql の設定ファイルは所有者が root のためgitコマンドを発行するのにいちいち sudo が要る。そして push することができない13。過ぎていく時間。全てを諦めて、git管理するのを諦めた。ごめんなさい。。。

しかしslow_query_log_fileの設定は全然変わらない。おかしいなぁと思い、もうそこで良いか、と思った瞬間に、設定値に""がついていたことに気づく。これを消してみたところ、反映された。mysql 5.7では動いたのに。。。えぇ・・・。

ようやくpt-query-digest の動作確認が取れる。この時点で午後13:30くらい。

13:30 頃 計測くん導入

本当はお昼にしたかったが、まだ計測くんを導入していなかったので、導入。 。。。インストール中の細かいエラーをやっつける。pip3が入ってなくてちょっとだけつまずく。と、ここで、ベンチマーカーの仕様がISUCON10のものと大きく異なることに気づく。あ、コマンドラインじゃなくてブラウザからボタンを押して運営にjobを積むタイプのやつじゃん、となる。curlなどでベンチを開始を押すことはできたかもしれないが、ハマりそうな気がしたので、ベンチは手動で押す方式にして、その後の計測はタイマー式で発火するように、プログラムを変更14

プログラム変更が終わり、ようやく指標が取れるようになる。時刻15:00。えぇ・・・(ここまでが午前で終わる想定だった)

この間に、ふたりがアプリの解析を進めてくれた。なんか排他ロックとかsqliteとか言ってて怖い・・・ ちゃんとチャンネルに気になったことをまとめてくれていてありがたい。

15:00 頃 休憩

お昼休憩。おにぎりとかチョコとか食べる。

15:15 頃 アプリの説明を受ける

@trans0826 にアプリの説明や構成などについて聞く。あぁSaaSってそういうことか、となる。コードの説明を聞いて、なんかMySQLsqliteが両方とも使われているらしいことを知る。ロックのとり方もなんか変。それってトランザクションでやるやつなんじゃ・・・。一意なIDを生成する場所もよくわからない重そうな処理をしている。stubって何に使われているんだ。sqlitemysql に移行できたらこの辺良くなりそう15。など考えていた。

15:30 頃 残りのインスタンスに計測くん導入

今後の方針をチームメイトと相談し、引き続き2人にはアプリの解析と改善を行ってもらうことにした。私は計測くんが他のインスタンスでも動くように設定することに。16

16:00 頃 ちょっと休憩

全てのインスタンスで計測くんが動くようになった。疲れたのでまたちょっと休憩した。

@tran0826 がsqliteのインデックスを貼ってくれたりしてくれている。ありがとう。また、pprofの結果を見るに、sqliteボトルネックらしいことも聞く。 sqlite はファイル開きまくるイメージがあったので、ファイルディスクリプタ数が足りてないんじゃないかと思い、そのあたりを調査することにした。問題はなさそうだった。 また、@ui_mtc がnginxの設定などのチューニングをしてくれたりしてた。さらに Unix Domain Socket への移行もやってくれるみたい。超助かる。お願いします。

また、このあたりで、ベンチを実行する際のホストの番号と、AWSで立ち上げたインスタンスに付与されているIPアドレスの順番が異なることに気づく。@ui_mtc が自分のインスタンスに負荷をかけているつもりで、実は@kakira9618のインスタンスに負荷をかけていたらしい。このあたりは私は気づいていた&ハマりやすいなと思っていたので、もっと早く共有できていれば、と思った。ごめんなさい。。。

16:30 頃 flock改善

もうあまり時間がないけど、インフラ側でできそうなことはだいたい終わった&今回はアプリがメインのチューニングポイントっぽいので、@kakira9618もアプリの改善をすることに。チームメイトにはN+1とかに取り組んでもらっているので、それ以外のところをチェック。

計測くんの pt-query-digest をみると、slow-queryが出ていることに気づく。mysql側でもインデックスが貼れそう。具体的にクエリで引いている表にどのくらいのレコード数があるかなどを検証したら、多重度高めでインデックス貼ったら効果大きそうだったので、@tran0826にインデックスを貼ることを提案。実装してくれた。でもスコアはあまり上がらなかった。

次。なんかロック取りまくっているところは、明らかに改善できそうだったので見てみる。トランザクションを入れることも考えたが、とりあえず、全部ロック外してみたらどうなるかな?と思い、外したバージョンを投げる。

-> 5000点くらいの点数(コンテスト中弊チーム最高点)。おおおおおおお!!!!!!!! となったが、再度キューに突っ込んだところエラーが。なんかデータのvalidationが通ってないらしい。実際にデータベースが壊れていた。

一個だけロック外したバージョンなども試してみたが、やはりDBが壊れてしまうようだったので、だめぽ。いったんロックをもとに戻し、コードを眺める。ロックの解放の位置を見てみると、全て defer fl.close() となっていたので、関数終了手前であることがわかる。本来ならそこまでロックを取る必要は無いはずなので、適切な位置で fl.close() を読んであげるように修正する。 排他ロックは、今回はファイルをつかったもの (flock) となっているため、diskのi/oがロックを取得するたびに発生する。これを /tmpfs などのメモリ上におけたら良いなと思い、 /etc/fstabなどを編集してメモリの領域をマウントする。これを書き込み先として設定して、、、スコアが伸びなかった(残念)。もうちょっと伸びてくれていいのに。と思ったが仕方ない。

@ui_mtc の Unix Domain Socket の実装が完成し、master にマージされる。これはベンチ見る限りちょっとだけ早くなってそう。ナイス!

17:50 頃 最終処理

ここまでで、競技時間は残り10分と、本来最終フェーズに割り当てた時間を大幅にオーバーしていた。流石にこれ以上改善するとリスクがあるよね、ということで、最終フェーズに移行する。デバッグログの設定を消し、mysqlのスロークエリやnginxのアクセスログを出力しない設定にしたりした。これで再起動、、っと。あれ、なんか失敗してる(5分前)

あ、なんか Unix Domain Socket 対応で追加したはずの .sock ファイルが消えてる・・・。修正。→あれ、うまく起動しない。

え、焦る自分。みんなの状態を確認すると、どうやら@tran0826のインスタンスだけ今正常に動いているらしい。ということで、残りの設定を@tran0826のPCでやることに。英語配列のキーボードが上手く操作できない。。。設定後、再起動などしてもらったのだが、そこでやはりエラーがでてデーモンが立ち上がらない。journalctl を見て、、、とかやろうとしているうちに、18:00 競技時間終了!でした。はい、0点です。

ISUCON12 予選に参加して良かったこと

以上のように、結果は奮わなかったISUCON12予選でしたが、良かったことや収穫もありました。

準備フェーズ

勉強会を定期的に開いたこと・実践を取り入れられたこと

週1〜週2くらいのペースで、リモートで勉強会を開催しました。定期的に勉強会を開くことによって、モチベーションを保ちつつ学習できました。リモートということもあり、やはり回数を重ねないとどうしても存在感が薄くなってしまうので、ある一定期間定期的に勉強会をすることには意味があるなと思いました。

次の勉強会までにやってくることを各自Discordに投稿しておいたのも良かったと思います。かなりやることは明確になっていたと思います。17 内容としては、かなり基礎的なもの(Web基礎、go tour、sshの設定、各種計測ツールの使い方等)でしたが、最低限これができなければお話にならない、というラインは押さえられていたんじゃないかと思います。

また、後半の勉強会では実践形式で、実際にサーバーを建てて練習できたのが良かったと思います。実際にサーバーで手を動かすことによって、躓くところなどを事前に洗い出しておくことができ、それを基にして isucon12-memo を作ったので、設定でハマるということが(想定された環境の通りであれば)あまりなかったと思います。予選本番でも、実際にサーバー内の環境を見てみて、どういう構成になっているのかを一から把握すること無く、「あ、今回はsystemdを使っているんだ」「webappはdockerの上で動いてるんだね」「スキーマ定義はここだね」などスムーズに把握することができたと思います。やっぱり手を動かすことは大切です。

isucon12-memo の準備

調べたことをDiscordに貼っていくというのを当初考えていたのですが、Discordはどんどんログが流れていってしまうので、ドキュメントの整備には向いていないと思い、github内のリポジトリで管理する形式としました。@ui_mtc に git コマンドに慣れてもらうといった意図もありました。

メモ(の多く)は以下の形式で書きました。

## title
### 概要

### インストール方法

### 使い方

### 備考・注意点・躓いた点

### 参考文献

また、質の高いドキュメントにするために、

  • ググってURLを貼って終わりにしない。基本的に動作確認を行ったものを書く。自分の環境の出力結果を載せる。
  • URL先がリンク切れになっても使えるように、コード、設定値等は(参考文献を明示した上で)引用しておく

という点を意識しながらドキュメントを書いていました。本番でもかなり役になってくれたと思います。

計測くん

メリットなどは「本番の前に」の節で既に述べているので割愛。

計測くんを作ったのはとても良い判断だったと思います。本番後になって気づいたのですが、実際に、ISUCON 12 の事前練習のスライドにも、デプロイ -> 性能計測 ->プロファイル まで一気通貫やれる仕組みが整っていると完璧、とありました。今回はこれが2/3ほど(デプロイ以外)実装できましたね。

speakerdeck.com

また、他のチームはどうしているのか調べたところ、fluentd + elasticsearch + kinaba 18でシステムを作っているチームやElastic Stack に加えて、Google Cloud Profiler などを利用しているチーム19もあるようでした。

pythonを使ってbotを作るという選択は、最小限の労力で高い効果を得たという意味で、良かったと思います。一方で、調べられる情報量と表示の仕方はfluentd + elasticsearch + kinabaといった構成のほうがベターだと思いました。セットアップにかかりそうな時間と慣れ、効果などを総合的に判断して導入する仕組みを決めたいですね。

インフラ層の典型を学べたこと

isucon12-memo を作っている中で、定番のネタ(OSのファイルディスクリプタ数をチューニングしたり、Unix Domain Socket を使った通信を行ったり、とか)を習得することができました。今まで概念だけ知ってたけど、設定方法は知らない、、、といった段階から、ドキュメント見ながらであれば、特に迷わず設定できる、といったレベルまで上げることができたと思います。

更に、インフラ層の典型は、アプリ層が変わっても基本的に有効なため、今後のISUCON、または業務などにも活かすことができるはずです。これは、ISUCON予選に出場して得た明確なメリットだと私は思います。

本番

作業環境

@tran0826の家の環境がとても良かったです。外部ディスプレイが2個あって、片方は縦置きだったのですが、縦置きディスプレイはログとか流しておくのにちょうどよいですね。ちょっと欲しくなってしまいました。

また、長時間イスに座って作業してたのですが、終わっても全然辛くなかったです。あのイスなんてやつだろう・・・?

進捗共有

直接声に出して伝える情報と、Discordにテキストとしてメモしておく情報をうまく使い分けられたのが良かったです。

今から〇〇やります/やってます、〇〇サービス今落ちてますーなどの状態等の共有は、直接声に出して、作業を進めました。同じインスタンスを2人以上の人が改変すると、謎の挙動が起きてしまうということがあると思いますが、そういうことは今回は起きなかったと思います。

また、ドキュメントやコードを見て思ったことなど、忘れやすそうなことは、声ではなくDiscordにメモしておいたのも良かったと思います。忘れるのを防止するというだけでなく、作業中の人の集中力をカットしてしまうことが防げたと思います。

計測くんをなんとか動かせたこと

せっかく用意しても動いていなかったら意味がないので…。

色々トラブルがありましたが、競技後半には計測くんが動いている状態になっていたことが良かったと思います。予期せぬ本番でのコード改変も、直前に自分がコードを書いていたので、あまり大量に時間を浪費せずに改変することができました。このあたりもオリジナルで計測botを作ったのが活きていると思います。

反省点・敗因分析

一方で反省すべき点も多いISUCONでした。

準備フェーズ

isucon12-memo の情報不足

isucon12-memo ですが、本番で実際に運用してみる(メンバーに使ってもらう)と、「このコマンドはどこで実行するのか」、「パスの指定は、この環境の場合どうすればよいのか」と言ったところで詰まるところが多かったようです。特に手元、サーバー、コンテナ内を行き来するISUCONにおいて、どこでコマンドを実行するべきかを記載しなかったのはかなり痛手だったなと思いました。自分でメモを作るときは、手元の端末の今の状態でコマンドを打てば良いので、どこでコマンドを打ったかという情報をメモすること自体を忘れてしまいがちなのが罠なのかなと思いました(パスも同様)。このあたりは、改善が必要な点だと思います。

また、単純に調べる時間が足りなくて、todo.md に複数 todo が残ったまま本番を迎えてしまいました。埋めれば、それだけネタの引き出しが増えるということで、本番に取れる作戦の幅が広がります。来年までには埋めたいですね。

全体的な練習不足、特にアプリ部分(go)に関する実践練習・習熟が足りなかったこと

今回、スコアがなかなか上がらなかった原因はこれだと思っています。

インフラ層の実践練習はそこそこ行ったのですが、アプリ層の練習はあまり行いませんでした。go を使うと決めたのに、goを使ったweb開発の練習が足りていませんでした。基礎的な文法とWAFの使い方くらいで勉強が終わっていたため、他のいろんなモジュールを使った開発や、周辺エコシステムに慣れるといったことをあまりしていませんでした。

DB周りについても、インデックスを貼る、ぐらいのことはできたものの、トランザクションを使ったり、副問合せをつかったクエリを組み立てたり、といった普通の開発でよく出てくる要素の練習をほとんどしていませんでした。

今回のISUCONはかなりアプリが複雑だったので、そういった開発の経験や改善の引き出しの数が十分に無いと、改善に踏み込むことができない(怪しいところはわかるものの、どうやって改善したら良いのかがわからない)問題だったと思います。

睡眠不足

謎にテンションが上がってしまい、前日は全く眠れませんでした。

本番中に寝てしまうことはオフラインだったのでなんとか防げたものの、やはり集中力は下がってしまうので、寝たほうが良いです(当然)。

とは言っても、実際に前日に十分寝るのはなかなか難しいです。解決策としては、前日はあまり作業を行わない、というのが良いのかなと思いました。作業をしてしまうと、どうしてもそのことを考えてしまうので。。。そのためにも、直前ではない日に、ISUCONの作業時間をとっておくのは大切だと思います。

本番

計測くんを入れるのに手間取った

計測くんを入れるためには、計測ツールをインストール・設定しなければならず、まずこれに時間がかかりました。 次に、計測くん自体を改変する必要があり、これにも時間がかかってしまいました。 結局、ISUCONの時間の半分を計測くんの導入にかけてしまっていた計算になります。これではアプリの改善に力を入れることはできません。

このあたりの解決策としては、

  • 計測くんをコンテナ化する
  • ansible などを使って、provisioning を自動化する

などがありそうです。今回は、時間不足でここまで踏み込むことができませんでしたが、今後は検討したいと思います。

インスタンスの使い方と戦略

今回は、各インスタンスごとに、gitリポジトリをもたせ、github の プライベートリポジトリに変更内容をcommit + pushして共有。他のインスタンスでの変更は、各インスタンス上でpullする、という形を取っていました。

この方式は、練習時は良かった(1人1台担当にすることができる、サーバー上でファイルを直接イジって動作を確認できる、並列に作業できる)のですが、本番では複数台を組み合わせた構成(アプリ1台+DB2台など)など当然各インスタンスでの役割は異なることが多いため、インスタンス上gitでデプロイを行う方式とは相性が悪かったです。

手元でコード管理をしつつ、デプロイコマンドを各インスタンスに向けて行う方式(Jenkins、ansibleとか)にしたほうが良かったかなと思いました。

また、各メンバーは、並列に改善を行っていたのですが、チームの特性を考えると、今回は(必要なセットアップが終わったら)みんなで一つの改善ポイントに向かい合う、といった形のほうがスコアが出たのではないか、と思います。このあたりは、個々のスキルや得意分野を見て、柔軟に決める必要がありそうです。

メンバーのトラブルシューティングにあまり関われなかった

(特に競技前半と中盤で)予想以上に環境のセットアップに時間がかかってしまったため、メンバーのヘルプにあまり付き合えなかったのが良くなかったです。ほとんど残りのメンバーにそのあたりの作業を丸投げしてしまいました。 次回以降は、環境セットアップの作業をもっと短縮した上で、重要度の高い

最終フェーズの時間の短さ

0点になってしまったのはこれが原因ですね。時間が足りなかったのはそうなのですが、流石に残り10分では厳しかった。。。

……もっとたくさん書きたいのですが、きりがないので、ここで終わりとします。

感想

ISUCON 12 楽しかったです!!!!今までももちろん楽しかったのですが、今回はしっかりと(当人比)練習して取り組んだだけに、今まで一番楽しかったと言っても過言ではないと思います。普段、あまり文章を書かない自分が、約19000文字もあるブログ記事を書くのはかなり異例のことで、自分にびっくりしています。

しかし、それだけに、0点という結果はかなり悔しかったです。せめて、5000点くらいは取りたかった。。。 ただ、悪いところばかりというわけでもなく、収穫もちゃんとあったと思うし、チームメンバーも最後までついてきてくれた。

今回のISUCONの悔しさをバネにして、来年こそは予選を突破したいと思います!

まとめ

  • 勉強会を定期的に開いて、Web全体像の講座をしたり、メモのリポジトリを作ってドキュメントを共有したよ
  • ベンチ実行 -> 計測 -> Discordに投稿してくれる bot を作ったよ
  • 環境セットアップや作戦など、改善すべき点がたくさん見つかったよ
    • 次は、ansible とか使ってデプロイ環境を整えつつ、app層の経験を積んでいくと良さそう
  • 前日は早めに寝ましょう

お疲れさまでした。良いISUCONライフを!

他のメンバーの参加記も読んでね


  1. ISUCON参加者向けに配られたクーポン。なんと2万円分という太っ腹ぶり。ありがとうございます!(AWSも本番用に5千円のクーポンを発行してくれた。ありがとうございます!!)

  2. 当初は、最初の2回くらいで1年分の問題、その後本番形式で、別の問題を1個やる、という想定だった。

  3. win (WSL) では eval ssh-agent の実行が ssh-add の前に必要だったりするやつ。

  4. 複数(3個)あるインスタンス間でどのようにコードを共有、メンテしていくか。途中でインスタンスを6個(dev+live)作る案が@ui_mtcから出てなるほどとなったが流石にリスクがありすぎたので、プライベートリポジトリをGithubに立てて、3台それぞれのインスタンスをメンバー一人ずつに割り当て、個々のインスタンスからcommitをpushする形に落ち着いた。

  5. コマンドをどこ(自分のPC上か、インスタンス上か、インスタンスの中のコンテナ上か)で打てば良いのか、とかホストとコンテナ間のパスの対応とかがきちんと示されていなかった。このあたりは、ある程度経験があれば察することができてしまうので、テキトーになってしまっていたが、初心者向けに質の良いドキュメントを書くには大切なことだなー、と再認識させられた。

  6. 自分は最初の環境のセットアップと、インフラ周り担当、@tran0826はアプリとDB担当、@ui_mtcは計測とmysqlのチューニングあたり担当という感じになった。また、午前は環境のセットアップを@kakira9618がしている間に、残りの2人はルールの熟読とアプリのコードを眺めてもらって、実際の改善は午後からスタートすることにした。最後の1時間は、ログの出力設定の削除、不要なサービスの終了設定、再起動試験などに充てることにした。

  7. 初めての Discord bot 制作だった。基本動作やエラー処理など面倒なところはほとんど discord.py が吸収してくれるので、あとはコードを書くだけという感じだった。むしろ最初の bot 連携のところがちょっと複雑だった(といっても数回マウスでポチる程度)かもしれない。

  8. どちらかというと、使うところまで改善が進まなかった、って感じですね。ぐぬぬ。。。

  9. がしかし本番ではベンチマークの走行がWebUIであることを失念していたので、本番でこの部分は使われることはなかった。本番中に急遽コードを書き換え、Discord上のbenchコマンドを受け取ってから、15秒後に計測コマンドを走らせる形式(ベンチ走行は手動で人間がWebUIから行う)とした。

  10. @ui_mtc はちゃぶ台机を使ってもらった。普段の開発もノートPCだけで行っているとのことだったけど、姿勢とか大変そう。申し訳なかったです・・・。

  11. 実際には VSCode の Remote SSH プラグインを使い、VSCode上で見て回った。慣れているUIなのでとても作業がしやすかった。またmacだとssh-agentとかもちゃんと有効なので、そのままgitできたりしてすごく便利だった。設定も ~/.ssh/config に書くだけなのでらくらく。

  12. sysctl もするべきだった。がすっかり忘れてた。

  13. sudo でユーザーが変わるので ssh-agent に持たせた秘密鍵を引き継ぐことができていない、のだと思う

  14. curlで叩きに行こうとしても認証付きで CSRF とかも対策しているかもしれずリクエストを生成する必要があるかも。またうまく行っても結局ベンチの出力は随時では得られず、全て終了してからでないと得られない、等の点からベンチ走行自体自動化は諦めたほうが良さそうと考えた。しかしタイマー式はタイマー式で、確実性にかける、というデメリットがある。特に運営側のjob処理が安定していないと、タイマーは使えない。今回はそういうことはない!と運営を信じてタイマー式に移行を決断。実際、今回はベンチマークがすごく安定していてすごい(語彙力無)と思った。

  15. しかしmysql 移行は罠だったらしい(ソースはTwitter

  16. このあたり chef とか ansible とか使ってうまく時間短縮できたかもなぁと思いました。ここで AMI を使うという案は頭の中にはあったんですが、当日マニュアル読んでちょっと怖くなったので、それはやめておきました。

  17. 以前までのISUCONは、軽く打ち合わせをして作戦を決め、1回だけ概念の確認・本番の動きを確認して終了、という形が多かったです。流石にそれでは練習不足で、本番になってコマンドをググったりすることが多かったです。今回も、本番中にググることはありましたが、どれも練習環境との違いに起因するもので、それ以外はほぼつまらずに設定できました。

  18. https://trap.jp/post/1628/

  19. https://nonylene.hatenablog.jp/entry/2022/07/24/184518