Home Microsoft FastTrack解説: Azure API Management Circuit BreakerとLoad BalancingによるAzure OpenAIレジリエンス設計
投稿
キャンセル

✍️ Microsoft FastTrack解説: Azure API Management Circuit BreakerとLoad BalancingによるAzure OpenAIレジリエンス設計

本記事は Using Azure API Management Circuit Breaker and Load balancing with Azure OpenAI Service(Microsoft FastTrack for Azure Blog)の解説記事です。

ブログ概要(Summary)

Microsoft FastTrack for Azureチームが公開したこのブログ記事は、Azure API ManagementのCircuit BreakerとBackend Pool(Load Balancing)を組み合わせて、Azure OpenAI Serviceの可用性とスループットを向上させる具体的な実装手順を示したものである。Bicepテンプレートによるインフラ定義とAPIポリシー(XML)の完全な構成例を含み、読者がそのまま本番環境に適用できる実装ガイドとなっている。

この記事は Zenn記事: Azure OpenAI負荷分散設計:API ManagementとPTUスピルオーバーで可用性99.9%を実現する の深掘りです。

情報源

技術的背景(Technical Background)

Azure OpenAI Serviceのエンドポイントは、デプロイメントに割り当てられたTPM(Tokens Per Minute)クォータを超過するとHTTP 429 (Too Many Requests)を返す。この応答にはRetry-Afterヘッダーが含まれ、クライアントが再試行までに待機すべき秒数を指示する。

単一エンドポイントに依存する構成では、この429応答がそのままエンドユーザーに伝搬し、サービス品質の低下を招く。Azure API ManagementのCircuit BreakerとBackend Pool機能を組み合わせることで、429応答を検知した際に自動的に別のバックエンドへルーティングし、エンドユーザーへの影響を最小化できる。

学術的には、Circuit Breaker Patternは Michael Nygard が著書 “Release It!”(2007年)で体系化したレジリエンスパターンであり、障害が連鎖するのを防ぐ「回路遮断器」として機能する。Azure API ManagementのCircuit Breakerは、このパターンにLLM API固有のacceptRetryAfter機能を追加し、バックエンドの回復時間に動的に適応する点が特徴である。

実装アーキテクチャ(Architecture)

システム構成

ブログで示されている構成は以下の通りである。

1
2
3
4
5
6
7
8
9
10
11
クライアント
  ↓
Azure API Management
  ├─ APIポリシー(レート制限・認証・メトリクス)
  └─ Backend Pool(Load Balancer)
       ├─ Backend-1: Azure OpenAI #1 [Priority 1, Weight 3]
       │    └─ Circuit Breaker: 429×1回/10秒でトリップ、10秒後復帰
       ├─ Backend-2: Azure OpenAI #2 [Priority 1, Weight 1]
       │    └─ Circuit Breaker: 同上
       └─ Backend-3: Azure OpenAI #3 [Priority 2]
            └─ Circuit Breaker: 同上(フォールバック用)

Circuit Breaker構成の詳細

ブログで示されているBicepテンプレートでは、Circuit Breakerを以下のように構成している。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
resource backend1 'Microsoft.ApiManagement/service/backends@2024-06-01-preview' = {
  name: 'backend-1'
  parent: apimService
  properties: {
    url: '${openAiEndpoint1}/openai'
    protocol: 'http'
    circuitBreaker: {
      rules: [
        {
          failureCondition: {
            count: 1
            errorReasons: [ 'Server errors' ]
            interval: 'PT10S'
            statusCodeRanges: [
              { min: 429, max: 429 }
            ]
          }
          name: 'breakerRule'
          tripDuration: 'PT10S'
          acceptRetryAfter: true
        }
      ]
    }
  }
}

ここで重要なパラメータを整理する。

パラメータ意味
failureCondition.count11回の429でトリップ
failureCondition.intervalPT10S10秒間の監視窓
failureCondition.statusCodeRanges429-429429のみをトリガー
tripDurationPT10S基本トリップ時間10秒
acceptRetryAftertrueバックエンドのRetry-Afterヘッダーで動的調整

acceptRetryAfter: trueの動作: tripDurationで設定した固定時間の代わりに、Azure OpenAIが返すRetry-Afterヘッダーの値をCircuitのOpen期間として使用する。これにより、バックエンドの実際の回復時間に合わせた適応的な制御が可能になる。

Backend Pool(Load Balancing)の構成

3つのバックエンドを組み合わせたプール構成が示されている。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
resource backendPool 'Microsoft.ApiManagement/service/backends@2024-06-01-preview' = {
  name: 'openai-backend-pool'
  parent: apimService
  properties: {
    type: 'Pool'
    pool: {
      services: [
        { id: backend1.id, priority: 1, weight: 3 }
        { id: backend2.id, priority: 1, weight: 1 }
        { id: backend3.id, priority: 2, weight: 1 }
      ]
    }
  }
}

