Try   HackMD

traP ISUCON初心者向け講習会

これは2022/7/9にtraP内で開催した講習会の資料を外部公開に際して一部変更をしたものです

ISUCON初心者向け講習会を開催しました | 東京工業大学デジタル創作同好会traP

目標

  • 予選本番で1つ以上の改善を行えるようになる
  • 公式解説や参加者writeupを読んで理解できる/実践できるようになるための基礎知識を学ぶ

※優勝したり上位に入るためのテクニックは扱いません

参加する上での気持ちについて

  • 初参加で完璧にやろうと気負わなくていい
  • 本番でなんらかの改善を入れて点数を上げられれば十分すごい!
  • 最近は学生枠での本戦出場もすごく難しい……

「やったことがないことはやらない」は勝つためには重要
しかし、ISUCON初参加/web初心者は 「やったことがない」にぶつかるのは当たり前

大会の時間中に「やったことはないけれど、これで改善できるかもしれない」まで至れるのはよいこと
はじめてのことを試してみて、それで学びを得られたら十分参加の意義がある

チーム連携

対面orリモート

どちらでもOK
チームで相談して選ぼう

対面

同じ部屋に他チームもいるのはダメなので要注意

メリット

  • すぐに相談できる
    • 画面を見せての相談も簡単
  • 「行き詰まってそうだな」というのがわかりやすい
    • 泥沼に陥ってて自力じゃ切り替えれない、時間配分的な見切りができないということはありがち

リモート

各自の自宅から通話をつなぎながら
各チームでDiscordやSlackを立てる必要がある

メリット

  • 慣れた環境で作業できる
    • モニター、キーボード、etc.

情報の共有場所

口頭での情報共有以外に、テキストで共有できる場所があるとよい

  • 同時編集での共有
    • 問題についての調査やボトルネック候補、試した改善手法の共有など
    • HackMD
  • 時間情報込みでの共有
    • 計測したログやスコアの共有など
    • SlackやDiscordのチャット、GitHubリポジトリのissue

役割分担

チームによって様々なので「正解」はない
ここではよくある(?)パターンを紹介するので、チーム内で方針を決めておくとよいです

チームメイト不要! 1人で完璧に戦えます!

玄人向け
常連チームでもそうそういない

どちらもISUCONの過去問練習、事前準備をがっつりやりこんでいる人

各自でゴリゴリ進めていく ソロプレイヤーの集合体

常連チームではそこそこいる

メンバー全員が各自で計測、分析、改善を行っていくスタイル
作業箇所が競合しないようにの管理、厳しい時の相談、タスクの受け渡しはちゃんとやっている
チームメンバー全員のレベルが高い&お互いの能力・思考がわかりあっている前提

役割分担をして堅実なチームプレイ

初参加ではこれがおすすめ
「役割分担」といっても大まかなゆるっとしたもの

  • 環境操作・計測担当x1
    • 本番環境の操作は基本的にすべてこの人が担当する
      • 本番環境での作業コンフリクトや手順忘れを防げる
    • 計測結果からの具体的なボトルネック特定までできるのが理想
    • 手が空いている時は改善に参加する
  • 改善専任担当x2
    • ボトルネックの改善作業に専念する
      • ボトルネックの種類によってはガッツリ実装を書く必要があることもある
    • チームによってはさらに細かくDB担当・アプリ担当などに分けていることもある
      • 実際は「そのときのボトルネック」に合わせていじるべき箇所が変わるので、細かい担当の固定化はあまり意味がない
    • 「より得意な領域」を共有しておくと、見つけたボトルネックの担当割り振りがやりやすい
  • マニュアル完全理解担当(他と兼任)
    • マニュアルをしっかり読みこんで理解しておく
      • 内容を覚える必要はないです 索引になれるくらいが理想
    • 「8時間で全てのボトルネックを解消する」のは無理なので、優先順位付けの指針にマニュアルが超重要
    • 「やっていいこと/ダメなこと」「スコアにつながりそうな改善/つながらない改善」をチームメンバーに共有・指摘していく

