Slide 1

Slide 1 text

ISUCON作問入門 2023.08.26 ISUCON夏祭り 2023 @fujiwara 藤原俊一郎

Slide 2

Slide 2 text

@fujiwara (Twitter, GitHub, Bluesky) 面白法人カヤック SREチーム ISUCON (1,2,5,11) (6) (4) 運営4回 (3,8,12,13) 代表作 github.com/kayac/ecspresso Amazon ECS デプロイツール

Slide 3

Slide 3 text

今日の目標 「聞いた人が社内/身内ISUCONを作って開催するぞという気持ちになること」 実際に開催されると泣いて喜びますのでぜひ報告を…

Slide 4

Slide 4 text

用語集 実は統一された用語集がない(!?)ので今回の発表内での定義 問題 = お題、参照実装、初期実装とも 選手が高速化するWebサービス(アプリケーション)を指す 作問者 = ISUCONの問題を作る人 選手 = 競技者とも。ISUCONに参加する人、問題を解く人

Slide 5

Slide 5 text

ISUCONの構成要素 問題 Webアプリケーション(サービス)の言語実装 データベースやWebサーバーなどのミドルウェア、OSが動くサーバー ベンチマーカー 問題に対して負荷を掛けてスコアを計測するプログラム ポータル 選手がベンチマーク実行をリクエストするWebアプリケーション 結果を保存、表示する レギュレーション ルール、スコア計算方法など 作問者が作るもの = 全部

Slide 6

Slide 6 text

作問について 「過去のISUCONでよくある例」を踏まえて 身内のISUCONを作るにはこうしてもいいんじゃない? という話をします

Slide 7

Slide 7 text

問題のネタ・テーマ ISUCON12予選 マルチテナント ISUCON12本選 スマホゲーム 自分達が苦しんでいること、過去に 苦しんだこと、社内のシステムや サービスをネタにすると考えやすい とはいえISUCONという競技を楽し みたいのであれば、題材はなんでもよい

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

作成するWebアプリケーションの形式 昔: HTMLを返すWebアプリケーション (ISUCON 5,6あたりまで) 今: JSONを返すWebAPI (ISUCON 7以降) 画面はSPA的に分離することが多くなった 今時のアプリケーションはHTMLを返さないことが多い ベンチマーカーがHTMLを解釈するコスト(CPU)が高い HTMLはテンプレート編集時にうっかり壊しやすい レギュレーションで「見た目を変更しないこと」があると… どちらでもよいが、いっそJSON APIだけでもいいのでは…?

Slide 10

Slide 10 text

問題の規模 API数は多くても10〜15程度(20あると相当大規模) DBのテーブル数は一桁個(10個越えると多すぎ) 選手が把握しきれない、コードを読み切れない 作問者がつらい(単純に量が多い) 変な穴ができる可能性が増える(やたら稼げてしまうポイントなど) なるべく機能を絞り込んでシンプルに

Slide 11

Slide 11 text

Webフレームワーク / ORM ISUCONの初期実装でよく使われているもの Go: Echo Ruby: Sinatra Python: Flask Node.JS: Express 移植の都合上、比較的シンプルなものが選択される ORMは使わないことが多い (SQLを直接書く) 自分らが馴染みがあるものを使うのもよいのでは Railsがメインな企業ならRailsで、などやってみてほしい

Slide 12

Slide 12 text

データベース 過去問はMySQLがほとんど (ごく稀に PostgreSQL, SQLite) MySQLを普段使っていないなら自分らが馴染みがあるものを使うほうがよい 正規化はわりとちゃんとされていることが多い 非正規化するとパフォーマンス上有利になりがち 正規化されているとN+1問題を作りやすい

Slide 13

Slide 13 text

