ローソンデジタルイノベーション テックブログ

ローソンデジタルイノベーション(LDI)の技術ブログです

JMeter に疲れてしまった。。。そんなあなたに k6(2) - 複数APIによる負荷試験

LDIで開発しているOKです。

前回に続き k6 ついて書きます。 今回はよりサーバの負荷試験を意識して、複数のAPIをそれぞれ別の rps (Request / Seconds)で負荷をかける際のやり方です。

実際に動いているサービスだとAPI毎に負荷って違いますよね。 「API①もAPI②も全部 10rps です」なんて事は無いと思うので、 そういった時の書き方です。

K6 環境構築

前回 の環境に加えて、 mix.js を追加しています。

k6Demo/
  - docker-compose.yml
  - src/
    - get.js
    - post.js
    - mix.js
  - env/
    - staging.yml
    - production.yml    

k6 のスクリプト作成

前回説明していなかった post.js と 今回追加した mix.js を書いていきます。

post.js

まずは post.js ですが、ほぼ前回の get.js と同じように書けます。 違いは body を指定しているのと URLとか名前くらいです。

import http from 'k6/http';
import { check, sleep, group } from 'k6';

// 1. 初期化
// 2. API実行前の処理
export function setup() {
    console.log(__ENV.ENV)
}

// 3. API実行
export default function() {
    group('PostGroup', postAPI);
}

export function postAPI() {
    const body = `{
        "hoge": "hogehoge"
    }`
    const res = http.post(__ENV.HOST + `/k6demo/post`, body, {
        tags: {
            name: "PostAPI",
        },
    });
    const checkStatus = check(res, {
        'status was 200': (r) => r.status === 200,
    });
    if (!checkStatus) {
        return;
    }
    const checkBody = check(res, {
        'body was OK': (r) => r.json().result === 'POST',
    });
    if (!checkBody) {
        return;
    }

    sleep(1);
}

mix.js

続いて mix.js です。

/**
 複数APIのパフォーマンスを測るテスト
 書き方は https://k6.io/docs/using-k6/scenarios/ を参照
 exec には実施するメソッド名を記載
 */
import { group } from 'k6';
import { getAPI } from "./get.js";
import { postAPI } from "./post.js";

export const options = {
    scenarios: {
        // Get APIのシナリオ
        // 例) 5分間 5rps(rate/timeUnit) exec を実行する
        get_scenario: {
            executor: 'constant-arrival-rate',
            duration: '5m',
            rate: 5,
            timeUnit: '1s',
            preAllocatedVUs: 2,
            maxVUs: 10,
            exec: 'get'
        },
        // Post APIのシナリオ
        // 例) 5分間 1.5rps(rate/timeUnit) exec を実行する
        post_scenario: {
            executor: 'constant-arrival-rate',
            duration: '5m',
            rate: 3,
            timeUnit: '2s',
            preAllocatedVUs: 2,
            maxVUs: 10,
            exec: 'post'
        },
    },
};

export function setup() {
    console.log(__ENV.ENV)
}

export function get() {
    group('GetGroup', getAPI);
}

export function post() {
    group('PostGroup', postAPI);
}

今回の肝になるのは 10行目から記載されている options に指定した scenarios になります。 ここに実行する API の設定を書いていきます。

まずは executorconstant-arrival-rate を指定してください。
これは 期間内 (timeUnit) に、指定のリクエスト数 (rate) を反復して繰り返す というような負荷をかけてくれます。
「rate/timeUnit」 で rps を計算できる形です。

その他のパラメータはこんな感じです。 詳細は こちら

項目 説明
duration 負荷をかけ続ける期間
preAllocatedVUs 負荷をかけ始める前に事前に保持しておくVU数(Virtual Users)
maxVUs 許容する最大のVU数(思ったよりパフォーマンスがでない場合にVU数をあげすぎないようにする感じかな)
exec そのシナリオで実行するメソッド名

k6 実行

次のコマンドで実行します。 今回は、mix.js に負荷の設定があるので、 --vus--duration の指定の必要がありません。

docker-compose -f docker-compose.yml run --rm k6 run --out csv=/output/result.csv /src/mix.js

