💪

k6でAPI負荷テストしてみる

2024/11/20に公開

3行でまとめ!

  • 🪨 k6を使用した負荷テストの方法をご紹介します!
  • 🤠 5つの負荷テストのパターンをお勉強します!
  • 🐳 Docker環境用意しました docker compose up -dとするだけで試すことができます!

🚀 ソースコードはGitHubで公開しています
https://github.com/zackerms/k6-playground

k6とは?

  • Grafanaが提供するオープンソースの負荷テストツール
  • JavaScriptで負荷テストを記述できる
  • 仮想的なユーザ数や、負荷の大きさ、許容エラー数の閾値設定などを簡単に行える

環境構築

今回は以下の構成でテスト環境を構築します:

  • Go言語で実装したAPIサーバー
  • MySQLデータベース
  • k6負荷テストツール

APIサーバーの実装

コードを公開しているので、APIサーバのエンドポイントだけ、ざっと紹介します。

エンドポイント メソッド 機能
/health GET ヘルスチェック
/users POST ユーザ作成(DBに保存)
APIサーバのコード(抜粋)
// app/main.go
package main

// ...

type User struct {
    ID        int       `json:"id,omitempty"`
    Name      string    `json:"name"`
    Email     string    `json:"email"`
    CreatedAt time.Time `json:"created_at,omitempty"`
}

func main() {
    // ...
    r.POST("/users", createUser)
    r.GET("/health", healthCheck)
    // ...
}

func createUser(c *gin.Context) {
	var user User
	if err := c.ShouldBindJSON(&user); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	result, err := db.Exec("INSERT INTO users (name, email, created_at) VALUES (?, ?, ?)",
		user.Name, user.Email, time.Now())
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	id, _ := result.LastInsertId()
	user.ID = int(id)

	c.JSON(http.StatusCreated, user)
}

func healthCheck(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"status": "healthy",
	})
}

k6で負荷テストを作ってみる

10人のユーザが1秒毎にリクエストを送信するような負荷テストを作ってみます。

何人のユーザで、どれくらいの期間負荷を与えるかという設定を
optionsオブジェクトに指定します。

// script.js
export const options = {
    vus: 10,        // 仮想ユーザー数
    duration: '30s' // テスト実行時間
};

APIリクエストの方法や、レスポンス内容の確認などを関数で定義します。

// script.js
export default function () {
    // APIサーバにリクエスト
    const res = http.get('http://localhost:8080/health');
    
    // レスポンスのチェック(ステータスコードをチェック)
    check(res, {
        'is status 200': (r) => r.status === 200,
    });

    sleep(1);
}

あとは、このスクリプトを実行するだけです!

k6 run script.js

実行すると、10人のユーザがリクエストを送りつける様子が確認できます。

k6の基本的な機能

Check

  • リクエストステータスは正常か
  • レスポンスは正しいか
  • レスポンス時間は一定時間内か

このような条件を設定することができます。

check(res, {
    'status is 201': (r) => r.status === 201,
    'response has user id': (r) => JSON.parse(r.body).id !== undefined,
    'response time OK': (r) => r.timings.duration < 500
});

Threshold

個々のリクエストの成功/失敗を判定するcheckとは別に、
テスト全体の合格/不合格の基準を設定するthresholdがあります。

export const options = {
    thresholds: {
        errors: ['rate<0.1'],  // エラー率10%未満
        http_req_duration: ['p(95)<500'],  // 95%のリクエストが500ms未満
    },
};

Stages

「だんだんと負荷を増やしていきたい」という願いを叶えてくれる機能です。
どれくらいで(duration)、どこまで増やすか(target)を指定するだけで、自動的に制御してくれます。

export const options = {
    stages: [
        { duration: '30s', target: 10 },  // 0→10ユーザーまで30秒かけて増加
        { duration: '1m', target: 10 },   // 10ユーザーを1分間維持
        { duration: '30s', target: 0 }    // 10→0ユーザーまで30秒かけて減少
    ]
};

メトリクス

テストが完了すると以下のようなメトリクスが一覧で表示されます。

基本的なメトリクス 説明
checks チェックの成功率
data_received 受信データ量
data_sent 送信データ量
http_req_blocked TCP接続待ち時間
http_req_duration リクエスト全体の所要時間
http_req_failed 失敗したリクエスト数
http_req_receiving レスポンス受信時間
http_req_sending リクエスト送信時間
http_req_waiting サーバー処理待ち時間
http_reqs 総リクエスト数
iterations 反復回数
vus 仮想ユーザー数

負荷テストパターン

k6を使用して、以下の5つの異なるテストパターンを実装します。

1. スモークテスト(基本機能確認)

基本的な機能が正常に動作することを確認するための軽量なテスト。

export const options = {
    vus: 2,              // 2人の仮想ユーザー
    duration: '30s',     // 30秒間の実行
    thresholds: {
        errors: ['rate<0.1'],  // エラー率10%未満
        http_req_duration: ['p(95)<500'],  // 95%のリクエストが500ms未満
    },
};

2. スパイクテスト(急激な負荷上昇)

突発的なトラフィック増加への対応を確認するテスト。

export const options = {
    stages: [
        { duration: '20s', target: 10 },   // 通常負荷
        { duration: '10s', target: 50 },   // スパイク
        { duration: '30s', target: 50 },   // スパイク継続
        { duration: '10s', target: 10 },   // 通常負荷に戻る
        { duration: '10s', target: 0 },    // 終了
    ],
};

3. ストレステスト(限界検証)

システムの限界を探るためのテスト。

export const options = {
    stages: [
        { duration: '30s', target: 20 },   // 徐々に上昇
        { duration: '1m', target: 50 },    // さらに上昇
        { duration: '1m', target: 100 },   // ピーク
        { duration: '30s', target: 0 },    // スケールダウン
    ],
};

4. 耐久テスト(長時間安定性)

長時間の安定性を確認するテスト。

export const options = {
    stages: [
        { duration: '30s', target: 30 },   // 徐々に上昇
        { duration: '3m', target: 30 },    // 3分間維持
        { duration: '30s', target: 0 },    // スケールダウン
    ],
};

5. ブレークポイントテスト(限界点特定)

システムが破綻するポイントを特定するテスト。

export const options = {
    stages: [
        { duration: '30s', target: 50 },   // 初期負荷
        { duration: '30s', target: 100 },  // 中負荷
        { duration: '30s', target: 200 },  // 高負荷
        { duration: '30s', target: 300 },  // より高負荷
        { duration: '30s', target: 0 },    // クールダウン
    ],
};

まとめ

JavaScriptで簡単に負荷テストが作れてしまうk6、ステキ!

[宣伝] komichi というアプリを作っています!

「どこ行こうかなぁ〜」と考えてたら休日が終わることってありませんか?
このアプリを使うと、現在地や好きな場所から、自動的に付近の場所を検索してお出かけプランを提案してくれます!
見知った場所でも意外としらないスポットが出てきて面白いですよ!
https://komichi.app

先日、Android版をリリースいたしましたので、Androidユーザの方はぜひインストールお願いします!
https://play.google.com/store/apps/details?id=app.komichi&hl=ja

参考リンク

Discussion