Locustで負荷テストをマスター:Pythonで数万ユーザーを簡単にシミュレート

Development tutorial - IT technology blog
Development tutorial - IT technology blog

背景:なぜ顧客が押し寄せるとシステムが「ダウン」するのか?

キャンペーン中にサーバーがダウンする感覚は、すべての開発者にとって悪夢です。ローカルやステージング環境で数人のユーザーでスムーズに動作していても、それは何の保証にもなりません。実際のトラフィックが急増すると、502エラーが発生し、データベースがフリーズし、顧客は離れていきます。これは、パフォーマンス計測(Performance Testing)を怠った代償です。

システムは正しく動作するかもしれませんが、1,000人や10,000人が同時に「購入」ボタンを押したときに、果たして高速に動作するでしょうか?以前はJMeterをよく使っていました。強力ですが、XML設定ファイルが非常に肥大化し、カスタマイズが困難でした。Locustは違います。純粋なPythonでテストシナリオを書くことができます。あらゆるライブラリを使用し、実際の機能を実装するようにループやロジックを記述できます。

5人の開発者が参加したウェブアプリのプロジェクトで、開発の初期段階からLocustを導入しました。結果は驚くべきものでした。同時ユーザー数が200人を超えると、SQLクエリのインデックス不足によりレスポンス時間が50msから5秒まで増加することが判明しました。もし本番稼働(Go-live)まで気づかなかったら、間違いなく大惨事になっていたでしょう。

Python環境へのLocustのインストール

Python 3.7以上がインストールされていれば十分です。Locustのインストールはpipを使って非常に素早く行えます:

pip install locust

インストールが完了したら、次のコマンドでバージョンを確認してください:

locust -V

ターミナルにバージョン情報(例:locust 2.15.1)が表示されれば、準備完了です。

Pythonによる詳細な負荷テストシナリオの構築

Locustでは、テストシナリオはPythonファイル(通常は locustfile.py)です。以下は、ECサイトをテストするための実践的な例です。

import time
from locust import HttpUser, task, between

class WebsiteUser(HttpUser):
    # 操作間の待ち時間をシミュレート(1〜5秒)
    wait_time = between(1, 5)

    @task(3)
    def view_homepage(self):
        """ユーザーがホームページを閲覧"""
        self.client.get("/")

    @task(1)
    def view_product_detail(self):
        """ユーザーが商品詳細を閲覧"""
        self.client.get("/product/123", name="/product/[id]")

    @task(2)
    def post_comment(self):
        """ユーザーがコメントを投稿"""
        self.client.post("/api/comment", json={
            "user": "test_user",
            "content": "素晴らしい商品です!"
        })

ソースコードの解説:

  • HttpUser: 「ボット」を表します。各ユーザーは実際のユーザーのように個別のセッションを保持します。
  • wait_time: 非常に重要です。実際のユーザーは次のクリックまでに内容を読む時間が必要であり、誰も1秒間に100回クリックすることはありません。
  • @task(weight): 括弧内の数字は重みです。上記の例では、ユーザーがコメントを投稿するよりもホームページにアクセスする確率が3倍高いことを示しています。
  • self.client: GET、POST、PUTを送信するために、一般的な requests ライブラリと同様に使用できます。

テストの実行とダッシュボード指標の読み方

シミュレートされた「攻撃」を開始するには、ターミナルを開いて次のように入力します:

locust -f locustfile.py

http://localhost:8089 にアクセスして管理画面に入ります。ここで、3つのパラメータを入力する必要があります:

  • Number of users: シミュレートしたい総ユーザー数(例:1000)。
  • Spawn rate: 1秒あたりの新規ユーザー生成速度(例:10)。
  • Host: テスト対象システムのURL。

これらの重要な指標を見逃さないでください:

グラフが動き始めたら、リクエスト数だけを見るのではなく、以下に注目してください:

  1. RPS (Requests Per Second): 1秒あたりに処理されたリクエスト数。ユーザーが増えているのにRPSが横ばいになった場合、システムが限界に達したことを意味します。
  2. Response Time (p95): これが鍵となる指標です。95%のユーザーがこの時間内にレスポンスを受け取ったことを示します。p95が2秒を超えると、顧客はストレスを感じ始めます。
  3. Failures: エラー率。5xxエラーが発生した場合は、すぐにログを確認して、データベースのコネクションプールが溢れていないかなどを確認してください。

実践的な経験:分散(Distributed)実行が必要なタイミングは?

一般的なPCでは、通常約1,000ユーザー程度のシミュレートが限界です。数万人(例:50,000 CCU)の負荷をテストする場合、1台の構成ではサーバーよりも先に自分のマシンのCPUがボトルネックになります。ここでの秘訣は、Master-Worker モードを使用することです。

制御用のMasterマシンを1台、リクエストを送信するためのWorkerマシンを複数用意します。実際にFintechシステムのストレス・テストを行った際、AWS上で20台のWorkerを使用して15分間で10万人のアクセスをシミュレートしました。その結果、キャッシュの保存しすぎによるRedisのメモリ溢れを発見できました。これは小規模なテストでは極めて発見しにくいエラーです。

# Masterマシンにて
locust -f locustfile.py --master

# Workerマシンにて
locust -f locustfile.py --worker --master-host=<IP_MASTER>

Locustは、デプロイ時の安心感を与えるだけでなく、インフラの限界を明確にするのにも役立ちます。顧客から苦情が来るのを待つのではなく、制御された方法で自らシステムを「ダウン」させ、より強固なシステムを構築しましょう。

Share: