grasys blog

負荷試験ツールをざっくり調査

前書き


こんにちは!!
grasy新人エンジニアtokuraです。
8月にgrasysに入社して、毎日勉強することがいっぱいで充実している日々を過ごしています。

さて、最近私は負荷試験でサーバーを調整する業務を担当しました。
主に、負荷がかかるサーバーなどの性能の調整をしていたのですが、負荷試験ツールそのものについて興味持ったので調べてみました。

実際に使用していたのはLocustでしたが、他にも負荷試験のツールは多くあります。
その中で気になった3つを調査してみます。

  • Locust
    業務で使用した負荷試験ツールです。
    pythonベースのオープンソースの負荷試験ツール。
    シナリオはpythonで記述されて、webUIでリアルタイムのモニタリングができる。
    また、シナリオがpythonで書くため条件の設定がわかりやすく、複雑なユーザー条件を試験しやすいらしい
    語源はイナゴ。

  • Apache Jmeter
    負荷試験で調べたらlocustかjmeterの二つがよく名前に挙がっていました
    Javaベースのオープンソース負荷試験ツール。
    HTTP、JDBC、LDAP、SOAPなど多くのプロトコルに対応。
    調べた限りでは、専用の用語が多いので慣れるまでが大変という声が多かったです。
    公式ドキュメントの索引ページを初めて見た時これを・・・読む・・・?と面食らいました。

  • K6
    負荷試験ツールとして良さそうな感想が見受けられました。
    Grafana Labs産のJavaScriptベースの負荷試験ツール。
    Grafana Labsが作ってるだけあって視覚的なデータの表示ができるらしい。
    出力される画面が親切。



それぞれ、インストールして簡単に動かしてみます。
インストール先の環境はGCPにVMを立てて以下のようになっています。

$ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.3 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.3 LTS (Jammy Jellyfish)"

APIについて、負荷をかけることになるので簡単なもの自作してみました。

from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/api', methods=['GET'])
def my_api():
    data = {"message": "Hello, World!"}
    return jsonify(data)

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5000)

こちらの単純にHello, World!が表示されるwebサーバーを対象にそれぞれの負荷試験ツールを動かしてみます。

Locust

公式ドキュメント ( https://docs.locust.io/en/stable/ ) に沿ってインストール

今回はpipのパッケージでインストールしました

$ pip3 -V
pip 23.2.1 from /usr/local/lib/python3.12/site-packages/pip (python 3.12)
$ locust -V
locust 1.4.3

簡単にAPIをリクエストするシナリオを作成。

from locust import HttpUser, task, between

class QuickstartUser(HttpUser):
    wait_time = between(1, 2.5)

    @task
    def hello_world(self):
        self.client.get("/api")

同じVMのサーバーで実行するので、localhostで実行!

locust -f hello.py --host=http://localhost:5000

ここで適当にuserとかspawn rateを上げて、かかる負荷を調整する。
今回はとりあえずUser=2で実行してみる

2023-12-25T06:08:49Z
[2023-12-25 06:08:49,980] tokura-test-web01/INFO/locust.main: Shutting down (exit code 0)
Type     Name                                                 # reqs      # fails |    Avg     Min     Max    Med |   req/s  failures/s
--------|---------------------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
GET      /api                                                     68     0(0.00%) |      3       3       5      4 |    1.13        0.00
--------|---------------------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
         Aggregated                                               68     0(0.00%) |      3       3       5      4 |    1.13        0.00

Response time percentiles (approximated)
Type     Name                                                         50%    66%    75%    80%    90%    95%    98%    99%  99.9% 99.99%   100% # reqs
--------|-------------------------------------------------------|--------|------|------|------|------|------|------|------|------|------|------|------
GET      /api                                                           4      4      4      4      4      5      5      5      5      5      5     68
--------|-------------------------------------------------------|--------|------|------|------|------|------|------|------|------|------|------|------
         Aggregated                                                     4      4      4      4      4      5      5      5      5      5      5     68

Pythonについてあまり明るくありませんでしたが、flaskのフレームワークを使って簡単なAPIサーバーを作ってシナリオを作成することができました。これがLocustの良さかもしれません。
もっとPythonが書けたら細かい条件とか色々設定できるので幅がすごく広がりそうです。

Apache Jmeter

こちらも公式ドキュメント ( https://jmeter.apache.org/download_jmeter.cgi ) に沿ってインストールします。

wget https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.6.2.tgz
tar -zxvf apache-jmeter-5.6.2.tgz

展開したapache-jmeter-5.6.2内の/bin/jmeterをリンク貼るなり、PATHを通すなりしてjmeterコマンドとして使えるように設定。
(最初、間違えてソース版をダウンロードして、ApacheJMeter.jarがありませんといった感じのエラーが出ていてハマりかけて、バイナリ版をダウンロードし直しました。)

$ jmeter -v
    _    ____   _    ____ _   _ _____       _ __  __ _____ _____ _____ ____
   / \  |  _ \ / \  / ___| | | | ____|     | |  \/  | ____|_   _| ____|  _ \
  / _ \ | |_) / _ \| |   | |_| |  _|    _  | | |\/| |  _|   | | |  _| | |_) |
 / ___ \|  __/ ___ \ |___|  _  | |___  | |_| | |  | | |___  | | | |___|  _ <
