ISUCON初心者のための勉強方法

# これはなに？
ISUCONに関する記事は数多あれど、初心者に対する勉強法をまとめてくれている記事が少ないように感じたので、筆者なりのISUCONの勉強方法を具体的にまとめました。

今回、筆者は2人チームで学生チームとして初めて参加をし、(筆者がサーバーを吹き飛ばすという事故がありながらも)全体で大体180位ぐらい(全チーム数が698チーム)というなんとも微妙な結果になってしまいました。

しかし、ISUCON本番では、プロファイリングを行う→ボトルネックを見つける→改善する→スコアが上がるというISUCONの醍醐味を味わうことができました。

練習や模擬戦では、ISUCONで最低限戦うための、勉強やShellスクリプトの準備に殆どの時間が費やされてしまい、肝心のチューニングで手を動かして勉強するということに時間をあまり費やすことができませんでした。

本記事はそうした、事前準備に加えて、筆者の今年の反省に基づいた勉強方法をまとめました。
来年以降参戦する方の助けになれば幸いです。

# ISUCONとは
https://isucon.net/
いい感じ(I)スピード(S)アップ(U)コンテスト(CON)でISUCONらしいです。
設定としては、超人気のサービスがリリースされることになったけど、8時間後にリリースされるから、8時間でスピードを上げてねという設定で、与えられたwebサービスを8時間の間にチューニングを行い、軽快なサービスにしていくことがコンテストの内容です。

ISUCONは今年が12回目の開催で毎年、ユニークなサービスが題材として取り上げられています。(ある年は、ISUUMO(まるで某賃貸検索サービス)、今年はISUPORTS(e-sportsではない)のポータルサイトでした。)

