AWS と Bees with Machine Guns で静的コンテンツの負荷テストを実施する

みなさま、こんにちは。
あわよくば生涯プログラマであり続けたいと願っているエンジニアの吉田でございます。

普段はマネジメントの業務をメインとしている立場ですから、エンジニアっぽい記事を投稿すると役員からお叱りを受ける可能性もございますが、たまには技術的な記事を書きたい気分のときもございますので、そういったリスクは特に気にせず久しぶりに技術的なブログを投稿してみたいと思います。

ちょうど今回の記事のタイトルには「マシンガン」という単語が含まれていますから、もし、お叱りを受けたとしても景気よくマシンガンをぶっ放す勢いで「リスクをとらない方がリスクなんだ!きっとそうなんだ!愛なんだ!」などと血迷った感のあるリスクテイク論をドヤ顔で言い放ちつつも、一方で、謙虚な低姿勢も確実に維持しつつ弁解に臨みたい次第でございます。

さて、ここから本題となります。

最近のインフラ構築はAWS等のクラウド環境を利用することが当たり前のような時代になってきましたね。
サーバーサイドのインフラの事例やノウハウも、ネットで検索すればかなりの記事が見つかるようになってきたのではないでしょうか。

しかしながら、AWSでスケールアウトするサーバ構成は整えたものの、「じゃあ、それって本当にそれだけの負荷を捌けるの?」「パフォーマンス要件を満たせるの?どうなの?なんなの?」という問いを投げられた場合、その問いへの回答を用意するには負荷テストを実施して検証し確証を得た上でご回答せねばなりません。

ネット上で負荷テストの記事をGoogleと呼ばれる人気のサイトで検索させて頂いても、クラスメソッドさんの頑張りが非常によく伝わってくる程度で、分散環境での負荷テストの記事は日本圏だとなかなか見つからないように感じております。

実際、負荷テストを実施している人は潜在的にたくさんいらっしゃるとは思うのですが、IT戦士は常に忙しくO’Reillyという本の表紙を飾っているような愛くるしい動物たちや、あるいはRed BullやMonsterといったエネルギッシュな動物たちと目まぐるしくも充実した日々を送っているため、なかなかアウトプットまで至らないというのが実情なのでしょうか。大変お疲れさまでございます。

そんなわけで、IT戦士の一員として業界に貢献していきたい気持ちもございますので、今回は「Bees with Machine Guns」というツールを利用して、静的コンテンツに対して負荷テストを実施する方法についてご紹介したいと思います。

 

ざっくりとしたパフォーマンス要件

例えば、次のような、ざっくりとした要件があったとしましょう。

「あ、とりあえず、10万人くらいドカーっときても大丈夫なようにしといてもらえます?」

要件というものは、いつもざっくりしているものです。

これをシステム的な観点でのパフォーマンス要件に変換すると、次のような言葉で定義できるかと思います。

  • 「暗黙の前提条件」 : 今回のサイトでは、静的コンテンツ(HTML)の配信を行います。
  • 「暗黙の前提条件」 : 1つのコンテンツのサイズは、平均で 50KByte 程度の見込みです。
  • 「ドカーっときても」 : 1分間で10万人がアクセスする見込みです(=1,666リクエスト/秒)。
  • 「大丈夫なように」 : 1リクエストあたりのレスポンスは0.1秒未満で返却しないとユーザーは離れてしまいます。

つまり、50KByteのファイル を 100ミリ秒未満 で 1,666リクエスト/秒 を捌くことができれば要件を満たせることになります。

今回は、このパフォーマンス要件が満たされるかどうかを Bees with Machine Guns を使って確かめてみたいと思います。

 

 beeswithguns

Bees with Machine Guns とは何か 

Bees with Machine Guns は Python で記述されている MIT Lisence な分散負荷テストツールです。

負荷テストツールといえば Apache Bench や curl-loader, Siege, JMeter など数多く存在しますが、Bees with Machine Guns では、分散したサーバ構成の環境から Apache Bench を利用して一斉に負荷テストを実施することが可能です。

Bees with Machine Guns は、Githubで公開されています。

https://github.com/newsapps/beeswithmachineguns

 

今回の記事では、Bees with Machine Guns について、以下のような流れで紹介していきたいと思います。

  • Bees with Machine Guns のインストール
  • Bees with Machine Guns を使ってみる
  • 負荷テストの結果
  • まとめ
  • 参考文献

 