Priority-basedルーティングの動作:

  1. 通常時: Priority 1のBackend-1とBackend-2にリクエストを分散。重み比3:1のため、Backend-1に75%、Backend-2に25%のリクエストが配分される。
  2. Backend-1で429発生: Circuit Breakerがトリップし、Backend-1が一時的にプールから除外。Backend-2が全リクエストを処理。
  3. Backend-1, 2ともに429: Priority 2のBackend-3にフォールバック。
  4. Retry-After経過後: Circuit Breakerが復帰し、通常の重み付き分散に戻る。

APIポリシーの構成

ブログでは、以下のポリシー構成を推奨している。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<inbound>
    <base />
    <!-- バックエンドプールへのルーティング -->
    <set-backend-service backend-id="openai-backend-pool" />

    <!-- トークンレート制限(サブスクリプション単位) -->
    <azure-openai-token-limit
        counter-key="@(context.Subscription.Id)"
        tokens-per-minute="20000"
        estimate-prompt-tokens="true" />

    <!-- マネージドID認証 -->
    <authentication-managed-identity
        resource="https://cognitiveservices.azure.com" />

    <!-- トークンメトリクス送信 -->
    <azure-openai-emit-token-metric namespace="openai-usage">
        <dimension name="Subscription" />
        <dimension name="API" />
    </azure-openai-emit-token-metric>
</inbound>

<backend>
    <!-- 429リトライ設定 -->
    <retry condition="@(context.Response.StatusCode == 429)"
           count="2"
           interval="1"
           first-fast-retry="true" />
</backend>

注目すべき設計判断:

  1. estimate-prompt-tokens="true": バックエンドへリクエストを転送する前に、API Management側でプロンプトのトークン数を事前推定する。制限超過が見込まれるリクエストをバックエンドに送らずにブロックすることで、クォータの無駄消費を防止している。

  2. first-fast-retry="true": 最初のリトライを即座に実行し、2回目以降はinterval秒間隔で実施する。429応答が一時的なスパイクであれば即時リトライで成功する可能性が高い。

  3. マネージドID認証: APIキーではなくAzure Managed Identityを使用することで、キーのローテーション管理が不要になり、セキュリティが向上する。

パフォーマンス最適化(Performance)

Circuit Breakerのチューニング指針

ブログに明示的なベンチマーク数値は記載されていないが、以下のチューニング指針が読み取れる。

failureCondition.countの調整: ブログではcount: 1(1回の429で即トリップ)を採用している。これは429応答自体がバックエンドの容量超過を明確に示すため、追加の確認は不要という判断に基づく。ただし、一時的なスパイクで頻繁にトリップする場合は、count: 3(3回以上で初めてトリップ)に緩和することで安定性を向上できる。

重み付きラウンドロビンの比率設計: Backend-1の重み3に対しBackend-2の重み1という構成は、Backend-1のTPMがBackend-2の3倍である場合に適合する。PTU(予約済み)バックエンドに高い重みを設定し、PAYG(従量課金)バックエンドに低い重みを設定することで、PTU容量の利用率を最大化する設計が推奨される。

レイテンシへの影響

API Managementを中間に挟むことでのレイテンシ増加は以下の要因から生じる:

  • ポリシー処理時間(トークン推定、認証、メトリクス送信)
  • ネットワークホップの追加(クライアント→APIM→Azure OpenAI)

著者らの構成ではAPIMとAzure OpenAIを同一リージョンにデプロイすることで、ネットワークレイテンシの増加を最小化している。ポリシー処理時間は通常数ミリ秒程度であり、LLMの推論時間(数百ミリ秒〜数秒)と比較して無視できるレベルである。

運用での学び(Production Lessons)

429エラーのハンドリング戦略

ブログで提示されている構成は、429エラーに対して3段階の防御を設けている。

  1. 第1段階: トークンレート制限(予防)azure-openai-token-limitでリクエストをバックエンドに送る前にブロック
  2. 第2段階: リトライ(即時復旧)retryポリシーで429に対する自動リトライ
  3. 第3段階: Circuit Breaker+フェイルオーバー(長期障害対応) — 連続429でCircuitを開き、別バックエンドに自動切替

メトリクス収集の重要性

azure-openai-emit-token-metricポリシーによるトークン使用量メトリクスの収集は、以下の運用判断に必須である:

  • サブスクリプション別のコスト配賦(チャージバック)
  • 容量計画の見直し(PTU追加の判断材料)
  • 異常検知(特定サブスクリプションのトークン消費急増)

Backend Poolのヘルスチェックに関する制限

Azure OpenAIはカスタムヘルスエンドポイントを提供していないため、能動的なヘルスプローブは実装できない。Circuit Breakerの429/500/503検知による受動的ヘルスチェックが唯一の手段であることをブログは暗に示している。