事前準備

Linuxの操作に慣れる

本番中は基本的に、Linuxコマンドを叩いて環境を操作しなければならない

Linux入門の入門 | さくら, 茗荷 |本 | 通販 | Amazon

よく使うコマンドがまとまった小冊子
「こういう操作をしたいけれどなんのコマンドを使えばいいかわからない」というときは、この本をペラペラ眺めてみると良い

Hacknet | Steam

ハッカーとしてサーバー上で操作をして進めていくゲーム
一部なんちゃってなコマンドもあるけれど、基本的な操作はLinuxコマンドと大体同じ
「コマンドを打つとめっちゃ文字が流れてくる」ことに慣れるのにおすすめ

Bandit | OverTheWire

実際にLinuxコマンドを使いながら問題を解いていく
全てのコマンドを覚える必要はないけれど、「こういうことができるコマンドがある」と知っておけるのは大事

GitHubのリポジトリを作っておく

ソースコードの管理・共有用のリポジトリは事前に作っておこう
必ずprivateリポジトリとして作成すること

競技中にpublicリポジトリに問題のソースコードをアップしてしまうと、失格になる可能性があります

以下の行為を特に禁止する。

  • 予選の競技終了時間までに、競技の内容に関するあらゆる事項 (問題内容・計測ツールの計測方法など)を公開・共有すること(内容を推察できる発言も含む)

ISUCON11 予選レギュレーション : ISUCON公式Blog

個人のprivateリポジトリを作って他メンバーをCollaboratorとして招待する形でOK
無料ユーザーの個人privateリポジトリは招待できる人数上限があるが、ISUCONチームメンバーの3人は問題なく招待できる

よく使うコマンドをスクリプトにまとめておく

ベンチマークを回す前にやることとか
結構いろんな操作が必要なので、都度手打ちの実行をするのは厳しい

「ISUCON Makefile」で調べるといろんな人が公開しているのが出てくる
ハンズオンで使うMakefileも主要な操作をまとめてあります

チートシートを作っておく

「やることに困ったらここを見る」ができるように
特に「競技が始まったらまずやること」はまとめておこう

チートシートもいろんな人が公開しているので参考にしてみるとよい


ここからハンズオン

ISUCON11予選(PISCON1回目の問題)を使います

oribe1115/traP-isucon-newbie-handson2022
このリポジトリをテンプレートにしてリポジトリを作成しておいてください
今回はpublic/privateどちらでもOK

テンプレートからリポジトリを作成する - GitHub Docs

上記のテンプレートリポジトリは今回のハンズオンに特化した内容になってます
今後使用してもらってもOKですが、他の問題ではそのまま使えないことに注意してください

含まれている問題

  • ISUCON11予選の環境を前提とした設定ファイル
  • 構成管理ツールを使わずにある程度の構成管理をするためのやや無理矢理な構成

oribeがデモをするリポジトリ: https://github.com/oribe1115/traP-isucon-newbie-handson2022-demo

最初にやること

ベンチマークを回す

インスタンスが立ったら何はともあれベンチマークを回す
SSHで入るよりも前にやる勢いでよい

目的

  • 最初の「何も触っていない状態でのスコア」の確認
  • インスタンスが正常に稼働しているかの確認
    • この状態でベンチマークが通らなければ、競技環境に問題がある可能性が高い

ツールの導入

以下、コマンドは基本的にサーバー上のホームディレクトリ(/home/isucon)で実行してください

Makefileの取得

curl https://raw.githubusercontent.com/oribe1115/traP-isucon-newbie-handson2022/main/Makefile -o Makefile

ツールのインストール、デプロイキー用の鍵の生成

make setup

適宜yesやEnterを押して進める
ssh-keygenで生成する鍵のパスフレーズは空でよいので、Enterを押して進める

git管理

デプロイキーの設定

privateリポジトリとのやりとりを円滑に行うためにデプロイキーを使用する

