Webサーバー向けのベンチマークツールを使ってみよう(後編)――Apache Jmeterとwbench
今回はWebサーバーに対するある特定のパターンでのアクセスによって発生する負荷を調べたい、といった場合に有用な「Apache Jmeter」と、クライアントでのJavaScriptの実行時間までも含めたWebページのロード時間を測定できる「wbench」を紹介する。
グラフィカルにテストシーケンスを作成できる「Apache Jmeter」
記事前編ではWebサーバーの負荷テストを行える「Apache Bench」や「Siege」といったツールを紹介した。Apache Benchは手軽さが、またSiegeでは複数のURLを対象にテストを実行できる点が特徴だが、これ以外にもWebサーバーのテストに活用できるツールはいくつか存在する。今回紹介する「Apache Jmeter」もその1つだ(図1)。
Apache Jmeter(以下、Jmeter)は、さまざまなサーバーに対する負荷テストを行えるテストツールだ。HTTPやHTTPSだけでなく、FTPやLDAP、SMTP、POP3、IMAPなどのプロトコルにも対応するほか、JDBC経由でデータベースの負荷テストを行うこともできる。また、GUIを使ってテストシーケンスを作成できるのも特徴だ。URLを一つずつ指定してテストシーケンスを構築できるだけでなく、プロキシとして動作させることでWebブラウザで行ったアクセスをそのままテストシーケンスとして取り込むこともできる。これにより、たとえば「ログイン」→「ページ表示」→「プレビュー」→「投稿」といった一連の作業のスループットを計測する、というような負荷テストが可能になる。
JmeterはJavaで実装されているため、Windows上でも利用が可能だ。テストはGUIを使わずにコマンドラインで実行可能で、Windows上でテストシーケンスを作成し、それをLinuxのシェルから実行する、といった使い方もできる。
Apache Jmeterのインストール
JmeterはJmeter公式Webサイトのダウンロードページでソースコードおよびバイナリがダウンロードできる。なお、実行にはJava 7以降の実行環境が必要だ。Windows環境の場合、ダウンロードしたアーカイブを展開し、binディレクトリ内にある「jmeter.bat」を実行するとGUIが起動する。
また、DebianやUbuntu環境ではJmeter本体および各プロトコル/サービスに対応するモジュールがそれぞれ別のパッケージで提供されている。たとえばHTTPモジュールを利用するためには、「jmeter」および「jmeter-http」パッケージをインストールすれば良い。ただし、Jmeterの公式サイトで提供されているものよりもバージョンが古い場合があるので注意したい。
「テスト計画」の作成
Jmeterではテストで行う一連のアクセスを「テスト計画(Test Plan)」として作成し、それを実行するという形でテストを行う。テスト計画を一から作成しても良いのだが、Jmeterではテンプレートとしてよく使われるテスト計画のひな形が用意されているので、まずはこれを利用することをおすすめする。テンプレートからテスト計画を作成するには、まず「ファイル」メニューの「Templates」をクリックする(図2)。
今回はWeb向けのテスト計画を作成するので、表示された「Templates」ウィンドウ内の「Select Template」ドロップダウンボックスで「Building a Web Test Plan」を選択して「Create」をクリックする(図3)。
これでWebサーバー負荷テスト用の設定が読み込まれ、いくつかの項目が追加されたテスト計画が作成される。
Jmeterの設定画面と各種用語
Jmeterのテスト計画は階層構造を持っており、各種設定やテスト内容を記述した子アイテムを追加して行くことでテスト計画を作成していく。Jmeterのメインウィンドウ左ペインにはツリー状にテスト計画が表示され、そこで選択した項目に関する情報が右ペインに表示されるようになっている。各項目には「名前」が付けられており、これはユーザーが自由に変更可能だ(図4)。
「Building a Web Test Plan」テンプレートで作成されたテスト計画では、テスト計画の下に「Scenario 1」という名前の「スレッドグループ」という項目が用意され、さらにその下にリクエスト方法を指定する「HTTP Request Defaults」や「HTTP Cookie Manager」、「HTTP Header Manager」といった「設定エレメント」と、「Home Page」、「ThinkTime 1s」、「Page Returning 404」といった「サンプラー」が追加されている。また、テスト計画の下には「View Results Tree」という「リスナー」も追加されている(図5)。
ここで「スレッドグループ」は、Webサーバーにアクセスするクライアントに相当する。また、「設定エレメント」はテストに必要なさまざまなパラメータを指定するための項目で、「サンプラー」は実際にリクエストを行ってデータを収集するための項目、「リスナー」は収集されたデータを表示/加工するための項目だ。これ以外にもさまざまな「エレメント」が用意されており、これらを組み合わせることで複雑なテストシーケンスを用意できるようになっている。
テスト計画の修正
それでは、テンプレートから作成したテスト計画を修正してテスト対象に応じたテスト計画を作成していこう。まず修正が必要なのは、デフォルトでテスト対象とするサーバーやパラメータを指定する「HTTP Request Defaults」(HTTPリクエスト初期値設定)エレメントだ。左ペインで「HTTP Request Defaults」を選択すると右ペインに「HTTPリクエスト初期値設定」が表示されるので、ここにパラメータを入力していく。
HTTP Request Defaultsにはいくつかの設定項目が用意されている。ここでまったく設定を行わなくても良いのだが、その場合個別のリクエスト項目にそれぞれサーバー名やポート番号などを入力する必要があり、煩雑になってしまう。そのため今回は最低限の設定として「サーバ名またはIP」欄にテスト対象のホスト名もしくはIPアドレスを、「ポート番号」にテスト対象のポート番号を入力しておく(図6)。
また、テスト対象のURLへのアクセスに認証が必要な場合は、その設定を行う「HTTP認証マネージャ」設定エレメントを追加しておく必要がある。これは、スレッドグループ(デフォルトでは「Scenario 1」と表示されている項目)を右クリックし、メニューから「追加」-「設定エレメント」-「HTTP認証マネージャ」を選択することで行える(図7)。
追加されたHTTP認証マネージャエレメントを選択し、「追加」ボタンをクリックして認証項目を追加したうえで、それぞれの項目に値を入力していく。ちなみに「基底URL」は認証が必要なURLの基底URLで、サイト全体を指定したい場合は「http:/」と設定すれば良い(図8)。
次はアクセスを行うURL(リクエスト)を登録しよう。テンプレートからテスト計画を作成した場合、あらかじめ「Home Page」および「Page Returning 404」というHTTPリクエストと「ThinkTime1s」という「Test Action」が登録されているので、まずはこれらを選択してDeleteキーを押すか、もしくはコンテキストメニューから「削除」を選択して削除する。次にテストシナリオを右クリックし、コンテキストメニューから「追加」-「サンプラー」-「HTTPリクエスト」を選択してHTTPリクエストを追加する(図9)。
左ペインに追加された「HTTPリクエスト」を選択するとリクエストするパスやプロトコル、メソッドなどを指定する画面が表示されるので、ここでテスト対象のパス(「/」など)を指定する(図10)。サーバー名やポート番号については特に指定しなければ「HTTP Request Defaults」で指定したものが利用されるため、空欄のままで良い。
HTTPリクエストの設定が終わったら、左ペインの「View Results Tree」項目を選択した状態でツールバーの「▶」(開始)ボタンをクリックしてみよう。指定したサーバー/ポート番号の指定したパスに対して連続的なアクセスが開始され、その結果が表示されるはずだ(図11)。
ちなみに、デフォルトの設定では手動でテストを停止させるまでアクセスが続けられる。テストを停止させるには、ツールバーの「停止」ボタンをクリックすれば良い。
テストの実行後、「結果をツリーで表示」ペインに表示されるリクエストをクリックして選択するとリクエスト結果が表示される(図12)。
また、リクエスト結果は「実行」メニューの「全て消去」もしくはCtrl-Eで消去できる(図13)
さて、この例では1つのURL(/)にしかアクセスを行っていないが、複数の「HTTPリクエスト」エレメントを追加することで、図14のように複数のURLに対してアクセスを行わせることが可能だ。この場合、テストシナリオの上にあるHTTPリクエストエレメントから順にアクセスが行われる。
また、同時にアクセスを行うスレッド数やスレッドの起動間隔、ループ回数などはスレッドグループの設定画面で指定できる(図15)。
「スレッド数」では同時にアクセスを行うスレッドの数を、「Ramp-Up期間」はスレッドの立ち上げを行う時間を指定できる。たとえばスレッド数が5、Ramp-Up期間が5であれば、5秒間かけて5つのスレッドを起動することになる。
「ループ回数」は各スレッドがスレッドグループで設定されている一連のリクエストを何回実行するかを指定するものだ。たとえば「2」を指定した場合、スレッドグループ内のリクエストを順に1回ずつ実行するという処理を2回繰り返すことになる。「無限ループ」にチェックを入れた場合、無限に処理が繰り返される。
また、「スケジューラ」にチェックを入れると、スケジューラが利用できるようになる。スケジューラではテストを何秒間実行するか(「持続時間」)と、テストを開始してから実際にリクエストを送信し始めるまで何秒待機するか(「起動遅延」)を指定可能だ。また、開始時刻や終了時刻も指定できる(どちらの値も同一の場合は無視される)。
リクエスト結果を集計する
テンプレートから作成したテスト計画では、アクセス結果を表示するエレメントとして「View Results Tree」のみが用意されている。これに加えて別の「リスナー」を追加することで、統計処理されたテスト結果を確認することが可能だ。リスナーは複数用意されているが、今回はグラフを出力する「グラフ表示」と、表を出力する「結果を表で表示」、統計処理されたデータを出力する「統計レポート」を使ってみよう。これらはテスト計画を右クリックして「追加」-「リスナー」を選択し、続けて追加したいリスナーをクリックすることで追加できる(図16)。
リスナーを追加した後にテストを実行すると、その結果が各リスナー画面に表示される。たとえば「グラフ表示」では、リクエスト結果およびその平均/中央値/偏差/スループットをグラフで確認できる(図17)。
また、「結果を表で表示」では全リクエストの結果を表で出力できる(図18)。
「統計レポート」では、各リクエスト結果の平均値や中間値、最小値、最大値、スループットなどの統計情報を出力できる(図19)。
Webブラウザで操作した一連の処理をテストシナリオとして取り込む
JmeterではGUIで詳細なテスト計画を作成できるが、これらをいちいち手入力するのは面倒だ。そこで便利なのが、Webブラウザで行ったアクセスを記録してそれをそのままテストシナリオとして取り込む「Recording」機能だ。Recording機能を利用するには、まず「Recording」テンプレートを利用してテスト計画を作成する。(図20)。
新規テスト計画を作成したら、続けて「HTTP Request Defaults」項目でテスト対象のサーバー名/IPアドレスやポート番号を入力しておこう(図21)。
次に、「WorkBench」以下に追加されている「HTTP(S) Test Script Recorder」という項目を選択する。ここで「開始」をクリックすると指定した設定でプロクシサーバーが立ち上がり、そのプロクシサーバーを経由したアクセスがJmeterに記録されるようになる(図22)。
デフォルトではローカルホストの8888番ポートでプロクシサーバーが立ち上がるので、このプロクシサーバーを利用するようWebブラウザの設定を行ってから「開始」をクリックし、WebブラウザでテストしたいURLにアクセスしていこう。最後に「停止」をクリックすると、WebブラウザでアクセスしたURLが「View Results Tree」エレメントに追加される(図23)。
また、同時に「Thread Group」内の「Recording Controller」以下にこのURLをベースとしたHTTPリクエストエレメントとHTTP認証マネージャが追加される(図24)。
これでリクエストの取り込みは完了となり、手動でHTTPリクエストなどを作成した場合と同様にツールバーの「実行」などからこれらリクエストを使った負荷テストが可能になる(図25)
コマンドラインからテスト計画を実行する
Jmeterでは、GUIからの負荷テストの実行が推奨されていない。GUIはテストの作成およびテストのデバッグのみに使用し、実際のテストはコマンドラインモードで実行するべきとされている。コマンドラインでテストを実行するには、jmeterをコマンドラインでの実行を指定する「-n」オプションと、テスト計画ファイル(拡張子は「.jmx」)を指定する「-t」オプション、そしてテスト結果を出力するファイルを指定する「-l」オプションを付けて実行すれば良い。出力ファイルの拡張子には「.jtl」が一般的に使われる。
jmeter -n -t <.jmxファイル> -l <.jtlファイル>
たとえば「test01.jmx」というファイルにテスト計画を保存していた場合、次のようになる。
$ ~/apache-jmeter-3.1/bin/jmeter -n -t test01.jmx -l output.jtl Writing log file to: /home/hylom/test/jmeter.log Creating summariser <summary> Created the tree successfully using test01.jmx Starting the test @ Thu Dec 15 18:23:43 JST 2016 (1481793823145) Waiting for possible Shutdown/StopTestNow/Heapdump message on port 4445 summary = 2587 in 00:00:15 = 171.5/s Avg: 83 Min: 6 Max: 360 Err: 159 (6.15%) Tidying up ... @ Thu Dec 15 18:23:58 JST 2016 (1481793838350) ... end of run
出力された.jtlファイルにはCSV形式で各リクエストの実行結果が格納されている。JmeterのGUIでこのデータを閲覧するには、「統計レポート」等リスナー画面で「参照」ボタンをクリックしてこのファイルを読み込ませれば良い(図26)。
なお、テスト計画を作成したJmeterとはバージョンの異なるJmeterを使用した場合、テスト計画の実行時に「Error in NonGUIDriver java.lang.NullPointerException」などのエラーが出ることがあるので注意しよう。
JavaScriptのロード時間などを含めたWebサイトのロード時間を測定できる「wbench」
最後に、Webサーバーの負荷テストツールとはやや毛色が違うものの、WebサイトやWebアプリケーションの構築時に便利なテストツールである「wbench」を紹介しよう。
昨今ではJavaScriptなどを使って動的にコンテンツをリクエストするWebページが増えている。Googleなどの検索エンジンでは、こういった動的なリクエストまでも含めたWebブラウザでのコンテンツ表示時間を計測し、検索結果の表示順に反映させているという。しかし、ここまで紹介してきたApache BenchやSiege、Jmeterなどのツールでは、クライアントとなるWebブラウザがリクエストを送信してからサーバーがそのレスポンスを返してデータの受信が完了するまでの時間は測定できるものの、実際にWebブラウザ側での読み込みが完了してコンテンツが画面に表示されるまでの時間は計測できない。
wbenchはWebブラウザを外部から遠隔操作する「WebDriver」という技術を利用し、実際のWebブラウザを使用してページの読み込み時間を計測するツールだ。WebDriverを利用するには対応Webブラウザが必要となるが、Google ChromeおよびFirefoxでは公式にWebDriverの対応が行われており簡単に利用できる。
wbenchのインストール
wbenchはRubyで実装されており、RubyおよびGoogle ChromeもしくはFirefoxが利用できる環境であれば動作する。LinuxやMac OS XだけでなくWindows環境でも利用が可能だ。WindowsにおけるRuby環境のインストールについては割愛するが、RubyInstaller for Windowsで導入したRuby環境で問題なく利用できることを確認している。
wbenchのインストールは、Rubyのパッケージ管理ツールであるgemコマンドで行える。
$ gem install wbench
また、Google ChromeもしくはFirefox用のWebDriverは別途インストールする必要がある。Chrome用のWebドライバー(「ChromeDriver」)はhttps://sites.google.com/a/chromium.org/chromedriver/downloadsからダウンロードが可能だ。また、Firefox用のWebドライバー(「geckodriver」)はGitHubのリリースページからダウンロードできる。
なお、ChromeDriverのバージョンによって対応するGoogle Chromeのバージョンが異なるので注意しよう。また、geckodriverの設定等についてはMozilla Developer NetworkのWebDriverページを参照してほしい。
ダウンロードしたアーカイブには「chromedriver」や「geckodriver」といった実行可能バイナリが含まれているので、アーカイブを展開してこれらをパスの通った適当なディレクトリにコピーしよう。また、当然ながらGoogle ChromeもしくはFirefoxのインストールも必要となる。
wbenchによるベンチマークテスト実行
wbenchでベンチマークを実行するには、引数としてテスト対象とするURLを指定してwbenchコマンドを実行すれば良い。
wbench <テスト対象とするURL>
また、デフォルトではテストにGoogle Chrome(ChromeDriver)が利用されるようになっている。Firefox(geckodriver)を利用する場合は「-b firefox」オプションを追加する必要がある。
このようにしてwbenchコマンドを実行すると、ブラウザが起動して指定したURLにアクセスし、ページが表示されたら終了する、という処理が一定回数(デフォルトでは10回)繰り返され、その結果が出力される(図27)。
たとえばこの例では、Webブラウザがページにアクセスを開始してからページのロードが完了するまで、1686~2846ミリ秒がかかっており、またその平均時間は1754ミリ秒だったことが分かる。
また、試行回数は「-l」オプションで指定可能だ。
-l <試行回数>
「-u」オプションでユーザーエージェント文字列を指定することもできる。
-u <User-Agent文字列>
セッション管理などのためにCookieを送信したい場合は、「-c」オプションで送信するCookieを指定できる。
-c <Cookie>
なお、Basic認証が必要なページにアクセスしたい場合は次のように「@」を使ってURLを指定すれば良い。
http://<ユーザー名>:<パスワード>@www.example.com/
Rubyスクリプトからwbenchを利用する
wbenchはRubyのモジュールとしても利用可能だ。たとえばChromeDriverを使って指定したURLを対象にベンチマークを実行し、その結果を出力するスクリプトは以下のようになる。
require 'wbench' require 'pp' url = 'https://example.com' benchmark = WBench::Benchmark.new(url, :browser => :chrome) results = benchmark.run(5) # runメソッドの引数には試行回数を指定する pp results
このスクリプトを実行すると、以下のような結果が得られる。
$ ruby test1.rb #<WBench::Results:0x00000004050808 @app_server=[nil, nil, nil, nil, nil], @browser= {"navigationStart"=>[0, 0, 0, 0, 0], "connectEnd"=>[58, 80, 59, 58, 60], "connectStart"=>[58, 80, 59, 58, 60], "fetchStart"=>[58, 80, 59, 58, 60], "domainLookupEnd"=>[58, 80, 59, 58, 60], "domainLookupStart"=>[58, 80, 59, 58, 60], "requestStart"=>[87, 107, 89, 89, 91], "responseStart"=>[114, 137, 118, 117, 125], "domLoading"=>[115, 139, 120, 118, 126], "responseEnd"=>[131, 154, 134, 137, 142], "domInteractive"=>[549, 610, 575, 577, 582], "domContentLoadedEventStart"=>[549, 610, 575, 577, 582], "domContentLoadedEventEnd"=>[566, 627, 592, 594, 599], "domComplete"=>[1743, 1784, 1744, 1784, 1793], "loadEventStart"=>[1743, 1784, 1744, 1784, 1793], "loadEventEnd"=>[1746, 1786, 1746, 1788, 1797]}, @latency= {"images.example.com:443"=>[8, 7, 11, 8, 8], "pagead2.googlesyndication.com:443"=>[7, 7, 9, 7, 7], "securepubads.g.doubleclick.net:443"=>[7, 7, 8, 8, 9], "example.com:443"=>[8, 7, 8, 8, 8], "www.google-analytics.com:443"=>[8, 7, 7, 7, 7], "www.googletagservices.com:443"=>[8, 8, 8, 7, 10]}, @loops=5, @time="Fri Dec 9 20:23:26 2016", @url="https://example.com">
また、ベンチマーク実行前に特定のURLにリクエストを送信する設定も可能だ。たとえば次の例は「/login」というパスに対して認証情報を送信してログインを行うものだ。
require 'wbench' require 'pp' url = 'https://example.com' benchmark = WBench::Benchmark.new(url, :browser => :chrome) benchmark.before_each do visit url + '/login' fill_in 'nickname', :with => 'hylom' fill_in 'passwd', :with => 'password' click_button 'login' end results = benchmark.run(5) # runメソッドの引数には試行回数を指定する pp results
wbenchでは「capybara」というWebブラウザを自動操作するためのモジュールが使用されており、「before_each」メソッドに与えているブロックではcapybaraのDSLを使って実行する操作を指定できる。詳しくはcapybaraのドキュメント等を参照して欲しい。
目的に応じて適切なツールを選択しよう
さて、2回に渡って4つのベンチマークツールを紹介してきたが、これらはそれぞれ排他的なものではなく、ケースに応じて組み合わせて利用するのが良い。たとえば単に特定のページのスループットを調べたいだけならApache Benchで十分で、わざわざJmeterを利用するのは手間がかかってしまう。逆に特定の操作を行った場合の負荷を調べたい、といったケースであればJmeterのほうが使いやすい場合もある。
また、昨今ではページのロード時間が検索結果に影響するようになっているため、スループットだけでなくページのロード時間に関する調査も同時に行っていく必要があり、それにはwbenchが有用だ。必要に応じてそれぞれのツールを活用して欲しい。