すべてのプロダクト
Search
ドキュメントセンター

Alibaba Cloud Service Mesh:リクエストヘッダーに基づく大規模言語モデルのトークンレート制限の実装

最終更新日:Mar 12, 2026

Service Mesh (ASM) では、特定のクライアントが大規模言語モデル(LLM)サービスを呼び出す際に消費できるトークン数を制限します。このレート制限は、TCP 属性、HTTP ヘッダー、パス、ホスト、ルート送信先などのリクエスト属性に基づいて適用されます。クライアントが割り当てられたトークン予算を超えると、サイドカープロキシは上流の LLM サービスへリクエストを転送せず、代わりにレート制限応答を返します。

適用範囲

  • LLM API のコスト管理:外部の LLM サービスは通常、トークン消費量に基づいて課金されます。トークンレート制限により、クライアントまたはユーザータイアごとの支出を上限付きで制御でき、予期せぬコスト増加を防止できます。

  • 共有推論サービスの保護:外部クライアントがクラスター内の推論サービスを呼び出す場合、トークンレート制限により、単一のクライアントによる計算リソースの独占を防ぎ、他のユーザの可用性低下を回避できます。

仕組み

ASM では、WebAssembly(Wasm)上で構築された 2 つのコンポーネントにより、LLM トークンレート制限を実装します。

  • レート制限プラグイン -- サイドカーフィルターとしてデプロイされる Wasm プラグインです。このプラグインは、送信される各 LLM リクエストをインターセプトし、レート制限キー(例: user-type ヘッダーの値)を抽出して、レート制限サービスに照会し、リクエストを許可するか拒否するかを判断します。

  • レート制限サービス — クライアントが運用・管理するバックエンドサービスで、トークン消費量の追跡およびレート制限ルールの適用を行います。ASM のレート制限プラグインは、標準的な HTTP インターフェイスを用いてこのサービスを呼び出します。ASM では、Redis をストレージバックエンドとするトーケンバケットアルゴリズムを採用したデフォルト実装を提供しています。カスタムロジックを実装する場合は、任意のアルゴリズム(トーケンバケット、リークバケット、スライドウィンドウ)を用いた独自サービスを構築できます。また、バックエンドサービスの負荷に応じて、レート制限ルールを動的に調整することも可能です。

Architecture diagram

リクエストフロー:

  1. クライアントがサイドカープロキシを経由して LLM リクエストを送信します。

  2. Wasm プラグインがリクエストヘッダーからレート制限キーを抽出します。

  3. プラグインがレート制限サービスに照会し、該当キーのトークン予算超過状態を確認します。

  4. 予算に余裕がある場合は、リクエストが LLM サービスへ転送されます。予算が超過している場合は、プロキシがレート制限応答(例:regular-user is being rate-limited)を返します。

  5. LLM サービスがリクエストを処理し、応答の `usage` フィールドにトークン使用量データ(例:prompt_tokenscompletion_tokenstotal_tokens)を含めて応答を返します。

  6. プラグインが消費されたトークン数をレート制限サービスへ報告し、記録を更新します。

説明

ステップ 6 は非同期で実行され、LLM 応答がクライアントに届くことをブロックしません。

前提条件

開始する前に、以下の条件を満たしていることを確認してください。

手順 1:レート制限サービスのデプロイ