Bees with Machine Guns のインストール

それでは、早速、 Bees with Machine Guns をインストールしてみましょう。

今回は以下のスペックのインスタンスを使用してみます。

  • OS: Amazon Linux AMI 2014.09.1 (HVM)
  • インスタンスタイプ: t2.small

EC2インスタンスが起動したあとは、SSHでログインして下記の手順を踏んでいきます。

Amazon Linux では、デフォルトで python の easy_install コマンドが使用できる状態となっていますが、Bees with Machine Guns をインストールするにあたって pip コマンドも必要になるため easy_install で pip のインストールを行います。

[root@ip-172-31-5-215 ~]# easy_install pip

次に、virtualenv を使用できるようにするため、pip コマンドを使って virtualenv および virtualenvwrapper をインストールします。

[root@ip-172-31-5-215 ~]# pip install virtualenv virtualenvwrapper
[root@ip-172-31-5-215 ~]# export WORKON_HOME=$HOME/.virtualenvs
[root@ip-172-31-5-215 ~]# export PROJECT_HOME=$HOME/Devel
[root@ip-172-31-5-215 ~]# source virtualenvwrapper.sh

次回以降のログイン時にもパスを通すようにするには、ログイン時の実行スクリプトにも追記しておきましょう。

[root@ip-172-31-5-215 ~]# vim ~/.bash_profile
export WORKON_HOME=$HOME/.virtualenvs
export PROJECT_HOME=$HOME/Devel
source virtualenvwrapper.sh

Bees with Machine Guns のインストールは、 Github のリポジトリから取得するため、 git クライアントをインストールします。

[root@ip-172-31-5-215 ~]# yum install -y git

また、 Bees with Machine Guns のインストール時にビルドで必要となる gcc と python-devel パッケージもインストールしておきます。

[root@ip-172-31-5-215 ~]# yum install -y gcc python-devel

インストールの準備が完了したら、 Github から Bees with Machine Guns のソースコードを取得します。
なお、この記事を書いている時点では、 0f8df8b9ac81fad690000a17d5e657bf079c9e20 のコミットのものを使用しています。

[root@ip-172-31-5-215 ~]# git clone git://github.com/newsapps/beeswithmachineguns.git
[root@ip-172-31-5-215 ~]# cd beeswithmachineguns

Bees with Machine Guns のソースコードを取得後、Python モジュールとして Bees with Machine Guns をインストールするのですが、ここでは Python パッケージを管理しやすくするため、mkvirtualenv で Python 仮想環境を作成します。

[root@ip-172-31-5-215 beeswithmachineguns]# mkvirtualenv --no-site-packages bees

ここでは bees という仮想環境を作成し、その環境内に pip コマンドを使用して Bees with Machine Guns をインストールします。

(bees)[root@ip-172-31-5-215 beeswithmachineguns]# pip install -r requirements.txt

ここまでの手順でBees with Machine Guns のインストールが完了します。

インストールに問題がなければ、bees コマンドを実行できるようになります。

なお、 Python の仮想環境を消したい場合は、 deactivate コマンドで仮想環境を抜けてから rmvirtualenv コマンドで削除ができます。

(bees)[root@ip-172-31-5-215 beeswithmachineguns]# deactivate
[root@ip-172-31-5-215 beeswithmachineguns]# rmvirtualenv bees

もし、deactivate で仮想環境を抜けた後、もう一度、仮想環境に入りたい場合は workon コマンドを使用します。

[root@ip-172-31-5-215 beeswithmachineguns]# workon bees

これで、bees コマンドを実行する準備は整ったのですが、もう少しだけ設定を続ける必要があります。

Bees with Machine Guns では、内部的に Boto という設定ファイルを扱うライブラリが採用されているのですが、AWS SDK の EC2インスタンスを起動するコマンドを使用するため、 AWS_ACCESS_KEY_ID と AWS_SECRET_ACCESS_KEY を Boto の設定ファイルに記述する必要があります。

(bees)[root@ip-172-31-5-215 ~]# vim ~/.boto
[Credentials]
aws_access_key_id = <your access key>
aws_secret_access_key = <your secret key>

<your access key> と <your secret key> には、AWSアカウントのアクセスキーとシークレットキーを記述して保存します。

また、それぞれのEC2インスタンスへSSHで接続する必要があるため、SSH秘密鍵を作成しておきます。

