Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Apache JMeterを使ったRailsアプリの負荷テスト完全ガイド

ソフトウェアをエンドユーザーにリリースする前に、私たちはさまざまな種類のテストを実施し、アプリケーションにバグがなくビジネス要件を満たしていることを確認します。しかし、どれほど多くのテストを行っても、実際にユーザーが使い始めるまで、ソフトウェアが安定しているとは断言できません。ユーザーがアプリケーションを使い始めた後、以下のような理由から、予期しない動作が発生する可能性があります。

  • ユーザーの行動は予測できない
  • ユーザーはさまざまな場所に分散している
  • 多数のユーザーが同時にアプリケーションを利用する可能性がある

大規模なアプリケーションでは、本格的なリリース前にこれらの点を把握しておくことが極めて重要です。アプリケーションが期待通りに動作することを保証するために、機能を展開する際には以下の点を考慮する必要があります。

  • 段階的リリース(Phased Rollout)
    段階的なリリースでは、すべてのユーザーにアクセス権を与える前に、一部のユーザーでアプリケーションをテストできます。これにより、ユーザーの行動パターンを把握することができます。
  • 負荷テスト(Load Testing)
    段階的リリースではユーザーの行動を把握できますが、多くのユーザーが異なる場所から同時にアプリケーションを利用した場合にプラットフォームがどう動作するかは分かりません。そこで負荷テストが必要になります。

負荷テストとは?パフォーマンステストとの違い

以下の3つの用語は似ているように聞こえますが、それぞれ異なるものです。

  • パフォーマンステスト(性能試験)
  • 負荷テスト(ロードテスト)
  • ストレステスト(耐久試験)

パフォーマンステストは、特定の入力に対してアプリケーションがどのように動作するかを評価するための総合的なテスト手法です。単一ユーザーでも複数ユーザーでも実施でき、レスポンスタイムやCPU・メモリ使用率といった指標を測定します。負荷テストとストレステストは、パフォーマンステストの一部(サブセット)です。

負荷テストは、指定された数のユーザーが一定期間同時にアプリケーションを利用した場合に、期待通りのパフォーマンスが得られるかを確認するために実施します。システムが何人のユーザーを処理できるかを把握するのに役立ちます。

ストレステストは負荷テストと密接に関連しています。ストレステストも負荷テストと同様の仕組みで実施できますが、テストの目的が異なります。負荷テストは「指定したユーザー数でアプリケーションが正常に動作するか」を確認するものですが、ストレステストは「負荷限界に達した後、アプリケーションがどのように振る舞い、障害を処理するか」を確認するものです。

また、ランプアップ期間(負荷を上げる期間)によって、パフォーマンステストはスパイクテストソークテストに分類されます。短時間で急激にユーザー数が増加するシナリオがスパイクテスト、長い時間をかけて緩やかにユーザー数を増加させるシナリオがソークテストです。

なぜパフォーマンステストが重要なのか

あるRailsプロジェクトでは、近いうちにユーザー数が大幅に増加すると予想していました。アプリケーションが期待通りにパフォーマンスを発揮し、ユーザー増加によって重要な機能が損なわれないことを保証したかったのです。そのため、負荷テストを実施し、想定されるユーザー増加に対応できるかどうかを検証しました。

パフォーマンステストが重要になるケースは他にもたくさんあります。

  • ブラックフライデーなど、特定の日にユーザー数が急増することが予想される場合、ランプアップ期間を短く設定したスパイクテストにより、システムの潜在的な問題を発見できます。
  • 負荷テストは、少数のユーザーしかいない状態では見えない、または影響がごくわずかなバグを発見するのに役立ちます。
  • 負荷増加がプラットフォームの速度に与える影響を評価できます。アプリケーションが遅ければ、顧客を失う可能性があります。
  • 高負荷時にシステムがどのように動作するか、1万人のユーザーがいた場合にCPUやメモリ使用率が高騰してシステムがクラッシュしないかを評価できます。
  • 特定のユーザー数に対してアプリケーションを運用するコストを見積もることができます。

