×
Copy
Open
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
k6による負荷試験 入門から日常的な実践まで 2025.03.26 Re:TechTalk#01 @fujiwara 藤原俊一郎
Slide 2
Slide 2 text
@fujiwara さくらインターネット株式会社 クラウド事業本部 ISUCON 1,2,5,11 優勝4回 ISUCON 3,8,12,13 運営(出題)4回 github.com/kayac/ecspresso Amazon ECS デプロイツール github.com/fujiwara/lambroll AWS Lambda デプロイツール
Slide 3
Slide 3 text
おことわり このトークは2023年6月の 「TechFeed Exports Night#20」 で発表したものを元に加筆修正したものです
Slide 4
Slide 4 text
今回話す「負荷試験」の範囲 測定対象のWebサービス(アプリケーション)に対して 機械的にリクエスト(HTTPなど)を送信して 「性能(パフォーマンス)」を計測する ところで 「性能」 とは…?
Slide 5
Slide 5 text
負荷試験をする「前」に考えること ゴールを決める レイテンシ(レスポンスタイム)が目標? どのレスポンスを何秒で返せればOKなのか 平均値? 中央値? P99? max? そのレイテンシが保てる最大の並列アクセス数は? スループット(単位時間内に処理できるリクエスト数)が目標? レイテンシ要件はない? レイテンシ1secで1,000並列 レイテンシ10secで10,000並列(どちらも1,000req/sec) あるサーバーリソースでのxxの最大化が目標?
Slide 6
Slide 6 text
負荷試験をする「前」に考えること その試験で測りたいものを言語化する レイテンシ スループット サーバーリソースの利用率・利用量(コストも含む) 運用中に実際に起きるどのような状況で、これらの数値目標を達成するのか 【具体的な例】 状況 : Push通知から○○分以内に○○人のユーザーがアプリを起動する シナリオ : アプリが起動してホーム画面に遷移するときに発生するAPIアクセス 目標 : 平均レイテンシ250msec、P99 2sec、サーバーエラーなし 詳しくは ISUCON本 1章へ
Slide 7
Slide 7 text
負荷試験をする「前」にやること 負荷をかける対象のサーバーリソースはできるだけ固定する →「AutoScaling」的なものを止めておく 負荷試験には 「変数が多い」 変数の例: シナリオ(測定対象URL)、リクエストの送信レート、並列度… 固定できないもの(最近多い)は、そのサイズ(キャパシティ)を同時に記録しておく
Slide 8
Slide 8 text
負荷試験をする「前」にやること サーバー側のメトリクスを本番同様に取得して、ダッシュボードを作る 最低でも以下を一目で見られる状態に 各種CPU使用率 HTTP スループット(req/sec)、レイテンシ、statusコード別 データベースへのクエリ数(など) アプリケーションサーバー固有の情報 同時リクエスト処理数(worker数) job queueがあるならそのメトリクス ネットワークトラフィック
Slide 9
Slide 9 text
ツールの選び方 最初におすすめ - k6 https://k6.io JavaScriptでシナリオが書ける (Node.JSではない) 実行エンジンは Go (軽量、省メモリ)
Slide 10
Slide 10 text
他のツール/自作を検討したほうがいい場合も リクエストやレスポンスbodyが特殊 (k6でもgRPC, GraphQLはサポート) gRPC https://k6.io/docs/using-k6/protocols/grpc/ GraphQL https://k6.io/blog/load-testing-graphql-with-k6/ JavaScriptよりも他の言語が得意な人が多い場合 複数シナリオ間で連係や同期を取る必要がある場合 ゲームのレイドバトルとか 自作用Go言語フレームワーク isucandar https://github.com/isucon/isucandar ISUCON本 付録Bに解説あり
Slide 11
Slide 11 text
シナリオの作り方 まず単一URL連打から import http from 'k6/http' import { check } from 'k6' export default function () { let res = http.get('https://example.com/') check(res, { 'success': (r) => r.status === 200 }) } いきなり複雑なものは書かない
Slide 12
Slide 12 text
k6で実行 最初は「並列度(vus=virtual users)」= 1 から $ k6 run --vus 1 --duration 300s simple.js checks.............: 99.98% ✓ 52644 ✗ 6 http_req_duration..: avg=285.56ms min=32.93ms med=144.61ms max=30.05s p(90)=521.34ms p(95)=788.36ms http_reqs.. ......: 52650 172.404676/s (いっぱい出力があるけど)とりあえずこの3個を見る checks: コード内の check() の成功・失敗数 レイテンシ: http_req_duration スループット: http_reqs
Slide 13
Slide 13 text
結果を評価する 1. k6の出力とサーバー側のメトリクスに齟齬がないか →メトリクスの取得がおかしい可能性が高いので見直す 2. checksは想定通りか →エラーが目標/想定より多い場合は性能を評価する意味がない 原因を調査する 3. レイテンシは目標に達しているか →これ以上並列度を増やすのはたいてい無意味 ボトルネックの調査をする 4. スループットは目標に達しているか or →並列度を2倍にして変化を見る 無闇に変数を変えて試さない
Slide 14
Slide 14 text
結果を評価しながら並列度/サーバーリソースを増やしていく 並列度=1で エラー率/レイテンシが目標に達している スループットが目標に達していない →並列度を増やすことでスループットが増加する「可能性がある」 並列度を増やしていくとある時点で… スループットが上がらない エラー率/レイテンシが目標に達しなくなる →「サーバーリソースを増強する」ことでレイテンシが改善/スループットが増加する 「可能性がある」 無闇にサーバーリソースを変えて試さない(時間の無駄)
Slide 15
Slide 15 text
シナリオを増やしていく 1. 参照系のリクエスト/画面遷移を足す 2. ログイン/更新系のリクエストを足す (コードの具体例はISUCON本 4章に) 本番のアクセスパターンと負荷を網羅しようと思うのはやめよう(大変) 1. 一連のシナリオごとに実行する 例: トップ→ログイン→マイページ→お知らせを見る これだけでも特定の箇所で大きな問題がある場合には改善点が分かる 2. 複数のシナリオを混ぜて実行する 複数シナリオを実際に想定される程度の割合で混ぜる
Slide 16
Slide 16 text
負荷試験中に気を付けること 負荷は5分以上掛ける メトリクスの解像度が1分の場合、実行開始と終了時を除いた3分間のメトリ クスしか信用できない エラー率/レイテンシが目標に達しない場合、並列度を増やさない たいてい状況は悪化するので時間の無駄 常に「目標」を意識して負荷を与える、結果を評価する ベンチマーカーがボトルネックになっていないか気を付ける シナリオが重い、ツールが速くない場合、クライアントが先に根を上げる
Slide 17
Slide 17 text
クライアント側で起きる問題の頻出例 (ISUCON本 9章を参照) ネットワーク帯域が上限を打っている 1Gbpsの回線で1MBのレスポンスを取得→(たったの)125req/sec(=125MB/sec) HTTP Keep-Aliveが無効になっている TCP 3 way handshake / TLS handshake のオーバーヘッドが大きい 無駄にファイルディスクリプタ(ソケット)を使う Goで自作した場合にやりがち: http.Response.Body を全て読み切らない →次のリクエスト時は新規のTCP接続になる ファイルディスクリプタを使い果たしている ulimit -n (max open files)がデフォルトの1024 ローカルポート(エフェメラルポート)が枯渇している 大量/高速にリクエストを送受信すると使い果たす
Slide 18
Slide 18 text
日常的に負荷試験をする 負荷試験=取りかかるのが大変(というイメージ) 小さいシナリオから始める 日常的に実行する 定期的に実行する 負荷試験を「日常の一部」にする→「いざという時にもすぐ取りかかれる」
Slide 19
Slide 19 text
日常に組み込む具体例 あるプロダクトで… 「ElastiCache Redisのバージョンアップ(フェイルオーバー)をしたい、 (ドキュメントによれば) ほぼ無停止でできるはずだが、 怖いのでメンテナンスを入れたい」 こういうときにも負荷試験ツールは役に立つ 「負荷試験」=「実際のリクエストをある程度再現して送信し続ける」 負荷試験を実行しながらフェイルオーバーを実行 → 何が起きるかを検証すればよい
Slide 20
Slide 20 text
用意したシナリオ 1. トップページにアクセス 2. トップページから呼ばれるAPI(認証なし)を複数並列で叩く 3. ログインフォームにアクセス 4. ユーザー名とパスワードをPOSTしてログインする 5. ログインセッションが必要なAPI(要認証)を叩く コンポーネント(CDN, LB, WebApp, MySQL, Redis)を一通り通過する ストレージからの読み込み、書き込み処理が両方ある これを回しながら作業すると、各コンポーネントのエラーの発生と回復が検知できる
Slide 21
Slide 21 text
「不安だからメンテ入れましょう」からの脱却 どれぐらいエラーが起きるか分からない どれぐらい復旧に時間が掛かるか分からない 「怖いので」サービス停止メンテナンスを入れてやりたい 知らない = 怖い だけなので、試せばよい 検証環境で負荷を掛けた状態でバージョンアップ(Failover)する 実際にどれぐらいのエラーが発生して、回復するか観察する 普段発生しているエラー数、頻度と比較してメンテナンスの必要性を考える (設定ミスなどで)回復がうまくできない状態も検知できる
Slide 22
Slide 22 text
本番でも負荷試験リクエストを流しながらメンテナンス 本番でも「軽い」負荷をかけながらメンテナンスするといいこともある フェイルオーバーなどでアプリケーション-ミドルウェア間の通信が切断 → アプリケーションでエラーが発生する 次のリクエストで再接続されて解消する アクセスがそれほど多くないサービスの場合、 「エラーを踏む」役目を 負荷試験のリクエストが肩代わりしてくれる k6 executor という概念があり、送信するリクエストのレートや並列度を制御できる https://grafana.com/docs/k6/latest/using-k6/scenarios/executors/
Slide 23
Slide 23 text
GitHub Actions等でも日常的に実行 k6 は CLI ツールなので GitHub Actions などでも簡単に実行できる リポジトリにシナリオを置いておく 本番/ステージング/開発環境等に向けられるようにしておくと便利 on: workflow_dispatch: inputs: target: description: "target to load test" required: true type: string jobs: load_test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: grafana/setup-k6-action@v1 - uses: grafana/run-k6-action@v1 with: path: | ./load_tests/${{ inputs.target }}.js
Slide 24
Slide 24 text
まとめ 負荷試験はやる「前」の準備が大事 ゴールと目標の設定 いたずらに変数を増やさないために準備 負荷試験をやっているときは「評価」が大事 結果を評価してから次の試行を いたずらに試行錯誤で時間を無駄にしない 負荷を与える側にもハマりどころはいろいろある クライアント側のボトルネックに気がつかないと時間を無駄に 日常的に負荷試験していくとよい 小さいシナリオから育てていく 本番でも負荷試験リクエストを流しながらメンテナンスする