(bees)[root@ip-172-31-5-215 ~]# vim ~/.ssh/ec2keypair.pem
(bees)[root@ip-172-31-5-215 ~]# chmod 400 ~/.ssh/ec2keypair.pem

これで、Bees with Machine Guns を使う準備が整いました。

 

Bees with Machine Guns を使ってみる

Bees with Machine Guns は、すべて bees コマンドで扱う事ができます。

まず、bees コマンドのヘルプを見てみましょう。

(bees)[root@ip-172-31-5-215 beeswithmachineguns]# ./bees -h
Usage:
bees COMMAND [options]

Bees with Machine Guns

A utility for arming (creating) many bees (small EC2 instances) to attack
(load test) targets (web applications).

commands:
  up      Start a batch of load testing servers.
  attack  Begin the attack on a specific url.
  down    Shutdown and deactivate the load testing servers.
  report  Report the status of the load testing servers.


Options:
  -h, --help            show this help message and exit

  up:
    In order to spin up new servers you will need to specify at least the
    -k command, which is the name of the EC2 keypair to use for creating
    and connecting to the new servers. The bees will expect to find a .pem
    file with this name in ~/.ssh/. Alternatively, bees can use SSH Agent
    for the key.

    -k KEY, --key=KEY   The ssh key pair name to use to connect to the new
                        servers.
    -s SERVERS, --servers=SERVERS
                        The number of servers to start (default: 5).
    -g GROUP, --group=GROUP
                        The security group(s) to run the instances under
                        (default: default).
    -z ZONE, --zone=ZONE
                        The availability zone to start the instances in
                        (default: us-east-1d).
    -i INSTANCE, --instance=INSTANCE
                        The instance-id to use for each server from (default:
                        ami-ff17fb96).
    -t TYPE, --type=TYPE
                        The instance-type to use for each server (default:
                        t1.micro).
    -l LOGIN, --login=LOGIN
                        The ssh username name to use to connect to the new
                        servers (default: newsapps).
    -v SUBNET, --subnet=SUBNET
                        The vpc subnet id in which the instances should be
                        launched. (default: None).

  attack:
    Beginning an attack requires only that you specify the -u option with
    the URL you wish to target.

    -u URL, --url=URL   URL of the target to attack.
    -K KEEP_ALIVE, --keepalive=KEEP_ALIVE
                        Keep-Alive connection.
    -p POST_FILE, --post-file=POST_FILE
                        The POST file to deliver with the bee's payload.
    -m MIME_TYPE, --mime-type=MIME_TYPE
                        The MIME type to send with the request.
    -n NUMBER, --number=NUMBER
                        The number of total connections to make to the target
                        (default: 1000).
    -C COOKIES, --cookies=COOKIES
                        Cookies to send during http requests. The cookies
                        should be passed using standard cookie formatting,
                        separated by semi-colons and assigned with equals
                        signs.
    -c CONCURRENT, --concurrent=CONCURRENT
                        The number of concurrent connections to make to the
                        target (default: 100).
    -H HEADERS, --headers=HEADERS
                        HTTP headers to send to the target to attack. Multiple
                        headers should be separated by semi-colons, e.g
                        header1:value1;header2:value2
    -e FILENAME, --csv=FILENAME
                        Store the distribution of results in a csv file for
                        all completed bees (default: '').
    -T TPR, --tpr=TPR   The upper bounds for time per request. If this option
                        is passed and the target is below the value a 1 will
                        be returned with the report details (default: None).
    -R RPS, --rps=RPS   The lower bounds for request per second. If this
                        option is passed and the target is above the value a 1
                        will be returned with the report details (default:
                        None).
    -A basic_auth, --basic_auth=basic_auth
                        BASIC authentication credentials, format auth-
                        username:password (default: None).

 

bees コマンドには、主に以下の4つのサブコマンドが用意されています。

  • bees up (負荷テストクライアントの起動)
  • bees attack (負荷テストの開始)
  • bees down (負荷テストクライアントの停止)
  • bees report (負荷テストクライアントの一覧出力)

これらのコマンドを使って、負荷テストを実施していきます。

まず、bees up コマンドで、負荷テストのクライアントとなるEC2インスタンスを一斉に起動します。
起動するEC2インスタンスの数やインスタンスタイプは、オプションで指定することが可能です。