実際、私たちは負荷テスト中にRailsアプリのバグを発見しました。ここで、同様のシナリオを紹介します。ホテル予約アプリに公開予約プロセスがあり、少数のユーザーが部屋を予約しようとしている間は問題ありませんでした。しかし、複数のユーザーが同じ部屋を予約しようとすると、2人の異なるユーザーが同じ部屋の予約に成功してしまうという事態が発生しました。負荷テストを実施することでこの問題を特定し、機能リリース前の早い段階で修正することができました。

Apache JMeterを使ってRailsアプリを負荷テストする

JMeterは、Apache 2.0ライセンスのオープンソース負荷テストツールです。スレッドベースの負荷テストを提供しており、多数のユーザーが同時にアプリケーションを使用する状況を簡単にシミュレートできます。また、テスト結果のレポート機能も充実しています。

ここでは、Apache JMeterを使って負荷テストを実施し、多数のユーザーがシステムを利用する状況をシミュレートすることで、システムの潜在的な問題やアプリケーションのレスポンスタイムを特定する方法を解説します。

JMeterは以下のリンクからダウンロードできます。
https://jmeter.apache.org/download_jmeter.cgi#binaries

JMeterの基本用語

  • テストプラン(Test Plan)
    テストプランは最上位の要素で、負荷テストの各コンポーネントをここに定義します。グローバル設定や変数もここで定義します。
  • スレッドグループ(Thread Group)
    スレッドとその設定(スレッド数、ランプアップ期間、スレッド間の遅延、ループ回数など)を定義します。これは「負荷テストを走らせる並行ユーザー数」と考えることができます。
  • サンプラー(Sampler)
    1つのスレッドが実行する処理です。HTTPリクエスト、SMTPリクエスト、TCPリクエストなど、さまざまな種類があります。
  • プリ/ポストプロセッサ(Pre/Post Processor)
    サンプラーの実行前後に何らかの処理を実行します。ポストプロセッサは、あるAPI呼び出しのレスポンスデータを受け取り、次の呼び出しに渡すことができます。
  • リスナー(Listener)
    サンプラーからのレスポンスを監視し、各スレッドのレスポンスタイムやレスポンス内容の集計レポートを提供します。
  • アサーション(Assertion)
    サンプラーから返されたレスポンスデータが期待通りであることを検証するのに役立ちます。
  • 設定エレメント(Config Element)
    HTTPヘッダー、HTTPクッキー、CSVデータセットなどの設定を定義します。

負荷テストを実行するには、まず上記のJMeter用語を定義したJMXファイルを作成する必要があります。

負荷テスト用JMXファイルの準備

JMXはXML形式で記述されたJMeterのプロジェクトファイルです。手動で書くのは難しいので、JMeterのGUIインターフェースを使ってファイルを作成しましょう。

JMeterを起動し、テストプランを開きます。テストプランの中に、スレッドと負荷テストの設定を追加していきます。

JMeterのテストプラン作成画面

テストプランは、負荷テストの目的に応じて名前を変更できます。スレッドグループ(Users)を設定しましょう。ここではデフォルト設定のまま進めて問題ありません。

Add -> Threads(Users) -> Thread Group

スレッドグループでは、デフォルトでスレッド数が1に設定されています。アプリケーションにアクセスするユーザー数をシミュレートできるよう、必要な数に変更してください。

今回はWebベースのRailsアプリを負荷テストするので、HTTPサンプラーを追加します。サンプラーはThreadGroup内に配置します。以下の手順でHTTP Samplerを追加してください。

Add -> Sampler -> HTTP Request

ここで、負荷テスト対象のIPアドレスまたはドメイン、HTTPメソッド、およびHTTPエンドポイントに必要なリクエストボディを設定します。

最後に、負荷テストのレポートを表示するために、スレッドグループにリスナーを追加します。

Add -> Listener -> View Result Tree

