Locustをローカル環境で動かす

# この記事は何か
Locustという負荷検証ツールを、ローカル環境で動かしてみました。

# Locustとは
Python製の負荷検証ツールです。
Web UI付きで、手軽に操作することができます。
[ドキュメント](https://docs.locust.io/en/stable/)

# ローカル環境で動かしてみた
## 動作環境を用意
Docker環境を用意しました。

https://github.com/tatsukoni-pra/Locust-local

コードは↑を参照ください。ポイントは以下。

- WebUI用に、`8089`ポートを開けておく
- Python環境にて、`pip install locust`でLocustをインストールする（`build-essential`がないとインストールに失敗したので、入れておく）

```shell-session
# python --version
Python 3.9.17

# locust --version
locust 2.15.1 from /usr/local/lib/python3.9/site-packages/locust (python 3.9.17)
```

## 負荷検証シナリオを書く
負荷検証のシナリオ（テストケース）を、Pythonファイルにて記述することができます。
ファイルは、`locustfile.py`という命名で用意します。
中身はこんな感じ。

```python
from locust import HttpUser, task, constant_pacing

class HelloWorldUser(HttpUser):
    #  1タスク = 1秒間に1回リクエストを送信する
    wait_time = constant_pacing(1)

    @task
    def hello_world(self):
        self.client.get("/hello-world")
```

上記だと、`/hello-world`というエンドポイントに対して、1秒間に1回、指定した接続数でGETリクエストを行う という意味になります。

他にも、リクエスト間隔や、リクエストの重み付けを指定することもでき、細かくシナリオチューニングすることが可能です。
より詳しくは以下を参照ください。
https://docs.locust.io/en/stable/writing-a-locustfile.html#writing-a-locustfile

## 動かしてみる
`locust`コマンドを実行することで、Locustサーバが起動します。
Dockerを用いている場合は、以下の形で実行できます。
```shell
docker-compose up -d
docker exec -it python locust

# テストファイルを locustfile.py 以外の命名で作成した場合は、-fでファイル名を指定する
# docker exec -it python locust -f <ファイル名>
```

起動後、http://localhost:8089/ にアクセスすると、以下のような負荷設定画面に移動します。

![スクリーンショット 2023-07-16 9.01.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351743/813eb3a0-8de2-b855-0c80-3fdb9924f283.png)

入力項目が3つありますが、それぞれ以下の意味です。

- Number of users:
    - 最大同時接続数
- Spawn rate:
    - 1秒あたりに増やしていく接続数。
    - Number of users と同じ値を指定すれば、最初から最大数でリクエストされます。
- Host:
    - リクエスト対象のURL（ベースURL）

今回は、別でテスト用のローカルサーバーを立てておいたので、そこで検証してみます。
※ LocustサーバーもDockerで構築しているため、`host.docker.internal`でホストマシンにアクセスする必要があります。
「Start swarming」を押下することで、テストが開始されます。

![スクリーンショット-2023-07-16-9-45-54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351743/4f62a0b3-f63c-04cc-5d94-f7f0fd96417c.png)

## テスト結果画面
実行を開始すると、以下のような表, チャート画面で、疎通状況, レスポンス時間などを表示してくれます。

![スクリーンショット 2023-07-16 9.49.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351743/2992fa59-5b82-e668-8f71-f85614e8753c.png)

![スクリーンショット 2023-07-16 9.49.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351743/305154f3-4ca2-5590-2329-1fbcb2190233.png)

STOPボタンを押すと、テストが終了します。
テスト実行後は、各種データをダウンロードすることが可能です。

![スクリーンショット 2023-07-16 9.52.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351743/5bca7248-2cf0-f98d-7382-4eb21dec053a.png)

![total_requests_per_second_1689468496.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351743/bfaf8b93-7718-e6e2-cdc0-732e0b6d51c9.png)

# 補足
## テスト対象のサイトにBasic認証がかかっている場合
テスト対象サイトにBasic認証がかかっている場合は、テストファイルを以下のように記述することで、認証を通過した上でテストを実施することが可能です。

```python
from locust import HttpUser, task, constant_pacing
from requests.auth import HTTPBasicAuth

class QuickstartUser(HttpUser):
    #  1タスク = 1秒間に1回リクエストを送信する
    wait_time = constant_pacing(1)

    @task
    def view_top(self):
        # Basic認証を通過するため、以下を記述する
        auth = HTTPBasicAuth(username="username", password="password")
        self.client.get("/hello-world", auth=auth)
```

## Docker Imageについて
動作環境用のDocker Imageは以下で用意されていたので、こちらを利用しても良いかと思います。（動作は未検証です）
https://hub.docker.com/r/locustio/locust