bees up コマンドで負荷テストクライアントを起動した後は、bees attack コマンドで一斉に負荷をかけることができます。
負荷をかける際の並列数や、負荷をかける合計のリクエスト数をオプションで指定することができます。

bees report コマンドを使うと、現在起動している負荷テストクライアントの一覧を表示することができます。

負荷テストが終わったあとは、bees down コマンドで負荷テストクライアントをまとめてTerminateすることができます。

大まかな流れをつかんだところで、早速 Bees with Machine Guns でマシンガンをぶっ放してみましょう。ミスチルが応援してくれそうな気分です。

 

負荷テストクライアントの起動

Bees with Machine Guns では bees up コマンドを実行することで、負荷クライアントとなるEC2インスタンスを起動できます。

(bees)[root@ip-172-31-5-215 beeswithmachineguns]# ./bees up -s 4 -g AWS-OpsWorks-Default-Server -k ec2keypair -z ap-northeast-1a -i ami-4985b048 -t t2.micro -l ec2-user
Connecting to the hive.
Attempting to call up 4 bees.
Waiting for bees to load their machine guns...
.
.
.
.
Bee i-55be954c is ready for the attack.
Bee i-54be954d is ready for the attack.
Bee i-57be954e is ready for the attack.
Bee i-56be954f is ready for the attack.

bees up コマンドでは、以下のようなオプションを指定することが可能です。

-s 起動するサーバの数
-g セキュリティグループ名
-k SSHのキーペア名
-z アベイラビリティゾーン
-i 起動するインスタンスのAMI-id
-t インスタンスタイプ
-l SSHログインユーザ名

 

負荷テストクライアントが起動した後に、AWS管理コンソールからEC2インスタンスの一覧をみると、以下のように「a bee!」というEC2インスタンス が起動されていることが確認できます。

 bees_up

 

起動している負荷テストクライアントの確認

bees report コマンドで起動しているインスタンスを確認できます。

(bees)[root@ip-172-31-5-215 beeswithmachineguns]# ./bees report
Read 4 bees from the roster.
Bee i-55be954c: running @ 54.65.152.4
Bee i-54be954d: running @ 54.65.58.5
Bee i-57be954e: running @ 54.65.151.6
Bee i-56be954f: running @ 54.65.120.7

 

Apache Bench のインストール

次に、Bee として起動した全てのサーバに Apache Bench をインストールします。
(起動元の AMI に Apache Bench がインストールされている場合は、この手順は必要ありません。)

ab コマンドは httpd-tools に含まれていますので、各インスタンスに httpd-tools パッケージをインストールします。

全てのサーバでに対して、一括のコマンドを実行してインストールしたいため、今回は pssh を使用します。

(bees)[root@ip-172-31-5-215 beeswithmachineguns]# yum install -y pssh

pssh コマンドでは、下記のように -H オプションで複数のサーバを指定することが可能です。

pssh -H "host1 host2 host3" -l ec2-user -x '-tt -oStrictHostKeyChecking=no -i /root/.ssh/ec2keypair.pem' -i 'sudo echo'

それぞれの Bee のIPアドレスは bees report で取得できるので、下記のコマンドでこれを1行にすることができます。

(bees)[root@ip-172-31-5-215 beeswithmachineguns]# ./bees report | grep 'Bee' | cut -d ' ' -f 5 | xargs
54.65.152.4 54.65.58.5 54.65.151.6 54.65.120.7

これを組み合わせ、下記のようなコマンドを実行すると、一括で各インスタンスにコマンドを送ることができます。

(bees)[root@ip-172-31-5-215 beeswithmachineguns]# pssh -H "`./bees report | grep 'Bee' | cut -d ' ' -f 5 | xargs`" -l ec2-user -x "-tt -oStrictHostKeyChecking=no -i /root/.ssh/ec2keypair.pem" -i 'sudo yum install -y httpd-tools'

これで、負荷テストクライアントの各インスタンスに Apache Bench をインストールすることができました。

 

アタックを仕掛ける

それでは、アタックを仕掛けてみましょう。

以下のコマンドでは、1000リクエストを10並列で実行しています。
※ -n で合計のリクエスト数を指定し、 -c で並列の実行数を指定します。

