LoginSignup
71
59

More than 3 years have passed since last update.

初学者がlocust 1.0で負荷試験を行う

Last updated at Posted at 2020-05-29

Webシステムの負荷試験を行う際、最近はjmeterではなくlocustがオススメと聞いたので、試してみました。

  • シナリオをpythonで書けるので複雑なケースに対応できる
  • 多重度を上げる際、jmeterだと1ユーザ1スレッドになるが、locustなら少ないリソースでも負荷をかけやすい

結論から言うと、公式ドキュメント通りにやれば動きます。
が、割と最近に出た1.0で破壊的変更があったようで、少し前の外部記事を元にすると動きません。そしてハマる(実際ハマった)。
この記事はそういった人がエラーメッセージから逆引きするもののつもりで書いています。

なお、いくつか動かない実装として他記事を引用していますが、あくまでlocustの破壊的変更によるものであり、引用元記事が間違っている訳ではないことは注釈しておきます。
細かな仕様や説明は当記事よりそちらの方が詳しいので、ご参考まで。

前提環境

  • 端末: MacBook Pro Mojave
  • 環境: docker for mac 2.0.0.3
  • locust: 1.0.2
$ docker -v
Docker version 18.09.2, build 6247962
$ docker-compose -v
docker-compose version 1.23.2, build 1110ad01

とある事情で、諸々最新ではない。

シングル構成でlocustを実行する

コンテナでlocustをインストール

公式のlocustio/locustを使ってもいいのだが、cmdではなくentrypointでlocustが指定されている。
locustの起動でエラーが起きるとコンテナが即死するだけなので、ログを追いにくくデバッグが辛い。

なのでまずはpythonイメージを元にして試してみる。
Webインターフェースを使えるように8089ポートを繋いでおく。

$ docker run -it -p 8089:8089 python:3.6 /bin/bash

locustのインストールはpipを叩くだけ。
このとき、古いやり方のpip install locustioだとLocust package has moved from 'locustio' to 'locust'. Please update your reference (or pin your version to 0.14.6 if you dont want to update to 1.0)と怒られる。

$ pip install locust

あと、そのままだとエディタがなくて辛いので、vimなどは適宜入れる。

$ apt-get update -y
$ apt-get install -y vim

locustfileを作る

最初、こちらの記事を参考にしたのだが、これはこのままでは動かなかった。

locustfile.py
from locust import HttpLocust, TaskSet, task, between, constant

class UserBehavior(TaskSet):
    @task(1)
    def profile(self):
        self.client.get("/sample", verify=False)

class WebsiteUser(HttpLocust):
    task_set = UserBehavior
    wait_time = constant(0)

実行すると、まず以下のエラーが出る。

ImportError: The HttpLocust class has been renamed to HttpUser in version 1.0. For more info see: https://docs.locust.io/en/latest/changelog.html#changelog-1-0

エラーメッセージ通り、ver1.0 の仕様変更によるもの。
このエラー自体は、単にHttpLocustHttpUserに変えれば良い。
しかしそれ以外にも、この状態で起動すると、あとで実際にリクエストを送るタイミングで以下のエラーが発生する。

10b1687a2aee/ERROR/locust.user.task: Cannot choose from an empty sequence
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/locust/user/task.py", line 280, in run
    self.schedule_task(self.get_next_task())
  File "/usr/local/lib/python3.6/site-packages/locust/user/task.py", line 408, in get_next_task
    return random.choice(self.user.tasks)
  File "/usr/local/lib/python3.6/random.py", line 260, in choice
    raise IndexError('Cannot choose from an empty sequence') from None
IndexError: Cannot choose from an empty sequence

どうやらTaskSet周りの実装方法が変わったらしい。
task_set = UserBehaviortasks = {UserBehavior:1}と書く必要がある。

結果、完成したファイルは以下の通り。
パス指定などについては引用元記事を参照まで。

locustfile.py
from locust import HttpUser, TaskSet, task, between, constant

class UserBehavior(TaskSet):
    @task(1)
    def profile(self):
        self.client.get("/sample", verify=False)

class WebsiteUser(HttpUser):
    tasks = {UserBehavior:1}
    wait_time = constant(0)

locustを起動する

以下を実行する。
(実際にはファイル名がこのままなら-f以降は省略できるが、常にこの名前に縛られるのも不便なので、今から明示的に指定)

$ locust -f ./locustfile.py

このあとでブラウザから http://localhost:8089/ につなぐと、WebUIが出てくる

スクリーンショット 2020-05-28 19.15.06.png

  • 最大ユーザ(接続)数
  • 接続数の増加率(1秒間に何接続増やすか)
  • アクセス先URL

図の例の場合、10ユーザになるまで1秒に2ユーザずつ増やし(1秒で2人、2秒で4人……)、10人に到達したら以降10ユーザでアクセスし続ける(10ユーザになったら止まるわけではない)。

