EmotionTechテックブログ

株式会社エモーションテックのProduct Teamのメンバーが、日々の取り組みや技術的なことを発信していくブログです。

1年間運用したAzure OpenAI Serviceの負荷分散構成の紹介

はじめに

こんにちは、 SREのおかざきです。 エモーションテックでは、Azure OpenAI Serviceを活用したテキストAI分析サービス「TopicScan®︎」の開発を進めています。 本記事では、Azure OpenAI Serviceの負荷分散を実現するために設計したアーキテクチャと、その1年間の運用を踏まえての感想をご紹介します。

課題:増え続けるAzure OpenAI Serviceリソースへのアクセス方法

Azure OpenAI Service (以下、Azure OpenAI)には、TPM (Tokens-per-Minute) という利用制限(*1)があり、サブスクリプション、リージョン、モデル毎に割り当てられています。 そのため、アプリケーションから呼び出し可能な合計TPMを多くするためにも、弊社では複数のサブスクリプション、リージョンに数十個のAzure OpenAIのリソースを作成しています。 また、このようにすることで、TPMの上限値を増やすだけではなく、一部リージョンの障害でAzure OpenAIが全く使えなくなるような事態を避けることができます。

このような構成にしていると、アプリケーションからAzure OpenAIへのアクセス方法が課題になります。 Azure OpenAIへのアクセス方法としては、簡単な方法としてAzure OpenAIからAPIキーを払い出し、Key Vault等に格納してアプリケーションに読み込ませるということもできます。 しかし、多数のAzure OpenAIリソースが存在する場合はのendpoint URLとAPIキーの指定ミスなどを誘発しやすくなり、運用が難しくなると考えます。

また、Azure OpenAIはリージョンによって利用可能なモデルが異なります。アプリケーション側でモデル毎に利用可能なAzure OpenAIを考慮してリクエストを送信するのは実装が複雑になり得ます。

*1: Azure OpenAI Service のクォータを管理する

learn.microsoft.com

解決策:API ManagementとFront Doorによる負荷分散

上記の課題を解決するために、以下のアーキテクチャを構築しました。

*Azure OpenAIの負荷分散に関わる要素をピックアップしており、その他のサーバー、ネットワーク等の構成要素は省略しています。

Azure API Management

Azure API Management (以下、API Management ) は、Microsoft Entra ID認証を用いてOpenAI認証のためのAPIキーを生成します。これにより個々のOpenAIでAPIキーを払い出す必要がなくなります。

OpenAIのAPIキーの代わりに、クライアントからの接続の際は以下のような手段でアクセス制限ができます。

  • Subscription Key (API ManagementにおけるAPIキーのようなもの)によるクライアントの認証
  • API ManagementのポリシーにてIP制限
  • Microsoft Entra IDによる認証

一つ一つのOpenAIでAPIキーを払い出すことと比べると、クライアントは一つのAzure API Managementを意識すれば良くなり、運用が楽になります。

Azure Front Door

Azure Front Door (以下、Front Door) は、複数のリージョンに分散配置されたAzure OpenAIインスタンスへの負荷分散を行います。

Origin GroupとRouteを設定することで、特定のリクエストヘッダーの値に基づいてリクエストを特定のリージョン(例:日本のAzure OpenAIに限定して転送)に転送することも可能です。 また、Azure OpenAIはリージョンによって利用可能なモデルが異なる場合もあります。 利用するモデルはリクエストURLから判別ができるので、URLの値に基づいて、転送先のOrigin Groupを変えるという制御も行えます。

Front DoorはCDNとしても扱えますが、この構成では負荷分散のみを目的として、キャッシュを無効化しL7ロードバランサーとして利用しています。

Clientから利用可能なAzure OpenAIのTPMについて

上図の例では、Front Doorよりリージョン1、2、NのAzure OpenAIにラウンドロビンで負荷分散するとします。 各リージョンにはGPT-4-TurboのTPMの上限として1000k割り当てられているとします。 このような構成にすることで、クライアントはGPT-4-Turboを利用する際に実質各リージョンのTPMの合計3000kを利用できることになります。

構成例

上記の構成において、設定する上でのポイントを記載します。

API Management

Microsoft Entra ID認証を用いてAzure OpenAIへのAPIキーを生成する部分は、以下のMicrosoftDocsの例を参考にInbound policyを設定します。

github.com

設定項目の中でAPI定義を設定する必要がありますが、 AzureのドキュメントにAzure OpenAIのSwagger Specのjsonファイルが公開されているので、これをTerraformでAPI ManagementにインポートしてAPIを作成します。

learn.microsoft.com

resource "azurerm_api_management" "this" {
  name                = var.api_management_name
  location            = var.location
  resource_group_name = var.resource_group_name
  publisher_name      = "hogehoge"
  publisher_email     = "hogehoge@fugafuga"
  sku_name            = var.api_management_sku
  identity {
    type = "SystemAssigned"
  }
}

resource "azurerm_api_management_api" "this" {
  name                = "azure-openai-endpoint"
  resource_group_name = var.resource_group_name
  api_management_name = azurerm_api_management.this.name
  revision            = "1"
  display_name        = "azure-openai-endpoint"
  protocols           = ["https"]

  service_url = "https://${azurerm_cdn_frontdoor_endpoint.this.host_name}"

  import {
    content_format = "openapi+json"
    content_value  = templatefile("${path.module}/azure-openai-2023-05-15.json", var.api_template_vars)
  }
}

Front Door

