サーバの負荷テストのための、何百万ものHTTPリクエストを発生させる方法

(注記:6/9、いただいた翻訳フィードバックを元に記事を修正いたしました。)

今回の記事は毎秒300万ものリクエストを処理できるほど強力で高性能なWebクラスタの構築についてのパート1になります。まず初めに、あまり多くはありませんが、私がこれまで使用したことのあるロードジェネレータツールをいくつか紹介します。私のようにてこずって時間をかけてしまわないよう、今回の記事が理解の手助けになれば幸いです。

ロードジェネレータはテストを目的とした数種類のトラフィックを発生させるプログラムです。それによって高負荷においてサーバがどのように動いているか、そのサーバの弱点はどこなのか、などが見えてきます。負荷テストを通じてサーバの限界を知ることは、サーバのレジリエンシーを測定する最適な方法であり、あらゆる問題に対する準備の手助けにもなります。

ロードジェネレータツール

負荷テストをする際に頭に入れておくべき重要なことはLinuxには限られたソケットしかないということです。これはカーネルをハードコーディングしたことによる制限であり、エフェメラルポート問題として知られています。/etc/sysctl.conf内で(ある程度は)拡張が可能ではありますが、基本的にはLinuxで一度に開けるソケットの数はおよそ64,000個しかありません。そのため、負荷テストをする際に1つの接続から可能な限りリクエストを送るようにして、ソケットを最大限に活用する必要があります。それに加えて、負荷の生成を行うマシンが複数台必要になります。そうしなければ、使用可能なソケット数が上限に達してしまい、十分な負荷を発生させられなくなります。

Apache Bench

最初は「ab」、つまりApache Benchから使用してみました。これは私の知る限り最もシンプルで一般的に使われているHTTPベンチマークツールです。さらにこれは、Apacheに付属しているものなので、おそらくどのシステムにも既に搭載されていると思います。しかし残念なことに私が使用したところ、毎秒たった900のリクエストしか得られませんでした。他で2,000ものリクエストを得られる結果を見たこともありますが、「ab」はあまりこの作業に向いていないとすぐに断言できました。

httperf

次は「httperf」を使用してみました。このツールはより強力ではありますが、それでも比較的単純で機能的に限界があります。毎秒どのくらいのリクエストを生成するかを知るということは単純に数を数える作業とは違って簡単ではありません。何回か試して、やっと毎秒数百以上のリクエストを得ることができました。以下がその例です。

以下では10万のセッションを生成します。レートは毎秒1,000です。各セッションがそれぞれ5回のコールを実行し、そしてそれは2秒ごとにバーストします。

httperf --hog --server=192.168.122.10 --wsess=100000,5,2 --rate 1000 --timeout 5
Total: connections 117557 requests 219121 replies 116697 test-duration 111.423 s

Connection rate: 1055.0 conn/s (0.9 ms/conn, <=1022 concurrent connections)
Connection time [ms]: min 0.3 avg 865.9 max 7912.5 median 459.5 stddev 993.1
Connection time [ms]: connect 31.1
Connection length [replies/conn]: 1.000

Request rate: 1966.6 req/s (0.5 ms/req)
Request size [B]: 91.0

Reply rate [replies/s]: min 59.4 avg 1060.3 max 1639.7 stddev 475.2 (22 samples)
Reply time [ms]: response 56.3 transfer 0.0
Reply size [B]: header 267.0 content 18.0 footer 0.0 (total 285.0)
Reply status: 1xx=0 2xx=116697 3xx=0 4xx=0 5xx=0

CPU time [s]: user 9.68 system 101.72 (user 8.7% system 91.3% total 100.0%)
Net I/O: 467.5 KB/s (3.8*10^6 bps)

結果として、これらの設定で毎秒6,622の接続が得られました。

httperf --hog --server 192.168.122.10 --num-conn 100000 --ra 20000 --timeout 5

(合計10万の接続が毎秒2万の固定レートで生成されました。)

「httperf」には潜在能力があり、わずかながら「ab」より多くの機能を持っていますが、今回のプロジェクトに使用するにはまだ不十分な性能です。分散したやり方で複数の負荷テストノードをサポートできるものが必要となります。従って、次に試すものはJMeterです。

Apache JMeter