/_/   \_\_| /_/   \_\____|_| |_|_____|  \___/|_|  |_|_____| |_| |_____|_| \_\ 5.6.2

Copyright (c) 1999-2023 The Apache Software Foundation

このときRDP設定をしていないVMで設定してて、GUIが使えないので、完全に非GUIでテストプランファイルを作ろうとしてハマってしまいました。
GUIが使えないと、インストール時に一緒にインストールされているテンプレートファイルからテストプランファイルを作成するのですが、設定するステータスを理解するのが大変でした。

Jmeterについての知識がない場合、RDP設定をすませたVMか、GUIが利用できる環境で設定した方が良さそうです。
ただ何とか設定して、Hello, World!にリクエストを送ることはできました。

$ jmeter -n -t jmeter-hello.jmx
Creating summariser <summary>
Created the tree successfully using jmeter-hello.jmx
Starting standalone test @ 2023 Dec 25 08:02:29 UTC (1703491349460)
Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
summary =      2 in 00:00:01 =    3.4/s Avg:    32 Min:     5 Max:    60 Err:     0 (0.00%)
Tidying up ...    @ 2023 Dec 25 08:18:38 UTC (1703492318088)
... end of run

jmxファイルがあまりに奥が深いです…。
さらにJmeterは歴史あるツールなので、URL ( https://jmeter-plugins.org/wiki/Start/ )のような多くのプラグイン拡張によって設定できるシナリオを詳細に設定できることや、今Jmeterで困っていることは先人たちが困っていて解消されている可能性が高いという強みがあります。

K6

こちらも公式(https://k6.io/docs/get-started/installation/ )に沿ってインストール

sudo gpg -k
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6
k6 new

このコマンドでテスト用にhttps://test.k6.ioへAPIを取得するscript.jsを作成してくれます。

最後のブロックで、localhostに変更してtest.jsを作成。

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

export const options = {
  // A number specifying the number of VUs to run concurrently.
  vus: 10,
  // A string specifying the total duration of the test run.
  duration: '30s',
};

// The function that defines VU logic.
//
// See https://grafana.com/docs/k6/latest/examples/get-started-with-k6/ to learn more
// about authoring k6 scripts.
//
export default function() {
  http.get('http://localhost:5000/api');
  sleep(1);
}

以下のような結果が出力されます。


$ k6 run test.js

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

  execution: local
     script: test.js
     output: -

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


     data_received..................: 59 kB 2.0 kB/s
     data_sent......................: 25 kB 825 B/s
     http_req_blocked...............: avg=346.15µs min=122.44µs med=245.81µs max=4.89ms   p(90)=588.67µs p(95)=788.61µs
     http_req_connecting............: avg=242.85µs min=67.13µs  med=151.02µs max=4.74ms   p(90)=450.49µs p(95)=662.38µs
     http_req_duration..............: avg=4.14ms   min=997.02µs med=3.02ms   max=32.62ms  p(90)=6.42ms   p(95)=7.99ms
       { expected_response:true }...: avg=4.14ms   min=997.02µs med=3.02ms   max=32.62ms  p(90)=6.42ms   p(95)=7.99ms
     http_req_failed................: 0.00% ✓ 0       ✗ 300
     http_req_receiving.............: avg=457.8µs  min=49.96µs  med=234.39µs max=3.47ms   p(90)=1.18ms   p(95)=1.69ms
     http_req_sending...............: avg=77.13µs  min=20.33µs  med=56.03µs  max=758.24µs p(90)=133.87µs p(95)=234.47µs
     http_req_tls_handshaking.......: avg=0s       min=0s       med=0s       max=0s       p(90)=0s       p(95)=0s
     http_req_waiting...............: avg=3.6ms    min=866.3µs  med=2.4ms    max=32.23ms  p(90)=5.67ms   p(95)=7.7ms
     http_reqs......................: 300   9.94293/s
     iteration_duration.............: avg=1s       min=1s       med=1s       max=1.04s    p(90)=1s       p(95)=1.01s
     iterations.....................: 300   9.94293/s
     vus............................: 10    min=10    max=10
     vus_max........................: 10    min=10    max=10


running (0m30.2s), 00/10 VUs, 300 complete and 0 interrupted iterations
default ✓ [======================================] 10 VUs  30s

さらにk6 run でオプションを色々追加して、出力されるメトリクスに閾値を設定したり、出力をJSON形式にしてinfluxdbにしたりと流石GrafanaLabsが作成してるだけあって、見やすい形に出力するための仕組みが随所に見られました。

あとがき

ざっくり調べた結果ですので、訂正・ご指摘等あればご教示ください。大変、勉強になります。
他にもいろんな負荷試験ツールがまだまだありましたが、結局はツールなのでそれぞれに優劣は無く、負荷試験の目的や使用環境に応じて適切なものを選ぶのが大事だと感じました。
最終的にはなんでもできるエンジニアになるのが目標なんですが、ひとまずは、ツールや負荷についての理解を深めて、どんな試験でもお任せあれと胸を張って言えるようなエンジニアを目指していきます。


採用情報
お問い合わせ