アプリケーション/ミドルウェアの起動方法 1. systemdから直接プロセスを起動 言語ランタイムをOSに直接インストールする必要がある 2. Docker(Compose)で起動 OSに言語ランタイムを用意しなくてよい 過去にはどちらの例もあるが… 選手が楽なのは直接起動 作問者が楽なのはDocker nginx, mysqldなどは直接起動、アプリケーションだけDockerというのもあり 社内なら特定プラットフォーム(クラウド)を前提にできる コンテナ、サーバーレス、FaaSなどでやるのもあり(やってほしい!)

Slide 14

Slide 14 text

ISUCON問題によくある技術要素 DBにインデックスがない primary keyしかないのでフルスキャンが多発する N+1問題 ループ中でSQLを発行するので大量のクエリが飛ぶ 不適切なコマンド呼び出し system() などで不要な外部コマンドを呼び出す あまり不自然に重く、汚く書く必要はない 知識が少ない人が素朴に書いたらこうなっちゃいました、ぐらいがよい

Slide 15

Slide 15 text

問題について ここまでのまとめ 問題はシンプル/小規模に いっそ画面はなくてもいいのでは…? 正直、API仕様だけでも競技できる気がする… 普段馴染みのある技術で作ってよい フレームワーク、データベース、クラウドなど 過去のISUCONが使った技術に縛られる必要はない 実装は素朴にしておく 変な罠とかは考えないでよい

Slide 16

Slide 16 text

では実際に作っていきましょう = Webサービスの高速化コンテスト = 機能を壊さずに高速化するコンテスト 問題 パフォーマンスがよろしくないが機能的には問題ないWebサービス ベンチマーカー 問題に対して負荷を掛けてスコアを計測するプログラムだが 機能が壊れていないか検証するE2Eテストでもある 両方セットで開発していく必要がある

Slide 17

Slide 17 text

例題 - GitHub Gistのようなサービス POST /api/login ログイン POST /api/logout ログアウト GET /api/gists 自分のGist一覧 GET /api/gists/:id Gistの詳細 POST /api/gist Gistの作成 public, privateが選択できる GET /api/all_gists すべての(他のユーザーのものを含む)Gist一覧 publicなもの+自分のprivateなものが時系列順に並ぶ これで6 API。物足りなければ とか編集・削除とかコメントとか機能追加すればよい

Slide 18

Slide 18 text

問題とベンチマーカーと初期データ生成器をセットで実装する ISUCONと普通のWebアプリのテストの違い ISUCONの問題は「パフォーマンスに問題を抱えている」必要がある → それなりの規模の初期データが必要 ベンチマーカーは問題のDBを参照できない → 問題に含まれる要素は選手が管理する。ベンチマーカーがアクセスできない 「初期データ生成器」によって以下を用意する それなりの規模のデータセット ベンチマーカーが使う事前知識 既存ユーザーの認証情報 初期データに含まれる内容(検証用にサンプリングしたもの) これがないと「初期データを全部消す」チートが可能になってしまう

Slide 19

Slide 19 text

例題(Gistみたいなやつ)で生成する初期データの例 ユーザー情報 (認証情報を含む) ユーザーが投稿したgistの情報 // users [ {"id":1,"name":"tagomoris","password_digest":"..."}, {"id":2,"name":"941","password_digest":"..."}, ... ] // gists [ {"id":"af3aece0d","user_id":1,"title":"...","content":"...","is_private":true,"created_at":"..."}, {"id":"cc6125c19","user_id":2,"title":"...","content":"...","is_private":false,"created_at":"..."}, ... ] faker系のライブラリを使うなどして作りましょう

Slide 20

Slide 20 text

ベンチマーカーは何で書く? (実装言語) Goで書く ISUCON 3以降、ほぼ全てのベンチマーカー実装はGo https://github.com/isucon/isucandar (ISUCON10以降で採用) ISUCON本 付録B にisucandarの使い方が書いてあります! 既存の負荷試験ツールで書く たとえば Grafana K6 https://k6.io/ ISUCON本 4章にk6の使い方が書いてあります! あなたの好きな言語で書く 高速・大量の並行処理が必要なので言語によっては大変かも…

Slide 21