作成したリポジトリのSettings > Deploy Keysで登録する
登録する公開鍵はサーバー上での以下の出力結果

cat .ssh/id_ed25519.pub

デプロイキーの管理 - GitHub Docs

リポジトリからの取得

git init git remote add origin {作成したリポジトリのSSH用URL}

git pull origin main
git pullでエラーが出る時は
error: The following untracked working tree files would be overwritten by merge:
        Makefile
Please move or remove them before you merge.
Aborting

上記のエラーが出た場合は以下のコマンドでMakefileを削除してからpullをやり直してみてください

rm Makefile

.gitignoreの設定

今回は設定済みなのでスキップ

必要のないファイル、大容量なファイル(DBの初期データなど)をリポジトリに含めてしまわないよう、適切に設定する必要がある

リポートリポジトリへのpush

git branch -m main make set-as-s1 make get-conf git add . git commit -m "init" git push origin main

問題の把握

出題動画を見る

ISUCON11 予選問題 - Youtube

競技のライブ配信で、競技開始時刻よりも前に流れ終わるように流れるので焦らずに見ておくとよい
ボトルネックや改善の仕方そのものの情報は出てこないが、アプリについてのドメイン知識の把握に有用

出題動画について

出題動画が用意されるようになったのはここ最近になってからです
予選の出題動画が用意されたのはISUCON11が初です
評判良かったし、多分今後も継続して用意されると思われます

マニュアルを読む

ISUCON11 予選当日マニュアル

ルールや環境の説明について書かれている

特に改善方針の決定に重要な箇所

ISUCONDITION アプリケーションマニュアル

問題のアプリ自体の説明が書かれている
アプリ独自の用語や機能を確認する

serviceの確認

動いているアプリケーションが何かを確認する

sudo systemctl list-units --type=service

s1/etc/systemd/system/isucondition.go.serviceを見てみる

テーブル構成を見る

webapp/sql/0_Schema.sql

こんな感じのデータを持ってるんだな〜とざっくり把握できればいい

isu.imageがLONGBLOB型で画像データがそのまま入ってそうでやばそうだけど、本当にボトルネックなのかはまだわからないので触らない

ソースコードをざっと見る

webapp/go/main.go

明らかに分量がやばいので頭から読もうとしない
ざっと「こんな関数があるんだな〜」って把握すれば十分
N+1っぽい多重for文があってもボトルネックかはまだわからないので触らない

main関数見てAPIの構成を把握しておくのはおすすめ
alpの設定にも必要な情報なので

postInitializeもざっと見ておくと良い

計測ツール

top

プロセスごとのcpu使用率、メモリ使用率をリアルタイムで確認できる

使い方

top

dstat

サーバーのリソースの使用具合をリアルタイムで確認できる

(開発終了してるらしいので別のに乗り換えた方がいいのかもしれない)

使い方

dstat

journalctl

systemdで動いているサービスのログを確認できる

使い方

make benchを実行すると、最後にjournalctlが立ち上がった状態になる

alp

tkuchiki/alp: Access Log Profiler

アクセスログを解析するツール
オプション指定で同種のリクエスト(e.g. URI内のIDだけ違う)を束ねることができるので便利

どのAPIにどれくらいリクエストが来ていて、それがどのくらい重いのかを調べるのに使う

使い方

make alp

設定の追加

s1/etc/nginx/nginx.conf

log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; + log_format ltsv "time:$time_local" + "\thost:$remote_addr" + "\tforwardedfor:$http_x_forwarded_for" + "\treq:$request" + "\tstatus:$status" + "\tmethod:$request_method" + "\turi:$request_uri" + "\tsize:$body_bytes_sent" + "\treferer:$http_referer" + "\tua:$http_user_agent" + "\treqtime:$request_time" + "\tcache:$upstream_http_x_cache" + "\truntime:$upstream_http_x_runtime" + "\tapptime:$upstream_response_time" + "\tvhost:$host"; - access_log /var/log/nginx/access.log main; + access_log /var/log/nginx/access.log ltsv;

