【負荷試験】ブラウザ上で JMeter を操作できるツールを作ってみた話【Flanker】

こんにちは。
突然ですが、皆様は負荷試験を実施したことはございますでしょうか?
私は仕事柄、時折、負荷試験案件に携わることがございまして。
あくまで私のケースですが、おおよそ下記のようなフェーズを辿ることが多いように感じています。

1. 試験設計 ( 方針設計 / 目標決め / シナリオ作成 etc )
2. JMeter 環境構築
3. 負荷試験環境調整 ( 申請 / モニタリング環境 etc )
4. JMeter 実行 ( シナリオ調整 / JMeter 調整 含む )
5. 調査 / 解析
6. 「4」と「5」のエンドレス…

「1」に関してはお客様と詳細なやり取りを行い、いわば要件定義に近い折衝を行う必要がございまして、シナリオ作成に関しては API 仕様書を元に、JMeter から発行するリクエスト内容を定義していかなくてはなりません。

「2」や「3」に関しては、 Ansible 等のツールで省力化に取り組んでいるのですが、その他のフェーズでは依然として雑多なタスクが重なり、大変ストレスがマッハでございました。

経緯

さて、つきましては、雑多なタスク減を目的に、この度「4」の JMeter 操作周りを WEB ベースで完結できるツールを作ってみようと思った次第でございます。
始めに必要な機能を洗い出し、実装する機能を下記 5 ポイントに絞りました。

・シナリオ ( .jmx ) のアップロード
・シナリオ内 Thread group コンポーネントの調整 ( 負荷強度の調整等 etc )
・クラスタリングした JMeter に紐づく Worker ノードの操作
・JMeter の実行、および非同期での進捗確認
・実行結果生成

前提として JMeter を扱うのですから、 最終的には JMeter の WEB クライアントを作る形となります。

既に JMeter 環境のデプロイ用途で Ansible を使っているのもあり、ツールの実行環境含めて Playbook に内包しました。

できたもの

・Flanker

https://github.com/snkk1210/flanker

ツール、および JMeter 環境のデプロイは、下記 Ansible の Playbook を流していただければ完了いたします。

※ 詳細は README を参照ください。

https://github.com/snkk1210/jmeter-MS

実際にツールを使って案件を進めてみたのですが、かなり仕事が楽になったように思います。
※ 作った人間ばいあす

これからも、これ系のツールを作って業務の効率化を進めていきたいと思います。
わぁい。

awk で特定の行の値を取得して計算してみる(Linux のモニタリングツールを作る)

Linux のモニタリングツールといえば、dstat、sarなんかが有名だと思いますが、環境によってはツールが入っていないこともしばしば。
というわけで、linux にプリインストールされているコマンドで必要最低限のモニタリングツールを用意してみます。

ログ調査とかテキストの整形とかでお世話になる awk ですが、実は計算が出来るんですよね。
例えば、 cat /proc/meminfo の出力結果から実メモリの使用率を計算してみようと思います。

/proc/meminfo

ちなみに meminfo の内容はこちら。
MemTotal、MemFree、MemAvailable の値を取得できれば、メモリ使用率を求めることができそうですね。
※ 今回はawkの挙動を確認したいので、あえて MemTotal と MemAvailable を取得してみます。

[root@verienv01 ~]# cat /proc/meminfo | head
MemTotal:         498672 kB
MemFree:           77104 kB
MemAvailable:     363416 kB
Buffers:               0 kB
Cached:           261200 kB
SwapCached:            4 kB
Active:           147176 kB
Inactive:         150668 kB
Active(anon):        492 kB
Inactive(anon):    43400 kB

awkでメモリ使用率を求める

awk で行を指定したい場合は「NR」を使えばよくて、1行目と3行目で且つ、スペースで区切った左から2列目の値を取得したいのでこうですね。

[root@verienv01 ~]# cat /proc/meminfo | awk 'NR==3 {print $2} NR==1 {print $2}'
498672
363332

取得した値をワンライナーで変数ava(MemAvailable)と変数total(MemTotal)に入れて、計算まで行う場合はこうです!
[root@verienv01 ~]# cat /proc/meminfo | awk 'NR==3 {ava=$2} NR==1 {total=$2} {per = ava / total}END{print 100 - per * 100}'
27.1136

小数点以下は切り捨てます。
これでバッファキャッシュも考慮したメモリ使用率を出力できましたね。
[root@verienv01 ~]# cat /proc/meminfo | awk 'NR==3 {ava=$2} NR==1 {total=$2} {per = ava / total}END{print 100 - per * 100}' | sed s/\.[0-9,]*$//g
27

