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

Alibaba Cloud Service Mesh:QuotaSchedulingPolicy を使用してリクエスト呼び出しクォータ管理を実装する

最終更新日:Jan 13, 2025

Service Mesh(ASM)トラフィックスケジューリングスイートによって提供される QuotaSchedulingPolicy CustomResourceDefinition(CRD)は、指定されたリクエスト呼び出しクォータに達した後の優先度ベースのリクエストスケジューリングをサポートしています。システムで処理されているリクエストのレートが指定されたクォータを超えると、後続のリクエストはキューに入れられ、優先度の高いリクエストがより速く処理されます。このトピックでは、トラフィックスケジューリングスイートによって提供される QuotaSchedulingPolicy を使用してリクエスト呼び出しクォータ管理を実装する方法について説明します。

背景情報

QuotaSchedulingPolicy は、トークンバケットアルゴリズムを使用して、指定されたサービスを呼び出すリクエストのレートを制御し、レートが指定されたクォータを超えたときにリクエストをキューに入れます。 QuotaSchedulingPolicy は次の方法で機能します。

  1. トークンバケットアルゴリズムを使用するレートリミッターは、リクエストレートを制限するために使用されます。レートリミッターのアルゴリズム実装の詳細については、「RateLimitingPolicy を使用してユーザー固有のスロットリングを実装する」の「背景情報」セクションをご参照ください。

  2. リクエストレートがクォータ制限を超えると、後続のリクエストはキューに入れられ、前のリクエストが処理された後に宛先サービスに送信されます。これにより、リクエストレートは常に指定された値に維持されます。さらに、優先度の高いリクエストは、キューから取り出されて宛先サービスに送信される可能性が高くなります。

スロットリングシナリオとは異なり、QuotaSchedulingPolicy を使用する場合、リクエストレートがクォータ制限を超えても、QuotaSchedulingPolicy はリクエストを直接拒否するのではなく、優先度キューに入れます。 QuotaSchedulingPolicy は、リクエストレートがクォータ制限内にあることを保証しながら、優先度に基づいてリクエストをスケジュールします。

前提条件

手順 1:QuotaSchedulingPolicy を作成する

  1. kubectl を使用して ASM インスタンスに接続します。詳細については、「コントロールプレーンで kubectl を使用して Istio リソースにアクセスする」をご参照ください。

  2. 次の内容を含む quotaschedulingpolicy.yaml ファイルを作成します。

    apiVersion: istio.alibabacloud.com/v1
    kind: QuotaSchedulingPolicy
    metadata:
      name: quotascheduling
      namespace: istio-system
    spec:
      quota_scheduler:
        bucket_capacity: 10
        fill_amount: 10
        rate_limiter:
          interval: 1s
        scheduler:
          workloads:
            - label_matcher:
                match_labels:
                  http.request.header.user_type: guest
              parameters:
                priority: 50.0
              name: guest
            - label_matcher:
                match_labels:
                  http.request.header.user_type: subscriber
              parameters:
                priority: 200.0
              name: subscriber
        selectors:
        - service: httpbin.default.svc.cluster.local

    次の表に、いくつかのフィールドについて説明します。関連フィールドの詳細については、「QuotaSchedulingPolicy フィールドの説明」をご参照ください。

    フィールド

    説明

    fill_amount

    interval フィールドで指定された時間間隔内に追加されるトークンの数。この例では、値は 10 で、これは interval フィールドで指定された各間隔の後でトークンバケットに 10 個のトークンが追加されることを示します。

    interval

    トークンがトークンバケットに追加される間隔。この例では、値は 1s で、これはトークンバケットに 1 秒ごとに 10 個のトークンが追加されることを示します。

    bucket_capacity

    トークンバケットの最大トークン数。リクエストレートがトークンバケットの補充レートよりも低い場合、トークンバケット内のトークン数は最大数である bucket_capacity に達するまで増加し続けます。bucket_capacity は、ある程度のバーストトラフィックを許可するために使用されます。この例では、値は 10 で、fill_amount フィールドの値と同じです。この場合、バーストトラフィックは許可されません。

    workloads

    リクエストヘッダーの user_type に基づいて 2 種類のリクエストが定義されています:guestsubscriberguest タイプのリクエストの優先度は 50 で、subscriber タイプのリクエストの優先度は 200 です。

    selectors

    スロットリングポリシーが適用されるサービス。この例では、httpbin.default.svc.cluster.local サービスが使用されており、これは httpbin.default.svc.cluster.local サービスで同時実行制限が実行されることを示します。

  3. 次のコマンドを実行して QuotaSchedulingPolicy を作成します。

