uniface.hub

ユニフェイスの開発者ブログ


Title 性能テストやシステム監視に役立つツールのご紹介
  • 2022年7月15日
  • 上村晃史
性能テストやシステム監視に役立つツールのご紹介

前回は「性能テストに関する記事」を投稿しましたが、今回はそれに役立つツールについてご紹介していきたいと思います。

ツールの選定基準

  • WEBシステムに対して利用できること
  • OSS(オープンソースソフトウェア)であること
    • この分野は(なぜか)高機能なのに無償で提供しているツールが豊富です
  • 公式サイトなど、ドキュメントが充実していること
  • シナリオテストが実施できること
    • Aユーザでログイン -> マスタ登録 -> ファイルアップロード -> ログアウト のように実際に操作するような流れで負荷をかけることができます
  • 後発のものであること
    • (単に新しいものが好きなだけ)

ここでは全部は紹介しませんが、調べていると本当に種類が多いので、気になった方は他にも調べてみて下さい。細かな点だと、シナリオテストで使える言語が XML や Python といった違いなどもあり、選択肢は多い印象です。

また本記事を執筆するときに新たに見つけたツールが良さそうだったので貼っておきます。
【参考リンク】:手軽に負荷テストができるツール「Taurus」がスゴい

当時、負荷検証を実施するときに先にこちらを見つけていたらこちらを採用していたかもしれません。(色々と楽そう)

システム構成

先ずは今回採用したツールとそのシステム構成についてざっくりと見ていきます。

今回パフォーマンス計測を実施したPCは、普段自身で利用しているWindows端末になります。開発用のPCなのでスペックはそれなりにありますが、どの程度負荷をかけたいかによって選定する必要があります。
青枠が「パフォーマンス計測」、赤枠が「システム監視」に関する内容となります。
大まかに説明すると、【k6】というツールで負荷をかけつつ、その結果を蓄積して、その際のハードウェアリソースやDBの状況といった監視情報に関しては【Zabbix】の方で収集しています。
(ハードウェアのリソース情報は外部からは見えないため、他のツールでも同様に、基本的にはエージェントをインストールして、エージェント経由で収集することになります。)

各ツールの概要

ツール名概要特徴
loadimpact/k6パフォーマンス
計測ツール
・OSS(オープンソース:AGPL v3)
・Golangで開発されていて軽量
・開発元は負荷テストサービス運営会社
・シナリオベースで処理を実行
・シナリオは JavaScript で記載
InfluxDB時系列データベース・OSS(オープンソース:MIT License)
・高い書込/読込スループット
・大量データの圧縮、定期削除、生データの要約
・データ更新/削除よりも登録/参照のパフォーマンスを優先
Grafana(OSS)ログ・データ等
可視化ツール
・OSS(オープンソース:AGPL v3)
・WEB UI、かつダッシュボードの作成が容易
・多くのデータソースに対応
Zabbix統合システム
監視ツール
・OSS(オープンソース:GPL v2.0)
・WEB UI、かつダッシュボードの作成が容易
・データ収集 や しきい値の判定 、 アラート通知に対応
・監視対象にエージェントを導入することで監視内容を追加

※ 全部OSSですが、Grafana は有料版も同名なので区別するために記載しています。

InfluxDB

特徴(詳細)

下記で紹介されている内容が分かり易過ぎるので読んでください。(丸投げ)

【参考リンク】:RDBエンジニアから見たInfluxDB(PDF)

Install 手順

k6 と連携する場合、バージョン2.0以降は別途、拡張機能を導入する必要があるので、今回はバージョン1.8を利用しています。

Windows にインストールする場合は以下のリンクを参考にしてください。(丸投げ(再))

【参考リンク】:InfluxDBの導入(Windows編)

サーバ起動方法

  • cmd や powershell で influxd.exe を実行
    • サービスとして自動実行されるわけではない
  • PowerShell実行例:
> ./influxd.exe
起動画面イメージ

CLI 操作方法

  • cmd や powershell で influx.exe を実行
    • -precision オプションにより時刻フォーマットを指定 ⇒ rfc3339(YYYY-MM-DDTHH:MM:SS.nnnnnnnnnZ)
  • PowerShell実行例:
> ./influx.exe -precision rfc3339
  • 問合せ言語は InfluxQL、または Flux を利用 ⇒ Flux が後継

コマンド一覧(抜粋)

コマンド概要
show databasesDBの一覧を確認
use <database名>デフォルトで使用するDBを指定
show measurementsメジャーメントの一覧を確認(RDBのテーブルのようなイメージ)
show retention policiesポリシーの一覧を確認。デフォルトは autogen(データ保持期間は無期限)

バックアップ&リストア方法

全てをバックアップするコマンド

> .\influxd.exe backup -portable <バックアップ先のパス>

バックアップディレクトリ内にあるすべてのデータベースを復元するコマンド

> .\influxd.exe restore -portable <バックアップ先のパス>

Grafana

公式ドキュメント

Grafana Labs – Grafana documentation(英語)

Install 手順