コピペ元: https://github.com/tkuchiki/alp#nginx

alpでの出力用の設定はtool-config/alp/config.yml

pprof

pprof package - net/http/pprof - Go Packages

Go言語用のプロファイリング可視化ツール
アプリケーション内でどこにどのくらい時間がかかっているのかを確認するのに使う

使い方

ベンチマーク中にmake pprof-recordで計測
make pprof-checkでWebUIによる閲覧

WebUIでの閲覧時にはポートフォワードが必要

# ローカルで実行 ssh -L 8090:localhost:8090 isucon@{グローバルIP}

設定の追加

webapp/go/main.go

import文に追加

import (
	_ "net/http/pprof"
    ...
)

main関数に追加

func main() {
    go func() {
		log.Fatal(http.ListenAndServe(":6060", nil))
	}()
    
    ...
}

pt-query-digest

pt-query-digest

スロークエリーログの解析に使う

スロークエリーログ: DBで実行されたクエリのうち実行に時間がかかったもののログ

使い方

make slow-query

設定の追加

s1/etc/ysql/mariadb.conf.d/50-server.cnf

- #slow_query_log_file = /var/log/mysql/mariadb-slow.log + slow_query_log_file = /var/log/mysql/mariadb-slow.log - #long_query_time = 10 + long_query_time = 0
[mariadb] + slow_query_log

改善

実演

参考: ISUCON11 予選問題実践攻略法

ベンチマークを回す前には必ずmake benchを実行する

[おまけ]複数台構成に切り替える(App + DB)

2台目での環境構築

リモートリポジトリの設定まではツールの導入と同じ

pullをすると以下のエラーが出てしまうので

error: The following untracked working tree files would be overwritten by merge:
        Makefile
        webapp/.gitignore
        webapp/NoImage.jpg
        webapp/ec256-public.pem
        ...

これで解決(力技)

git fetch origin main git reset --hard FETCH_HEAD
git branch -m main make set-as-s2 make get-conf git add . git commit -m "s2 init" git push origin main

ユーザーの追加

2台目で操作
make access-dbでデータベースに入れる

# データベース接続ユーザーの確認 mysql> SELECT host,user,password FROM mysql.user; mysql> CREATE USER 'isucon'@'%' IDENTIFIED BY 'isucon'; mysql> GRANT ALL ON *.* TO 'isucon'@'%' WITH GRANT OPTION; mysql> SELECT host,user,password FROM mysql.user;
CREATE USERによるユーザー追加の記法
mysql> CREATE USER '{ユーザー名}'@'{許可するホスト}' IDENTIFIED BY '{パスワード}'; mysql> GRANT ALL ON *.* TO '{ユーザー名}'@'{許可する}' WITH GRANT OPTION;

ISUCONなのでワイルドカード(%)を使って任意のホストからのアクセスを許可する設定にしてしまってOK
DBのport(MariaDBのデフォルトportは3306)を外部に対して開けていなければ攻撃される心配はないです

設定を変更

s2/etc/mysql/mariadb.conf.d/50-server.cnf

# Instead of skip-networking the default is now to listen only on # localhost which is more compatible and is not less secure. - bind-address = 127.0.0.1 + bind-address = 0.0.0.0

s1/home/isucon/env.sh

- MYSQL_HOST="127.0.0.1" + MYSQL_HOST="{2台目のプライベートIPアドレス}" MYSQL_PORT=3306 MYSQL_USER=isucon MYSQL_DBNAME=isucondition MYSQL_PASS=isucon POST_ISUCONDITION_TARGET_BASE_URL="https://isucondition-1.t.isucon.dev" SERVER_ID=s1

ベンチマークを回す前に両方のリポジトリを最新にする&make bnechを実行するのを忘れずに

参考資料

追記

2022/7/22: ハンズオンで使用するMakefileでログローテーションがうまく行えていなかった問題を修正しました