プログラマでありたい

おっさんになっても、プログラマでありつづけたい

Bees with machine gunsを使って負荷テスト

 静的サイトの運用であれば、現状ではS3 Web Hosting機能が最適と考えています。OSやミドルウェアのアップデートも不要で、かつ仮想サーバを起動するより圧倒的に安価です。一方で、ほぼ無敵のS3といえども、一定時間で過剰なアクセスがあった場合はスロットリング(throttling)といって、規制されてアクセス制限が掛かる可能性があります。しかし、その閾値がどれくらいなのか、謎です。(全体のリソース状況によるので、一定ではないとのこと)
 ちょっと試してみたかったので、負荷テストをおこなってみました。S3に対する負荷テストの場合、そんじょそこらの負荷では太刀打ち出来ません。そこで、複数のサーバから負荷を掛けてみることにしてみました。と言っても、複数のサーバをコントロールするのは面倒臭いので、Bees with machine gunsを使ってみました。

Bees with machine gunsとは?



 Python製の負荷テストツールです。Bees with machine gunsをインストールしたマシン(親機)から、Beeとよばれる子機(EC2 micro instance)を操作します。子機は、任意の数を起動可能です。複数のBeeから一斉に負荷を掛けて、最後に親機で集計します。負荷テスト自体は、Apache Benchを利用しているようです。
f:id:dkfj:20140728051149p:plain

Bees with machine gunsのインストール



Bees with machine gunsは、以下のモジュールに依存しています。

  • Python 2.6
  • boto
  • paramiko

 特別なものが無いので、楽勝と思いきやハマりました。Amazon Linuxを利用してたのですが、手順通り進めていくと、下記のようなエラーにぶち当たります。

/usr/lib64/python2.6/site-packages/Crypto/Util/number.py:57: PowmInsecureWarning: Not using mpz_powm_sec. You should rebuild using libgmp >= 5 to avoid timing attack vulnerability.
_warn("Not using mpz_powm_sec. You should rebuild using libgmp >= 5 to avoid timing attack vulnerability.", PowmInsecureWarning)

Traceback (most recent call last):
File "/usr/bin/bees", line 3, in
from beeswithmachineguns import main
File "/usr/lib/python2.6/site-packages/beeswithmachineguns/main.py", line 27, in
import bees
File "/usr/lib/python2.6/site-packages/beeswithmachineguns/bees.py", line 36, in
import paramiko
File "/usr/lib/python2.6/site-packages/paramiko/__init__.py", line 69, in
from transport import SecurityOptions, Transport
File "/usr/lib/python2.6/site-packages/paramiko/transport.py", line 32, in
from paramiko import util
File "/usr/lib/python2.6/site-packages/paramiko/util.py", line 32, in
from paramiko.common import *
File "/usr/lib/python2.6/site-packages/paramiko/common.py", line 98, in
from Crypto import Random
File "/usr/lib64/python2.6/site-packages/Crypto/Random/__init__.py", line 29, in
from Crypto.Random import _UserFriendlyRNG
File "/usr/lib64/python2.6/site-packages/Crypto/Random/_UserFriendlyRNG.py", line 38, in
from Crypto.Random.Fortuna import FortunaAccumulator
File "/usr/lib64/python2.6/site-packages/Crypto/Random/Fortuna/FortunaAccumulator.py", line 39, in
import FortunaGenerator
File "/usr/lib64/python2.6/site-packages/Crypto/Random/Fortuna/FortunaGenerator.py", line 36, in
from Crypto.Cipher import AES
File "/usr/lib64/python2.6/site-packages/Crypto/Cipher/AES.py", line 50, in
from Crypto.Cipher import _AES
ImportError: /usr/lib64/python2.6/site-packages/Crypto/Cipher/_AES.so: undefined symbol: rpl_malloc

 暗号化あたりのモジュールの問題のようで、GNU MPとMPIRを入れ替えた上で、再ビルドが必要のようです。この辺りを参考に、設定します。
The Bug Charmer: Building PyCrypto on Amazon EC2

# yum groupinstall "Development Tools"
# yum install python-devel
# 
# ./configure
# make
# make check
# make install
# wget https://ftp.gnu.org/gnu/gmp/gmp-6.0.0a.tar.bz2
# bzip2 -dc gmp-6.0.0a.tar.bz2 | tar xvf -
# cd gmp-6.0.0
# ./configure
# make
# make check
# make install
# wget http://www.mpir.org/mpir-2.6.0.tar.bz2
# bzip2 -dc mpir-2.6.0.tar.bz2 | tar xvf -
# cd mpir-2.6.0
# ./configure
# make
# make check
# make install
# yum reinstall python
# wget http://ftp.dlitz.net/pub/dlitz/crypto/pycrypto/pycrypto-2.6.tar.gz 
# tar xzvf pycrypto-2.6.tar.gz 
# cd pycrypto-2.6
# vi configure
if test $ac_cv_func_malloc_0_nonnull = yes; then :

$as_echo "#define HAVE_MALLOC 1" >>confdefs.h

#else
#  $as_echo "#define HAVE_MALLOC 0" >>confdefs.h

#   case " $LIBOBJS " in
#  *" malloc.$ac_objext "* ) ;;
#  *) LIBOBJS="$LIBOBJS malloc.$ac_objext"
# ;;
#esac


$as_echo "#define malloc rpl_malloc" >>confdefs.h

# pip uninstall PyCrypto
# python setup.py build
# pyton setup install
# pip install beeswithmachineguns

インストール後に、botoの設定を行ないます。.botoファイルとpemの用意をします。

