Blog

k6で負荷テストを行った話

この記事は、リレーブログ企画「24新卒リレーブログ」の記事です。

はじめに

こんにちは。
初めまして、新卒1年目のけにです。

現在OJT期間で課金システムチームに所属しており、社内向けのAPIの開発に携わっています。そのAPIに対して負荷テストを行うにあたり、チーム内で負荷テストツールとしてk6を採用するという話になりました。

k6は簡単にテストシナリオを書けるツールでありながら、非常に強力な負荷テストを実行することができます。本記事では、そのk6について基本的な使い方からinfluxDBとGrafanaを用いた結果の可視化方法まで、調べた内容について自分の理解の整理も含めて記事にしたいと思います。

負荷テストを行う理由

現在のAPI開発において、ユーザー数の急激な増加や予期しないトラフィックの集中が起こることを想定し、それら状況下においてもシステムが安定して機能することが求められます。そのため、パフォーマンスの検証、限界値の測定、リソース使用状況の把握など、対象のシステムの品質や信頼性、リソースの最適化などを目的とする負荷テストは非常に重要な役割を持ちます。

今回私達のチームでは、開発したAPIが実用に耐えうる構成となっているのかについて実際のアクセス数を元に確認することを主な目的として、スパイクテストとストレステストを行いました。

  • 想定される範囲で急激な負荷の上昇が起きたとしても想定通りの速度が出せるか
  • 想定される以上の高負荷がかかった場合でもAPIは正常に動き十分なレスポンス速度を出すことができるか
  • 長時間負荷がかかった場合にどのような挙動をするのか

などです。また、負荷テストを実行した際に

  • CPUやメモリなどのリソースは現在の構成で適切なものになっているか
  • API / サーバーの設定は適切なものになっているか

についても確認を行いました。

これらにより、システムの限界値や潜在的な問題点を事前に把握し必要な対策を講じることで、システムの品質と信頼性を向上させユーザー満足度を高めることに繋がります。

ツールの選定

負荷テストを行うことができるツールは様々あります。
それらのうち、無料で利用できる7つを比較してみます。

ツール名公式シナリオ記述言語結果確認
方法
詳細
k6k6.io, githubJavaScriptgrafana / CUI / CloudGolangで開発され、高負荷環境でもスムーズに動作、拡張性が高い
Apache Benchapache.orgCLICUI単一のURLへのリクエストを生成するツールのため、シナリオベースでWebアプリケーションをテストすることには向いていない
Apache JMeterjmeter.apache.org, githubGUI / JavaGUI拡張性が高く、結果可視化方法が豊富
Tsungtsung, githubXMLtsung-recorderErlang言語で開発され、データベースやメッセージングシステムのテストを行える
Gatlinggatling.io, githubScala.htmlJavaVM上で動作
Vegetavegeta, githubGolang.html / plotコマンド同時接続数やリクエストレートの詳細な制御が可能
Locustlocust.io, githubPythonwebサーバスケーラブルで、分散実行が可能
負荷テストツールの比較

私達のチームでは、主にPythonを使用していることから Locust と、今後チーム内に導入するにあたって学習コストが低い JavaScript で記述でき、現在も盛んに開発が行われている k6が選択肢に上がりました。それらの内、OSSでアップデートが盛んに行われているため新しい技術に対応可能である点から、k6を採用することにしました。

実行環境

準備

今回は以下のGrafanaが用意しているk6のリポジトリを使用します。
k6で行った負荷テスト結果をinfluxDBに保存し、Grafanaを用いて可視化します。

エンドポイントについては、今回はこちらを使います。

これは、k6 の実験用HTTP、 WebSocket API のコレクションとなっています。
このうち https://test-api.k6.io/public/crocodiles/{id} に対して負荷テストを行います。
注意点として、これは共有テスト環境であるため高負荷テストは避ける必要があります。

APIの挙動

curlコマンドを用いて一度APIを叩いてみます。
叩くことができるidの一覧は以下の結果のとおりです。

それを元に対象のAPIを叩いてみます。返却値は以下のようになっています。

プロキシ設定の変更

本リポジトリでは、k6 → influxdb → grafana の順にデータが送られます。
コンテナ間の通信が発生するため、~/.docker/config.json にproxyが設定されていると503エラーとなる可能性があります。そのため、設定をしている人は修正が必要になります。

シナリオ

シナリオの選定

