📈

【k6】1日のアクセス推移に基づいた負荷テスト

2023/07/04に公開

はじめに

今回は負荷テストツール「k6」を使って負荷テストを実施したので、その記録を記事にして残しておこうと思います。

k6とは

「k6」はオープンソースの負荷テストツールで、JavaScriptでテストシナリオを記述することができ導入のハードルが低いのが特徴です。
ちなみにk6自体はGoで動いており、それだけでなんかモダンに感じてしまいます。

今回使用するk6はローカルにインストールし実行するタイプですが、クラウド上で負荷テストを実行できるk6クラウドというサービスもあります。
https://k6.io/

実施したテスト

  • ロードテスト:通常の利用で想定される負荷での動作を検証
  • ストレステスト:極端に大きな負荷での動作を検証
  • ロングランテスト:長時間負荷をかけ続けた際の動作を検証

この中からロードテストで実際に使用したスクリプトを紹介しようと思います。

いざ実践!

インストールに関して記載は省きますが、公式でos毎にインストール手順が公開されているので特に難しいことはなかったです。

テストデータ

どの程度の負荷をかけるかは、タイトルにある通り1日のリクエスト数の推移をもとに考えます。
今回はリクエスト数が1日に約330万件送られる場合を想定し、今回は下図のように朝、昼、夜にそれぞれリクエスト数のピークが来るよう設定してみました。
こちらの推移を再現できるようスクリプトを作成していきます。

使用したスクリプト

使用したExecutorはRamping Arrival Rateでこれは区切られた時間内で変動するイテレーション回数を実行するものになっています。
duration(反復時間)を1時間に設定することで、1時間ごとにイテレーションのレートがtargetValues内の値で変化するよう設定しました。
これにより24時間のリクエスト推移の再現を行いました。

import http from 'k6/http';
import { check } from "k6";

export const options = {
  discardResponseBodies: true,
  scenarios: {
    contacts: {
      executor: 'ramping-arrival-rate',
      // timeUnit毎の開始時のイテレーション
      startRate: 1000,
      // 単位時間
      timeUnit: '1h',
      // 事前に割り当てるVUの数
      preAllocatedVUs: 10,
      // VUの最大値
      maxVUs: 1000,
      
      stages: generateStages(),
    },
  },
};

function generateStages() {
  // イテレーションレート
  const targetValues = [
    110000, 70000, 60000, 50000, 40000, 40000, 60000, 90000, 120000, 230000,
    190000, 200000, 230000, 180000, 170000, 170000, 170000, 190000, 190000,
    180000, 220000, 210000, 190000, 160000
  ];
  //反復時間
  const duration = '1h';
  
  const stages = [];
  for (let i = 0; i < targetValues.length; i++) {
    const target = targetValues[i];
    const stage = { target, duration };
    stages.push(stage);
  }

  return stages;
}

export default function () {
  const url = 'https://example.com'  //リクエストURL
  const random_id = Math.random().toString(32).substring(2); //id
  const now = new Date().toISOString(); //時刻

  //リクエストbody
  const payload = JSON.stringify({
      "type":"page_view",
      "params":{
          "id":random_id,
          "page_id":"001",
          "title":"top-page",
          "time":now,
        }
  })

  //リクエストheader
  const params = {
    headers: {
      'Content-Type' : 'application/json;charset=UTF-8',
      'Origin' : 'https://example.com',
    },
  }
  
  //リクエスト送信
  const response = http.post(url, payload, params);
  check(response, {
    'status is 200': (r) => r.status === 200,
  })
}

実行結果

このスクリプトを実行した結果は以下の通りです。
checksを見てみると約320万件のリクエストを送信し、失敗したリクエストが4件なのでまずまずの結果といったところですね。
ただ、http_req_waitingを見てみると最大で29秒もの応答待機時間が発生しています。さすがに許容範囲を超えているのでアーキテクチャ側のコンソールなどを確認しボトルネックを特定する必要がありますね。また今後のストレステストでも注視する必要がありそうです。

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

  execution: local
     script: ./sample.js
     output: -

  scenarios: (100.00%) 1 scenario, 50 max VUs, 24h0m30s max duration (incl. graceful stop):
           * contacts: Up to 63.89 iterations/s for 24h0m0s over 24 stages (maxVUs: 5-50, gracefulStop: 30s)

     ✗ status is 200
      ↳  99% — ✓ 3220478 / ✗ 4

     checks.........................: 99.99%  ✓ 3220478   ✗ 4      
     data_received..................: 1.9 GB  23 kB/s
     data_sent......................: 2.2 GB  26 kB/s
     dropped_iterations.............: 18      0.000217/s
     http_req_blocked...............: avg=20.9µs   min=0s      med=0s      max=1.33s    p(90)=0s      p(95)=0s      
     http_req_connecting............: avg=6.3µs    min=0s      med=0s      max=1.05s    p(90)=0s      p(95)=0s      
     http_req_duration..............: avg=70.04ms  min=16.24ms med=65.63ms max=29.09s   p(90)=80.42ms p(95)=115.53ms
       { expected_response:true }...: avg=70.01ms  min=16.24ms med=65.63ms max=5.11s    p(90)=80.42ms p(95)=115.53ms
     http_req_failed................: 0.00%   ✓ 4         ✗ 3220478
     http_req_receiving.............: avg=281.24µs min=0s      med=0s      max=602.88ms p(90)=911µs   p(95)=999.7µs 
     http_req_sending...............: avg=164.78µs min=0s      med=0s      max=105.48ms p(90)=514.5µs p(95)=568.5µs 
     http_req_tls_handshaking.......: avg=10.54µs  min=0s      med=0s      max=436.62ms p(90)=0s      p(95)=0s      
     http_req_waiting...............: avg=69.6ms   min=0s      med=65.21ms max=29.09s   p(90)=79.93ms p(95)=114.94ms
     http_reqs......................: 3220482 38.894769/s
     iteration_duration.............: avg=70.53ms  min=18.96ms med=66.12ms max=29.09s   p(90)=80.88ms p(95)=116ms   
     iterations.....................: 3220482 38.894769/s
     vus............................: 4       min=0       max=19   
     vus_max........................: 23      min=5       max=23   


running (23h59m59.9s), 00/23 VUs, 3220482 complete and 0 interrupted iterations
contacts ✓ [ 100% ] 00/23 VUs  24h0m0s  44.44 iters/s

おわりに

今回はk6で1日のアクセス数を再現したスクリプトを作成し、ロードテストを実施しました。
正直まだまだ経験が浅いのでもっといいやり方や便利なオプションなどがあるんだろなーと思いつつもそれっぽい実行結果を得ることができて一安心です。
今後も負荷テストにはk6を使用していく予定なのでガンガン触っていきたいと思います。

参考文献

https://k6.io/docs/get-started/installation/
https://qiita.com/hajimeni/items/40c0fd6a86e758be43d5
https://dev.classmethod.jp/articles/lets-try-k6/
https://yuya-hirooka.hatenablog.com/entry/2020/12/28/221756

株式会社アクティブコア

Discussion