なお、私の場合、アクセス先URLの指定をホスト名のみにしたら(e.g. http://hoge)、以下のエラーが発生した。

ConnectionError(MaxRetryError("HTTPConnectionPool(host='hoge.huga.com', port=80): Max retries exceeded with url: /sample(Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x....>: Failed to establish a new connection: [Errno -5] No address associated with hostname',))",),)

curlではホスト名のみでも正常にアクセスできたが、locustではFQDNにしないと接続できなかった。
詳細は調べていない。

実行した後はWeb上に色々記事があるので、そちらを参照まで。

docker-compose で並列実行する。

せっかくlocustを使うので、master/slave構成で使いたいと言う欲が出る。

docker-credential-desktopのエラー回避

docker for macの2.0.0.3のバグ(?)で、docker-composeの初期化で以下のエラーが出た

$ docker-compose up -d
Creating network "locust_default" with the default driver
Creating volume "locust_tests" with default driver
Pulling locust-master (locustio/locust:)...
Traceback (most recent call last):
  File "docker-compose", line 6, in <module>
  File "compose/cli/main.py", line 71, in main
  File "compose/cli/main.py", line 127, in perform_command
  File "compose/cli/main.py", line 1080, in up
  File "compose/cli/main.py", line 1076, in up
  File "compose/project.py", line 475, in up
  File "compose/service.py", line 352, in ensure_image_exists
  File "compose/service.py", line 1217, in pull
  File "compose/progress_stream.py", line 101, in get_digest_from_pull
  File "compose/service.py", line 1182, in _do_pull
  File "site-packages/docker/api/image.py", line 381, in pull
  File "site-packages/docker/auth.py", line 48, in get_config_header
  File "site-packages/docker/auth.py", line 96, in resolve_authconfig
  File "site-packages/docker/auth.py", line 127, in _resolve_authconfig_credstore
  File "site-packages/dockerpycreds/store.py", line 25, in __init__
dockerpycreds.errors.InitializationError: docker-credential-desktop not installed or not available in PATH
[60116] Failed to execute script docker-compose

確かに、/Applications/Docker.app/Contents/Resources/bin/をみてもdocker-credential-desktopなんて存在しない。

https://github.com/docker/for-mac/issues/3785 によると、2.1.0.0では解消されているので、更新すれば良い。
が、何かしらの事情で更新できない場合、~/.docker/config.jsoncredsStoreを手修正してdesktopからosxkeychainにすれば動くようになる。

config.json
{
  "auths" : {
    "https://index.docker.io/v1/" : {

    }
  },
  "stackOrchestrator" : "swarm",
  "experimental" : "disabled",
  "credsStore" : "desktop <= ここをosxkeychainに変える",
  "credSstore" : "osxkeychain"
}

チケットに書いてある通り、確かにどう見てもtypoに見える。。。

docker-composeファイルを作る

こちらの記事を参考にしたのだが、これもそのままでは動かなかった。

ちなみに、このYAMLはアンカーとエイリアスがあるが、パース後に実際に生成されるオブジェクトが知りたい場合は、YAMLをjsonにするオンラインパーサがあるので、活用するとわかりやすいかも。

docker-compose.yaml
version: "3.4"

x-common: &common
  image: locustio/locust
  environment: &common-env
    TARGET_URL: http://example.com
    LOCUSTFILE_PATH: /tests/basic.py
  volumes:
    - tests/:/tests

services:
  locust-master:
    <<: *common
    ports:
      - 8089:8089
    environment:
      <<: *common-env
      LOCUST_MODE: master

  locust-slave:
    <<: *common
    environment:
      <<: *common-env
      LOCUST_MODE: slave
      LOCUST_MASTER_HOST: locust-master

docker-composeを実行しても、Exited(1)で即死してしまう。
なお、エラーログはdocker logs (コンテナID)で見れる

$ docker-compose up -d
Pulling locust-master (locustio/locust:)...
latest: Pulling from locustio/locust
cbdbe7a5bc2a: Pull complete
26ebcd19a4e3: Pull complete
a29d43ca1bb4: Pull complete
979dbbcf63e0: Pull complete
30beed04940c: Pull complete
d4954e7e8c67: Pull complete
45867b33845c: Pull complete
2be134228162: Pull complete
Creating locust_locust-slave_1  ... done
Creating locust_locust-master_1 ... done
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
2fd911a1ee59        locustio/locust     "locust"            5 minutes ago       Exited (1) 5 minutes ago                       locust_locust-master_1
9c35f427c9eb        locustio/locust     "locust"            5 minutes ago       Exited (1) 5 minutes ago                       locust_locust-slave_1

公式を見ると、パラメータを環境変数から取らなくなった様子。
LOCUSTFILE_PATH: /tests/basic.py と書いても効果はなく、以下のエラーが出る。

Could not find any locustfile! Ensure file ends in '.py' and see --help for available options.

commandlocustコマンドへの引数を渡せるので、それでコントロールする。
entrypointが定義されたコンテナイメージでのcmdの扱いについては、こちらがわかりやすい。

なお、volumesは tests/ではなく./testsと書く。
でないとtestsという名前付きVolumeと見なされ、以下のエラーが発生する。

ERROR: Named volume "tests:/tests:rw" is used in service "locust-master" but no declaration was found in the volumes section.

結果、以下だと動く。

docker-compose.yaml
version: "3.4"

x-common: &common
  image: locustio/locust
  volumes:
    - tests/:/tests

services:
  locust-master:
    <<: *common
    ports:
      - 8089:8089
    command: -f /tests/basic.py --master -H http://example.com

  locust-slave:
    <<: *common
    command: -f /tests/basic.py --worker --master-host locust-master

なお、workerの数を増やしたい場合、yamlを変更する必要はなく、docker-composeを実行する際に--scaleを指定すれば良い。

$ docker-compose up -d --scale locust-slave=3
Creating network "locust_default" with the default driver
Creating locust_locust-slave_1  ... done
Creating locust_locust-slave_2  ... done
Creating locust_locust-slave_3  ... done
Creating locust_locust-master_1 ... done

スクリーンショット 2020-05-29 10.43.36.png

これで気軽に分散負荷試験をできるようになった!

続編として、locustをECSで動かす記事も書いたので、ご参考まで。
https://qiita.com/sekikatsu/items/e6fad26fded58f92fb25

71
59
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
71
59