この手順では、ユーザーごとのトークン予算を適用するトーケンバケットアルゴリズムを用いたレート制限サービスをデプロイします。例では、異なるトークン割り当てを持つ 2 つのユーザータイア — regular-user および subscriber — を定義しています。

  1. 以下の内容で token-limit.yaml というファイルを作成します。

    YAML コンテンツの表示

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: asm-llm-token-rate-limit-example
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: asm-llm-token-rate-limit-example
      labels:
        app: asm-llm-token-rate-limit-example
        service: asm-llm-token-rate-limit-example
    spec:
      ports:
      - name: http
        port: 80
        targetPort: 8080
      selector:
        app: asm-llm-token-rate-limit-example
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: asm-llm-token-rate-limit-example
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: asm-llm-token-rate-limit-example
          version: v1
      template:
        metadata:
          labels:
            app: asm-llm-token-rate-limit-example
            version: v1
          annotations:
            sidecar.istio.io/inject: "true"
        spec:
          tolerations:
          - key: "node.kubernetes.io/disk-pressure"
            operator: "Equal"
            value: ""
            effect: "NoSchedule"
          serviceAccountName: asm-llm-token-rate-limit-example
          containers:
          - image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-wasm-token-rate-limit-example:v1.23.6.34-g92d6a4b-aliyun
            imagePullPolicy: IfNotPresent
            name: asm-llm-token-rate-limit-example
            ports:
            - containerPort: 8080
            env:
            # Redis 接続:実際の Redis エンドポイントおよびポートに置き換えてください
            - name: REDIS_ADDRESS
              value: ${redis-address}:${redis-port}
            # Redis 認証情報:実際の Redis ユーザー名およびパスワードに置き換えてください
            - name: REDIS_PASSWORD
              value: "${redis-user}:${password}"
            # レート制限ルール:ユーザータイアごとに 1 件のエントリ
            - name: RATE_LIMIT_CONFIG
              value: |
                [
                  {
                    "rate_limit_key_regex": "regular-user.*",
                    "redis_expired_seconds": 300,
                    "fill_interval_second": 30,
                    "tokens_per_fill": 50,
                    "max_tokens": 200
                  },
                  {
                    "rate_limit_key_regex": "subscriber.*",
                    "redis_expired_seconds": 600,
                    "fill_interval_second": 60,
                    "tokens_per_fill": 100,
                    "max_tokens": 1000
                  }
                ]
            resources:
              limits:
                memory: 256Mi
                cpu: 200m
              requests:
                memory: 64Mi
                cpu: 50m

    以下のプレースホルダーを実際の値に置き換えてください。

    プレースホルダー説明
    ${redis-address}Redis ホストアドレスr-bp1xxxxxx.redis.rds.aliyuncs.com
    ${redis-port}Redis ポート番号6379
    ${redis-user}Redis ユーザー名default
    ${password}Redis パスワードMyP@ssw0rd

    RATE_LIMIT_CONFIG 環境変数には、トーケンバケットルールが定義されています。各ルールは正規表現でレート制限キーをマッチさせ、個別のバケットを適用します。

    パラメーター説明
    rate_limit_key_regexリクエストヘッダーから抽出されたレート制限キーと一致させる正規表現パターン。
    max_tokensトーケンバケットの最大容量。この上限を超えるリクエストは拒否されます。
    tokens_per_fill各充填間隔でバケットに追加されるトークン数。
    fill_interval_secondトークンの充填間隔(秒単位)。
    redis_expired_secondsRedis 内のレート制限レコードの有効期限(TTL)。この期間が経過すると、レコードは期限切れとなり、バケットはリセットされます。

    以下に、この例で設定されたレート制限ルールを示します。

    ルールキー正規表現バケット容量充填レートRedis TTL
    一般ユーザregular-user.*200 トークン30 秒ごとに 50 トークン300 秒
    サブスクライバーsubscriber.*1,000 トークン60 秒ごとに 100 トークン600 秒

    サブスクライバーは、一般ユーザより 5 倍大きなバケット容量と充填トークン数を享受し、継続的な高ボリュームな LLM 利用をサポートします。

  2. データプレーン クラスターの kubeconfig を用いて、構成を適用します。

    kubectl apply -f token-limit.yaml
説明

これは ASM が提供するデフォルトのレート制限実装です。異なるアルゴリズムやストレージバックエンドなど、カスタム要件がある場合は、「GitHub 上のソースコード」をご参照ください。

手順 2:Wasm プラグインのデプロイ