View Result Treeには各スレッドのレスポンスタイムが表示されます。他の種類のレポートも追加可能です。なお、「View Result Tree」はデバッグ目的でのみ使用すべきであり、実際のテストには使わないようにしましょう。

このようにして、シンプルなテストプランを作成し実行できます。JMeter上部バーの再生アイコンをクリックするだけでテストが実行されます。

Railsアプリを負荷テストする前に考慮すべきこと

上記の例は非常にシンプルな単一エンドポイントへのHTTPリクエストでした。しかし実際のRailsアプリでは、テストしたいエンドポイントは認証で保護されているのが一般的です。そのため、以下の要素を事前に整えておく必要があります。

  • Webクッキー
    HTTPエンドポイントに対して負荷テストを実行するには、クッキーヘッダーが必要です。JMeterには、ユーザーログイン後にクッキーを追加する機能があります。次のセクションでは、ブラウザのリクエストを記録してJMXファイルに変換する方法を紹介します。クッキーの記録方法についても取り上げます。
  • Rails CSRFトークン
    RailsはCSRFトークンによってセキュリティ脆弱性からアプリケーションを保護しています。負荷テストを実行する前に、リクエストヘッダーにCSRF認証が含まれていることを確認する必要があります。このCSRFトークンは、HTML内のmetaタグのheaderセクションに存在します。

Rails CSRFトークンは、JMeterのポストプロセッサを使って取得できます。CSRFトークンを含むWebページを読み込むHTTPリクエストを右クリックし、Add -> Post Processor -> Regular Expression Extractorを選択します。以下の正規表現抽出設定を追加することで、header metaタグからCSRF値を読み取れます。

  • Reference Name: csrf_value
  • Regular Expression: name="csrfToken" content="(.+?)"
  • Template: $1$
  • Match No: 1

これで、変数csrf_valueを使ってリクエストを送れるようになります。

ブラウザからのリクエストを記録してJMXファイルを作成する

HTTPエンドポイントの数が少なければ、JMeterのGUIから直接JMXファイルを作成するのは簡単です。しかし、大規模なテストケースになると手作業での作成は困難になります。また、ユーザーがアプリケーションを操作する際に実際に行われるリクエストを見落としてしまう可能性もあります。ブラウザから送信される実際のリクエストを記録し、JMXファイルを自動生成したいところです。

JMeterは、Railsアプリとブラウザの間にプロキシとして配置できます。こうすることで、すべてのリクエストがJMeter経由でRailsサーバーに転送されます。これはMITM(Man in the Middle:中間者攻撃)と呼ばれる手法と同じ仕組みです。

JMeterによる記録の様子

JMeterで記録を作成するには、file -> templates -> recordingに移動してCreateをクリックします。記録対象のホスト名を指定します。これにより、認証に必要なクッキーを保存するクッキーマネージャーなどが自動的に生成されます。

JMeter SSL証明書によるHTTPSリクエストの検証

ブラウザから送信されたリクエストはJMeterに転送され、JMeterがそれをWebサービスに転送しながらリクエストを記録します。この記録をもとにJMeterから負荷テストを実行できます。アプリケーションがSSL接続にHTTPSプロトコルを要求する場合は、証明書をブラウザに追加する必要があります。Firefoxを開き、settings > Privacy > Manage certificateに移動して、JMeterの証明書を追加しましょう。これにより、ブラウザがJMeterが生成した証明書を認識できるようになります。

cmd + sht + gを押してパス/usr/local/Cellar/jmeter/5.2/libexec/bin/jmeterを入力すると、証明書を追加できます。

FirefoxでJMeterをプロキシとして設定する

次に、FirefoxからのリクエストをJMeterの記録スクリプトに転送する必要があります。これは、Firefoxのプロキシ設定で行えます。Firefoxを開き、Preferences -> Advanced -> Connection(settings)に移動します。ここで、HTTPプロキシを「localhost」、ポートを「8080」に設定し、「Use this proxy server for all protocols」にチェックを入れます。