モニタリングツール

というわけで、CPU使用率、メモリ使用率、ロードアベレージ、タイムスタンプを出力するシェルスクリプトを用意します。

#!/bin/bash

MEMORY=$(cat /proc/meminfo | awk 'NR==3 {ava=$2} NR==1 {total=$2} {per = ava / total}END{print 100 - per * 100}' | sed s/\.[0-9,]*$//g)
CPU=$(vmstat | awk 'NR==3 {print $13,$14}')
LA=$(uptime | awk '{print $8,$9,$10}')
TIME=$(vmstat -t | awk 'NR==3 {print $18,$19}')

echo "--cpu-- --mem--  --------la--------  --------time--------"
echo " " $CPU "    " $MEMORY "   " $LA " " $TIME

いろいろとツッコミ処があると思いますが、、まぁいいでしょう。
[root@verienv01 ~]# ./monitor.sh
--cpu-- --mem--  --------la--------  --------time--------
  0 0      27     0.00, 0.01, 0.01   2020-12-05 23:56:43

whileでループすれば、モニタリングツールの出来上がり(小並感)
[root@verienv01 ~]# while true; do ./monitor.sh ; sleep 1; done
--cpu-- --mem--  --------la--------  --------time--------
  0 0      27     0.00, 0.01, 0.01   2020-12-06 00:02:42
--cpu-- --mem--  --------la--------  --------time--------
  0 0      27     0.00, 0.01, 0.01   2020-12-06 00:02:43
--cpu-- --mem--  --------la--------  --------time--------
  0 0      27     0.00, 0.01, 0.01   2020-12-06 00:02:44
--cpu-- --mem--  --------la--------  --------time--------
  0 0      27     0.00, 0.01, 0.01   2020-12-06 00:02:45
--cpu-- --mem--  --------la--------  --------time--------
  0 0      27     0.00, 0.01, 0.01   2020-12-06 00:02:46
--cpu-- --mem--  --------la--------  --------time--------
  0 0      27     0.00, 0.01, 0.01   2020-12-06 00:02:48
--cpu-- --mem--  --------la--------  --------time--------
  0 0      27     0.00, 0.01, 0.01   2020-12-06 00:02:49

終わりに

正直 dstat と sar と vmstat でいいですね。

リストファイルを分割して複数のサーバに配布するスクリプト

JMeter での負荷試験時に slave の個数分だけ CSVファイルを分割して、それぞれの slave サーバに scp で転送したかったのです。
そんな背景で取り急ぎ作成したよくわからない何かです。

スクリプトみたいな何か

#!/bin/bash

############################################
# 第一引数に配布先マシンのIPアドレス(9つまで)
# 第二引数に分割して配布するリストファイル
############################################

# ファイルの転送先ディレクトリ
DIR=/var/tmp/dlist.txt
# 接続ユーザ
USER=vagrant
# 接続に使用する秘密鍵
SECRET_KEY=/home/vagrant/.ssh/id_rsa

# 引数の数をチェック
if [ $# != 2 ]; then
    echo "引数の数が不正です"
    exit 1
fi

# 配布先マシンの数量を取得
TARGET_COLUMN=`cat $1 | wc -l`
# リストファイルの行数を取得
LIST_COLUMN=`cat $2 | wc -l`
# 1ターゲット辺りのリスト数を取得
ONE_TARGET_NUM=$[LIST_COLUMN / TARGET_COLUMN]

# 配布先マシンのIPアドレスが9つ以上あるかチェック
if [ $TARGET_COLUMN -gt 9 ]; then
    echo "配布先IPアドレスは9つまでしか設定できません"
    exit 1
fi

# 配布先マシンの数量分だけファイルを分割する
split -l $ONE_TARGET_NUM -a 1 --numeric-suffixes=1 $2 list.

# 分割したファイルを転送する
while read VAL;
do
  NUM=$((NUM+1))
  scp -i $SECRET_KEY ./list.$NUM $USER@$VAL:$DIR

  if [ $? -ne 0 ]; then
    echo "scp コマンドが正常に終了しませんでした"
    exit 1
  fi

  # 分割したファイルを削除
  rm -f ./list.$NUM
done < $1

スクリプトの第一引数に下記のような形で転送先サーバの IP アドレスを記載したファイルを渡せば OK です。
※ 第二引数に分割したいリストファイルを指定してください。
192.168.33.11
192.168.33.12
192.168.33.13
192.168.33.14
192.168.33.15
192.168.33.16

JMeter5.3 で The JVM should have exited but did not のエラーがでた話

JMeter5.3 のクラスタ環境を作って、CLI から JMeter を実行すると表題のエラーが出たんですね。

... end of run
The JVM should have exited but did not.
The following non-daemon threads are still running (DestroyJavaVM is OK):
Thread[DestroyJavaVM,5,main], stackTrace:
Thread[AWT-EventQueue-0,6,main], stackTrace:sun.misc.Unsafe#park
java.util.concurrent.locks.LockSupport#park at line:175
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject#await at line:2039
java.awt.EventQueue#getNextEvent at line:554
java.awt.EventDispatchThread#pumpOneEventForFilters at line:187
java.awt.EventDispatchThread#pumpEventsForFilter at line:116
java.awt.EventDispatchThread#pumpEventsForHierarchy at line:105
java.awt.EventDispatchThread#pumpEvents at line:101
java.awt.EventDispatchThread#pumpEvents at line:93
java.awt.EventDispatchThread#run at line:82

Thread[AWT-Shutdown,5,system], stackTrace:java.lang.Object#wait
sun.awt.AWTAutoShutdown#run at line:314
java.lang.Thread#run at line:748

どうも、シナリオ内にスクリプトレコーダを入れていると、CLI モードで実行した際に処理が正常に終了しなくなる不具合があるとのことです。
https://bz.apache.org/bugzilla/show_bug.cgi?id=64479

対応としては、シナリオ中のスクリプトレコーダを削除すればよいようです。

【Ansible】Master/Slave構成の JMeter をデプロイする【IaC】

Ansible で Master/Slave 構成の JMeter をデプロイする Playbook を作ってみました。
https://github.com/keisukesanuki/jmeter-MS.git
※ 詳細は GitHub の README を参照くださいませ。

ディレクトリ構成

.
├── README.md
├── ansible.cfg
├── group_vars
│   ├── all.yml
│   └── all.yml.example
├── hosts.example
├── roles
│   ├── common
│   │   ├── README.md
│   │   └── tasks
│   │       ├── etckeeper_commit.yml
│   │       ├── host_change.yml
│   │       └── main.yml
│   ├── dummy
│   │   ├── README.md
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       └── dummy.txt
│   ├── jmeter51
│   │   ├── README.md
│   │   ├── files
│   │   │   ├── jmeter.properties
│   │   │   └── start-controller_cui.sh
│   │   └── tasks
│   │       └── main.yml
│   ├── jmeter53
│   │   ├── README.md
│   │   ├── files
│   │   │   ├── jmeter.properties
│   │   │   └── start-controller_cui.sh
│   │   └── tasks
│   │       └── main.yml
│   ├── jmeter54
│   │   ├── README.md
│   │   ├── files
│   │   │   ├── jmeter.properties
│   │   │   └── start-controller_cui.sh
│   │   └── tasks
│   │       └── main.yml
│   ├── jmeter55
│   │   ├── README.md
│   │   ├── files
│   │   │   ├── jmeter.properties
│   │   │   └── start-controller_cui.sh
│   │   └── tasks
│   │       └── main.yml
│   ├── minimum
│   │   └── httpd
│   │       ├── README.md
│   │       ├── handlers
│   │       │   └── main.yml
│   │       ├── tasks
│   │       │   ├── main.yml
│   │       │   └── security.yml
│   │       └── templates
│   │           ├── mpm.conf
│   │           └── security.conf
│   ├── python-scripts
│   │   ├── README.md
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       ├── csv2gspread.py
│   │       ├── sacred-drive.json
│   │       └── start-controller_cui.sh
│   ├── reboot
│   │   └── tasks
│   │       └── main.yml
│   ├── slave-jmeter
│   │   ├── README.md
│   │   ├── files
│   │   │   ├── jmeter-node.service
│   │   │   └── jmeter-server.sh
│   │   └── tasks
│   │       └── main.yml
│   └── tigervnc
│       ├── README.md
│       ├── files
│       │   ├── vncpasswd.sh
│       │   └── vncserver@.service_root
│       └── tasks
│           └── main.yml
├── scenario
│   └── example
│       └── zabbix
│           └── zabbix_load_scenario.jmx
├── target.yml
└── target.yml.example

38 directories, 51 files