前回までの記事は以下参照ください。
※ HCIBench の公式情報や質問は本家のサイトにコメントすると開発者の方が即返信してくれます。
https://labs.vmware.com/flings/hcibench
なお、本投稿内のグラフ、IOPS 性能値などは特定の機器について述べたものではなく、サンプルとして数値に手入れをしたものを利用しています。
HCIBench で生成する パラメータファイル の保存先
HCIBench の画面から「Select a Workload Parameter File」の下の ADD ボタンを押して作成したパラメータファイルは、HCIBench 仮想マシンの /opt/automation/ 配下にテキストファイルで保存されます。
自分で作成したパラメータファイルを UPLOAD ボタンを押してインポートする事も可能ですし、SSHで直接接続して編集したり、作成も可能で、HCIBench 仮想マシンから ローカルに転送して保存も可能です。
HCIBench 2.0 からは、FIO 用と VDBench 用でファイルの保存パスは以下になります。
/opt/automation/vdbench-param-files
/opt/automation/fio-param-files
パラメータファイルを毎回 Web UI で作成したり、アップロードするのは面倒なので手元にテキストで多数のパラメータを保存しておき、HCIBench 仮想マシンをデプロイしたら SCP でアップロードする運用がお勧めです。
そうしておかないと HCIBench のバージョンが新しくなるたびにファイルを作る羽目になります (HCIBench は新しいバージョンがリリースされたら基本的にOVAから再デプロイするため)。
VDBench パラメータファイル の中身の説明
HCIBench で作成したパラメータファイルは、UI で設定した値に基づいてファイル名が 「 vdb-10vmdk-100ws-4k-70rdpct-100randompct-2threads 」などの様に付与されるテキストファイルです。
ファイルの記載は以下のようになっています。
*Auto Generated VDBench Parameter File *10 raw disk, 100% random, 70% read *SD: Storage Definition *WD: Workload Definition *RD: Run Definition debug=86 data_errors=10000 sd=sd1,lun=/dev/sda,openflags=o_direct,hitarea=0,range=(0,100),threads=2 sd=sd2,lun=/dev/sdb,openflags=o_direct,hitarea=0,range=(0,100),threads=2 sd=sd3,lun=/dev/sdc,openflags=o_direct,hitarea=0,range=(0,100),threads=2 sd=sd4,lun=/dev/sdd,openflags=o_direct,hitarea=0,range=(0,100),threads=2 sd=sd5,lun=/dev/sde,openflags=o_direct,hitarea=0,range=(0,100),threads=2 sd=sd6,lun=/dev/sdf,openflags=o_direct,hitarea=0,range=(0,100),threads=2 sd=sd7,lun=/dev/sdg,openflags=o_direct,hitarea=0,range=(0,100),threads=2 sd=sd8,lun=/dev/sdh,openflags=o_direct,hitarea=0,range=(0,100),threads=2 sd=sd9,lun=/dev/sdi,openflags=o_direct,hitarea=0,range=(0,100),threads=2 sd=sd10,lun=/dev/sdj,openflags=o_direct,hitarea=0,range=(0,100),threads=2 wd=wd1,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),xfersize=4k,rdpct=70,seekpct=100 rd=run1,wd=wd1,iorate=100,elapsed=60,warmup=60,interval=1
一番上に * アスタリスク でコメントアウトしてある様に、10個の VMDK ファイルを RAW デバイスとしてLinux OS (PhotonOS) に見せて、負荷のパターンを設定しています。
このファイルの中身についての概要です。
*SD: Storage Definition
ストレージ構成(VMDK・LUN)の定義です。sd=sdXX,lun=/dev/sdX,openflags=o_direct,hitarea=0,range=(0,100),threads=2と記載がありますが、ストレージの定義を ワーカー VM につける VMDK ファイル数分記述します。
- range= は ディスクの書き込むエリアの指定。 (0,100) なら最初から最後まで書き込む設定
- threads= は IO の多重度(スレッド)の設定threads は [ RD: Run Definition ] でも定義可能です。詳細は後述。
負荷の定義です。
HCIBench の UI から作成すると1行 (1WD) しか作成されませんが、wd=wd2 などワークロード名を変えれば何行でも作成できます。
wd=wd1,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),xfersize=4k,rdpct=70,seekpct=100
- sd= で Storage Definition で設定したディスクを指定
- xfersize= で IO のブロックサイズ (kバイトで指定)
- rdpct= で 読み込み比率を指定 ( rdpct=70 なら Read 70 Write 30 の比率 )
- seekpct= で Random IOの比率を指定 ( seekpct=100 なら Random IO 100%、seekpct=0 なら Sequential IO 100% )。
通常私は seekpct=100 か seekpct=0 のどちらかでテストしています。
※ seek は シーケンシャル (Sequential) ではなく、HDD の磁気ヘッドが書き込み位置まで移動する シークタイムの seek です。なので 意味的には Random Seek Percentage がこのパラメータとなります。
*RD: Run Definition
負荷の実行ジョブの定義です。
rd=run1,wd=wd1,iorate=100,elapsed=60,warmup=60,interval=1
HCIBench の UI から作成すると1行 (1RD) しか作成されませんが、rd=run2 など実行名を変えれば何行でも作成できます。
- iorate= で 1ワーカーVMあたりの IOPS の上限を設定(iorate=1000 なら 1000 IOPS の負荷を掛ける。8VM 展開すれば全体で 8000 IOPS の負荷を掛けるという事)
- elapsed= で1ジョブあたりの実行時間を設定 (秒単位)
- warmup= で1ジョブあたりのウォームアップ時間を指定 (秒単位。厳密な負荷試験でなければ特に設定は不要)
- interval= でレポート間隔を指定 (秒単位 interval=1 とても、HCIBench はその1秒ごとの詳細レポートとは別に、負荷全体のサマリを出してくれます)
この他にも後述の forthreads= や forxfersize= などのパラメータも Run Definition で指定します。
VDBench パラメータファイル のカスタム方法
ここからが本投稿の本題です。
前項で記載したように、HCIBench の UI で生成したパラメータでも十分な設定が可能ですが、1ジョブ = 1パラメータファイル にまとまってしまい、多数の試験パターンを一気に試したいときなど手間がかかってしまいます。(※ 突き詰めた性能検証したいときは1ジョブ毎にしっかりとやった方が良いです)
前項で記載したように、HCIBench の UI で生成したパラメータでも十分な設定が可能ですが、1ジョブ = 1パラメータファイル にまとまってしまい、多数の試験パターンを一気に試したいときなど手間がかかってしまいます。(※ 突き詰めた性能検証したいときは1ジョブ毎にしっかりとやった方が良いです)
また、VDBench で利用可能なその他のパラメータ ( forオプションや重複排除・圧縮の期待値など) を適用したい場合はカスタムが必要となります。
1つは、それこそ様々な IO パターン (ブロックサイズ、スレッド数、R/W比率、Rnd/Seq) を一気に最大負荷でかけて、そのストレージがどの IO 特性に強いのかを探るパラメータ。
もう一つは、ある程度 IO パターンは絞り、その代わり徐々に IOPS の制限を上げて IOPS と IO 遅延の関係性を探る事を目的としたパラメータです。
※ どちらも1ジョブ60秒で実施しても、500~800パターンの IO を掛けるので半日近く要します。
様々な IO パターンを一気に掛けるためのパラメータサンプル
私が良く使うパターンの一つですが、
Random IO か Sequential IO か、Read 比率を 100% から 0% まで10% ~ 20% 刻みに、ブロックサイズを 4k 8K 16K 32K ... と順次変えて、必要であればスレッド数も変化させて、という形で 400 パターンくらいを掛けるパラメータとなります。
私は同じパターンを vSANなどのHCI、3Tierの外部ストレージ(FC/NFS/iSCSI)などに同一条件で IO 負荷を掛けて、IOPS や IO遅延を収集しつつ、 ESXi ホスト側の CPU 利用率、外部ストレージの CPU 利用率などをモニタリングして負荷の限界がどのあたりにあるのかを探る目的で利用しています。
私は同じパターンを vSANなどのHCI、3Tierの外部ストレージ(FC/NFS/iSCSI)などに同一条件で IO 負荷を掛けて、IOPS や IO遅延を収集しつつ、 ESXi ホスト側の CPU 利用率、外部ストレージの CPU 利用率などをモニタリングして負荷の限界がどのあたりにあるのかを探る目的で利用しています。
※ HCIBench 1.6.5.2 から レポートを Excel フォーマットで出力する機能が追加されましたが、カスタムパラメータファイル内に複数の実行ジョブが含まれている場合も、それぞれ結果をまとめて出力されますので、試験後にグラフにする際にも便利です。
※ 以前のバージョンだとテキストファイルで出力されたリスト形式のレポートを正規表現利用してCSVに整形しないと使い物にならなかったのが、現在は大幅に改善されており検証後のレポート作成がかなり楽になっています。
※ 以前のバージョンだとテキストファイルで出力されたリスト形式のレポートを正規表現利用してCSVに整形しないと使い物にならなかったのが、現在は大幅に改善されており検証後のレポート作成がかなり楽になっています。
* SD: Storage Definition * ストレージ定義では スレッド数(threads)を指定せず、Run Definitionで指定します。 * ここでは 10 個の vmdk を使う構成です。 sd=sd1,lun=/dev/sda,openflags=o_direct,hitarea=0,range=(0,100) sd=sd2,lun=/dev/sdb,openflags=o_direct,hitarea=0,range=(0,100) sd=sd3,lun=/dev/sdc,openflags=o_direct,hitarea=0,range=(0,100) sd=sd4,lun=/dev/sdd,openflags=o_direct,hitarea=0,range=(0,100) sd=sd5,lun=/dev/sde,openflags=o_direct,hitarea=0,range=(0,100) sd=sd6,lun=/dev/sdf,openflags=o_direct,hitarea=0,range=(0,100) sd=sd7,lun=/dev/sdg,openflags=o_direct,hitarea=0,range=(0,100) sd=sd8,lun=/dev/sdh,openflags=o_direct,hitarea=0,range=(0,100) sd=sd9,lun=/dev/sdi,openflags=o_direct,hitarea=0,range=(0,100) sd=sd10,lun=/dev/sdj,openflags=o_direct,hitarea=0,range=(0,100) * WD : Workload Definition * ワークロード定義では Read 比率 を徐々に変えつつ、Random IO 100% と Sequential IO 100% の2パターンを定義しています。 * ワークロード定義では IO ブロックサイズ(xfersize)を指定せず、Run Definitionで指定します。 * ワークロード名は Read 比率 と Random/Sequential が区別付くように wd= に指定しています。 wd=wd_r100_w0_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=100,seekpct=0 wd=wd_r80_w20_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=80,seekpct=0 wd=wd_r70_w30_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=70,seekpct=0 wd=wd_r60_w40_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=60,seekpct=0 wd=wd_r50_w50_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=50,seekpct=0 wd=wd_r40_w60_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=40,seekpct=0 wd=wd_r30_w70_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=30,seekpct=0 wd=wd_r20_w80_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=20,seekpct=0 wd=wd_r0_w100_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=0,seekpct=0 wd=wd_r100_w0_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=100,seekpct=100 wd=wd_r80_w20_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=80,seekpct=100 wd=wd_r70_w30_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=70,seekpct=100 wd=wd_r60_w40_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=60,seekpct=100 wd=wd_r50_w50_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=50,seekpct=100 wd=wd_r40_w60_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=40,seekpct=100 wd=wd_r30_w70_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=30,seekpct=100 wd=wd_r20_w80_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=20,seekpct=100 wd=wd_r0_w100_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=0,seekpct=100 * RD : Run Definition * 実行ジョブ定義では、「 rd=default 」定義を1行作成し、各ワークロード定義に対して同一の IO 負荷を掛けるように共通パターンを指定します。 * ここでは、スレッド数(threads) と ブロックサイズ(xfersize) を for 構文で指定し、1行の指定で様々な IO パターンを設定します。 rd=default,iorate=max,elapsed=60,interval=1,forthreads=(2,4,8),forxfersize=(4k,8k,16k,24k,32k,64k,128k) *このパターンでは、IOPS制限無し(max)、60秒間の負荷、1秒毎のレポート、スレッド数を 2,4,8 でそれぞれ実施、IO ブロックサイズは 4k から 128k まで複数負荷を掛けます。 rd=r100_w0_rnd100,wd=wd_r100_w0_rnd100 rd=r80_w20_rnd100,wd=wd_r80_w20_rnd100 rd=r70_w30_rnd100,wd=wd_r70_w30_rnd100 rd=r60_w40_rnd100,wd=wd_r60_w40_rnd100 rd=r50_w50_rnd100,wd=wd_r50_w50_rnd100 rd=r40_w60_rnd100,wd=wd_r40_w60_rnd100 rd=r30_w70_rnd100,wd=wd_r30_w70_rnd100 rd=r20_w80_rnd100,wd=wd_r20_w80_rnd100 rd=r0_w100_rnd100,wd=wd_r0_w100_rnd100 rd=r100_w0_Seq100,wd=wd_r100_w0_Seq100 rd=r80_w20_Seq100,wd=wd_r80_w20_Seq100 rd=r70_w30_Seq100,wd=wd_r70_w30_Seq100 rd=r60_w40_Seq100,wd=wd_r60_w40_Seq100 rd=r50_w50_Seq100,wd=wd_r50_w50_Seq100 rd=r40_w60_Seq100,wd=wd_r40_w60_Seq100 rd=r30_w70_Seq100,wd=wd_r30_w70_Seq100 rd=r20_w80_Seq100,wd=wd_r20_w80_Seq100 rd=r0_w100_Seq100,wd=wd_r0_w100_Seq100 * このパターンでは Workload Definition で設定した、18 パターンに対して、3つのスレッドパターン、7つのブロックパターンの負荷を掛けるので * 合計 18 x 3 x 7 = 378 パターンとなり、ジョブの実行には7時間くらい必要となります。このパターンで出力された Excel レポートをピボットグラフなどで整形すると次のようなグラフで性能傾向を確認できます。
次のパターンでは棒グラフが 左軸の IOPS 値、線が右軸の IO 遅延(ms)を示していて、Random/Sequential, IO ブロックサイズ, Read 比率 を下からグループ化したグラフとなります。
この値を見ながら、このストレージは Max の負荷を掛けた時に どのブロックサイズの IO が得意なのか、Read 比率はどの値が得意なのか、などのあたりを付ける事が出来ます。
ここでターゲットの IO パターンを絞り込んで Max の負荷でどこまで IOPS が出るのかを把握したら、次に Max で確認した IOPS まで 徐々に IOPS 負荷を上げていきながら、どの時点で IO 遅延が大きく崩れるのかを試します。
ストレージの種類、ドライブ種類、RAID構成などで同じ負荷を掛けても得意とする IO 傾向がかなり異なるので面白いと思います。
徐々に IO 負荷を上げていき IOPS と 遅延の関係性 を探るパラメータ
上記で紹介した様々な IO パターンで負荷を探る際は、iorate=max で IO 遅延を考慮せずに最大の負荷を掛けました。ただし通常、ストレージはドライブ性能、IO 経路のスループットが上限に達した時以外にも、ストレージコントローラのCPU利用率が 100% 近くになってしまったときに IO 遅延が大きくなり、性能が不安定になります。
HCIBench + VDBench を利用して徐々に IOPS 負荷を上げていくパターンのテストを行うと、この傾向が良くわかるので私は以下のパラメータでその変化を確認しています。
* RAW Storage Settings * ストレージ定義では スレッド数(threads)を指定せず、Run Definitionで指定します。 * ここでは 10 個の vmdk を使う構成です。 sd=sd1,lun=/dev/sda,openflags=o_direct,hitarea=0,range=(0,100) sd=sd2,lun=/dev/sdb,openflags=o_direct,hitarea=0,range=(0,100) sd=sd3,lun=/dev/sdc,openflags=o_direct,hitarea=0,range=(0,100) sd=sd4,lun=/dev/sdd,openflags=o_direct,hitarea=0,range=(0,100) sd=sd5,lun=/dev/sde,openflags=o_direct,hitarea=0,range=(0,100) sd=sd6,lun=/dev/sdf,openflags=o_direct,hitarea=0,range=(0,100) sd=sd7,lun=/dev/sdg,openflags=o_direct,hitarea=0,range=(0,100) sd=sd8,lun=/dev/sdh,openflags=o_direct,hitarea=0,range=(0,100) sd=sd9,lun=/dev/sdi,openflags=o_direct,hitarea=0,range=(0,100) sd=sd10,lun=/dev/sdj,openflags=o_direct,hitarea=0,range=(0,100) * Workload Definition * ワークロード定義では Read 比率 を徐々に変えつつ、Random IO 100% と Sequential IO 100% の2パターンを定義しています。 * ワークロード定義では IO ブロックサイズ(xfersize)を指定せず、Run Definitionで指定します。 * ワークロード名は Read 比率 と Random/Sequential が区別付くように wd= に指定しています。 wd=wd_r100_w0_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=100,seekpct=0 wd=wd_r80_w20_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=80,seekpct=0 wd=wd_r70_w30_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=70,seekpct=0 wd=wd_r60_w40_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=60,seekpct=0 wd=wd_r50_w50_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=50,seekpct=0 wd=wd_r40_w60_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=40,seekpct=0 wd=wd_r30_w70_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=30,seekpct=0 wd=wd_r20_w80_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=20,seekpct=0 wd=wd_r0_w100_rnd100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=0,seekpct=0 wd=wd_r100_w0_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=100,seekpct=100 wd=wd_r80_w20_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=80,seekpct=100 wd=wd_r70_w30_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=70,seekpct=100 wd=wd_r60_w40_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=60,seekpct=100 wd=wd_r50_w50_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=50,seekpct=100 wd=wd_r40_w60_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=40,seekpct=100 wd=wd_r30_w70_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=30,seekpct=100 wd=wd_r20_w80_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=20,seekpct=100 wd=wd_r0_w100_Seq100,sd=(sd1,sd2,sd3,sd4,sd5,sd6,sd7,sd8,sd9,sd10),rdpct=0,seekpct=100 * Run Definition * 実行ジョブ定義では、「 rd=default 」定義を1行作成し、各ワークロード定義に対して同一の IO 負荷を掛けるように共通パターンを指定します。 * ここでは、スレッド数(threads) と ブロックサイズ(xfersize) を for 構文で指定し、1行の指定で様々な IO パターンを設定します。 rd=default,elapsed=60,interval=1,forthreads=(2,4,8),forxfersize=(4k,8k,16k) *このパターンでは60秒間の負荷、1秒毎のレポート、スレッド数を 2,4,8、IO ブロックサイズは 4k 8k 16k で負荷を掛けます。 * IOの上限は それぞれの rd= で 1VM あたり 10000 IOPS の負荷から始め、2000 IOPS 刻みで 20000 IOPS、最後に制限なしで * 負荷を掛けるパターンだと以下の書き方になります。 rd=r100_w0_rnd100_Limit10000,wd=wd_r100_w0_rnd100,iorate=10000 rd=r80_w20_rnd100_Limit10000,wd=wd_r80_w20_rnd100,iorate=10000 rd=r70_w30_rnd100_Limit10000,wd=wd_r70_w30_rnd100,iorate=10000 rd=r60_w40_rnd100_Limit10000,wd=wd_r60_w40_rnd100,iorate=10000 rd=r50_w50_rnd100_Limit10000,wd=wd_r50_w50_rnd100,iorate=10000 rd=r40_w60_rnd100_Limit10000,wd=wd_r40_w60_rnd100,iorate=10000 rd=r30_w70_rnd100_Limit10000,wd=wd_r30_w70_rnd100,iorate=10000 rd=r20_w80_rnd100_Limit10000,wd=wd_r20_w80_rnd100,iorate=10000 rd=r0_w100_rnd100_Limit10000,wd=wd_r0_w100_rnd100,iorate=10000 rd=r100_w0_Seq100_Limit10000,wd=wd_r100_w0_Seq100,iorate=10000 rd=r80_w20_Seq100_Limit10000,wd=wd_r80_w20_Seq100,iorate=10000 rd=r70_w30_Seq100_Limit10000,wd=wd_r70_w30_Seq100,iorate=10000 rd=r60_w40_Seq100_Limit10000,wd=wd_r60_w40_Seq100,iorate=10000 rd=r50_w50_Seq100_Limit10000,wd=wd_r50_w50_Seq100,iorate=10000 rd=r40_w60_Seq100_Limit10000,wd=wd_r40_w60_Seq100,iorate=10000 rd=r30_w70_Seq100_Limit10000,wd=wd_r30_w70_Seq100,iorate=10000 rd=r20_w80_Seq100_Limit10000,wd=wd_r20_w80_Seq100,iorate=10000 rd=r0_w100_Seq100_Limit10000,wd=wd_r0_w100_Seq100,iorate=10000 rd=r100_w0_rnd100_Limit12000,wd=wd_r100_w0_rnd100,iorate=12000 rd=r80_w20_rnd100_Limit12000,wd=wd_r80_w20_rnd100,iorate=12000 rd=r70_w30_rnd100_Limit12000,wd=wd_r70_w30_rnd100,iorate=12000 rd=r60_w40_rnd100_Limit12000,wd=wd_r60_w40_rnd100,iorate=12000 rd=r50_w50_rnd100_Limit12000,wd=wd_r50_w50_rnd100,iorate=12000 rd=r40_w60_rnd100_Limit12000,wd=wd_r40_w60_rnd100,iorate=12000 rd=r30_w70_rnd100_Limit12000,wd=wd_r30_w70_rnd100,iorate=12000 rd=r20_w80_rnd100_Limit12000,wd=wd_r20_w80_rnd100,iorate=12000 rd=r0_w100_rnd100_Limit12000,wd=wd_r0_w100_rnd100,iorate=12000 rd=r100_w0_Seq100_Limit12000,wd=wd_r100_w0_Seq100,iorate=12000 rd=r80_w20_Seq100_Limit12000,wd=wd_r80_w20_Seq100,iorate=12000 rd=r70_w30_Seq100_Limit12000,wd=wd_r70_w30_Seq100,iorate=12000 rd=r60_w40_Seq100_Limit12000,wd=wd_r60_w40_Seq100,iorate=12000 rd=r50_w50_Seq100_Limit12000,wd=wd_r50_w50_Seq100,iorate=12000 rd=r40_w60_Seq100_Limit12000,wd=wd_r40_w60_Seq100,iorate=12000 rd=r30_w70_Seq100_Limit12000,wd=wd_r30_w70_Seq100,iorate=12000 rd=r20_w80_Seq100_Limit12000,wd=wd_r20_w80_Seq100,iorate=12000 rd=r0_w100_Seq100_Limit12000,wd=wd_r0_w100_Seq100,iorate=12000 ~~ 途中省略 ~~ rd=r100_w0_rnd100_Limit20000,wd=wd_r100_w0_rnd100,iorate=20000 rd=r80_w20_rnd100_Limit20000,wd=wd_r80_w20_rnd100,iorate=20000 rd=r70_w30_rnd100_Limit20000,wd=wd_r70_w30_rnd100,iorate=20000 rd=r60_w40_rnd100_Limit20000,wd=wd_r60_w40_rnd100,iorate=20000 rd=r50_w50_rnd100_Limit20000,wd=wd_r50_w50_rnd100,iorate=20000 rd=r40_w60_rnd100_Limit20000,wd=wd_r40_w60_rnd100,iorate=20000 rd=r30_w70_rnd100_Limit20000,wd=wd_r30_w70_rnd100,iorate=20000 rd=r20_w80_rnd100_Limit20000,wd=wd_r20_w80_rnd100,iorate=20000 rd=r0_w100_rnd100_Limit20000,wd=wd_r0_w100_rnd100,iorate=20000 rd=r100_w0_Seq100_Limit20000,wd=wd_r100_w0_Seq100,iorate=20000 rd=r80_w20_Seq100_Limit20000,wd=wd_r80_w20_Seq100,iorate=20000 rd=r70_w30_Seq100_Limit20000,wd=wd_r70_w30_Seq100,iorate=20000 rd=r60_w40_Seq100_Limit20000,wd=wd_r60_w40_Seq100,iorate=20000 rd=r50_w50_Seq100_Limit20000,wd=wd_r50_w50_Seq100,iorate=20000 rd=r40_w60_Seq100_Limit20000,wd=wd_r40_w60_Seq100,iorate=20000 rd=r30_w70_Seq100_Limit20000,wd=wd_r30_w70_Seq100,iorate=20000 rd=r20_w80_Seq100_Limit20000,wd=wd_r20_w80_Seq100,iorate=20000 rd=r0_w100_Seq100_Limit20000,wd=wd_r0_w100_Seq100,iorate=20000 rd=r100_w0_rnd100_LimitMax,wd=wd_r100_w0_rnd100,iorate=Max rd=r80_w20_rnd100_LimitMax,wd=wd_r80_w20_rnd100,iorate=Max rd=r70_w30_rnd100_LimitMax,wd=wd_r70_w30_rnd100,iorate=Max rd=r60_w40_rnd100_LimitMax,wd=wd_r60_w40_rnd100,iorate=Max rd=r50_w50_rnd100_LimitMax,wd=wd_r50_w50_rnd100,iorate=Max rd=r40_w60_rnd100_LimitMax,wd=wd_r40_w60_rnd100,iorate=Max rd=r30_w70_rnd100_LimitMax,wd=wd_r30_w70_rnd100,iorate=Max rd=r20_w80_rnd100_LimitMax,wd=wd_r20_w80_rnd100,iorate=Max rd=r0_w100_rnd100_LimitMax,wd=wd_r0_w100_rnd100,iorate=Max rd=r100_w0_Seq100_LimitMax,wd=wd_r100_w0_Seq100,iorate=Max rd=r80_w20_Seq100_LimitMax,wd=wd_r80_w20_Seq100,iorate=Max rd=r70_w30_Seq100_LimitMax,wd=wd_r70_w30_Seq100,iorate=Max rd=r60_w40_Seq100_LimitMax,wd=wd_r60_w40_Seq100,iorate=Max rd=r50_w50_Seq100_LimitMax,wd=wd_r50_w50_Seq100,iorate=Max rd=r40_w60_Seq100_LimitMax,wd=wd_r40_w60_Seq100,iorate=Max rd=r30_w70_Seq100_LimitMax,wd=wd_r30_w70_Seq100,iorate=Max rd=r20_w80_Seq100_LimitMax,wd=wd_r20_w80_Seq100,iorate=Max rd=r0_w100_Seq100_LimitMax,wd=wd_r0_w100_Seq100,iorate=Maxwd= のパターン数に、さらに IOPS制限毎の rd= を定義するので非常にテスト数が多くなってしまいますが、ストレージに対して徐々に IOPS の負荷上限を上げていく事で、安定した低遅延な IO を提供できる IOPS がどのあたりで崩れ始めるかが分かります。
上記のようなグラフで徐々に高まる IOPS 負荷の下で、どこまでならば安定した低遅延な IO で IOPS 値が出るのかが確認できます。
その上で、最後にターゲットとなる ブロックサイズ、Random/Sequential の IO パターンなどパラメータを絞り込んで、ある程度の時間をかけて負荷を掛ける事で、
一時的なトップスピードの最高性能ではなく、想定する負荷で安定して動くことをツールを使って確認する事が出来ます。
また、徐々に IO 負荷を高めていく事で外部ストレージなどの場合、ストレージコントローラの CPU 負荷が徐々に高まり、恐らく CPU 利用率が上限近くになった時に IO 遅延が大きくなる傾向も確認できるかと思いますので、
コントローラ障害時などの影響も考慮したサイジングが可能になるかと思います。
重複排除率や圧縮率の期待値を設定するためのパラメータ
重複排除・圧縮機能が利用できるストレージを利用している場合、どの程度の効果があるのか、重複排除・圧縮の期待値によってどの程度性能、ストレージコントローラの負荷が変わるのかを確認するために VDBench では 期待値の設定が可能です。※重複排除率、圧縮率の期待値設定はグローバル設定となるので Storage Definition より上に記載します。通常はファイルの一番上方の debug=86 data_errors=10000 の上に書いておきます。
* Deduplication Ratio
- dedupratio=2 * 重複排除率を指定 (デフォルトは1、数字を増やしていく。2 の場合は 2:1 の重複排除率でデータが書き込まれる)
- dedupunit=8k * 重複排除判定される際のブロックサイズを指定(デフォルトは128k)
※ xfersize で指定する IO のブロックサイズは必ず dedupunit のサイズの倍数である必要があります。倍数でない場合や xfersize が dedupunit より小さいとエラーになるのでご注意を。
- compratio=2 * 圧縮率を指定 (デフォルトは1、数字を増やしていく。2 の場合は 2:1 の圧縮率でデータが書き込まれる)
https://www.oracle.com/technetwork/server-storage/vdbench-downloads-1901681.html
HCIBench のレポートのダウンロード
レポートは HCIBench の UI (https://<HCIBench IP>:8443)、または http://<HCIBench IP> からダウンロードします。または、HCIBench VM に SSH・SCPなどで接続して、/opt/output/results フォルダからごっそりダウンロードする事も可能です。
HCIBench のレポートのカスタマイズ方法
HCIBench 1.6.5.2 以降では負荷レポートを Excel シートに自動的に集計してくれるようになりましたので、グラフ化などの手間が非常に楽になりました。Excel シートは 以下のようにジョブ毎のサマリシートと、指定した Interval値 で収集した全数値のシートで取得されます。
1行毎に パラメータファイル・Run Definition (rd=) 毎に分かれて結果が記録されます。
forthreads, forxfersize など for オプションを利用した場合、rd= 名にその値が追加されます。
- Case Name : パラメータファイル名
- Run Definition : rd= の実行ジョブ名 forオプションで処理をループさせた場合は [For loops: threads=4 xfersize=4k] などと追加されます。
- Number of VMs : VM数
- IOPS : IOPS 値
- Throughput : スループット (MB/s)
- Latency : IO 遅延 (Read Write 合計)
- Read Latency : 読み込み遅延 (ms)
- Write Latency : 書き込み遅延 (ms)
- Latency Standard Deviation : 待ち時間標準偏差
- Blocksize : IO ブロックサイズ
- Read Percentage : 読み込み比率
- Total Outstanding IO : Queue Depth (この場合未処理 IO を純粋に意味するかも?)
- Physical CPU Usage : ESXi ホストの CPU 利用率
- Physical Memory Usage : ESXi ホストのメモリ利用率
- vSAN CPU Usage : vSAN の場合、ESXi ホストの CPU のうち vSAN が利用したCPU 利用率
iorate オプション、for オプションを利用した場合など Run Definition の項目を正規表現使いテキスト置換でカンマ区切りでCSV表を作りなおす必要がありますが、
出力された Excel シートを元に、ピボットテーブル化・ピボットグラフにしてスライサーを付けると必要な IO パターンでのグラフをサクッと作る事が出来るので便利です。
※ 参考 : 現在のバージョンでも出力されますが、以前は各ジョブ名のフォルダ配下に出力される以下の様なテキストファイル内の情報を正規表現など利用してCSVに整形するなどの手間がありました。
============================= Run Def: RD=r100_w0_rnd100_Limit3000; I/O rate: 3000; elapsed=60; For loops: threads=16 xfersize=4k VMs = 8 IOPS = 23825.48 IO/s THROUGHPUT = 93.07 MB/s LATENCY = 0.6331 ms R_LATENCY = 0.6331 ms W_LATENCY = 0.0000 ms 95%tile_LAT = 1.3488 ms ============================= Run Def: RD=r100_w0_rnd100_Limit3000; I/O rate: 3000; elapsed=60; For loops: threads=16 xfersize=8k VMs = 8 IOPS = 23794.36 IO/s THROUGHPUT = 185.90 MB/s LATENCY = 0.6098 ms R_LATENCY = 0.6098 ms W_LATENCY = 0.0000 ms 95%tile_LAT = 1.3948 ms =============================
vSAN データストアに対するベンチマークの場合、vSAN Observer の詳細ログも生成されダウンロードできますが、ここでは割愛します。
以上、長々と HCIBench を利用して様々なパターンの負荷試験を行う際のポイントをご紹介しましたが、
ベンチマークテストは同じ条件で、同じテスト内容を異なる機器、異なるバージョンなどの試験対象に負荷を掛けてその比較をする事に意味があると私は考えています。
- 様々な IO パターンで全体の傾向を把握し、
- 徐々に IOPS 負荷を上げていきながら IO 遅延、コントローラ CPU 負荷などを把握し、
- 最後に 特定 IO パターンに絞って厳密な試験をする、
この三段階の試験を通してストレージ毎の性能傾向をフェアに比較するとより良い製品選定につながるかと思います ( HCIBench は vSphere ESXi 環境限定ですがそこは仮想化のデファクトスタンダードという事で...)