Windows にインストールする場合は(略
【参考リンク】:Qiita – Grafanaのフロントエンド開発環境を構築する (on Windows10)

分析画面イメージ

本来は k6 用の分析画面を自分で作る必要がありますが公開されていました。圧倒的感謝!
分析画面がスタイリッシュなので傍らに開いておくとテンションが上がる効果があります。(個人差)
【DLリンク】:New K6 Load Testing Results dashboard for Grafana | Grafana Labs

公開されているものは英語ですが、自分で JSON Model を編集して日本語化しました。

loadimpact/k6

公式ドキュメント(Install 手順あり)

Grafana Labs – Welcome to the k6 documentation(英語)

テストシナリオ(例)

■ フォルダ構成

■ シナリオ実行用の PowerShell スクリプト

$OutputEncoding = [Console]::OutputEncoding
$filename = Get-Date -Format "yyyy-MMdd-HHmmss"
Start-Transcript ./logs/$filename.log

# --verbose は PowerShell の詳細情報を出力する用のオプション
# 「k6db」という仮のDB名を指定
&"C:\Program Files\k6\k6.exe" run "C:\k6\scenario.js" --verbose --out influxdb=http://localhost:8086/k6db

Stop-Transcript

Pause

■ JavaScript で記載したシナリオの内容
「[ログイン] 画面からログイン -> [ファイルアップロード] 画面で登録処理 -> [ユーザマスタ] 画面に遷移」といった流れを記載しています。(例用に色々書き換えたので間違ってるところがあるかも・・・。あと、コメントアウトされた処理は覚え書きとしてあえて残してます。)

import { check, sleep } from'k6';
import http from 'k6/http';
import { Rate } from 'k6/metrics';
import { FormData } from 'https://jslib.k6.io/formdata/0.0.2/index.js';
import { getUser } from './users.js';

const failRate = new Rate('failed requests');
const uploadFileName = 'testdata.csv';  // アップロードファイルの相対パス
const mimeType = 'application/vnd.ms-excel.sheet.macroEnabled.12'
const uploadFile = open(uploadFileName, 'b');

export const options = {
  scenarios: {
    ramping_up_scenario: {
      executor: 'ramping-vus',  // 指定された時間、可能な限り多くの反復を実行
      startVUs: 1,              // 開始時の VU 数
      stages: [
        { duration: '10m', target: 20 }, // 経過時間に比例して VU 数を増加
        { duration: '5m', target: 0 },   // 経過時間に比例して VU 数を減少
      ]
    }
  },
  //httpDebug: 'full',
                                // http-debug          本文をスキップして、HTTPリクエストとレスポンスをログに記録します。
                                // http-debug = "full" 本文を含むHTTPリクエストとレスポンスをログに記録します。
  insecureSkipTLSVerify: true,  // 安全ではないTLS証明書の検証をスキップ
  thresholds: {
    'failed requests': ['rate < 0.05'],	// リクエストのエラー率が5%未満
    'http_req_duration': ['p(95) < 500']   // 95%のリクエストの応答時間が500msec未満
  },
};

// 接続先の URI
const ENDPOINT = 'https://192.168.XXX.XXX';

export default function() {
  // ユーザー情報を取得
  const user = getUser();
  
  // ログインAPIを呼ぶ
  const loginRes = http.post(`${ENDPOINT}/login.php`,
    user,
    {
      // headers: { 'Content-Type': 'text/html; charset=EUC-JP' },
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    }
  );
  const loginResult = check(loginRes, { 'login success': r => r.status === 200 });
  if (!loginResult) {
    failRate.add(true);
    return;
  }
  const reqId = loginRes.headers['X-Request-Id'];	    // ヘッダ名は大文字小文字を正確に
  const accessToken = loginRes.json('accessToken');	// GJSONでパス指定

  // ユーザ操作を想定した待ち
  sleep(1);

  // ファイルアップロード画面(仮)で登録処理を実行
  let res = http.get(`${ENDPOINT}/main/file_upload.php`);
  res = res.submitForm({
    formSelector: 'form',
    // formSelector: 'main',
    fields:
    {
      act: 'reg',
      bikou: 'test',
      upfile: http.file(uploadFile, uploadFileName, mimeType)
    },
  });
  check(res, { 'is status 200': (r) => r.status === 200, });

  // ユーザマスタ画面(仮)に遷移
  res = http.post(`${ENDPOINT}/master/user_list.php`,
    {
      url: ENDPOINT,
      // title: 'ユーザマスタ',
    },
    {
      // headers: { 'Content-Type': 'text/html; charset=EUC-JP' },
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    },
  );
  const result = check(res, { 'is status 200': (r) => r.status === 200, });
  failRate.add(!result);
}

■ 別ファイルに定義したユーザ情報

const users = [
  {
    UserId: 'A0001',
    Password: '1234',
  },
  {
    UserId: 'A0002',
    Password: '5678',
  },
];

let userIndex = 0;

export function getUser() {
  const user = users[userIndex];
  if (++userIndex == users.length) {
    userIndex = 0;
  }
  
  return user;
}

参考リンク

実行した内容は Grafana で確認するため割愛します。詳細については下記を参考にしてください。

Zabbix

Zabbix は今回既にインストールされていたものを利用したため、手順については割愛します。
また Zabbix は一度使い慣れるととても便利な反面、設定構造が非常ーーーーに複雑なため、下記のリンクを参考にしながら色々触りつつ慣れていくのが良いと思われます。
この他、ディスカバリルールという仕組みがかなり強力です。物凄くざっくりと説明すると、監視対象の監視項目を手で1つ1つを追加しなくても、ある程度まとめて勝手に追加してくれるというものです。
Zabbixに関しては、またの機会に別途紹介記事を作ろうかなぁと思います。

参考リンク

分析画面イメージ

独自のダッシュボードを作成してグラフ表示することも容易です。

まとめ

だいぶ参考リンク任せの感がありますが、環境の構築や利用できるようになるまでに、実際に参考にさせてもらって非常にお世話になりましたので、合わせて紹介させてもらいました。
また今回は、時系列データベースにいう新たな概念に触れることができて良い刺激となりました。

前回投稿した記事の「性能テストにおける評価指標」は、この構成で監視/収集/評価できるのではないでしょうか。全部OSSで賄えるのは凄いことですね・・・。
費用を抑えつつ、このようなツールを活用していくことで、今後も品質の向上を図っていきたいと思います。