Slide 21 text

作問の流れ 1. WebアプリケーションのAPI仕様、DB構造を決める 2. 初期データ生成器を作る 初期実装用のDBに取り込めるデータを出力 ベンチマーカーの事前知識(JSONのファイル)を出力 データセットのサイズはあとで調整するので可変できるように 3. Webアプリケーションを実装する 4. 実装した仕様を満たしているか確認するE2Eテストシナリオを書く これがベンチマーカー(の一部) 事前知識を元にして検証を行う

Slide 22

Slide 22 text

ベンチマーカーの実行フェーズ 最近のISUCONベンチマーカーの動作は2フェーズに分かれている 1. 整合性チェックフェーズ 問題アプリケーションが仕様を満たしているかを検証する 途中で失敗したら負荷走行しないで終了する(競技体験のため) i. 問題の initialize APIを呼び出して初期状態にする ii. 事前知識を元にしてE2Eテストを実行する ここで全てのAPIを網羅する 2. 負荷走行フェーズ i. 複数のシナリオを平行で走らせて、問題に負荷を掛ける ii. 規定時間(大抵は1分)に達する、打ち切り条件(エラー数など)になるまで実行 iii. スコアを算出して終了

Slide 23

Slide 23 text

例題の整合性チェックフェーズのシナリオ例 1. POST /api/login でログイン 2. GET /api/gists で自分のGist一覧を取得 3. GET /api/gists/:id で自分のGistの詳細を取得 × n 4. POST /api/gist でGistを作成(private) 5. GET /api/gists で自分のGist一覧を取得 4.で作成したGistが含まれていることを確認 6. GET /api/gists/:id で 4.で作成したGistを取得 7. GET /api/all_gists ですべてのGist一覧を取得 4.で作成したGistが含まれていることを確認 8. POST /api/logout でログアウト 9. GET /api/all_gists ですべてのGist一覧を取得 4.で作成したGistが含まれていないことを確認(privateなので)

Slide 24

Slide 24 text

整合性チェックでチェックすべきこと 正常系のリクエストに対して正常なレスポンスが返されるか 異常系のリクエストに対して仕様通りのレスポンスが返されるか (例: ログインしていない状態でprivate gist一覧は出ない) どちらも仕様に対して網羅的にやる必要がある 特に異常系のチェックを念入りに キャッシュしてはいけない内容をキャッシュしてもpassするような穴になるため 「ベンチマーカーが仕様違反を検出しない」=「結果は有効」 ベンチマーカーを常に正としておかないと結果の判定に恣意的な解釈が入ってしまう (とはいえ身内向けなら、最悪ごめんなさいで済むかも)

Slide 25

Slide 25 text

負荷走行フェーズでやること 整合性チェックシナリオを最低1個走らせておく 複数平行で実行した場合に壊れないように注意 データを作成→一番最初に反映されている のような判定は危険 同一ユーザーのシナリオを同時に回すと競合が発生しやすいので避ける 整合性チェックよりもチェックが緩い/単純なシナリオを複数走らせる 厳密にチェックしているとベンチマーカーの負荷が高くなることがある とはいえステータスコードとタイムアウトぐらいはちゃんと見る スコアをカウントする 失格(fail)条件で打ちきる failの場合、1分を待たずにすぐ終わったほうが競技体験がよい 条件はレギュレーションに明示しておくこと

Slide 26

Slide 26 text

ベンチマーカーの並列度 「負荷走行中に並列度を動的に変化させるかさせないか」 正直これは派閥がある 「ベンチマーク」== ある負荷に対しての性能を計測するもの なので ベンチマーカーが動的に負荷を変化させてはいけないよ派 初期実装の遅い状態でちゃんとスコアが計測できつつ 最後数百倍に高速化した状態でスコア差がつく負荷を与えるためには 並列度を動的にコントロールしないと無理じゃない?派

Slide 27

Slide 27 text