特定のヘッダーに基づいてリクエストを特定のリージョン(例:東日本リージョンのAzure OpenAI)にルーティングする設定について、 Terraformでの記述例を紹介します。

resource "azurerm_cdn_frontdoor_origin_group" "japan" {
  name                     = "${var.frontdoor_name}-origingroup-japan"
  cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.this.id
  session_affinity_enabled = false
  load_balancing {}
}

resource "azurerm_cdn_frontdoor_origin" "japan" {
  for_each = toset(var.japan_openai_endpoint)

  # reverse(split("-", split(".", each.value)[0]))[0]でOpenAI endpoint FQDNにおける、aaa-bbb-ccc.ddd.eeeにおけるcccを取得して、origin nameに利用している。 
  name                           = "${var.frontdoor_name}-origin-japan-${reverse(split("-", split(".", each.value)[0]))[0]}"
  cdn_frontdoor_origin_group_id  = azurerm_cdn_frontdoor_origin_group.japan.id
  enabled                        = true
  certificate_name_check_enabled = true
  host_name                      = each.value

  origin_host_header = each.value
  https_port         = 443
  priority           = 1
  weight             = 1
}

他構成案

上記で紹介した構成案以外に、検討した案についても共有します。

Front Doorの代わりにApplication Gateway を利用するパターン

以下の理由から採用を見送りました。

  • Application Gatewayでも負荷分散は行えますが、VNetとセットで作成する必要があります。 VNetは特定のリージョンに紐づくので、当該リージョンのVNetで障害が発生するとOpenAIに接続できなくなる可能性があることを懸念しました。
  • OpenAIが存在するリージョンと合わせてApplication Gatewayを作成するのも、複数のApplication Gatewayが必要となり、 グローバルサービスであるFront Door単体を構築することと比較して運用負荷が高まるのではと考えました。

また、以下のドキュメントにどの負荷分散用製品を利用するかのデシジョンツリーがあり、そちらを参照して、Front Doorが妥当であると考えました。

learn.microsoft.com

Front Doorを使わずにAPI Managementだけでリクエストを分散するパターン

Front Doorを利用しなくても、API ManagementのInbound Policyを用いて負荷分散することはできそうではありました。 ただし、Inbound PolicyのXMLを作り込む必要があり、単純なラウンドロビンだけではなく転送先リージョンや利用モデルに応じたリクエスト転送を実現しようとすると動作確認も大変になりそうと考えました。 標準機能として負荷分散処理やルーティング機能が備わっているFront Doorを利用する方が管理しやすいのではと考えたため、Front Doorを利用することにしました。

運用してみてわかったこと

よかった点

各OpenAIのAPIキーをクライアントから意識する必要がなくなったため、運用は楽になりました。

Front DoorでのOriginへの振り分けルールが柔軟に設定ができて、振り分けの要件の追加にも対応しやすく便利に感じました。 課題に記載した、モデル毎に利用可能なAzure OpenAIを考慮してリクエストを送信することもアプリケーションの変更無く実現できました。

課題

一部のAzure OpenAIを自動で一時的に負荷分散対象から外すのが難しい

一部リージョンのAzure OpenAIで障害が発生することもあり、正常な応答が返せなくなったものについては自動で一時的に負荷分散対象から外したいと考えますが、現状はできていません。

Front Doorの機能で、health probesというものがあり、こちらはOriginからstatus code 200で返却される場合に正常とみなされますが、 Azure OpenAIは、health probesから送信可能な単純なリクエストに対しては404を返却してしまいます。helth probeから送信できるリクエストをカスタマイズできるなら良いのですが、そうではないので、health probesを活用できておりません。 現状は、アプリケーション側にリトライ機構を実装することで部分的なAzure OpenAIの障害に対処しています。

Front Doorの設定変更に時間が掛かるようになった

Front Doorに多数のOriginを設定しているからかもしれませんが、Originの変更をする際にTerraform applyで20分程度時間がかかってしまうことを観測しています。一時的に負荷分散対象を変更したいような場合に待ち時間が発生するので少し悩ましいです。

Front DoorのOrigin数の上限を意識する必要がある

以下のドキュメントに記載がある通り、Front Door ProfileやOrigin Group毎にOriginの設定数に上限があります。

learn.microsoft.com

弊社では複数のサブスクリプション、リージョンに渡ってAzure OpenAI リソースを起動してOriginに指定していたところ、Front Door ProfileあたりのOrigin数の上限に引っかかってしまうことがあったため、活用頻度の低いOriginを整理するといった対応が必要となっておりました。

API ManagementのAPIバージョンアップが必要

こちらは困り事というより考慮が必要なタスクの意味合いが強いですが、API ManagementのAPIを定義する際に、前述の通り Azureのドキュメントに公開されているAzure OpenAI APIのSwagger Specを利用しておりますが、Azure OpenAI APIのバージョンアップが行われるため、バージョンアップに追従していく必要があります。(2024年10月時点で確認したところ、stableは間隔がまちまちのようですが、previewは1、2ヶ月に一つバージョンが上がっているようです。)

まとめ

いかがでしたでしょうか。 今回はAzure OpenAIのTPMを増やしたり冗長性を高めるための構成例をご紹介しました。 個人的にはAzure OpenAIを用いてできるだけ短時間で多くの処理を行うため、Azure製品の組み合わせ方を考える良い機会になりました。 エモーションテックでは顧客体験、従業員体験の改善をサポートし、世の中の体験を変えるプロダクトを開発しています。ご興味のある方はぜひ採用ページからご応募をお願いいたします。

hrmos.co