学術研究との関連(Academic Connection)

  • Circuit Breaker Pattern (Nygard, 2007): 本ブログの実装は古典的なCircuit Breakerパターンに基づいているが、acceptRetryAfterによる動的トリップ期間はAzure固有の拡張である。
  • Llumnix (Sunら, 2024, arXiv:2401.12843): LLM推論における動的スケジューリングの研究。ブログのPriority-based Load Balancingは、Llumnixの「ホットインスタンスを避ける」設計思想と共通する。
  • DistServe (Zhongら, 2024, arXiv:2403.02310): Goodput最適化の概念は、PTUの利用率最大化(Circuit Breakerで429をトリガーにフォールバック)と本質的に同一の目標を持つ。

Production Deployment Guide

AWS実装パターン(コスト最適化重視)

Azure API ManagementのCircuit Breaker+Load Balancingと同等の機能をAWSで実現する構成を示す。

トラフィック量別の推奨構成:

規模月間リクエスト推奨構成月額コスト主要サービス
Small~3,000Serverless$80-200Lambda + Bedrock + ALB
Medium~30,000Hybrid$400-1,000ECS Fargate + ALB + ElastiCache
Large300,000+Container$2,500-6,000EKS + NLB + Bedrock Cross-Region

Circuit Breaker相当機能のAWS実装:

AWSではALB(Application Load Balancer)のTarget Group Health Checkと、アプリケーション層のCircuit Breaker(resilience4j、tenacity等)を組み合わせる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
import boto3

bedrock = boto3.client('bedrock-runtime', region_name='ap-northeast-1')
bedrock_fallback = boto3.client('bedrock-runtime', region_name='us-east-1')

class ThrottlingError(Exception):
    pass

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=1, max=30),
    retry=retry_if_exception_type(ThrottlingError)
)
def invoke_with_fallback(prompt: str, model_id: str) -> dict:
    """Circuit Breaker付きBedrock呼び出し

    Args:
        prompt: 入力プロンプト
        model_id: Bedrockモデル ID

    Returns:
        Bedrock応答
    """
    try:
        response = bedrock.invoke_model(
            modelId=model_id,
            body=json.dumps({"prompt": prompt})
        )
        return json.loads(response['body'].read())
    except bedrock.exceptions.ThrottlingException:
        # フォールバックリージョンへルーティング
        response = bedrock_fallback.invoke_model(
            modelId=model_id,
            body=json.dumps({"prompt": prompt})
        )
        return json.loads(response['body'].read())

コスト試算の注意事項:

  • 上記は2026年2月時点のAWS ap-northeast-1(東京)リージョン料金に基づく概算値です
  • 最新料金は AWS料金計算ツール で確認してください

Terraformインフラコード

ALB + Lambda構成(Circuit Breaker相当):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# --- ALBターゲットグループ(ヘルスチェック付き) ---
resource "aws_lb_target_group" "primary" {
  name        = "bedrock-primary"
  target_type = "lambda"

  health_check {
    enabled             = true
    healthy_threshold   = 2
    unhealthy_threshold = 3
    interval            = 30
  }
}

resource "aws_lb_target_group" "fallback" {
  name        = "bedrock-fallback"
  target_type = "lambda"

  health_check {
    enabled             = true
    healthy_threshold   = 2
    unhealthy_threshold = 3
    interval            = 30
  }
}

# --- ALBリスナールール(フェイルオーバー) ---
resource "aws_lb_listener_rule" "failover" {
  listener_arn = aws_lb_listener.main.arn
  priority     = 100

  action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.primary.arn
  }

  # プライマリがunhealthyの場合フォールバック
  action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.fallback.arn
  }

  condition {
    path_pattern {
      values = ["/v1/*"]
    }
  }
}

コスト最適化チェックリスト

  • ALBのヘルスチェック間隔を最適化(30秒推奨)
  • Lambda同時実行数制限でコスト上限設定
  • Bedrock Prompt Caching有効化(30-90%削減)
  • CloudWatch アラームでスロットリング率監視
  • AWS Budgets月額予算設定
  • フォールバックリージョンのBedrock利用をCross-Region Inferenceで代替検討

まとめと実践への示唆

Microsoft FastTrackチームの本ブログは、Azure API ManagementのCircuit BreakerとBackend Poolを使ったAzure OpenAIの負荷分散を、Bicepコードとポリシー定義を含む完全な形で提示した実装ガイドである。

特にacceptRetryAfter: trueによる動的トリップ期間の調整は、Azure OpenAIの429応答パターンに最適化された機能であり、固定時間のCircuit Breakerでは実現できないきめ細かい制御を可能にする。また、azure-openai-token-limitによるプロンプトトークンの事前推定は、不要なバックエンド呼び出しを削減し、クォータの有効活用に寄与する。

Zenn記事で解説した構成は、このブログで示されているBicepテンプレートをベースにしており、本ブログの詳細を理解することで、パラメータチューニングやポリシーカスタマイズの判断根拠を得られる。

参考文献


:::message この記事はAI(Claude Code)により自動生成されました。内容の正確性については元のMicrosoft公式ブログと照合していますが、最新情報は公式ドキュメントをご確認ください。 :::

この投稿は CC BY 4.0 でライセンスされています。