🤖

負荷試験ツール k6 を Docker Compose で速攻で動かす

2022/07/12に公開

💁‍♂️ はじめに

株式会社EGSTOCKの日下繕章です。
現在担当している案件で負荷試験を行なうツールを検討していたところ、k6がイケてるということなので検証がてらDocker Composeを使用してk6を動かしてみた

https://k6.io/

公式サイト: https://k6.io/
GitHub: https://github.com/grafana/k6

📃 k6の特徴

  • JSでテストロジックを記述できるため保守性・再利用性が高い
  • テストロジックと設定オプションがJSで記述できるのでバージョン管理が容易
  • HTTP/1.1, HTTP/2, WebSocket, gRPCプロトコルをサポート
  • checksthresholds などのCI向けのサポートがある

🏃‍♂️ 動かしてみる

とりあえずgit cloneしてサンプルで付属しているHTTPのGET通信のシナリオをDocker Composeで動かしてみる

$ git clone https://github.com/grafana/k6.git
$ cd k6 
$ docker compose up influxdb grafana -d
$ docker compose run --rm -T k6 run - < samples/http_get.js

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

  execution: local
     script: -
     output: InfluxDBv1 (http://influxdb:8086)

  scenarios: (100.00%) 1 scenario, 1 max VUs, 10m30s max duration (incl. graceful stop):
           * default: 1 iterations for each of 1 VUs (maxDuration: 10m0s, gracefulStop: 30s)


running (00m00.7s), 0/1 VUs, 1 complete and 0 interrupted iterations
default ✓ [ 100% ] 1 VUs  00m00.7s/10m0s  1/1 iters, 1 per VU

     data_received..................: 21 kB 29 kB/s
     data_sent......................: 446 B 600 B/s
     http_req_blocked...............: avg=526.25ms min=526.25ms med=526.25ms max=526.25ms p(90)=526.25ms p(95)=526.25ms
     http_req_connecting............: avg=198.15ms min=198.15ms med=198.15ms max=198.15ms p(90)=198.15ms p(95)=198.15ms
     http_req_duration..............: avg=215.06ms min=215.06ms med=215.06ms max=215.06ms p(90)=215.06ms p(95)=215.06ms
       { expected_response:true }...: avg=215.06ms min=215.06ms med=215.06ms max=215.06ms p(90)=215.06ms p(95)=215.06ms
     http_req_failed................: 0.00% ✓ 0        ✗ 1
     http_req_receiving.............: avg=87µs     min=87µs     med=87µs     max=87µs     p(90)=87µs     p(95)=87µs    
     http_req_sending...............: avg=82.4µs   min=82.4µs   med=82.4µs   max=82.4µs   p(90)=82.4µs   p(95)=82.4µs  
     http_req_tls_handshaking.......: avg=259.22ms min=259.22ms med=259.22ms max=259.22ms p(90)=259.22ms p(95)=259.22ms
     http_req_waiting...............: avg=214.89ms min=214.89ms med=214.89ms max=214.89ms p(90)=214.89ms p(95)=214.89ms
     http_reqs......................: 1     1.346112/s
     iteration_duration.............: avg=741.48ms min=741.48ms med=741.48ms max=741.48ms p(90)=741.48ms p(95)=741.48ms
     iterations.....................: 1     1.346112/s

こんな感じであっさり実行できて結果が取得できた

数値についてはこちらで解説してくれていた
https://qiita.com/hajimeni/items/40c0fd6a86e758be43d5#項目

Grafana
http://localhost:3000/dashboards
上記URLでk6 performance test選択するとビジュアライズされた結果を参照できる

🔧 設定ポイント

samples/http_scenario.jsを準備し実際の負荷試験を想定した設定サンプル

import http from "k6/http";
import { group, check } from "k6";
import { scenario } from 'k6/execution';

export const options = {
	// シナリオ設定
	scenarios: {
		'scenarios': {
			executor: 'shared-iterations', // 複数のVUでiterationを共有
			vus: 2, // 同時接続数
			iterations: 2, // シナリオの総反復回数
			maxDuration: '30s', // 試験の実行時間
		},
	},
	// テスト対象システムの期待性能の合格・不合格を判断する閾値
	thresholds: {
		http_req_failed: ['rate<0.01'], // エラーが1%を超えない
		http_req_duration: ['p(95)<200'], // リクエストの95%は200ms以下であること
	},
};

export function setup() {
	// テスト実行前に実行
	console.log("setup");
}

export default function () {
	
	// scenario.iterationInTestはシナリオを通して1テスト毎に一意な値を取得可能
	const uuid = ( '10000000' + scenario.iterationInTest ).slice( -8 );

	// 認証を行う通信を実行
	const params = {
		headers: {
			'Content-Type': 'application/json',
		}
	};
	const requestBody = JSON.stringify({
		uuid: uuid
	});
	let accessToken;
	group("/xxxxx/login", () => {
		const response = http.post('http://localhost/api/xxxxx/login', requestBody, params);
		check(response, {
			'is status 200': (r) => r.status === 200,
		});
		accessToken = response.json().res.accessToken;
	});

	// アクセストークンの必要な通信実行
	const authParams = {
		headers: {
			'Content-Type': 'application/json',
			'Authorization': 'Bearer '+ accessToken,
		}
	};
	group("/xxxxx/xxxxx", () => {
		const response = http.post('http://localhost/api/xxxxx/xxxxx', null, authParams);
		check(response, {
			'is status 200': (r) => r.status === 200,
		});
	});
}

export function teardown() {
	// テスト実行後に実行
	console.log("teardown");
}

下記で実行

$ docker compose run --rm -T k6 run - < samples/http_scenario.js
EGSTOCK,Inc.

Discussion