SMARTCAMP Engineer Blog

スマートキャンプ株式会社(SMARTCAMP Co., Ltd.)のエンジニアブログです。業務で取り入れた新しい技術や試行錯誤を知見として共有していきます。

ArtilleryでServerlessな負荷試験を試してみた

スマートキャンプ、エンジニア井上です。

突然ですがみなさん、負荷試験はどのように実施していますか?

私はJmeterなどで負荷試験をすることがあるのですが、テスト作成からテスト実施までがとても時間がかかり継続的にやるのはかなり大変だなと感じてます。

そんなときに見つけた、負荷テストツールArtilleryについて簡単にご紹介できればと思います。

Artilleryとは

Artillery は yamlファイルで宣言的にシナリオを作成し、負荷をかけることができる Nodejs 製の負荷テストツールです。

Artilleryのドキュメントにも開発者の生産性が何よりも優先され、そのために簡単に記述して実行できる構造になっていると記載がある通り 複雑になりがちなテストをyamlで書けるので、簡単にかけて理解がしやすい構造が魅力の1つと感じています。

また、CircleCIやGithubActionにも簡単に組み込むことができるのも良いなと思う点です。

Artilleryで負荷試験を実装してみる

では、実際にArtilleryでのテスト実行を試してみます。

事前時準備

npm i -g artillery

まずはクイック実行

負荷試験でシンプルに下記のコマンドで負荷をかけることができます

※ quickはお試し用のコマンドなのでhttpのみテスト可能で、WebSocketsなどはテストできません

下記は例として20人の仮想ユーザーが5回ずつ、計100リクエストをhttp://example.comに投げます

artillery quick --count 5 --num 20  http://example.com

コマンドを実行すると下記のような実行結果が表示されRPSやLatencyが計測可能です。

All virtual users finished
Summary report @ 08:15:22(+0900) 2021-09-15
  Scenarios launched:  5
  Scenarios completed: 5
  Requests completed:  100
  Mean response/sec: 10.71
  Response time (msec):
    min: 110
    max: 445
    median: 230
    p95: 373
    p99: 417.5
  Scenario counts:
    0: 5 (100%)
  Codes:
    200: 100

yamlからテストを実行する

yamlで定義すると、より詳細に負荷テストのシナリオを構築可能です。 今回は例として下記のような負荷試験を行いたいと思います。

例) http://example.comに対して、10秒間に1人のユーザーで下記のようなシナリオを実行する

  1. example.comにログインする
  2. indexにGet Requestする
  3. createにPost Requestでnameを送る
config:
  target: "http://example.com"
  phases:
    - duration: 10
      arrivalRate: 1
scenarios:
  - flow:
      - log: "get index"
      - get:
          url: "/index" 
      - log: "post create"
      - post:
          url: "/create"
          json:
            name: "test"

テストを実行する

$ artillery run script.yml
All virtual users finished
Summary report @ 09:45:20(+0000) 2021-09-13
  Scenarios launched:  10
  Scenarios completed: 10
  Requests completed:  20
  Mean response/sec: 2.11
  Response time (msec):
    min: 0
    max: 1
    median: 0.5
    p95: 1
    p99: 1
  Scenario counts:
    0: 10 (100%)
  Codes:
    200: 20

さらに高負荷にしたい

さらに負荷を上げたい場合にはServerless-artilleryというツールを使います。

Serverless-artilleryとは

serverless-artillery はArtilleryをサーバーレス環境で実行できるツールです。

Serverless-artilleryで負荷試験をする利点

通常の負荷試験をする際には、負荷をかける側のサーバースペックが懸念点になります。 高い負荷をかけるにはそれに見合うサーバースペックが必要になります。 サーバースペックの不足により想定していただけの負荷がかからず試験失敗となるケースがあるため、負荷試験計画時にはそれを考慮する必要があります。

Serverless-artilleryを利用すると、Serverless Frameworkと組み合わせてArtilleryをAWS Lambdaで実行できるためサーバースペックを意識せずにテストが可能になり、前述の懸念が解消できます。

Serverless-artilleryを準備する

Serverless-artilleryのinstall

$ npm i -g serverless@1.83.3
$ npm i -g serverless-artillery

※2021/09時点では2系のserverlessではエラーになるため1系をinstallしています。

AWSにデプロイする

$ slsart configure
$ slsart deploy
        Deploying function...
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service serverless-artillery-test.zip file to S3 (16.85 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...

Lambdaでのテスト実行

AWSへのdeployが完了したら先程つくったテストファイルでテストを実行してみます。

$ slsart invoke
Invoking test Lambda
{
    "timestamp": "2021-09-13T09:27:44.639Z",
    "scenariosCreated": 10,
    "scenariosCompleted": 0,
    "requestsCompleted": 0,
    "latency": {
        "min": null,
        "max": null,
        "median": null,
        "p95": null,
        "p99": null
    },
    "rps": {
        "count": 10,
        "mean": 1.06
    },
    "scenarioDuration": {
        "min": null,
        "max": null,
        "median": null,
        "p95": null,
        "p99": null
    },
    "scenarioCounts": {
        "0": 10
    },
    "errors": {
        "ENOTFOUND": 10
    },
    "codes": {},
    "matches": 0,
    "customStats": {},
    "phases": [
        {
            "duration": 10,
            "arrivalRate": 1
        }
    ]
}
        Your function invocation has completed.
{
  "timestamp": "2021-09-13T09:27:44.639Z",
  "scenariosCreated": 10,
  "scenariosCompleted": 0,
  "requestsCompleted": 0,
  "latency": {
    "min": null,
    "max": null,
    "median": null,
    "p95": null,
    "p99": null
  },
  "rps": {
    "count": 10,
    "mean": 1.06
  },
  "scenarioDuration": {
    "min": null,
    "max": null,
    "median": null,
    "p95": null,
    "p99": null
  },
  "scenarioCounts": {
    "0": 10
  },
  "errors": {
    "ENOTFOUND": 10
  },
  "codes": {},
  "matches": 0,
  "customStats": {},
  "phases": [
    {
      "duration": 10,
      "arrivalRate": 1
    }
  ]
}

Github Actionsで実行する

GithubActionsで Serverless-artilleryを実行してみます。

やることはシンプルで事前にslsart deployを実行し環境を構築していれば テストを実行するだけになるので下記の設定ファイルで実行できます。

name: serverless-rtillery sample
on:
  push:
    branches:
      - main
    tags:
      - "!*"
jobs:
  serverless-artillery-sample:
    runs-on: ubuntu-latest
    steps:
      - name: checkout
        uses: actions/checkout@v1
      - name: setup Node
        uses: actions/setup-node@v1
        with:
          node-version: 12.x
      - name: build
        run: |
         npm i -g artillery
         npm i -g serverless@1.83.3
         npm i -g serverless-artillery
      - name: run test
        env:
          AWS_SECRET_ACCESS_KEY: ${{secrets.AWS_SECRET_ACCESS_KEY}}
          AWS_ACCESS_KEY_ID: ${{secrets.AWS_ACCESS_KEY_ID}}
                run: |
          slsart invoke

実行すると下記のようになります。

まとめ

いかがでしたでしょうか

負荷試験はテスト自体の設計だけでなく、準備のコストも高いため継続的に実施が難しいですが、Serverless-artilleryのようにサーバレスで構築されたものであればリソースが足りず負荷が想定よりかけれてないなどの問題もなくなるのでテストの設計だけに集中できそうで良いなと思いました。