こんにちは、AIエンジニアリンググループの矢澤です。 世間はGWでしたが、毎年この時期は交通機関や観光地が混むイメージがあるので、私はほぼ自宅で過ごしました。 ただ、イベントへの参加は新たなインスピレーションなど業務に繋がる可能性もあるので、別の時期に出掛けられればと思っています。
近年、生成AIによるチャットボットが広く普及してきたと感じています。 これまで生成AIについては、社内向けのチャットボットなど一部の業務や機能において検証・導入を進めてきました。 今後は、より具体的なユースケースに向けた活用や量産化も視野に入れていく予定です。
ボットなどのAIサービスを社外に向けて活用していくことを見据えると、基本的な機能だけでなく、処理速度やセキュリティ面など非機能要件にもさまざまな課題が見えてきます。 特に、多くのユーザーによる同時利用が想定される場合、システムダウンが発生するたびにユーザーとコミュニケーションを行ったりシステム改修を行うのは現実的でないため、ある程度事前の準備が必要となります。 例えばアクセス負荷に耐えられるようなシステム・リソースを準備したり、場合によってはリクエスト数を制限するなどの対応が考えられます。 システムがどの程度の負荷に耐えられるか、数人の協力者やスクリプトによってリクエストを送り確認することも可能ですが、より効率的に負荷テストを行う方法としてAzure Load Testingというサービスがあることを知りました。 Load TestingはAzureが提供する負荷テスト用サービスで、いくつかの条件(アクセス先や同時リクエスト数、認証方法など)を設定するだけで、簡単に大量リクエストを送ることができます。
また、場合によってはAPIで負荷分散を行うことで、応答可能な同時リクエスト数を増やすことができます。 負荷分散についても、Azure API Managementのバックエンド機能を使うことでスムーズに実現できることが分かりました。 そこで今回は、Azure API Managementを使って負荷分散システムを組んだ上で、Azure Load Testingで条件ごとのエラー状況や応答可能リクエスト数を確認してみました。
Azure Load Testing とは
はじめに、今回初めて使用したLoad Testingの概要を説明します。 Load Testingでは大規模な負荷を自動で生成し、システムパフォーマンスのボトルネックを特定することができます。 Azure向けのフルマネージドサービスであり、開発者やテスト担当者は簡単にシミュレーションを実行することができます。 ロードテストについて詳しくなくてもテストをすばやく作成したり、既存の Apache JMeter1 スクリプトをアップロードしたりできます。 自動化されたCI/CDワークフローを通じて、実用的な分析情報(パフォーマンス、スケーラビリティ、容量)を取得し、継続的な改善をサポートします。
実際、私自身は負荷テストをこれまで行ったことがありませんでしたが、AzureポータルのGUIのみで簡単に使用することができました。
実験内容
構成図
今回検証したシステムの構成図は以下の通りです。
基本的には Azure Web App + Azure AI Search + Azure OpenAI で、RAGを使用したチャットボットのAPIを構築しています。 API Management から上記のAPIを呼び出しますが、この時にバックエンドのロードバランサーを使用することで、負荷分散を実現しています。 上記システムの複数ポイントに対して Load Testing でリクエストを送り、同一条件下での負荷テストを行いました。
API Managementでの負荷分散
負荷分散時の性能を確認するため、API Managementバックエンドのロードバランサー機能を使用しました。 概要や設定方法は、Microsoftの公式ドキュメントで確認できます。 ただし、本ドキュメントにはAPIMに関する詳細な内容も含まれるため、同様の実験をすぐに試したい方向けにチュートリアルとして今回行ったことを簡単に共有します。
まずはポータル上で作成済みのAPI Managementリソースにアクセスし、「APIs」-「Backends」→「バックエンド」→「新しいバックエンドを作成する」をクリックしてAPIバックエンドを作成します。 「バックエンド ホスティングの種類」でAzureリソース(今回は既存のWeb App)を選択すると、バックエンドサービスのURLが「ランタイム URL」に自動追加されます。 続いて、負荷分散を行うために別のWeb Appを用意し、同様の方法で二つ目のバックエンドを作成してURLを設定します。
その後APIMの「Backends」画面に戻り、「ロードバランサー」→「新しいプールを作成する」を選んでロードバランサーを作成します。 「構成の定義」-「バックエンドをプールに追加する」欄で先ほど作成した2種類のバックエンドを指定することで、リクエストを分散できるようになります。 さらに、各バックエンドに対するリクエスト送信の重みを指定することも可能ですが、今回は同率(「Send requests evenly」)としました。
これでバックエンドとロードバランサープールの作成が完了したので、次にAPIの設定を行います。
再びAPIMのリソース画面に戻り、「APIs」-「API」→「All APIs」-「[作成済みのAPI名]」-「Design」でAPIの設計画面を開きます。
「All operations」-「{対象の操作名}」をクリック(操作が無い場合は新規作成)すると、APIへのリクエスト処理の詳細が表示されます。
操作ごとの設定が表示されるので、「Inbound processing」-「Policies」の横の「」ボタンを選択して、インバウンド処理のポリシーXMLを表示します。
続いて、
<inbound> <set-backend-service backend-id={作成したロードバランサー名} /> </inbound>
以上でAPIMの設定は完了です。 これにより、本APIへのリクエストはロードバランサーで指定したバックエンドに負荷分散されるようになります。 実際にAPIの「Test」画面などで確認すると、テスト実行ごとにリクエストの送信先が変わることが確認できるかと思います。
Load Testingでの負荷テスト
APIの負荷分散設定が完了したので、いよいよLoad Testingでの負荷テストを行います。 こちらも詳細はMSのクイックスタートページなどのドキュメントを見ていただければと思います。
今回は上記の構成図の通り、APIMやロードバランサー、AOAIの有無などの影響を確認するため、複数のエンドポイントに対してテストを行いました。 ロードテスト作成時に、リクエストの送付先URLや内容を指定できるので、各エンドポイントに適切なフォーマットで質問が送られるようにします。 テスト自体の構成は全て同じ条件で、線形に仮想ユーザー数が増加するようにしました。
その他リソースのプランなどは以下の通りです。
- AOAIのレート制限:最大(~30Mトークン/分、~300Kリクエスト/分)
- App Serviceの価格プラン:Premium v3 P1V3(vCPU x2)
- APIMの価格プラン:Basic(99.95% SLA)
実験結果
以降に、エンドポイントや条件ごとのLoad Testingの実行結果を記載します。
全結果のまとめ
実験ID | エンドポイント | AOAIの使用(不使用の場合は単純なFlask APIを使用) | 負荷分散 | エラー発生時の並列数(エラーステータス) | 方針 |
---|---|---|---|---|---|
1 | AOAI | 〇 | - | - (25並列でもエラーなし) |
AOAIのスケールアップは不要 |
2 | Web App | - | - | - (25並列でもエラーなし) |
Web Appのスケールアップは不要 |
3 | Web App | 〇 | - | 10 (403, 502, Non HTTP Response code, 499) |
実験2との比較からAOAI起因のNWエラーと思われるため、APIMでの負荷分散(AOAIリソース分離)を後ほど試す(実験7) |
4 | APIM | - | - | 5 (502, 500) |
実験2との比較からAPIM起因のNWエラーと思われるため、APIMでの負荷分散を試す(実験5) |
5 | APIM | - | 〇 | 6 (502, 500) |
実験4との比較から負荷分散ありでもAPIM起因のNWエラーは解消しないが、実際のアプリではAOAIでキュー処理が行われる可能性があるので試す(実験6) |
6 | APIM | 〇 | 〇 | 5 (502, 500, Non HTTP Response code) |
実験5との比較から、AOAI使用時もAPIM起因のNWエラーが解消していないと思われるが、AOAIのリソースを分けることで解決する可能性もあるので試してみる(実験7) |
7 | APIM | 〇 | 〇 (AOAIリソース分離3) |
14 (500, 502) |
実験6との比較から、AOAIリソースを分離することで多少NW負荷を軽減できそうなので、要件に応じて複数リソースの使用を検討 |
以上より、最も良い条件(実験7)では10並列ぐらいまで耐えられることが示唆されたので、同時リクエスト数がそれ以上多くならなそうであればAPIMで負荷分散(AOAI分離)を使用すれば良いことが分かりました。
それ以上リクエスト数が増えそうな場合は、フロント側でのアクセス制御を行うか、ユーザーに再試行を依頼するなどの対応が必要となります。 また、負荷分散のバックエンド数を更に増やしたり、APIMのプランを変更したりすることで、対応可能なリクエスト数が増える可能性もあります。 Web AppやAPIMを使用せずに、サーバーマシンなどでAPIを直接ホストするという方法も考えられますが、今回はAzureのサービスを活用してセキュリティ管理などをスピーディーに行いたかったので、試していません。
各実験の結果詳細
参考までに、各実験でのLoad Testingのクライアント側メトリックを共有します。
Load Testingメトリック
まとめ
本記事では、Azure API Managementでの負荷分散とLoad Testingでの負荷テストの方法・結果について説明しました。 Load Testingでは、設定を変更することで様々な条件下でテストを行い、各種メトリックやエラー内容を確認することが可能です。 またAzureの他リソース(APIM, Web App, AOAIなど)側でも、メトリック・エラーや詳細なログを確認したり、アラートを設定したりすることができます。 さらに、Datadogなどの外部ツールに対してデータを送信することで、様々なサービスを統合的に監視することも考えられます。 今後はそのようなオブザーバビリティに関する機能も活用して、システムの安定性を更に高めていきたいです。
- Apache JMeterは機能動作の負荷テストとパフォーマンス測定を目的とした、Javaで作成されたオープンソースソフトウェアです。当初はWebアプリのテスト用に設計されましたが、その後に他のテスト機能にも拡張されました。↩
- ポリシーを編集して保存すると、ポータルの右上に「Could not load policies.」というエラーが表示されることがあります。Microsoftのサポートに確認したところ、こちらは既知のバグのようで、APIの動作には影響ありません。↩
- AOAIのリソース分離について、サブスクリプションとリージョン(東日本)は同一とし、別のリソースグループにAOAIリソースを作成した上で、各リソースにモデルをデプロイしました。↩