【Locust】コピペで使えるコード・コマンド集

これまでJmeterを使っていたのですが、
楽にスケーリングができそうということで最近はLocustを使うようになったので、
備忘録を兼ねて使い方をまとめます!

自分の現場でも使っているようなコード・コマンドをコピペでそのまま使えるようまとめてみていますので、
読者のみなさまの「Locustを使ってみる」ハードルを下げられたら幸いです!

環境構築

ここではCentOSベースのサーバーに構築してみます。

# pythonのバージョンを確認(2023/01時点では3.7以上が必要)
$ python --version
Python 3.11.0

# インストール
$ pip3 install locust

公式のインストール手順はこちらです

高負荷なテストを行いたい場合、file open数も気にしておくとよいです。
仮に同時に300リクエストを実効するテストを行いたい場合file openも300以上にしておく必要があります。

# 確認
ulimit -n
> 65535

# 変更したい場合
vi /etc/security/limits.conf

とりあえず動くところまで

準備

Locustの動作に関わる定義はlocustfile.pyに記載します。

ひとまずgetとpostのAPIにリクエストするサンプルです。

from locust import task, constant_throughput, HttpUser

# クラス名は任意(HttpUser)を継承する
class SampleUser(HttpUser):
    # API(シナリオ)ごとに@taskを付与したメソッドを定義する
    # @taskの引数には負荷をかける比重を指定(この実装だとget:post = 2:1の割合でAPIコールする)
    @task(2)
    def get_request(self):
        value = "value"
        self.client.get(f"/sample?key={value}")

    @task(1)
    def post_request(self):
        self.client.post("/sample", json={"key": "value"})

    # 1スレッド当たりの1秒間の最大実行数を指定
    # 1に指定しておけばスレッド数以上の性能がでないので、負荷をコントロールしやすい
    wait_time = constant_throughput(1)
    # (任意)UI上のデフォルトのホストを指定する
    host = 'http://localhost:8080'

起動

$ locust
# これと一緒
# locust -f ./locustfile.py

8089ポートでUIが起動するので http://localhost:8089 にアクセスします。

  • Number of users
    • 想定ユーザー数
    • アクセス元のスレッド数と考えればOK
    • 目標rps = ユーザー数 / (レスポンスタイム + time wait)
  • Spawn rate
    • 例えば10と設定すると1秒間に10ずつユーザー数が増えていく
  • Host
    • プロトコル+ホスト+ポートを指定
    • http://localhost:8080とか

Start swarmingで起動!

Chartsタブを見ると試験状況が確認できます!

簡単に見方や使い方を

  • STATUS
    • 〇〇users部分で現在のユーザ数(スレッド数)を把握できます
    • 「Edit」で「Number of users」と「Spawn rate」を再設定できます
    • →段階的に負荷を上げたい場合にはこのEditから
  • RPS
    • 現在のRPSが表示されています
    • 複数のAPIの合計のrpsが表示されているので、個別の情報が見たい場合は「Statistics」タブから
  • FAILURES
    • HTTPステータスが200以外の割合が表示されています
    • 基本200以外があったら負荷に耐えきれてない系だと思います


master / slave構成

Locustはmaster/slaveを使用することでスケーリング可能です!

ここではDockerを使用して構成してみたいと思います。
※Kubernetesとかにも応用が利くはず…!

docker準備

インストール手順は省略しますが、
以下のコマンドが叩けていればOKです!

$ docker --version
Docker version 20.10.23, build 7155243
$ docker-compose --version
docker-compose version 1.28.2, build 67630359

起動sh作成

環境変数によって起動モードを変更できるようシェルを作成します。

#!/bin/bash

echo "mode: $MODE"

if [ "$MODE" == 'master' ]; then
  locust --master
elif [ "$MODE" == 'slave' ]; then
  locust --slave --master-host=locust-master
fi

Dockerfile

FROM python:3.11.0

RUN pip3 install locust

WORKDIR /locust
COPY ./locustfile.py .
COPY ./run.sh .
RUN chmod +x ./run.sh

ENTRYPOINT ["./run.sh"]

docker-compose.yml

slaveを3台起動する例です!

※コマンド的にはslave → workerとなっていたのでworkerが正しい言葉なのかも…

version: '2'
x-locust-service: &locust-service
  build: .
services:
  locust-master:
    <<: *locust-service
    ports:
      - "8089:8089"
    environment:
      MODE: master
  locust-slave-1:
    <<: *locust-service
    environment:
      MODE: slave
  locust-slave-2:
    <<: *locust-service
    environment:
      MODE: slave
  locust-slave-3:
    <<: *locust-service
    environment:
      MODE: slave

起動

docker-compose -f docker-compose.yml up -d
# 開発時は毎回build
# docker-compose -f docker-compose.yml up -d --build

カスタマイズ

ここからは「やりたいこと」をベースにLocustのカスタマイズ方法をまとめます!

エラー情報をログに出力

UIの「Failures」でもエラーが発生したことはわかるのですが、
原因まで追おうとすると情報が足りないことが多いです。

そんなときは@events.request.add_listenerを使用してログを出力するとよいです!
※コードは変更点のみ

from locust import events
import uuid
import logging

class SampleUser(HttpUser):
    @task(1)
    def error(self):
        # listenerでリクエスト・レスポンスの紐づけをするためにcontextを設定を設定しておく
        self.client.get("/ng", context={"requestId": str(uuid.uuid4())})

# 1リクエストが完了した段階で呼ばれるListenerの定義
@events.request.add_listener
def logging_response(request_type, name, response_time, response_length, response,
                     context, exception, **kwargs):
    # エラー発生時のみログ出力
    if not response.ok:
        requestId = context["requestId"]
        logging.info(f"[request event] path: {name} status: {response.status_code} body: {response.text} requestId: {requestId}")
[2023-01-26 10:53:49,471] XXXX-XXXXXXXX/INFO/root: [request event] path: /ng status: 500 body: ng requestId: f92ccc00-a970-41e5-9a68-b434474f705f
[2023-01-26 10:53:50,471] XXXX-XXXXXXXX/INFO/root: [request event] path: /ng status: 500 body: ng requestId: ac01dd82-ab75-4af6-874c-fb793c72e2fb

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です