これは完全な機能を備えたWebアプリケーションのテストスイートで、実生活におけるユーザの振る舞いについてはどんな類いのものでも再現できます。例えばJMeterのプロキシを使って自分のWebサイトを訪れ、自由にクリックしたりログインしたりと、ユーザがしそうな操作をいろいろと行います。そしてそれらの操作をJMeterにテストケースとして記録します。するとJMeterは、こちらの指定した”ユーザ”数で、それらのアクションを繰り返し実行します。これは非常に面白かったです。設定は「ab」や「httperf」よりもだいぶ複雑ですけどね。

このテストを実行したところ、すぐに毎秒14,000リクエストを達成しました。これは間違いなく、よい方向に向かっていると思いました。

私はJMeter-Pluginsからプラグインをいくつか追加しました。そして「Stepping Threads」と「HTTP RAW」リクエストを使ったところ、ついに毎秒3万のリクエストを生成することができたのです。しかしそこで頭打ちだったので、次を試すことにしました。こちらのリンク先に、私が以前作成したJMeterの設定を載せています。もし誰かの役に立てれば嬉しいです。完璧には程遠いものですが、出発点さえ定まればいい、ということもありますよね。

Tsung:高負荷、分散、マルチプロトコルのテストツール

これは明らかに良いツールでした。使い始めてすぐに、私はこのツールで毎秒4万リクエストを達成したのです。JMeterのように実行するテスト用に操作を記録できますし、SSLやHTTP、WebDAV、SOAP、PostgreSQL、MySQL、LDAP、Jabber/XMPPなどの多くのプロトコルをテストできます。またJMeterと違って複雑なGUIがないので悩むこともありません。XMLの設定ファイルと、選択した分散ノードへのSSH鍵がいくつかあるだけです。このツールの簡潔さと効率性、そしてロバストネスとスケーラビリティは、私にとってどれも負けず劣らず魅力的でした。このツールは非常にパワフルだと感じました。設定を誤ることなく数百万のHTTPリクエストを生成するのです。

それに加えて、TsungはHTMLで実行したテストについてグラフを作成し、詳細な報告を上げてくれます。テスト結果は分かりやすいですし、画像があるので上司に見せることだってできるのです。

3部構成の記事のパート1のラストとして、これ以降はこのツールについて書きたいと思います。以下の設定についての説明をご覧ください。または次のページへスキップして頂いても結構です。

Cent OS 6.2にTsungをインストールする

まず必要なのが、EPELリポジトリ(Erlang用)です。先に進む前に、これをセットアップしておきましょう。それが完了したら、負荷の生成に使用する各ノードで必要となるパッケージをインストールします。まだノード間のパスワードレスのSSH鍵を設定していないようでしたら、それも行いましょう。

yum -y install erlang perl perl-RRD-Simple.noarch perl-Log-Log4perl-RRDs.noarch gnuplot perl-Template-Toolkit firefox

最新のTsungを、GithubまたはTsungのWebサイトからダウンロードします。

get http://tsung.erlang-projects.org/dist/tsung-1.4.2.tar.gz

展開してコンパイルします。

tar zxfv  tsung-1.4.2.tar.gz
cd tsung-1.4.2
./configure && make && make install

設定の例を~/.tsung. にコピーします。これはTsungの設定ファイルとログファイルの位置です。

cp  /usr/share/doc/tsung/examples/http_simple.xml /root/.tsung/tsung.xml

ご自分の仕様に合わせてこのファイルを編集することもできますし、私の設定をそのまま使っていただいても結構です。以下は私が思考錯誤して設定したもので、7つの分散ノードで使用すると、今や毎秒500万のHTTPリクエストを生成します。

<?xml version="1.0"?>
<!DOCTYPE tsung SYSTEM "/usr/share/tsung/tsung-1.0.dtd">
<tsung loglevel="notice" version="1.0">

<clients>
<client host="localhost" weight="1" cpu="10" maxusers="40000">
<ip value="192.168.122.2"/>
</client>
<client host="loadnode1" weight="1" cpu="9" maxusers="40000">
<ip value="192.168.122.2"/>
</client>
<client host="loadnode2" weight="1" maxusers="40000" cpu="8">
<ip value="192.168.122.3"/>
</client>
<client host="loadnode3" weight="1" maxusers="40000" cpu="9">
<ip value="192.168.122.21"/>
</client>
<client host="loadnode4" weight="1" maxusers="40000" cpu="9">
<ip value="192.168.122.11"/>
</client>
<client host="loadnode5" weight="1" maxusers="40000" cpu="9">
<ip value="192.168.122.12"/>
</client>
<client host="loadnode6" weight="1" maxusers="40000" cpu="9">
<ip value="192.168.122.13"/>
</client>
<client host="loadnode7" weight="1" maxusers="40000" cpu="9">
<ip value="192.168.122.14"/>
</client>
</clients>

