Ubuntu14.04でwrkを使用してHTTPレイテンシーをベンチマークする方法
序章
この記事では、 wrk と呼ばれるオープンソースのHTTPベンチマークツールに焦点を当てています。このツールは、高負荷でHTTPサービスのレイテンシを測定します。
レイテンシーは、リクエストが行われた瞬間(wrkによる)と応答が受信された瞬間(サービスから)の間の時間間隔を指します。 これは、訪問者がブラウザまたはHTTPリクエストを送信するその他の方法を使用してサイトにアクセスしたときに、サイトで発生する遅延をシミュレートするために使用できます。
wrkは、次のようなHTTPに依存するWebサイトまたはアプリケーションのテストに役立ちます。
- Railsおよびその他のRubyアプリケーション
- Expressおよびその他のJavaScriptアプリケーション
- PHPアプリケーション
- Webサーバー上で実行されている静的Webサイト
- Nginxなどのロードバランサーの背後にあるサイトとアプリケーション
- キャッシングレイヤー
テスト
wrkはオープンソースであり、GitHubにあります。
それは非常に安定しており、マルチスレッドの性質のおかげで高負荷をシミュレートすることができます。 wrkの最大の機能は、 Lua スクリプトを統合する機能です。これにより、次のような多くの可能性が追加されます。
- Cookieを使用したリクエストのベンチマーク
- カスタムレポート
- 複数のURLのベンチマーク-人気のあるab、ApacheHTTPサーバーベンチマークツールではできないこと
前提条件
このチュートリアルで使用するインフラストラクチャは、次の図に示されています。
ご覧のとおり、非常に単純なシナリオでwrkを使用します。 Node.jsアプリケーションでExpressのベンチマークを行います。
2つのドロップレットをスピンアップします。1つは負荷を生成するwrk用で、もう1つはアプリケーション用です。 それらが同じボックスにある場合、それらはリソースをめぐって競争し、私たちの結果は信頼できません。
ベンチマークを行うマシンは、ストレスのかかったシステムを処理するのに十分な強度が必要ですが、この場合、アプリケーションは非常に単純なので、同じサイズのマシンを使用します。
- 同じリージョンで2つのドロップレットをスピンアップします。これらはプライベートIPで通信するためです。
- 1つのドロップレットwrk1ともう1つのapp1を呼び出して、このチュートリアルに従ってください
- 2GBのメモリを選択します
- Ubuntu14.04を選択します
- 利用可能な設定セクションでプライベートネットワークを選択します
- 各サーバーにsudoユーザーを作成します
小さいドロップレットでも機能しますが、テスト結果ではより多くの遅延が予想されます。 実際のテスト環境では、アプリサーバーは本番環境で使用する予定のサイズと同じである必要があります。
ドロップレットの設定についてサポートが必要な場合は、この記事を参照してください。
ステップ1—両方のサーバー:Dockerをインストールします
私たちの生活を楽にするために、 Docker を使用して、コンテナー内でwrkとアプリケーションを開始できるようにします。 これにより、Node.js環境、npmモジュール、およびdebパッケージの設定をスキップできます。 必要なのは、適切なコンテナをダウンロードして実行することだけです。 節約された時間は、wrkの学習に投資されます。
Dockerに慣れていない場合は、Dockerの概要ここを読むことができます。
注:このセクションのコマンドは、両方のドロップレットで実行する必要があります。
Dockerをインストールするには、サーバーにログインして次のコマンドを実行します。 まず、パッケージリストを更新します。
- sudo apt-get update
WgetとcURLをインストールします。
- sudo apt-get install -y wget curl
Dockerをダウンロードしてインストールします。
- sudo wget -qO- https://get.docker.com/ | sh
ユーザーをdocker
グループに追加して、sudoなしでDockerコマンドを実行できるようにします。
- sudo gpasswd -a ${USER} docker
- sudo service docker restart
- newgrp docker
別のLinuxディストリビューションを使用している場合、Dockerにはインストールドキュメントがあり、おそらくあなたのケースをカバーしています。
docker
が正しくインストールされていることを確認するには、次のコマンドを使用します。
- docker --version
次のような出力が得られるはずです。
OutputDocker version 1.7.1, build 786b29d
ステップ2—テストアプリケーションを準備する
app1ドロップレットでこれらのコマンドを実行します。
テストの目的で、作成者はパブリックDockerレジストリにDockerイメージを公開しました。 Node.jsで記述されたHTTPデバッグアプリケーションが含まれています。 これはパフォーマンスの獣ではありませんが(今日は記録を破ることはありません)、テストとデバッグには十分です。 ソースコードはこちらで確認できます。
もちろん、実際のシナリオでは、独自のアプリケーションをテストする必要があります。
アプリケーションを起動する前に、DropletのプライベートIPアドレスをAPP1_PRIVATE_IP
という変数に保存しましょう。
- export APP1_PRIVATE_IP=$(sudo ifconfig eth1 | egrep -o "inet addr:[^ ]*" | awk -F ":" '{print $2}')
次のコマンドでプライベートIPを表示できます。
- echo $APP1_PRIVATE_IP
出力:
Output10.135.232.163
プライベートIPアドレスは異なりますので、メモしておいてください。
digitalocean.comコントロールパネルからプライベートIPを取得することもできます。 ドロップレットを選択してから、下の画像に示すように設定セクションに移動します。
次に、次のコマンドを実行するだけでアプリケーションを起動します。
- docker run -d -p $APP1_PRIVATE_IP:3000:3000 --name=http-debugging-application czerasz/http-debugger
上記のコマンドは、最初に必要なDockerイメージをダウンロードしてから、Dockerコンテナーを実行します。 コンテナはデタッチモードで開始されます。これは、単にバックグラウンドで実行されることを意味します。 オプション-p $APP1_PRIVATE_IP:3000:3000
は、ポート3000
のローカルコンテナとの間、およびポート3000
のホストプライベートIPとの間のすべてのトラフィックをプロキシします。
次の図は、この状況を示しています。
次に、curl
でテストして、アプリケーションが実行されているかどうかを確認します。
- curl -i -XPOST http://$APP1_PRIVATE_IP:3000/test -d 'test=true'
期待される出力:
OutputHTTP/1.1 200 OK
X-Powered-By: Express
X-Debug: true
Content-Type: text/html; charset=utf-8
Content-Length: 2
ETag: W/"2-79dcdd47"
Date: Wed, 13 May 2015 16:25:37 GMT
Connection: keep-alive
ok
アプリケーションは非常にシンプルで、ok
メッセージのみを返します。 したがって、wrkがこのアプリケーションを要求するたびに、小さなok
メッセージが返されます。
最も重要な部分は、アプリケーションログを分析することで、アプリケーションに対してwrkからどのような要求が行われたかを確認できることです。
次のコマンドを使用して、アプリケーションログを表示します。
- docker logs -f --tail=20 http-debugging-application
サンプル出力は次のようになります。
Output[2015-05-13 16:25:37] Request 1
POST/1.1 /test on :::3000
Headers:
- user-agent: curl/7.38.0
- host: 0.0.0.0:32769
- accept: */*
- content-length: 9
- content-type: application/x-www-form-urlencoded
No cookies
Body:
test=true
必要に応じて、ベンチマークテストの実行中にこれを実行したままにすることができます。 CTRL-C
でテールを終了します。
ステップ3—wrkをインストールします
wrk1 サーバーにログインし、wrkをインストールする準備をします。
Dockerがあるので、とても簡単です。 次のコマンドを使用して、Dockerレジストリハブから williamyeh /wrkイメージをダウンロードするだけです。
- docker pull williamyeh/wrk
上記のコマンドは、wrkを含むDockerイメージをダウンロードします。 wrkをビルドしたり、追加のパッケージをインストールしたりする必要はありません。 wrk(コンテナー内)を実行するには、このイメージに基づいてコンテナーを開始するだけで済みます。これはすぐに実行します。
イメージは非常に小さいため、ダウンロードには数秒かかります(3 MB未満)。 お気に入りのLinuxディストリビューションに直接wrkをインストールする場合は、このwikiページにアクセスし、指示に従ってください。
また、このサーバーでAPP1_PRIVATE_IP
変数を設定します。 app1ドロップレットのプライベートIPアドレスが必要です。
次のコマンドで変数をエクスポートします。
- export APP1_PRIVATE_IP=10.135.232.163
10.135.232.163
IPアドレスをapp1ドロップレットのプライベートIPに変更することを忘れないでください。 この変数は現在のセッションでのみ保存されるため、次回ログインしてwrkを使用するときに忘れずに再設定してください。
ステップ4—wrkベンチマークテストを実行します
このセクションでは、最終的にwrkの動作を確認します。
このセクションのすべてのコマンドは、wrk1ドロップレットで実行する必要があります。
wrkが利用できるオプションを見てみましょう。 --version
フラグのみを指定してwrkコンテナーを実行すると、その使用法の簡単な要約が出力されます。
- docker run --rm williamyeh/wrk --version
出力:
Outputwrk 4.0.0 [epoll] Copyright (C) 2012 Will Glozer
Usage: wrk <options> <url>
Options:
-c, --connections <N> Connections to keep open
-d, --duration <T> Duration of test
-t, --threads <N> Number of threads to use
-s, --script <S> Load Lua script file
-H, --header <H> Add header to request
--latency Print latency statistics
--timeout <T> Socket/request timeout
-v, --version Print version details
Numeric arguments may include a SI unit (1k, 1M, 1G)
Time arguments may include a time unit (2s, 2m, 2h)
概要がわかったので、テストを実行するコマンドを作成しましょう。 このコマンドはコンテナ内から実行されていないため、まだ何も実行されないことに注意してください。
wrkで実行できる最も単純なケースは次のとおりです。
wrk -t2 -c5 -d5s -H 'Host: example.com' --timeout 2s http://$APP1_PRIVATE_IP:3000/
つまり:
-t2
:2つの別々のスレッドを使用します-c5
: 6つの接続を開きます(最初のクライアントはゼロです)-d5s
:5秒間テストを実行します-H 'Host: example.com'
:Host
ヘッダーを渡します--timeout 2s
:2秒のタイムアウトを定義しますhttp://$APP1_PRIVATE_IP:3000/
ターゲットアプリケーションは$APP1_PRIVATE_IP:3000
をリッスンしています- アプリケーションの
/
パスをベンチマークします
これは、ホームページを5秒間繰り返し要求する6人のユーザーとして説明することもできます。
次の図は、この状況を示しています。
接続
テストの実際のコマンドは次のとおりです。
説明したシナリオをwrkDockerコンテナーで実行してみましょう。
- docker run --rm williamyeh/wrk -t2 -c5 -d5s -H 'Host: example.com' --timeout 2s http://$APP1_PRIVATE_IP:3000/
テストが実行されるまで数秒待ってから、次のステップで分析する結果を確認します。
ステップ5—出力を評価する
出力:
OutputRunning 5s test @ http://10.135.232.163:3000
2 threads and 5 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 3.82ms 2.64ms 26.68ms 85.81%
Req/Sec 550.90 202.40 0.98k 68.00%
5494 requests in 5.01s, 1.05MB read
Requests/sec: 1096.54
Transfer/sec: 215.24KB
-
現在の構成の概要:
Running 5s test @ http://10.135.232.163:3000 2 threads and 5 connections
ここに、ベンチマーク構成の簡単な要約を示します。 ベンチマークには5秒かかり、ベンチマークマシンのIPは
10.135.232.163
で、テストでは2つのスレッドを使用しました。 -
レイテンシーとリクエスト/秒の統計の正規分布パラメーター:
Thread Stats Avg Stdev Max +/- Stdev Latency 3.82ms 2.64ms 26.68ms 85.81% Req/Sec 550.90 202.40 0.98k 68.00%
この部分では、ベンチマークの正規分布の詳細、つまりガウス関数が持つパラメーターを示します。
ベンチマークは常に正規分布であるとは限らないため、これらの結果は誤解を招く可能性があります。 したがって、常にMaxおよび+/-Stdevの値を確認してください。 これらの値が高い場合は、裾が重い分布である可能性があります。
-
リクエスト数、転送データ、スループットに関する統計:
5494 requests in 5.01s, 1.05MB read Requests/sec: 1096.54 Transfer/sec: 215.24KB
ここでは、
5.01
秒の間に、wrkが5494
リクエストを実行し、1.05MB
のデータを転送できることがわかります。 単純な計算(total number of requrests/benchmark duration
)と組み合わせると、1秒あたりの1096.54
リクエストの結果が得られます。
一般に、設定するクライアントが多いほど、1秒あたりのリクエスト数は少なくなります。 レイテンシーも大きくなります。 これは、アプリケーションの負荷が高くなるためです。
どのような結果が最適ですか?
あなたの目標は、Requests/sec
をできるだけ高く、Latency
をできるだけ低く保つことです。
理想的には、少なくともWebページでは、待ち時間が長すぎないようにする必要があります。 アセットを使用したページの読み込み時間の制限は、2秒以下の場合に最適です。
ここで、おそらく自問するでしょう。550.90 Requests/sec
のレイテンシーは3.82ms
で良い結果ですか? 残念ながら、簡単な答えはありません。 それは次のような多くの要因に依存します:
- 前に説明したように、クライアントの数
- サーバーリソース-それは大きなインスタンスですか、それとも小さなインスタンスですか?
- アプリケーションを提供するマシンの数
- サービスの種類-静的ファイルを提供するキャッシュですか、それとも動的応答を提供する広告サーバーですか?
- データベースの種類、データベースクラスタのサイズ、データベース接続の種類
- リクエストとレスポンスのタイプ-それは小さなAJAXリクエストですか、それともファットAPI呼び出しですか?
- そして他の多く
ステップ6—レイテンシを改善するためのアクションを実行する
サービスのパフォーマンスに満足できない場合は、次のことができます。
- サービスを調整します-コードをチェックして、より効率的に何ができるかを確認します
- データベースをチェックして、それがボトルネックかどうかを確認します
- 垂直方向にスケーリング-マシンにリソースを追加します
- 水平方向にスケーリング-サービスの別のインスタンスを追加し、ロードバランサーに追加します
- キャッシングレイヤーを追加する
アプリケーションの改善に関する詳細については、本番Webアプリケーションサーバーのセットアップを改善する5つの方法を参照してください。
サービスに変更を適用した後、サービスのベンチマークを忘れないでください。そうして初めて、サービスが改善されたことを確認できます。
このLuaがなかったら、それだけだと思うかもしれません。 . .
Luaスクリプトを使用して高度なHTTPリクエストをシミュレートする
wrkにはLuaJIT(Lua用のジャストインタイムコンパイラ)が組み込まれているため、Luaスクリプトで拡張できます。 はじめに述べたように、これはwrkに多くの機能を追加します。
wrkでLuaスクリプトを使用するのは簡単です。 -s
フラグにファイルパスを追加するだけです。
Docker内でwrkを使用するため、最初にこのファイルをコンテナーと共有する必要があります。 これは、Dockerの-v
オプションを使用して実現できます。
wrk用のLuaスクリプトの一部
一般的な形式では、test.lua
というスクリプトを使用すると、コマンド全体は次のようになります。
- docker run --rm -v `pwd`/scripts:/scripts williamyeh/wrk -c1 -t1 -d5s -s /scripts/test.lua http://$APP1_PRIVATE_IP:3000
前の手順で、wrkコマンドとそのオプションについて説明しました。 このコマンドはあまり追加しません。 スクリプトへのパスと、コンテナーの外でスクリプトを見つける方法をDockerに指示するいくつかの追加コマンド。
--rm
フラグは、コンテナーが停止した後、コンテナーを自動的に削除します。
しかし、Luaスクリプトの書き方を実際に知っていますか? 恐れるな。 あなたはそれを簡単に学ぶでしょう。 ここでは簡単な例を見ていきます。独自のより高度なスクリプトを自分で実行できます。
まず、wrkの内部ロジックを反映する所定のスクリプト構造について説明します。 次の図は、1つのスレッドについて示しています。
wrkは、次の実行フェーズを実行します。
- 解決ドメインのIPアドレス
- スレッドsetupから始めます
- ストレステストフェーズを実行します。これは、実行中フェーズと呼ばれます。
- 最後のステップは単に完了と呼ばれます
複数のスレッドを使用する場合、1つの解決フェーズと1つの完了フェーズがありますが、2つのセットアップフェーズと2つの実行フェーズがあります。
さらに、実行フェーズは、 init 、 request 、およびresponseの3つのステップに分割できます。
提示された図とドキュメントによると、Luaスクリプト内で次のメソッドを使用できます。
-
setup(thread)
:すべてのスレッドが初期化されているが、まだ開始されていない場合に実行されます。 スレッドにデータを渡すために使用されます -
init(args)
:各スレッドが初期化されるときに呼び出されますこの関数は、スクリプトの追加のコマンドライン引数を受け取ります。この引数は、
--
でwrk引数から分離する必要があります。例:
wrk -c3 -d1s -t2 -s /scripts/debug.lua http://$APP1_PRIVATE_IP:3000 -- debug true
-
request()
:リクエストごとにHTTPオブジェクトを返す必要があります。 この関数では、メソッド、ヘッダー、パス、および本文を変更できますwrk.format
ヘルパー関数を使用して、リクエストオブジェクトを形成します。例:
return wrk.format(method, path, headers, body)
-
response(status, headers, body)
:応答が戻ってきたときに呼び出されます -
done(summary, latency, requests)
:すべてのリクエストが終了し、統計が計算されたときに実行されますこの関数内では、次のプロパティを使用できます。
財産 説明 summary.duration
マイクロ秒単位の実行時間 summary.requests
完了したリクエストの総数 summary.bytes
受信した合計バイト数 summary.errors.connect
総ソケット接続エラー summary.errors.read
ソケット読み取りエラーの合計 summary.errors.write
合計ソケット書き込みエラー summary.errors.status
HTTPステータスコードの合計>399 summary.errors.timeout
リクエストの合計タイムアウト latency.min
テスト中に到達した最小遅延値 latency.max
テスト中に到達した最大遅延値 latency.mean
テスト中に到達した平均遅延値 latency.stdev
待ち時間の標準偏差 latency:percentile(99.0)
99パーセンタイル値 latency[i]
リクエストの生のレイテンシデータ i
各スレッドには独自のLuaコンテキストがあり、その中には独自のローカル変数があります。
ここでいくつかの実用的な例を見ていきますが、wrkプロジェクトのscriptsディレクトリでさらに多くの便利なベンチマークスクリプトを見つけることができます。
例:POST
リクエスト
POST
リクエストをシミュレートする最も簡単な例から始めましょう。
POST
リクエストは通常、サーバーにデータを送信するために使用されます。 これはベンチマークに使用できます。
-
HTMLフォームハンドラー:HTMLフォームの
action
属性にあるアドレスを使用します。<form action="/login.php"> ... </form>
-
POST
APIエンドポイント:RESTful APIを使用している場合は、記事を作成するエンドポイントを使用してください。POST /articles
wrk1ドロップレットにscripts/post.lua
ファイルを作成することから始めます。
- cd ~
- mkdir scripts
- nano scripts/post.lua
次のコンテンツを追加します。
wrk.method = "POST"
wrk.body = "login=sammy&password=test"
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"
このスクリプトは非常に単純であり、前述の方法はまだ使用していません。 グローバルwrk
オブジェクトのプロパティを変更しました。
requestメソッドをPOST
に変更し、いくつかのログインパラメータを追加し、Content-Type
ヘッダーをHTMLフォームが使用するMIMEタイプに指定しました。
ベンチマークを開始する前に、スクリプト、Dockerコンテナー、およびアプリサーバーがどのように関連しているかを視覚化するのに役立つ図を次に示します。
真実の瞬間-このコマンドでアプリケーションのベンチマークを行います( wrk1 ドロップレットで実行):
- docker run --rm -v `pwd`/scripts:/scripts williamyeh/wrk -c1 -t1 -d5s -s /scripts/post.lua http://$APP1_PRIVATE_IP:3000
出力:
OutputRunning 5s test @ http://10.135.232.163:3000
1 threads and 1 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.04ms 718.38us 12.28ms 90.99%
Req/Sec 1.02k 271.31 1.52k 66.00%
5058 requests in 5.00s, 0.97MB read
Requests/sec: 1011.50
Transfer/sec: 198.55KB
出力は、前に見たものと似ています。
ここでは、接続が1つだけのベンチマークを行っていることに注意してください。 これは、1人のユーザーだけがユーザー名とパスワードを渡して継続的にログインしたい状況に対応します。 これは、CSS、画像、またはJavaScriptファイルを要求していません。
より現実的なシナリオでは、クライアントとスレッドの数を増やし、同時にレイテンシーパラメーターを監視して、アプリケーションがユーザーの資格情報を検証できる速度を確認する必要があります。
例:複数のURLパス
もう1つの一般的なニーズは、アプリケーションの複数のパスを同時にテストすることです。
data
ディレクトリにpaths.txt
というファイルを作成し、ベンチマーク中に使用するすべてのパスを追加しましょう。
- cd ~
- mkdir data
- nano data/paths.txt
以下のdata/paths.txt
の例を見つけてください。
/feed.xml
/contact/
/about/
/blog/
/2015/04/21/nginx-maintenance-mode/
/2015/01/06/vagrant-workflows/
/2014/12/10/top-vagrant-plugins/
次に、この単純なスクリプトを取得し、scripts/multiple-url-paths.lua
として保存します。
-- Load URL paths from the file
function load_url_paths_from_file(file)
lines = {}
-- Check if the file exists
-- Resource: http://stackoverflow.com/a/4991602/325852
local f=io.open(file,"r")
if f~=nil then
io.close(f)
else
-- Return the empty array
return lines
end
-- If the file exists loop through all its lines
-- and add them into the lines array
for line in io.lines(file) do
if not (line == '') then
lines[#lines + 1] = line
end
end
return lines
end
-- Load URL paths from file
paths = load_url_paths_from_file("/data/paths.txt")
print("multiplepaths: Found " .. #paths .. " paths")
-- Initialize the paths array iterator
counter = 0
request = function()
-- Get the next paths array element
url_path = paths[counter]
counter = counter + 1
-- If the counter is longer than the paths array length then reset it
if counter > #paths then
counter = 0
end
-- Return the request object with the current URL path
return wrk.format(nil, url_path)
end
このチュートリアルはLuaスクリプトを詳細に教えようとはしていませんが、スクリプトのコメントを読むと、Luaのスクリプトが何をするのかをよく理解できます。
multiple-url-paths.lua
スクリプトは、/data/paths.txt
ファイルを開き、このファイルにパスが含まれている場合、それらは内部paths
配列に保存されます。 次に、リクエストごとに次のパスが選択されます。
このベンチマークを実行するには、次のコマンドを使用します( wrk1 ドロップレットで実行します)。 コピーを簡単にするために、いくつかの改行を追加していることに気付くでしょう。
- docker run --rm \
- -v `pwd`/scripts:/scripts \
- -v `pwd`/data:/data \
- williamyeh/wrk -c1 -t1 -d5s -s /scripts/multiple-url-paths.lua http://$APP1_PRIVATE_IP:3000
出力:
Outputmultiplepaths: Found 7 paths
multiplepaths: Found 7 paths
Running 5s test @ http://10.135.232.163:3000
1 threads and 1 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 0.92ms 466.59us 4.85ms 86.25%
Req/Sec 1.10k 204.08 1.45k 62.00%
5458 requests in 5.00s, 1.05MB read
Requests/sec: 1091.11
Transfer/sec: 214.17KB
JSONとYAMLを使用した高度なリクエスト
これで、他のベンチマークツールでもこれらのタイプのテストを実行できると思うかもしれません。 ただし、wrkには、JSONまたはYAML形式を使用して高度なHTTPリクエストを処理する機能もあります。
たとえば、各リクエストを詳細に記述したJSONまたはYAMLファイルを読み込むことができます。
著者は、著者の技術ブログでJSONリクエストを使用した高度な例を公開しています。
wrkとLuaを使用して、考えられるあらゆる種類のHTTPリクエストのベンチマークを行うことができます。
結論
この記事を読むと、wrkを使用してアプリケーションのベンチマークを実行できるようになります。 補足として、Dockerの美しさと、アプリケーションとテスト環境のセットアップを大幅に最小限に抑える方法についても説明します。
最後に、wrkでLuaスクリプトを使用して、高度なHTTPリクエストをさらに活用できます。