目次
前書き
こんにちは!!
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が作成してるだけあって、見やすい形に出力するための仕組みが随所に見られました。
あとがき
ざっくり調べた結果ですので、訂正・ご指摘等あればご教示ください。大変、勉強になります。
他にもいろんな負荷試験ツールがまだまだありましたが、結局はツールなのでそれぞれに優劣は無く、負荷試験の目的や使用環境に応じて適切なものを選ぶのが大事だと感じました。
最終的にはなんでもできるエンジニアになるのが目標なんですが、ひとまずは、ツールや負荷についての理解を深めて、どんな試験でもお任せあれと胸を張って言えるようなエンジニアを目指していきます。