実行すると以下のようにサマリが出力されます。 残念な事にサマリの結果はシナリオ毎に出してくれないです。

          /\      |‾‾| /‾‾/   /‾‾/   
     /\  /  \     |  |/  /   /  /    
    /  \/    \    |     (   /   ‾‾\  
   /          \   |  |\  \ |  (‾)  | 
  / __________ \  |__| \__\ \_____/ .io

  execution: local
     script: /src/mix.js
     output: InfluxDBv1 (http://influxdb:8086)

  scenarios: (100.00%) 2 scenarios, 20 max VUs, 5m30s max duration (incl. graceful stop):
           * get_scenario: 5.00 iterations/s for 5m0s (maxVUs: 2-10, exec: get, gracefulStop: 30s)
           * post_scenario: 1.50 iterations/s for 5m0s (maxVUs: 2-10, exec: post, gracefulStop: 30s)

INFO[0000] Docker環境                                      source=console

running (5m00.6s), 00/10 VUs, 1944 complete and 0 interrupted iterations
get_scenario  ✓ [======================================] 00/07 VUs  5m0s  5.00 iters/s
post_scenario ✓ [======================================] 00/03 VUs  5m0s  1.50 iters/s

     █ setup

     █ PostGroup

       ✓ status was 200
       ✓ body was OK

     █ GetGroup

       ✓ status was 200
       ✓ body was OK

     checks.........................: 100.00% ✓ 3888     ✗ 0   
     data_received..................: 271 kB  900 B/s
     data_sent......................: 222 kB  737 B/s
     dropped_iterations.............: 6       0.019961/s
     group_duration.................: avg=1s       min=1s       med=1s       max=1.29s   p(90)=1s       p(95)=1s      
     http_req_blocked...............: avg=41.12µs  min=2.4µs    med=9.18µs   max=7.12ms  p(90)=15.06µs  p(95)=56.23µs 
     http_req_connecting............: avg=15.64µs  min=0s       med=0s       max=6.12ms  p(90)=0s       p(95)=0s      
     http_req_duration..............: avg=2.06ms   min=479.29µs med=1.82ms   max=52.51ms p(90)=3ms      p(95)=3.69ms  
       { expected_response:true }...: avg=2.06ms   min=479.29µs med=1.82ms   max=52.51ms p(90)=3ms      p(95)=3.69ms  
     http_req_failed................: 0.00%   ✓ 0        ✗ 1944
     http_req_receiving.............: avg=208.93µs min=12.34µs  med=128.93µs max=6.68ms  p(90)=399.84µs p(95)=553.98µs
     http_req_sending...............: avg=130.05µs min=9.41µs   med=63.05µs  max=3.56ms  p(90)=260.15µs p(95)=395.01µs
     http_req_tls_handshaking.......: avg=0s       min=0s       med=0s       max=0s      p(90)=0s       p(95)=0s      
     http_req_waiting...............: avg=1.72ms   min=425.94µs med=1.52ms   max=52.42ms p(90)=2.46ms   p(95)=2.98ms  
     http_reqs......................: 1944    6.467257/s
     iteration_duration.............: avg=1s       min=121.91µs med=1s       max=1.29s   p(90)=1s       p(95)=1s      
     iterations.....................: 1944    6.467257/s
     vus............................: 10      min=6      max=10
     vus_max........................: 10      min=6      max=10

結果の見方(詳細)

個々のAPIの結果について確認したい場合は out で指定した csv 側を確認していきます。

画像を見てわかる通り name の列 で GetなのかPostなのか判別ができます。
これは、ソースのなかで tags として指定したものが出力されています。(post.js だと 21行目)

他にも group など APIの判別方法はいくつかありますが、group だと、実行するメソッドの中で複数のAPIを呼び出すようなケースの場合、それらは同じ名前の group で出力されます。 なので、どういった単位でまとめたいかで調整すると良いかと思います。

k6出力結果

あとは、 http_req_durationname でフィルターすれば、API毎の応答時間なんかを見る事ができます。

あとがき

前回に続き、今回は k6 についてより突っ込んだ使い方の1つを紹介しました。 これ以外にもいろんな方法があるので、お使いのサービスに合わせてより良い負荷の掛け方をチューニングしてもらうと良いと思います。

次回は結果の見方について、より深掘りできたらと思います。
それではまた!!