# ISUCONのための勉強方法
それでは早速ISUCONのための勉強法について説明していきます。
# ISUCON本/事前講習会
ご存知の方も多いとは思いますが、[達人が教えるWebパフォーマンスチューニング
〜ISUCONから学ぶ高速化の実践](https://gihyo.jp/book/2022/978-4-297-12846-3)という本が今年になって出版されました。筆者も予約して購入したのですが、とてもいい本でした。
![ISUCON本](http://image.gihyo.co.jp/assets/images/cover/2022/9784297128463.jpg)
ISUCONという大会をハックするための本というよりはWebアプリケーションのパフォーマンスを上げるにはどうしたら良いのかという考え方から、具体的なツール、そして社内ISUCONを用いたハンズオンまで解説されている本でした。
非常に充実した内容で、何度も何度も読み返しました。
また、[ISUCON事前講習会](https://isucon.connpass.com/event/248325/)というISUCONの猛者がISUCONでの戦い方を教えてくれるイベントもあります。


以下ではISUCON本を利用しつつ、どうやって勉強していくかを説明していきます。

流れとしては以下の4ステップです。

1. 本を通読する。
2. `private-isu`に取り組んで見る 
2. ISUCON本番での運用を学ぶ
2. 過去問に取り組んで見る

## ISUCON本を通読する
まずは一通り、ISUCON本を読んでみましょう。
おすすめの読み方としては、1度さらっと全体に目を通したあと、Ankiを使用して、フラッシュカードを作りながら、読んでいく方法です。
ISUCON上位者のブログを読んでいると、その引き出しの多さと実装の速さに驚きます。
少しでも勉強した内容をすぐに引き出せるように、Ankiを使って、自分の知らなかった内容を中心に覚えながら読むことをおすすめします。

## `private-isu`に取り組んで見る
ISUCON本の付録には`private-isu`と呼ばれる社内ISUCONをハンズオン形式で解説してくれているパートがあるので、これを見ながら、実際に手を動かして解いてみましょう。
その際の環境構築ですが、できればローカルではなく、クラウドでサーバーを借りて、環境構築することをおすすめします。これは、ISUCONの本番では実際にサーバーに入って作業を行うため、`ssh`でサーバーに入って`tmux`、`vim`といったツールを使ってチューニングを行うという経験を積めたほうが、本番で＋に働くと思われるからです。

## 過去問でISUCON本番での運用を学ぶ
さて、いよいよ過去問を使って、模擬戦を行っていきます！
模擬戦では今までのISUCON本を使った学習では学べなかった、本番での具体的な戦い方について勉強していきます。

学ぶべき(準備すべき)項目は以下の4点になります。
- 最初の30min~1hで何をするか
- ソースコードをどうやって運用するのか
- プロファイリング準備→ベンチ→プロファイリング→ボトルネック改善のループをどうやって高速に回すか
- ボトルネックの改善方法

### 最初の30min~1hで何をするか
ISUCONにおけるスタートダッシュにおいて、やることは大体決まりきっています。
例えば、解析に必要なツールのインストールや初回のベンチマークの試行などです。
以下のcatatsuyさんの記事では3人チームでISUCONで戦う上での初動何をするのかのリストを公開してくださっており、非常に参考になります。

https://catatsuy.medium.com/%E8%87%AA%E5%88%86%E3%81%AE%E3%83%81%E3%83%BC%E3%83%A0%E3%81%AEisucon%E3%81%A7%E3%81%AE%E6%88%A6%E3%81%84%E6%96%B9-c8fe121316aa

模擬戦ではここの流れ、役割分担をしっかり確認、コマンドなどはメモしておくことをおすすめします。筆者は今年、2人チームで出場したので、役割を変更して、以下のような分担で行いました。

![スクリーンショット 2022-07-26 6.16.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307982/333bd17c-5159-7d5f-1430-9cb13720bf73.png)

例えば、メンバー全員の公開鍵をサーバーに登録するには、以下のコマンドを打てばいいです。

```
curl https://github.com/{umepon0626,user1,user2}.keys >> ~/.ssh/authorized_keys #user_id間にスペースいれると動かないので注意
```

こういったふうに、この段階ではこういうコマンドを打つと事前にメモしておけば、当日焦ること無くコンテストに集中することができます。

逆にこの部分をしっかりしておかないと、今年の筆者のように、提供されたサーバーをふっとばすという事故を起こして、時間を浪費するということにつながってしまいます。

### ソースコードの運用方法
ISUCONでは、渡されたサーバー上のコードを書き換えて、チューニングを行っていくのですが、最低限git管理(githubに上げるかどうかは別として)は必要だと思います。
その理由としては、もしgit管理をしていないと、どのような変更をして、このスコアになったのかということが管理できないからです。
git管理をしていれば、後から振り返ったときに、自分たちがどういう改善をいつしたのかと言うのを振り返るのにも役立ちます。何より、ベンチマークのスコアが改善されなかった場合、gitで元のソースコードに戻すことができます。

サーバーからgithubへの`push`には`deploy key`の登録が必要です(リポジトリは必ず<b>private</b>で作成するようにしましょう！)

https://qiita.com/tamorieeeen/items/c24f8285448b607b12dd

さて、gitでの運用方法なのですが普段の開発業務とは違った管理をする必要があると思われるので注意です。その理由としては、nginxやmysqlなどの設定を変更することが多いので、アプリケーションコードだけではなく、`/`配下のコードもgit管理に入れる必要があります。(ここらへん`itamae`や`ansible`などのIacを使えばうまく管理できるのかもしれないですが、筆者にはその知見が不足しており、有識者の方いらっしゃればコメントよろしくおねがいします。)

しかし、`/`はroot権限のファイルも混ざっているので、単純に`/`配下で`git init`するだけではうまくできません(`git add`や`git commit`する際にsudoが必要。)
また必要のないファイルはgit管理したくないので、適切に`.gitignore`をする必要があります。

これに関しては、matsuuさんの今年のISUCONでの`.gitignore`が参考になります。

https://github.com/matsuu/isucon12q/blob/main/.gitignore

ちなみにですが、`/`配下ではroot権限のファイルが含まれるので、毎回`git`コマンドを打つときにsudoが必要なので、めんどくさい。えーい、`chown /`しちゃえーとなると、sudoコマンドが使えなくなるので注意してください(まぁそんなこと普通に考えて皆さんしないですよね。え、僕はやりましたが...)

https://qiita.com/xishan/items/dd8336bae5c6ef955468

git管理することは良いとして、更にコードの運用で考える必要のあることがあります。
それは、コードの編集をどこで行うかです。パターンとしては3つ主に考えられるかなと思っていまして、

1. 1台のサーバー上で直接コードを編集する。
2. 1人1台のサーバーを使って、サーバー上で直接コードを編集する。 
3. ローカルに落としてきたコードを編集し、`git push`→サーバー上で`git pull`してサーバーにデプロイする。

ただ、2に関しては、サーバーのスペックの差異などが、git上の差分として検出されそうで色々とトラブルになりそうなので、筆者のチームではあまり考えなかったです。

1か3の違いはアプリケーションコードを書き換える人数が1人か2人以上かで変わってきます。アプリケーションコードを書き換える人が1人の場合、その人がサーバー上で作業してしまったほうが、効率が良いです。

しかし、複数人がアプリケーションコードを書き換えるとなると、コンフリクトの恐れがあるので、3の方法の方が良いと感じました。

ここはチームによって色々変わる部分があると思うので、模擬戦で試しながら、色々ブラッシュアップしていくと良いと思います。

### ボトルネック改善のループをどうやって高速に回すか
実際にボトルネックを改善するループを回すためにはやらなければいけない細々したことがたくさんあります。

1. logの出力設定を行う
1. ベンチマークを回す(負荷試験)
1. ベンチマークの結果からlogの分析を行う。
1. logの分析結果が消えないようにファイル名を書き換える

これらの作業はどれか一つを抜かすと、前の結果が失われたり、正確なプロファイリングをすることができなくなってしまいます。
そこで、これらの作業を自動化できるようなスクリプトを作成する必要があります。
これは使うプロファイリングツールによっても違うのですが、筆者のチームではmysqlのスロークエリの分析に`pt-query-digest`,nginxのアクセスログ解析に、`alp`を使用していたので、ISUCON12予選本番では、以下のようなベンチマークのスクリプトを作成していました。

```pre-bench.sh
#!/bin/bash
set -ex

sudo systemctl restart mysql
sudo systemctl restart isubnb.go
sudo systemctl restart nginx
# 以下はnri-isuconでコマンドでbenchが用意されているときに実行。
# sudo query-digester -duration 150 &
# pid1=$! 
# TARGET_PORT=80 TargetIpAddr=127.0.0.1 DATA_PATH=/home/isucon/isubnb/bench/initial-data /home/isucon/isubnb/bench/benchmarker
# wait $pid1
```

```after-bench.sh
now_date=$(date "+%Y%m%d_%H%M%S")
sudo pt-query-digest /var/log/mysql/mysql-slow.log > /tmp/mysql-slow.log.${now_date}
sudo cat /tmp/mysql-slow.log.${now_date}| less

sudo cat /var/log/nginx/access.log | alp json --sort=sum -r -q --qs-ignore-values -m '/api/organizer/player/[0-9a-zA-Z]+/disqualified,/api/organizer/competition/[0-9a-zA-Z]+/finish,/api/organizer/competition/[0-9a-zA-Z]+/score,/api/player/player/[0-9a-zA-Z]+,/api/player/competition/[0-9a-zA-Z]+/ranking' -o uri,count,1xx,2xx,3xx,4xx,5xx,min,max,avg,sum,p99 > /tmp/access.log.${now_date}
sudo less /tmp/access.log.${now_date}
```

### ボトルネックの改善方法
ここまでの準備がきっちりとできれば、スムーズにボトルネックの改善に集中することができます。
3h~4hなど時間を決めて、最初は自分たちだけで、ベンチマークの結果をみて、手を動かして改善をしていきましょう。
今年の筆者のチームではこの部分の練習があまり積めずに、本番でも実装に時間を取られてしまいました。普段から開発を行っているから、実装はまぁなんとかなるかという甘い考えがあったのですが、現実は甘くありませんでした。

例えば、ISUCONだと、sqlのクエリが直書きされていることが多いです。
普段からORマッパーを使ってクエリを発行していると、変数の埋め込みを使ってクエリを発行することは中々無いと思うのですが(SQLインジェクションなどのリスクも孕むので...)、「あれ、insert文のvaluesに値を入れたけど、syntaxエラーが出てしまう...」みたいなことになってしまうので、本番で焦らないためにも、模擬戦で十分に練習をしておくことをおすすめします。

また、予選であればたくさんの人がブログに解法や感想などを載せていらっしゃることが多いです。自分たちで実装が終わった後に、それらの解法をなぞることもおすすめです。
猛者たちは自分たちの知らないことを、知っているので、解法の引き出しを増やすという意味でも、この「解法をなぞる」という練習は大変勉強になると思います。

## ISUCON過去問リンク
最後になりますが、過去のISUCON予選の解説などのリンクを貼っておきますので、勉強にご活用ください。

### ISUCON12(予選)
公式まとめ：https://isucon.net/archives/56571716.html
上位チームの解法
2位チーム:https://matsuu.hatenablog.com/entry/2022/07/24/153403
7位チーム:https://blog.p1ass.com/posts/isucon12/
学生1位:https://blog.das82.com/post/isucon12q/

### ISUCON11(予選)
公式まとめ:https://isucon.net/archives/55821036.html
公式解説:https://isucon.net/archives/56044867.html
上位チームの解法
1位チーム:https://www.y1r.org/posts/20210831-isucon11-qualify
8位チーム:https://matsuu.hatenablog.com/entry/2021/08/22/141130
10位チーム:https://tech.mirrativ.stream/entry/2021/08/25/120426

以上になります。
ISUCONは今年が初参加でしたが、とても楽しく学びのある大会だったと思います。
運営の方々、このような機会を用意していただき本当にありがとうございます。

本記事に関する、ご意見やご指摘は大歓迎ですので、コメントなどしていただけるとありがたいです。読んでいただき、ありがとうございました。