iimon TECH BLOG

iimonエンジニアが得られた経験や知識を共有して世の中をイイモンにしていくためのブログです

入門編: k6を触ってみる

こんにちは、インフラエンジニアのhogeです。本記事はiimonアドベントカレンダー12日目の記事となります。今回は負荷テストツールのk6に入門してみたので、記事にしてみようと思います。

k6を触ろうと思ったモチベーション

負荷テストツールを探していました。負荷テストツールとして真っ先に名前がでてくるのは、Apache JMeterです。最初は歴史が長く、最も情報量が多いのでApache JMeterを使おうと思っていましたが、個人的には以下の難点がありました。

  • GUIでぽちぽちテストシナリオを作る必要がある
  • テスト用のシナリオをXMLでも記述できるが、書きづらそう。可読性が高くない。

上記の難点になっている部分を解消できるシナリオをプログラムで書けるツールを探していたところ、javascriptで書けるk6を発見したので、使ってみることにしました。

k6とは?

https://github.com/grafana/k6

実装言語

  • Go

シナリオ実装言語

特徴

  • リソース消費を最小限に抑え絵得るように最適化されており、パフォーマンスが高い
  • 別のツールを使えば負荷テスト結果をグラフで表示できる
  • Saas版もある

社内で使う利点

  • Javascriptでシナリオを記述できるので実装しやすそう。読める人が多そう。

k6でできないこと

  • k6が実行するJavaScriptのランタイムはNode.jsではないのでNode Modulesが利用できない
  • master/slave構成で並列実行をする仕組みは公式には提供されていない
    • ただし、垂直スケールの効率が良いことが強みとしており、単一のインスタンスで1秒あたり100,000~300,000のリクエスト程度であれば捌けると記述されている(チューニングを頑張れば)
    • k6 cloudなら並列実行できる

k6macにインストールする

単一のバイナリ実行ファイルだけなので、プラットフォーム別のバイナリファイルを配備すれば動く

brew install k6

スクリプトを作成する

test_script.js

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

export default function () {
  const res = http.get('https://www.google.com/');
  check(res, { 'success': (r) => r.status === 200})
}

単一URLを連打するスクリプト

実行すると以下の出力が得られる

代表的な出力メトリクス

  • checks:コード内のcheck()の成功率 上記例では100%
  • http_req_failed:失敗リクエスト数(エラー率・可用性) 上記例では0%
  • http_req_duration:全リクエストの合計レイテンシー 上記例では平均125.83ms
  • http_reqs: リクエスト数 上記例では1リクエス
  • iterations: イテレーション数 上記例では1回

オプションでユーザ数(vus)とduration(期間)を指定することができる 10ユーザ並列で30秒間リクエストを繰り返す場合、以下のようにコマンドを作る

k6 run --vus 10 --duration 30s script.js

テスト結果の詳細を出力することができる

  • ファイル書き出し(csv,json)
  • サービスへのストリーム
    • cloudwatch,datadog,newrelic,InfluxDB

xk6-dashboardダッシュボードで確認してみる

バイナリをgithubのreleaseから取得できるので、解凍して使う

https://github.com/grafana/xk6-dashboard/releases/tag/v0.6.1

tar -zxvf xk6-dashboard_v0.6.1_darwin_amd64.tar.gz

実行する

./k6 run test-script.js --vus 10 --duration 30s --out dashboard=open

http://127.0.0.1:5665/にアクセスすると、ダッシュボードが表示される

先ほどのコマンドの実行結果も表で見やすく表示される

シナリオの自動生成

jsでシナリオを実装するということはクライアント側のコードを再実装することになりそうだと懸念していましたが、k6のコミュニティではシナリオ作成を柔軟にするため、Postman,Swagger,OpenAPI等で定義されているAPIを変換することができる機能が提供されていました。

今回の例では、postmanのcollectionからk6スクリプトを作成します。

postmanのcollectionからk6に変換するツールをインストールします。

npm install -g @apideck/postman-to-k6

collectionをjsonにエクスポートする

exportしたcollectionのjsonを引数に指定し、scriptを作成します

postman-to-k6 test-api.json -e env.json -o k6-script.js

k6-script.jsには以下のようなコードが出力されます(必要に応じて加工して使う必要はあると思いますが)

import './libs/shim/core.js';
import './libs/shim/urijs.js';
import URI from './libs/urijs.js';
import { group } from 'k6';