(bees)[root@ip-172-31-5-215 beeswithmachineguns]# ./bees attack -n 1000 -c 10 -u http://example.com/
Read 4 bees from the roster.
Connecting to the hive.
Assembling bees.
Each of 4 bees will fire 250 rounds, 2 at a time.
Stinging URL so it will be cached for the attack.
Organizing the swarm.
Bee 0 is joining the swarm.
Bee 1 is joining the swarm.
Bee 2 is joining the swarm.
Bee 3 is joining the swarm.
Bee 2 is firing her machine gun. Bang bang!
Bee 0 is firing her machine gun. Bang bang!
Bee 1 is firing her machine gun. Bang bang!
Bee 3 is firing her machine gun. Bang bang!
Bee 1 is out of ammo.
Bee 3 is out of ammo.
Bee 0 is out of ammo.
Bee 2 is out of ammo.
Offensive complete.
 Complete requests: 1000
 Failed requests: 0
 Requests per second: 1916.510000 [#/sec] (mean of bees)
 Time per request: 4.176000 [ms] (mean of bees)
 50% responses faster than: 4.081000 [ms]
 90% responses faster than: 4.523000 [ms]
Mission Assessment: Target crushed bee offensive.
The swarm is awaiting new orders.

実行結果の中に「Each of 4 bees will fire 250 rounds, 2 at a time.」という表示がありますが、これは、2並列で250リクエスト( ab -c 2 -n 250 )を4つのインスタンスで実行したということを意味しています。

bees -n 1000 -c 10 という指定をしましたが、実際のところは 2並列 × 4インスタンスとなり、8並列で実行されたことになります。

 

負荷テストを終了する

負荷テストが完了したあとは、負荷テストクライアントのインスタンスを落とします。
インスタンスの停止は bees down コマンドを実行します。

(bees)[root@ip-172-31-5-215 beeswithmachineguns]# ./bees down
Read 4 bees from the roster.
Connecting to the hive.
Calling off the swarm.
Stood down 4 bees.

これで、負荷テストのクライアントは Terminate されます。

 bees down コマンドを実行した後に、AWS 管理コンソールから EC2 インスタンスの一覧をみると、以下のように EC2 インスタンス が Terminate されていることが確認できます。

bees_down

 

負荷テストの結果

ここまでの手順で負荷テストを実施することができましたが、はたして結果はどうだったのでしょうか。

まず、リクエスト数に関しては、bees attack コマンド実行結果の下記部分に注目します。

Requests per second: 1916.510000 [#/sec] (mean of bees)

Requests per second は、秒間のリクエスト数を示すもので、この結果からは 1,916 リクエスト/秒 を捌くことができたということがわかります。

次に、レスポンス速度に関してはどうだったのでしょうか。

レスポンス速度は、bees attack コマンド実行結果の以下の出力行に注目します。

Time per request: 4.176000 [ms] (mean of bees)
50% responses faster than: 4.081000 [ms]
90% responses faster than: 4.523000 [ms]

この結果からは、レスポンス速度は平均で 4.176ミリ秒だったことがわかります。また、レスポンスの50%以上は 4.081ミリ秒未満、レスポンスの90%以上は 4.523ミリ秒未満の速度で応答することができたということもわかります。

上記の結果をまとめると、冒頭で掲げられていた 「50KByte のファイルを 100ミリ秒未満 で 1666リクエスト/秒 を捌けること」 というパフォーマンス要件を満たせることが確認できました。

 

まとめ

今回は、Bees with Machine Guns を使った負荷テストについてご紹介しましたが、いかがだったでしょうか。

分散したサーバ環境からの負荷テストは、負荷をかけるためのサーバを用意したり、各サーバの環境を整えたりする手間がかかって敷居が高いようなイメージもありますが、 Bees with Machine Guns を使う事で、比較的簡単に分散環境から負荷テストを実施できるということがお伝えできていれば幸いです。

もし、負荷テストを実施する機会があった際は、Bees with Machine Guns という候補があることを覚えておくと、Red Bullなどのエネルギッシュな動物たちから離れた幸せなエンジニアライフを送ることができるかもしれませんので、みなさんも 是非ご活用くださいませ。

今回は特定URLに対しての静的コンテンツの負荷テストのご紹介となりましたが、次回は動的なコンテンツに対しての負荷テストについての方法もご紹介できればと思います。

最後までお読みいただき、ありがとうございました。
寒い季節ですので風邪などひかぬようお気を付けくださいませ。

 

参考文献

感謝の意も込め、今回の記事を書く際に参考にしたサイトを以下に挙げさせて頂きます。