この手順では、サイドカープロキシを構成して LLM リクエストをインターセプトし、user-type ヘッダーからレート制限キーを抽出し、レート制限サービスに対して照会を行う WasmPlugin をデプロイします。

  1. 以下の内容で wasm.yaml というファイルを作成します。

    apiVersion: extensions.istio.io/v1alpha1
    kind: WasmPlugin
    metadata:
      name: llm-token-ratelimit
      namespace: default
    spec:
      failStrategy: FAIL_OPEN
      imagePullPolicy: IfNotPresent
      selector:
        matchLabels:
          app: sleep
      match:
      - mode: CLIENT
        ports:
        - number: 80
      phase: STATS
      pluginConfig:
        matches:
        - host:
            exact: "dashscope.aliyuncs.com"
        rateLimitKeys:
        - "{{request.headers.user-type}}"
        rateLimitService:
          service: asm-llm-token-rate-limit-example.default.svc.cluster.local
          port: 80
      priority: 10
      url: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-wasm-llm-token-ratelimit:v1.23.6.34-g92d6a4b-aliyun

    以下の表に、主要な構成フィールドの説明を示します。

    フィールド説明
    .spec.pluginConfig.matchesレート制限を適用するリクエストを定義します。マッチしないリクエストは、レート制限チェックなしで通過します。
    .spec.pluginConfig.rateLimitKeysレート制限キーの抽出方法を指定します。Envoy リクエスト属性 構文を使用します。本例では、{{request.headers.user-type}} により、user-type ヘッダーの値が抽出されます。
    .spec.pluginConfig.rateLimitServiceレート制限サービスのエンドポイントを指定します。Kubernetes Service の完全修飾ドメイン名(FQDN)を指定してください。
  2. コントロールプレーン クラスターの kubeconfig を用いて、構成を適用します。

    kubectl apply -f wasm.yaml

手順 3:構成の検証

両方のユーザータイアでテストリクエストを送信し、レート制限が正しく機能することを確認します。

以下の各コマンドを、データプレーン クラスターの kubeconfig を用いて複数回実行します。

一般ユーザとして:

kubectl exec deployment/sleep -it -- curl 'http://dashscope.aliyuncs.com' \
  --header 'Content-Type: application/json' \
  --header "user-type: regular-user" \
  --data '{
      "messages": [
          {"role": "user", "content": "自己紹介をお願いします"}
      ]
  }'

サブスクライバーとして:

kubectl exec deployment/sleep -it -- curl 'http://dashscope.aliyuncs.com' \
  --header 'Content-Type: application/json' \
  --header "user-type: subscriber" \
  --data '{
      "messages": [
          {"role": "user", "content": "自己紹介をお願いします"}
      ]
  }'

期待される動作:

  • 両方のユーザータイアからの初期リクエストでは、トークン数を含む `usage` フィールドを含む通常の LLM 応答が返されます。

  • 数回のリクエスト後に、regular-user が最初にレート制限に達し、「regular-user is being rate-limited」という応答を受け取ります。

  • subscriber は、レート制限に達するまでより多くのリクエストを送信でき、高いトークン予算が正しく適用されていることが確認できます。

これにより、レート制限サービスがユーザータイアを区別し、設定されたトークン予算を適切に適用していることが検証されます。

次のステップ

  • レート制限ルールのカスタマイズ:本番環境のトラフィックパターンに合わせて、max_tokenstokens_per_fillfill_interval_secondRATE_LIMIT_CONFIG で調整します。

  • カスタムレート制限サービスの構築:「サンプル実装」をフォークし、カスタムアルゴリズム(リークバケット、スライドウィンドウ)の実装や、異なるストレージバックエンドの利用を実現します。

  • 対象属性の拡張:レート制限キーの抽出元を、パス、ホスト、TCP 属性などの他のリクエスト属性に変更するには、rateLimitKeys を編集します。「Envoy 属性」で利用可能なオプションをご確認ください。