並列度固定派の実装で気を付けること クライアントは負荷走行中の最後までタイムアウトしない タイムアウトでの減点や失格をしない 「5秒でレスポンスを返さないとエラー、エラー10個でfailです」 【理由】 極端に遅い初期実装に対して大量の固定負荷を掛けると 長時間かかるレスポンスが多発する 初期実装でfailしてしまうと選手が改善する手がかりが得られない シナリオで送るリクエスト間には意図的なsleepを入れずに レスポンスが届いたら即次のリクエストを送るように実装する

Slide 28

Slide 28 text

動的負荷可変派の実装で気を付けること 「5秒以内にレスポンスを返すこと」のような仕様を設けておく 一定時間内にタイムアウトが発生しなかった場合 並列度を増加させるようなロジックを組む タイムアウト発生=過負荷とみなしてそれ以上並列度を増やさない シナリオでリクエストを送る場合 ユーザーの実際の行動を模した間隔を開けてリクエストすることができる

Slide 29

Slide 29 text

選手に負荷を選択させるかどうか(昔話) ISUCON 3,4では選手が負荷を選択できた ( -workload オプション) 何並列でリクエストを送るか(を指数化したもの) 結論からするとあまり良くない(ので廃れた) そもそも設定があることに気が付かない ずっと低い負荷のままで競技が終わってしまった人が… チューニングせずworloadを変えて試してしまう人が出る そこでガチャをされても困る 極端に大きいworkloadを指定することでベンチマーカーの挙動がおかしくなり 謎に高いスコアが出る不具合(を突いた人がいた) 境界値をチェックするべきとはいえ…

Slide 30

Slide 30 text

ポータルはどうする? 本物ISUCONのポータルは大変高機能 1000人以上の参加者でスムーズな運営をするためには必要なので… 参加登録、チームメンバー管理 GitHub/Discordのアカウント連携 競技用サーバーを起動するためのCloudFormation Template ベンチマーカーの実行 結果の集計、表示をするダッシュボード clar(clarifications)のやりとり 運営側のチーム管理機能 ISUCON 10〜12で使われたもの https://github.com/isucon/portal/ 動かすのは相当大変(Rails, MySQL, Redis, SQS...) 身内向けにはオーバースペック

Slide 31

Slide 31 text

ポータルを低コストでなんとかする 身内向けISUCONで最低限必要な機能は2個だけ! 1. ベンチマーカーの実行 2. 結果の集計、表示をするダッシュボード これだけなんとかする logicaさんのRemote-BMIもどうぞ! https://github.com/logica0419/remote-bmi

Slide 32

Slide 32 text

最小限のリモートベンチマークWebアプリケーション 1. POST / を実装する リクエスト元IPアドレスに対してベンチマーカーを実行する ./bench -target http://{ リクエスト元IP アドレス}/ ベンチマーカーの標準出力をHTTPのレスポンスにして返却する 2. 選手は競技サーバーで curl -XPOST http://{ ベンチマーカーのアドレス}/ ベンチマーカーの実行結果がレスポンスで返ってくる! ベンチマーカーは別プロセス(バイナリ)で外部コマンド呼び出しするのを推奨 ベンチマーカーは不意に(実装の不備で)死ぬことがある 組み込んでしまうと死んだ場合の結果を選手に返せない

Slide 33

Slide 33 text

最小限のダッシュボード ISUCONの結果 == スコア(数値) なので 「メトリックを収集できるなにか」にスコアを送信してグラフを書いてやればよい Amazon CloudWatchで実装した例 (カヤック社内ISUCON 2022)

Slide 34

Slide 34 text

身内向けISUCON作問のまとめ 問題はシンプルに APIだけでもよいと思います 普段馴染みのある技術で作ろう 本物ISUCONに縛られなくてもよい ベンチマーカーは……頑張って! 今のところISUCON本が世界で唯一の作問者向け解説本です ポータルは最低限でなんとか (ネタ帳にあったけど入らなかったこと) /initialize問題, password hashの話, スコアの配点...