これで、JMeterに戻り、先ほど選択したテンプレートのScript recordingセクションに移動します。Startボタンを押すと、JMeterが着信リクエストの受け入れを開始します。Firefoxで負荷テスト対象のアプリケーションを操作すると、その操作が記録され、JMXファイルに変換されます。このファイルを使って負荷テストを実行できます。

JMeterによる分散負荷テスト

テストの準備段階であれば、ローカルマシン1台からテストを実行しても問題ありません。しかし、実際のテストを実行する際には、この方式を変える必要があります。単一マシンでの負荷テストにはハードウェアの制限(CPUやメモリ)があり、リクエストの発信場所にも制約があります。これらのテストは、実際のユーザーによるトラフィックをシミュレートするために実施するものなので、テストを複数のサーバーに分散させ、すべての結果を一箇所で確認できるようにする必要があります。

JMeterは、テストを統括するマスターノード(主ノード)と、テストを実行する複数のスレーブノード(従ノード)を提供しています。これにより、実際のユーザーがアプリケーションを利用する状況をリアルにシミュレートできます。テストサーバーを、実際のユーザーに近い地域に分散配置することも可能です。

JMeterの分散テスト構成

分散テストを実施するには、まずマスターサーバーとスレーブサーバーの両方にJMeterをインストールします。

スレーブサーバー側で行うこと:

  • jmeter/binディレクトリに移動し、jmeter-serverコマンドを実行します。これでテスト実行用のサーバーが起動します。
  • テストにCSV入力が必要な場合は、それらのファイルをこのサーバーに配置します。

マスターサーバー側で行うこと:

  • jmeter/binディレクトリに移動し、jmeter.propertiesファイルを開きます。
  • remote_hostsの行を編集し、スレーブサーバーのIPをカンマ区切りで追加します:remote_hosts=<s1_ip>,<s2_ip>
  • JMeterテストを実行します。

スレーブサーバーが実際のテスト実行を担当し、マスターサーバーがレポートを集約します。

負荷テストを実行する際は、UIからテストを起動せず、必ずCLIコマンドを使いましょう。UIからの実行は負荷テストサーバーのパフォーマンスに悪影響を与える可能性があります。JMXファイル名を指定してJMeterコマンドを実行します。

> jmeter -n -t path/to/test.jxm -r

または

> jmeter -n -t path/to/test.jxm -R s1_ip,s2_ip,…

-r: jmeter.propertiesで指定されたリモートサーバーを使用
-n: GUIなしモードで実行
-t: JMXファイルへのパス

Puma vs Unicorn — 負荷テストデータに基づくWebサーバー選定

PumaとUnicornは、Rails向けの2つの代表的なWebサーバーです。どちらにも利点があるため、どちらが最適かはアプリケーションによります。あるアプリケーションではUnicornが適し、別のアプリケーションではPumaが適します。私たちはあるRailsアプリでUnicornとPumaのどちらを選ぶべきか判断が必要となり、負荷テストから得られたデータに基づいて決定しました。Railsアプリに対して一度はUnicorn、もう一度はPumaを使って負荷テストを実施しました。変更したのはRailsアプリのWebサーバーだけです。

その結果、以下のような結果が得られました。

PumaとUnicornのレスポンスタイムの差

その結果、プラットフォーム上に大量のユーザーがいる場合、Pumaの方が優れたパフォーマンスを発揮することが分かりました。つまり、より少ない数のアプリケーションサーバーでより多くのユーザーを処理できるということです。

注:これは使用するサーバーインスタンスのタイプや、アプリが実行するビジネスロジックの種類によって変わります。