<servers>
<server host="192.168.122.10" port="80" type="tcp"/>
</servers>

<load>
<arrivalphase phase="1" duration="10" unit="minute">
<users maxnumber="15000" arrivalrate="8" unit="second"/>
</arrivalphase>

<arrivalphase phase="2" duration="10" unit="minute">
<users maxnumber="15000" arrivalrate="8" unit="second"/>
</arrivalphase>

<arrivalphase phase="3" duration="30" unit="minute">
<users maxnumber="20000" arrivalrate="3" unit="second"/>
</arrivalphase>

</load>

<sessions>
<session probability="100" name="ab" type="ts_http">
<for from="1" to="10000000" var="i">
<request> <http url="/test.txt" method="GET" version="1.1"/> </request>
</for>
</session>
</sessions>
</tsung>

最初のうちは非常に難しく感じますが、一度理解してしまえばとても簡単です。

  • <client>は単純にTsungが実行されるホスト(複数の場合もあります)のことです。Tsungで使いたいIPと最大CPU数を指定できます。またmaxusersを使って、ノードが再現するユーザ数の制限を設定することもできます。再現されたそれぞれのユーザは、後ほど定義する操作を実行することになります。
  • <servers>はテストをしたい[http]サーバの名前(複数の場合もあります)のことです。個々のサーバだけでなく、クラスタIPアドレスをテストする際にもこのオプションを利用します。
  • <load>は、再現されるユーザがWebサイトに”訪問”するタイミングと速さを定義します。
    • <arrivalphase>の設定内容によると、フェーズ1は10分間持続し、で毎秒8ユーザずつ、最大15,000ユーザが訪問します。
<arrivalphase phase=”1″ duration=”10″ unit=”minute”>
<users maxnumber=”15000″ arrivalrate=”8″ unit=”second”/>
  • 上記のコードの他に2つのarrivalphaseがあり、ユーザの訪問について同じような設定がされています。
  • 全てのarrivalphaseを合わせたものがで、毎秒いくつのリクエストを生成するかを制御しています。

    • <sessions>というセクションでは、Webサイトに訪問した後、ユーザがどのような操作をするかを定義します。
    • probabilityではユーザがする可能性のある、不規則な操作を定義することができます。例えばこちらをクリックする時もあれば、別の時はあちらをクリックするといった具合です。probabilityは必ず合計で100%になるようにします。
    • 上記の設定ではユーザは1つの行為しかしないため、probabilityも1つで100%に設定されています。
    • <for from="1" to="10000000" var="i">で定義された操作を100%の確立で行うこととなります。1,000万回ループし、/test.txtというWebページを<request>するという設定になっています。

それぞれの意味を正しく理解できれば、この便利なエイリアスを作成して、すぐにTsungのレポートを確認することができるようになります。

vim ~/.bashrc
alias treport="/usr/lib/tsung/bin/tsung_stats.pl; firefox report.html"
source ~/.bashrc

それでは、Tsungを使ったテストを開始しましょう。

[root@loadnode1 ~] tsung start
Starting Tsung
"Log directory is: /root/.tsung/log/20120421-1004"

テストが終了したらレポートを確認します。

cd /root/.tsung/log/20120421-1004
treport

tsung-thumb

Tsungを利用してクラスタ構築を計画する

さて、これで十分に優れた負荷テストツールが手に入ったので、残りのクラスタ構築を計画することが可能です。

  1. Tsungを利用して単一のhttpサーバをテストし、基本ベンチマークを獲得します。
  2. Webサーバを徹底的に調整します。常時、Tsungを使ってテストし、改善しているか確認しましょう。
  3. システムのTCPソケットを調整し、最適なネットワークパフォーマンスを実現しましょう。ここでも、やはり何度もテストを繰り返してください。
  4. 完璧に調整されたWebサーバを含むLVSクラスタを構築します。
  5. IPクラスタ上でTsungを使ってLVSのストレステストを行います。

このパート1に続く2つの記事では、皆さんのWebサーバで最速のパフォーマンスを得るための方法と、クラスタソフトウェアのLVSを導入する方法をご紹介します。

このシリーズのパート2、「Tuning Nginx for Best Performance」 はこちらからどうぞ。