条件毎に、必要なシナリオに応じてパターンを考える必要があります。
特にGrafanaでは、負荷テストの種類を6つ挙げています。(参考

テスト名時間内容
Smoke test数分、数秒   最小限の負荷でシステムの機能を検証し、基準となるパフォーマンス値を収集するテスト
Average-load test中(5〜60分)標準的な負荷下でシステムがどのように動作するかを評価するテスト
Stress test中(5〜60分)トラフィックのピーク時の負荷でシステムがどのように機能するかを調べるテスト
Spike test長時間突然の使用状況の急増に対してシステムが耐えて機能するかどうかを検証するテスト
Breakpoint test数分システムの限界を計測するために行うテスト
Soak test必要な限り数時間から数日の長期間行う平均負荷テスト
負荷テストのシナリオ例

シナリオの実装

k6 テスト スクリプトでは、load_test.js内のoptionsオブジェクトを使用してシナリオを構成できます。
各シナリオには一意の名前を付け、executor タイプとその構成を指定する必要があります。

上記は、スパイクテストとストレステストの例です。

  • スパイクテスト
    • ramping-vus executorを使用
    • 仮想ユーザーの秒間アクセス数を5分掛けて 0 から 100 まで徐々に増やし、2分掛けて 0 まで減らしている
  • ストレステスト
    • constant-vus executorを使用
    • 10 分間、秒間50回の仮想ユーザーからのアクセスを保っている

シナリオを複数指定している場合、上から順に実行されます。
executorには他にも種類があり、それぞれ記述方法が異なります。

参考:https://k6.io/docs/using-k6/scenarios/executors/#all-executors

負荷テストの実行

今回は上記のうち、ストレステストとスパイクテストを行ってみます。

実行コード

起動画面

結果の確認

1. k6

実行結果

確認できるメトリクスは以下の通りになります。

メトリクス名説明
data_received受信したデータ量
data_sent送信されたデータ量
http_req_blockedリクエストを開始するまでにブロックされた時間
http_req_connectingリモートホストとのTCPコネクションの確立にかかった時間
http_req_durationリクエストを送ってから帰ってくるまでの時間
(http_req_sending + http_req_waiting + http_req_receiving)
http_req_failedリクエストに失敗した数
http_req_receivingリモートホストからの応答データ受信にかかった時間
http_req_sendingリモートホストへのデータ送信にかかった時間
http_req_tls_handshakingリモートホストとのTLSセッションのハンドシェイクにかかった時間
http_req_waitingリモートホストからの応答待ち時間
http_reqs生成した HTTP リクエストの合計数
iteration_duration1回のイテレーションの実行に要した時間
iterationsvirtual usersが実行したデフォルト関数の回数
vusvirtual users、同時にアクセスする仮想ユーザー数
vus_max仮想ユーザーの最大可能数
確認できるメトリクスの一覧

特に確認する必要があるのは http_req_duration , http_reqs, http_req_failedの3つです。
リクエストにかかった時間、リクエストの数、失敗した数です。

この結果から、このAPIのレスポンス速度は約200ms程度であり、ほとんどの場合に於いてその速度を出していることがわかります。

平均値最小値中央値最大値90パーセン
タイル
95パーセン
タイル
成功数失敗数
203.49ms176.55ms200.83ms1.21s216.69ms223.42ms3112回0回
実行結果

2. grafana

コンテナを立ち上げた際に立ち上がっているため、そちらにアクセスします。

アクセス先:http://localhost:3000/dashboards

これでは結果が分かりにくいため、凡例とクエリを修正します。

修正先:grafana/dashboards/xk6-output-influxdb-dashboard.json

▶︎ 修正コード

修正後結果

凡例をID毎に設定したことで、各IDのレスポンス速度が可視化できるようになりました。

凡例名概要
ALL全ID平均の統計値 (クエリを追加)
各ID (タグで変更可能) (凡例を修正)対象IDの統計値
Active VUs (Virtual Users)動いたVirtual Users の数
RPS (Requests Per Second)1秒あたりのリクエスト数
設定した凡例
  • 修正前
  • 修正後

全APIのレスポンスの平均速度

別途クエリを作成し表示しています。
毎秒毎に平均を算出したことで、全IDの平均値の推移を可視化するようにしました。
このAPIでは、レスポンス速度の平均が197msであることがわかり、ほぼ一定の速度を出せていることがわかります。

VUs (Virtual Users)

このグラフは一秒間に何回のアクセスがあったかを表しています。
シナリオの通り、開始2秒でアクセス数が20まで跳ね上がり、その後2分をかけて徐々に数が減っている様相が見て取れます。

RPS (response per second)

直前の一秒間に帰ってきたリクエストの数を返しています。
時間経過で徐々に減少していることが見て取れ、想定通りの挙動をしていることがわかります。

3. データのインポート

コンテナを立ち上げた際に立ち上がっているため、そちらにアクセスします。
csv形式でデータを保存する場合の手順は以下の通りです。

  1. influxDB (http://localhost:8086/) にアクセスする
  2. Data → Buckets → demo に遷移する
  3. フィルター設定をする (demo / http_req_duration を選択) (その他フィルターは場合により設定)
  4. submit を押す
  5. 下図赤枠のボタンを押す

おわりに

今回はinfluxdb, grafanaを用いたk6の使い方についてまとめてみました。k6は簡単に使えるツールでありながら、非常に強力な負荷テストを実行することができます。influxDBとGrafanaを組み合わせることで、テスト結果を視覚的に分析することも可能になります。

他にも、使用しているサーバーのCPU使用率やRAM使用率の計測や、発生したエラーの調査などのアプローチが必要になります。

はじめにも触れている通り、そういった手順が品質や信頼性の担保に繋がります。システムのパフォーマンスを把握し、潜在的な問題を早期に発見するために非常に重要です。そのため負荷テストは単なる技術的な作業ではなく、ユーザー体験の向上やビジネスの成功に直結する取り組みとなります。

今回紹介した手法を参考に、皆さんのプロジェクトでより安定したシステム運用につなげていただければ幸いです。

次回は、後藤さんです。どんな記事になるか楽しみですね!

ニフティでは、
さまざまなプロダクトへ挑戦する
エンジニアを絶賛募集中です!
ご興味のある方は以下の採用サイトより
お気軽にご連絡ください!

Tech TalkやMeetUpも開催しております!
こちらもお気軽にご応募ください!

connpassでニフティグループに
参加いただくと
イベントの
お知らせが届きます!