Railsアプリのストレステストでよく遭遇する障害と対策

  • 最適化されていないデータベースクエリ
    • N+1クエリ問題を解消する
    • アクセスパターンに応じてインデックスを追加する
    • データベースの前にRedisなどのキャッシュ層を導入する
  • Rubyコードのパフォーマンス低下
    • メモ化(Memoization)でコードを最適化する
    • O(n²)計算量の箇所を特定し、より効率的なアルゴリズムに置き換える
  • マイクロサービスアーキテクチャにおけるサービス間のHTTP呼び出し過多(ネットワーク通信は遅い)
    • メッセージングシステムを活用してマイクロサービス間のHTTP呼び出し回数を削減する
  • 同時実行時の同一レコード重複作成
    • データベースに一意制約を追加する
    • FIFOイベント(キュー)ベースのリソース作成を活用する
  • 可能な限りSidekiqなどのバックグラウンド処理を活用する
  • APIレスポンスタイムにSLAを定め、パフォーマンステストを開発ライフサイクルの一部に組み込む

負荷テストにはどの環境を使うべきか

本番環境で負荷テストを実施するのは理想的ではありません。問題が発生し、最悪の場合ダウンタイムにつながる恐れがあるからです。本番環境に問題を起こしたくない一方で、テストレポートには本番環境に近い正確なデータを反映させたいものです。負荷テスト/ストレステストでは、本番環境のレプリカ環境を構築することが推奨されます。具体的には以下のような項目です。

  • アプリケーションサーバーの台数
  • データベースサーバーのハードウェアスペック(レプリカを含む)
  • テスト用データベースに投入する本番相当のデータ(本番環境とほぼ同等のデータ量を含める)

本番相当の環境を構築するのは難しく、コストもかかります。そのため、負荷テストの目的に応じて、テスト対象のコンポーネントに関わるインフラだけを本番相当にアップグレードすれば、コストを節約できます。負荷テスト/ストレステストは3ヶ月に1回程度実施するのが望ましいでしょう。一方、単一ユーザーでのパフォーマンステストと、レスポンスタイムが定義された基準(たとえば200ms)以内であることの検証は、開発サイクルに組み込むべきです。

負荷テストの実施中には、ターゲットサーバーのCPU/メモリ使用率といったデータポイントも収集する必要があります。メモリやCPU使用率の急増はアプリケーションのクラッシュを引き起こす可能性があります。ハードウェアKPIを測定するために、負荷テスト開始前にPrometheusなどの監視ツールを導入しておきましょう。

Apache JMeterは強力な負荷テストツールです。私たちはRailsアプリの負荷テストにApache JMeterを使いましたが、あらゆる技術スタックで構築されたアプリケーションの負荷テスト/ストレステストに活用できます。負荷テストは、アプリケーションに関する意思決定をデータに基づいて行う助けになります。負荷テストはハードルが高いと感じられがちですが、最初に少し投資すれば、長期的にアプリケーションに大きな安定性と信頼性をもたらしてくれるでしょう。

  1. AppLockでAndroidアプリをロックして保護する方法|指紋・パターン・パスワード対応

    Androidスマートフォンは画面ロックで端末全体を保護できますが、修理店に預けるときや、子どもや友人に一時的に貸すときなど、「端末ごとではなく特定のアプリだけをロックできたらいいのに」と思う場面は少なくありません。そんなときに役立つのが、Systweak社の「App Lock – With Fingerprint, Pattern & Password」です。 Androidアプリをロックできる「App Lock – With Fingerprint, Pattern & Password」とは 「App Lock – With Fingerprint, Pattern & Password

  2. ビジネス宣伝に使えるおすすめマーケティングアプリ10選【Android・iOS対応】

    起業家にとって、顧客に直接リーチできることは非常に重要です。そのための手法やプラットフォームは数多く存在しますが、スマートフォンの利用がますます普及している今、モバイルアプリを活用するのが最も効果的といえます。ビジネスをスマホで宣伝できるマーケティングアプリは多数あり、AndroidとiOSの両方で、小規模ビジネスから大企業まで幅広く活用できるアプリが揃っています。これは、テクノロジーに依存してつながりを保ち、オンラインでサービスを受ける私たちの生活スタイルが背景にあるからです。本記事では、AndroidおよびiOSデバイスで利用できる最高のデジタルマーケティングアプリをご紹介します。おすすめ