export const options = {
  maxRedirects: 4,
  duration: '1m',
  vus: 100,
};

const Request = Symbol.for('request');
postman[Symbol.for('initial')]({
  options,
  collection: {
    BASE_URL: 'https://test-api.k6.io/',
  },
  environment: {
    USERNAME: 'test@example.com',
    PASSWORD: 'superCroc2020',
    FIRSTNAME: 'John',
    LASTNAME: 'Doe',
    EMAIL: 'test@example.com',
    ACCESS: null,
    REFRESH: null,
    CROCID: null,
  },
});

export default function () {
  group('Public APIs', function () {
    postman[Request]({
      name: 'List all public crocodiles',
      id: '3ddd46c4-1618-4883-82ff-1b1e3a5f1091',
      method: 'GET',
      address: '{{BASE_URL}}/public/crocodiles/',
    });

    postman[Request]({
      name: 'Get a single public crocodile',
      id: '9625f17a-b739-4f91-af99-fba1d898953b',
      method: 'GET',
      address: '{{BASE_URL}}/public/crocodiles/1/',
    });
  });

先ほどは、コマンドの引数で指定していましたが、コード内からオプションを実行のオプションを指定することもできます

export const options = {
  maxRedirects: 4,
  duration: '1m',
  vus: 100,
};

実行すると以下のような結果を得ることができます

$ k6 run k6-script.js

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

  execution: local
     script: k6-script.js
     output: -

  scenarios: (100.00%) 1 scenario, 100 max VUs, 1m30s max duration (incl. graceful stop):
           * default: 100 looping VUs for 1m0s (gracefulStop: 30s)

     █ Public APIs

     data_received..............: 7.1 MB  117 kB/s
     data_sent..................: 3.0 MB  50 kB/s
     group_duration.............: avg=700.83ms min=349.43ms med=485.62ms max=3.9s     p(90)=1.43s    p(95)=1.8s
     http_req_blocked...........: avg=4.78ms   min=0s       med=2µs      max=486.97ms p(90)=5µs      p(95)=7µs
     http_req_connecting........: avg=2.16ms   min=0s       med=0s       max=208.22ms p(90)=0s       p(95)=0s
     http_req_duration..........: avg=344.91ms min=172.24ms med=212.48ms max=2.11s    p(90)=717.42ms p(95)=1.12s
     http_req_failed............: 100.00% ✓ 17184      ✗ 0
     http_req_receiving.........: avg=99.78µs  min=6µs      med=43µs     max=22.4ms   p(90)=110.7µs  p(95)=178.84µs
     http_req_sending...........: avg=36.18µs  min=3µs      med=20µs     max=9.27ms   p(90)=41µs     p(95)=58µs
     http_req_tls_handshaking...: avg=2.52ms   min=0s       med=0s       max=275.32ms p(90)=0s       p(95)=0s
     http_req_waiting...........: avg=344.77ms min=172.2ms  med=212.36ms max=2.11s    p(90)=717.37ms p(95)=1.12s
     http_reqs..................: 17184   284.158078/s
     iteration_duration.........: avg=700.88ms min=349.47ms med=485.71ms max=3.9s     p(90)=1.43s    p(95)=1.8s
     iterations.................: 8592    142.079039/s
     vus........................: 27      min=27       max=100
     vus_max....................: 100     min=100      max=100

running (1m00.5s), 000/100 VUs, 8592 complete and 0 interrupted iterations
default ✓ [======================================] 100 VUs  1m0s

まとめ

k6の触りの部分を紹介しました。JavaScriptで簡単に実装できて、Postman CollectionやOpenAPI等の既存の資産からコードを生成できるため、サクッと負荷テストを行うことができそうです。 今後は

  • 実用的なシナリオテストを作成する
  • 負荷テスト分析データをInfluxDBやcloudwatchに入れて可視化する
  • 実行環境を考える

等を調べていずれ記事にしたいと思います。

最後に

この記事を読んで興味を持って下さった方がいらっしゃればカジュアルにお話させていただきたく、是非ご応募をお願いします。

Wantedly / Green

次のアドベントカレンダーの記事は白水さんです!どんな記事を書くのかとても楽しみです!

参考

https://techblog.kayac.com/tonamel-installing-stress-test

https://k6.io/

https://speakerdeck.com/fujiwara3/k6niyorufu-he-shi-yan-ru-men-karashi-jian-made

https://k6.io/blog/load-testing-with-postman-collections/