vi ~/.boto
[Credentials]
aws_access_key_id = 'your access key'
aws_secret_access_key = 'your secret access key'

Bees with machine gunsの利用



 設定が全て終わっていれば、次のようなコマンドでBee(子機)の起動できます。

bees up -s 4 -g セキュリティグループ名 -k pemファイル名

 -sが起動するサーバ数、-gがセキュリティグループ名です。注意点としてはNon-VPCのネットワークで起動するので、Non-VPCのセキュリティグループを作成しておく必要があります。そして、親機からsshでアクセス出来るようにしておく必要があります。

 負荷テストは、次のような形です。
nが負荷リクエストの総数。cが同時接続数です。少ない台数で同時接続数を上げ過ぎると、処理しきれずにエラーがでます。注意点としては、/で終わるURLでないとテストできません。

# bees attack -n 10000 -c 250 -u http://xxxxxxxxxx.s3-website-ap-northeast-1.amazonaws.com/
Read 4 bees from the roster.
Connecting to the hive.
Assembling bees.
Each of 4 bees will fire 2500 rounds, 62 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 3 is firing his machine gun. Bang bang!
Bee 2 is firing his machine gun. Bang bang!
Bee 0 is firing his machine gun. Bang bang!
Bee 1 is firing his machine gun. Bang bang!
Bee 1 is out of ammo.
Bee 0 is out of ammo.
Bee 2 is out of ammo.
Bee 3 is out of ammo.
Offensive complete.
     Complete requests:		10000
     Requests per second:	406.720000 [#/sec] (mean)
     Time per request:		621.828500 [ms] (mean)
     50% response time:		544.250000 [ms] (mean)
     90% response time:		804.250000 [ms] (mean)
Mission Assessment: Target successfully fended off the swarm.
The swarm is awaiting new orders.

 上記の結果をみると、毎秒406リクエストをこなしていたのが解ります。念の為、アクセスログを見てみると、全て200 O.K.でした。

 では、S3の限界に挑戦する為に、台数を増やしてみます。追加でBeeを増やせないようなので、一度落とします。

# bees down

 10台立ててみました。Beeが一杯です。
f:id:dkfj:20140728051259p:plain

# bees attack -n 100000 -c 250 -u http://xxxxxxxxxx.s3-website-ap-northeast-1.amazonaws.com/
Read 10 bees from the roster.
Connecting to the hive.
Assembling bees.
Each of 10 bees will fire 10000 rounds, 25 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 4 is joining the swarm.
Bee 5 is joining the swarm.
Bee 6 is joining the swarm.
Bee 7 is joining the swarm.
Bee 8 is joining the swarm.
Bee 9 is joining the swarm.
Bee 5 is firing his machine gun. Bang bang!
Bee 6 is firing his machine gun. Bang bang!
Bee 3 is firing his machine gun. Bang bang!
Bee 0 is firing his machine gun. Bang bang!
Bee 2 is firing his machine gun. Bang bang!
Bee 8 is firing his machine gun. Bang bang!
Bee 4 is firing his machine gun. Bang bang!
Bee 4 is out of ammo.
Bee 6 is out of ammo.
Bee 0 is out of ammo.
Bee 5 is out of ammo.
Bee 2 is out of ammo.
Bee 8 is out of ammo.
Bee 3 is out of ammo.
Offensive complete.
     3 of your bees didn't make it to the action. They might be taking a little longer than normal to find their machine guns, or may have been terminated without using "bees down".
     Complete requests:		70000
     Requests per second:	330.450000 [#/sec] (mean)
     Time per request:		529.952571 [ms] (mean)
     50% response time:		523.142857 [ms] (mean)
     90% response time:		548.285714 [ms] (mean)

 3台のBeeが行方不明になってしましました。親機の性能不足でしょうか?それとも、子機の性能不足でしょうか?念の為、親機をc3.largeに変更しておきます。

 同時接続数を減らして、再度実行します。

# bees attack -n 10000 -c 200 -u http://xxxxxxxxxx.s3-website-ap-northeast-1.amazonaws.com/
Read 10 bees from the roster.
Connecting to the hive.
Assembling bees.
Each of 10 bees will fire 1000 rounds, 20 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 4 is joining the swarm.
Bee 5 is joining the swarm.
Bee 6 is joining the swarm.
Bee 7 is joining the swarm.
Bee 8 is joining the swarm.
Bee 9 is joining the swarm.
No handlers could be found for logger "paramiko.transport"
Traceback (most recent call last):
  File "/usr/bin/bees", line 5, in <module>
    main.main()
  File "/usr/lib/python2.6/site-packages/beeswithmachineguns/main.py", line 127, in main
    parse_options()
  File "/usr/lib/python2.6/site-packages/beeswithmachineguns/main.py", line 119, in parse_options
    bees.attack(options.url, options.number, options.concurrent)
  File "/usr/lib/python2.6/site-packages/beeswithmachineguns/bees.py", line 325, in attack
    results = pool.map(_attack, params)
  File "/usr/lib64/python2.6/multiprocessing/pool.py", line 148, in map
    return self.map_async(func, iterable, chunksize).get()
  File "/usr/lib64/python2.6/multiprocessing/pool.py", line 422, in get
    raise self._value
AssertionError: PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()

 paramiko.transportのエラーがでました。

感想



 この後も、何度もパラメータや起動台数を変更しながら試してみました。どうも台数や接続数が多いと安定しない模様です。もう少し、調べてみる必要があります。S3 WebHositingに関して言えば、秒間400接続くらいであれば、余裕でこなせる模様です。流石!!


参照:
newsapps/beeswithmachineguns · GitHub
The Bug Charmer: Building PyCrypto on Amazon EC2