kubectl apply -f quotaschedulingpolicy.yaml

手順 2:QuotaSchedulingPolicy が有効になっていることを確認する

この例では、ストレステストツールである Fortio を使用します。詳細については、GitHub Web サイトの Fortio の「インストール」セクションをご参照ください。

  1. 2 つのターミナルを開き、次の 2 つのストレステストコマンドを同時に実行してテストを開始します。テスト全体を通して、2 つのターミナルが想定どおりに動作していることを確認してください。2 つのターミナルでのテストでは、10 個の同時リクエストがサービスに送信され、1 秒あたりのクエリ数(QPS)は 10,000 で、これはサービスが処理できる同時リクエスト数を大幅に超えています。

    fortio load -c 10 -qps 10000  -H "user_type:guest" -t 30s -timeout 60s -a http://${ASMイングレスゲートウェイのIPアドレス}/status/201
    fortio load -c 10 -qps 10000  -H "user_type:subscriber" -t 30s -timeout 60s -a http://${ASMイングレスゲートウェイのIPアドレス}/status/202
    説明

    上記のコマンドの ${ASMイングレスゲートウェイのIPアドレス} を、ASMイングレスゲートウェイの IP アドレスに置き換えます。ASMイングレスゲートウェイの IP アドレスを取得する方法の詳細については、「Istio リソースを使用してトラフィックをサービスの異なるバージョンにルーティングする」トピックの手順 3 のサブステップ 1 をご参照ください。

    テスト 1 からの予期される出力:

    ...
    # target 50% 4.83333
    # target 75% 5.20763
    # target 90% 5.38203
    # target 99% 5.48668
    # target 99.9% 5.49714
    Sockets used: 10 (for perfect keepalive, would be 10)
    Uniform: false, Jitter: false
    Code 201 : 70 (100.0 %)
    Response Header Sizes : count 70 avg 249.94286 +/- 0.2871 min 248 max 250 sum 17496
    Response Body/Total Sizes : count 70 avg 249.94286 +/- 0.2871 min 248 max 250 sum 17496
    All done 70 calls (plus 10 warmup) 4566.839 ms avg, 2.1 qps
    Successfully wrote 4693 bytes of Json data to 2024-07-26-232250_114_55_5_155_status_201_iZbp1cz9ur77robaiv085tZ.json

    テスト 2 からの予期される出力:

    fortio load -c 10 -qps 10000  -H "user_type:subscriber" -t 30s -timeout 60s -a http://114.55.xx.xx/status/202
    ...
    # target 50% 0.253333
    # target 75% 1.875
    # target 90% 4.26635
    # target 99% 4.47301
    # target 99.9% 4.49367
    Sockets used: 10 (for perfect keepalive, would be 10)
    Uniform: false, Jitter: false
    Code 202 : 250 (100.0 %)
    Response Header Sizes : count 250 avg 250.264 +/- 0.4408 min 250 max 251 sum 62566
    Response Body/Total Sizes : count 250 avg 250.264 +/- 0.4408 min 250 max 251 sum 62566
    All done 250 calls (plus 10 warmup) 1226.657 ms avg, 8.0 qps
    Successfully wrote 4509 bytes of Json data to 2024-07-26-232250_114_55_5_155_status_202_iZbp1cz9ur77robaiv085tZ.json

    上記の出力は、テスト 2 の平均リクエストレイテンシがテスト 1 の約 1/4 であり、QPS がテスト 1 の約 4 倍であることを示しています。これは、以前に定義されたポリシーでは、subscriber タイプのリクエストの優先度が guest タイプのリクエストの 4 倍であるためです。2 つのテストで 30 秒以内に合計 320 個のリクエストが処理されます。ウォームアップに使用された 20 個のリクエストを除くと、サービスが受信したリクエストレートは 1 秒あたりちょうど 10 リクエストです。これは、サービスが受信したリクエストが常に指定された制限内にあることを証明しています。

