こんにちは、メディアプラットフォーム事業部の今雪です。
前回のPLAY DEVELOPERS BLOGでは負荷試験ツールLocustを紹介しました。 今回は当社で併用している負荷試験ツールArtilleryについての紹介と環境構築、その使い方についてLocustと同じような流れで簡単にご紹介します。
最後に「Artillery」と「Locust」両ツールの使い分けについて考察します。本記事が負荷試験ツールの導入の参考になれば嬉しいです。
本記事の内容はMacでの操作を想定しています。
- 負荷テストツールArtilleryについて
- Artilleryのインストール
- ローカルでArtilleryを負荷試験を実行してみよう
- 負荷テストの結果を見てみよう
- テストシナリオを構築してみよう
- AWSでArtilleryを起動して、負荷をかけてみよう
- AWSで秒間1万リクエスト以上の負荷をかけてみよう
- 「Artillery」と「Locust」両ツールの使い分けを比較
- まとめ
負荷テストツールArtilleryについて
Artilleryはyamlファイルで宣言的にシナリオを作成し、負荷をかけることができるNode.js製の負荷テストツールです。 Artilleryは「砲兵団」や「大砲」という意味持つそうです。かっこいいですね。 特徴としては下記のことがあげられます。
- yamlでテストシナリオが書けるので学習コストが低い
- テストの進行状況をリアルタイムで表示するCUI
- 手間いらずのクラウドを使った負荷テスト
特に「手間いらずのクラウドを使った負荷テスト」という点ですが、コマンドひとつでAWS Lambdaを使ってある程度の負荷をかけられるので重宝しています。
Artilleryのインストール
下記の手順に沿ってローカルの環境を構築してみましょう。
本記事ではNode.js v17.6.0で行います。
Node.jsのバージョン確認
% node -v v17.6.0
Artilleryをインストール
% npm install -g artillery@latest
Artilleryのバージョン確認
% artillery -v ___ __ _ ____ _____/ | _____/ /_(_) / /__ _______ __ ___ /____/ /| | / ___/ __/ / / / _ \/ ___/ / / /____/ /____/ ___ |/ / / /_/ / / / __/ / / /_/ /____/ /_/ |_/_/ \__/_/_/_/\___/_/ \__ / /____/ VERSION INFO: Artillery: 2.0.0-29 Node.js: v17.6.0 OS: darwin
これでインストールは完了です。
ローカルでArtilleryを負荷試験を実行してみよう
下記のようにartilleryというフォルダ配下にscript.yamlというファイルを作成しましょう。
./artillery └── script.yaml
script.yamlには下記を記述しておいてください。 targetの部分とurlの部分にはdummyAPIの関数URLを入れてください。 今回はtargetに前回の記事で作成したAPIと同じものを作って設定します。
script.yaml
# dummyAPI config: target: dummyAPIの関数URL phases: - duration: 120 arrivalRate: 5 rampTo: 100 name: Ramp up load scenarios: - name: "dummyAPI" flow: - get: url: dummyAPIの関数URL
上記のyamlは以下のシナリオになっています。
- シナリオの期間は120秒。
- 5人のユーザーからスタートし、最大100人のユーザーになるまで1秒ごとにユーザーを増やす。
- シナリオの期間が終わるまで指定したURLにGETリクエストを行い続ける。
./artilleryの配下でArtilleryを実行してみましょう。
% artillery run script.yaml -------------------------------------- Metrics for period to: 19:39:40(+0900) (width: 0.298s) -------------------------------------- http.codes.200: ................................ 5 http.request_rate: ............................. 5/sec http.requests: ................................. 5 http.response_time: min: ......................................... 22 max: ......................................... 134 median: ...................................... 58.6 p95: ......................................... 98.5 p99: ......................................... 98.5 http.responses: ................................ 5 vusers.completed: .............................. 5 vusers.created: ................................ 5 vusers.created_by_name.dummyAPI: ............... 5 vusers.failed: ................................. 0 vusers.session_length: min: ......................................... 55.4 max: ......................................... 238.4 median: ...................................... 94.6 p95: ......................................... 153 p99: ......................................... 153
上記のようなレポートが出力できていれば、Artilleryを起動できています。 Artilleryは、テストの実行中に10秒ごとにコンソールにレポートを出力します。 120秒間のレポートを見ていきましょう。
負荷テストの結果を見てみよう
-------------------------------- Summary report @ 19:41:41(+0900) -------------------------------- errors.ENOTFOUND: .............................. 2 http.codes.200: ................................ 6298 http.request_rate: ............................. 52/sec http.requests: ................................. 6300 http.response_time: min: ......................................... 9 max: ......................................... 298 median: ...................................... 19.1 p95: ......................................... 29.1 p99: ......................................... 58.6 http.responses: ................................ 6298 vusers.completed: .............................. 6298 vusers.created: ................................ 6300 vusers.created_by_name.dummyAPI: ............... 6300 vusers.failed: ................................. 2 vusers.session_length: min: ......................................... 28.2 max: ......................................... 1299.7 median: ...................................... 44.3 p95: ......................................... 77.5 p99: ......................................... 262.5
上記が120秒後に表示された最終的なレポートになります。
120秒間で6,300リクエストを行い、そのうちの2リクエストが失敗しました。
また http.request_rate
の値を見ると平均で秒間52リクエストしていることがわかります。
失敗している2件のリクエストですが、errors.ENOTFOUNDが2件出ている事からlocalとLambdaとの通信で失敗したものが2件いたのではないかと思われます。
テストシナリオを構築してみよう
もう少し複雑なシナリオを構築してみます。script.yamlを下記のように書き換えましょう。
script.yaml
config: target: dummyAPIの関数URL phases: - duration: 180 arrivalRate: 1 rampTo: 10 name: Ramp up load # dummyAPIをユーザーはSpawnのタイミングで叩く before: flow: - log: "dummyAPI" - post: url: dummyAPIの関数URL # 継続的にdummyAPI2を叩く scenarios: - name: "dummyAPI2" flow: - get: url: dummyAPI2の関数URL
上記のyamlは以下のシナリオになっています。
- シナリオの期間は180秒。
- 1人のユーザーからスタートし、最大10人のユーザになるまで1秒ごとに徐々にユーザーを増やす。
- 初回はPOSTリクエストを行い、その後はシナリオの期間が終わるまでGETリクエストを行い続ける。
AWSでArtilleryを起動して、負荷をかけてみよう
では実行してみましょう。
# awsの環境が意図したところに向いてるか確認 % aws sts get-caller-identity { "UserId": "xxx", "Account": "yyy", "Arn": "zzz" } % artillery run \ --platform aws:lambda \ --platform-opt region=ap-northeast-1 \ --count 1 \ script.yaml
コマンドを実行すると以下のようなログが出力されます。
λ Creating AWS Lambda function... - Bundling test data - script.yaml - Installing dependencies - Creating zip package Preparing AWS environment... - Lambda function: artilleryio-9625d23b-d456-4f47-a450-a40e5223e97e - Region: ap-northeast-1 - AWS account: xxxxxxx Phase started: Ramp up load (index: 0, duration: 180s) 21:00:33(+0900)
AWSのコンソールからLambdaを確認すると作成されていることがわかります。
AWSで秒間1万リクエスト以上の負荷をかけてみよう
以下のスクリプトを作成します。
script.yaml
config: target: dummyAPIの関数URL phases: - duration: 60 arrivalRate: 100 name: Ramp up load before: flow: - log: "dummyAPI" - post: url: dummyAPIの関数URL scenarios: - name: "dummyAPI2" flow: - get: url: dummyAPI2の関数URL
上記のyamlは以下のシナリオになっています。
- シナリオの期間は60秒。
- 100ユーザーがリクエストし続ける。
- 初回はPOSTリクエストを行い、その後はシナリオの期間が終わるまでGETリクエストを行い続ける。
次に以下のコマンドを実行します。
% artillery run \ --platform aws:lambda \ --platform-opt region=ap-northeast-1 \ --count 200 \ script.yaml
--count 200
のオプションをつけることで、上記のシナリオを実行するLambdaが200個同時に実行されます。
つまり、100ユーザー * 200
で秒間20,000リクエストできる想定です。
実行した結果は以下のようになりました。
All VUs finished. Total time: 1 minute, 13 seconds -------------------------------- Summary report @ 18:51:14(+0900) -------------------------------- http.codes.200: ................................ 553217 http.codes.429: ................................ 646783 http.request_rate: ............................. 19997/sec http.requests: ................................. 1200000 http.response_time: min: ......................................... 0 max: ......................................... 731 median: ...................................... 7.9 p95: ......................................... 22.9 p99: ......................................... 27.9 http.responses: ................................ 1200000 vusers.completed: .............................. 1200000 vusers.created: ................................ 1200000 vusers.created_by_name.dummyAPI2: .............. 1200000 vusers.failed: ................................. 0 vusers.session_length: min: ......................................... 7.9 max: ......................................... 1065.9 median: ...................................... 22.4 p95: ......................................... 38.5 p99: ......................................... 47 Estimated AWS Lambda cost for this test: $1.344
60秒間で1,200,000リクエストを行い、1,200,000のレスポンスを受け取ることができています。
また http.request_rate
の値を見ると平均で秒間19,997リクエストしていることがわかります。
受け取ったレスポンスのステータスコードを見ると、ステータス200で帰ってきているのが553,217件、ステータス429で帰ってきているのが646,783件でした。
これはターゲットとなっている負荷をかけられる側のLambdaで同時実行数の上限に達してスロットリングエラーが発生しているためでした。
シナリオでは期間を60秒と設定しておりましたが、テスト終了に1分13秒かかったのは全てのユーザーがシナリオを完了させるのに時間がかかったためです。
リクエストしてからレスポンスを受け取るまでの時間も含めるため、リクエスト数が多いとテスト終了までに時間がかかるようです(公式ドキュメントでも説明されております) 。
結果の最後にArtilleryが負荷をかけるために実行したAWS Lambdaの使用料金の予測が出ているので、AWSの難しい料金計算をしなくても良いので助かりますね。(※今回負荷をかけるターゲットにしているLambdaの使用料金は計算されていません。)
Estimated AWS Lambda cost for this test: $1.344
「Artillery」と「Locust」両ツールの使い分けを比較
- 負荷テストのモニタリングをCUIベースで行うか、GUIベースで行うか
Artillery => CUIだけで完結したい
Locust => GUIでグラフでもモニタリングしたい
- 負荷テストの結果をどのように保存しておくか
Artillery => CUIのレポートログだけで良い
Locust => 整形されたファイル形式で残しておきたい
- エラーした原因を負荷テストツール側でみれるかどうか
Artillery => 見れない
Locust => 見れる
- 複雑なテストシナリオを書く際の向き不向き
Artillery => 不向き。特にレスポンスを利用する際は煩雑になる
Locust => 向いている。Pythonで書くためプログラミングと同じ感覚で組める
- 数万リクエストの負荷試験をすぐに行いたい時
Artillery => AWS上で実行する環境構築はコマンド一発でできるのでAWSの恩恵を受けて数万リクストはすぐに出せる
Locust => AWS上で実行する環境構築には別の技術のある程度の理解が必要
本記事では触れることはありませんでしたが、Artilleryは、継続的インテグレーション(CI)と自動化をサポートしているようです。 CI/CDパイプラインの一部としてステージング/機能環境に対してテストを実行して、パフォーマンスの低下を早期に発見し、SLOを検証することができるようです。
まとめ
今回はNode.js製の負荷試験ツール「Artillery」を使って
「ローカルでの負荷試験の実行方法」
「AWS上での負荷試験の実行方法」
「AWS上で秒間1万リクエスト以上の負荷試験をする」
を見てきました。
PLAY DEVELOPERS BLOG 2回に渡って「Locust」と「Artillery」、2つの負荷試験ツールの紹介と比較を行いました。 月並みではありますが、使用用途に合わせた負荷試験ツールの選定が重要になります。