関連情報

Grafana で QuotaSchedulingPolicy が有効になっているかどうかを確認できます。Grafana の Prometheus インスタンスが ASM トラフィックスケジューリングスイート で構成されていることを確認する必要があります。

次の内容を Grafana にインポートして、QuotaSchedulingPolicy のダッシュボードを作成できます。

詳細を表示するにはクリックしてください

{
  "annotations": {
    "list": [
      {
        "builtIn": 1,
        "datasource": {
          "type": "grafana",
          "uid": "-- Grafana --"
        },
        "enable": true,
        "hide": true,
        "iconColor": "rgba(0, 211, 255, 1)",
        "name": "Annotations & Alerts",
        "type": "dashboard"
      }
    ]
  },
  "editable": true,
  "fiscalYearStartMonth": 0,
  "graphTooltip": 0,
  "id": 44,
  "links": [],
  "liveNow": false,
  "panels": [
    {
      "datasource": {
        "type": "prometheus",
        "uid": "${datasource}"
      },
      "description": "Signal derived from periodic execution of query: (sum(rate(workload_requests_total{policy_name=\"quotascheduling\",component_id=\"root.0\",decision_type=\"DECISION_TYPE_ACCEPTED\"}[30s])) / sum(rate(workload_requests_total{policy_name=\"quotascheduling\",component_id=\"root.0\"}[30s]))) * 100",  // 定期的に実行されるクエリから派生したシグナル: (sum(rate(workload_requests_total{policy_name=\"quotascheduling\",component_id=\"root.0\",decision_type=\"DECISION_TYPE_ACCEPTED\"}[30s])) / sum(rate(workload_requests_total{policy_name=\"quotascheduling\",component_id=\"root.0\"}[30s]))) * 100
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "palette-classic"
          },
          "custom": {
            "axisCenteredZero": false,
            "axisColorMode": "text",
            "axisLabel": "",
            "axisPlacement": "auto",
            "barAlignment": 0,
            "drawStyle": "line",
            "fillOpacity": 10,
            "gradientMode": "none",
            "hideFrom": {
              "legend": false,
              "tooltip": false,
              "viz": false
            },
            "lineInterpolation": "linear",
            "lineWidth": 1,
            "pointSize": 5,
            "scaleDistribution": {
              "type": "linear"
            },
            "showPoints": "auto",
            "spanNulls": false,
            "stacking": {
              "group": "A",
              "mode": "none"
            },
            "thresholdsStyle": {
              "mode": "off"
            }
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green",
                "value": null
              },
              {
                "color": "red",
                "value": 80
              }
            ]
          },
          "unit": ""
        },
        "overrides": []
      },
      "gridPos": {
        "h": 10,
        "w": 24,
        "x": 0,
        "y": 0
      },
      "id": 1,
      "interval": "10s",
      "options": {
        "legend": {
          "calcs": [],
          "displayMode": "list",
          "placement": "bottom",
          "showLegend": true
        },
        "tooltip": {
          "mode": "single",
          "sort": "none"
        }
      },
      "pluginVersion": "v10.1.0",
      "targets": [
        {
          "datasource": {
            "type": "prometheus",
            "uid": "${datasource}"
          },
          "editorMode": "code",
          "expr": "(sum by (policy_name)(rate(workload_requests_total{component_id=\"root.0\",decision_type=\"DECISION_TYPE_ACCEPTED\"}[30s])) / sum by (policy_name)(rate(workload_requests_total{component_id=\"root.0\"}[30s]))) * 100",
          "intervalFactor": 1,
          "legendFormat": "policy={{policy_name}}",
          "range": true,
          "refId": "A"
        }
      ],
      "title": "ACCEPT_PERCENTAGE - runs every 10s, metrics: workload_requests_total", // 承認率 - 10 秒ごとに実行、メトリック: workload_requests_total
      "type": "timeseries"
    },

    // ... (Rest of the JSON is translated similarly)
    

ダッシュボードは次のとおりです。

image