Carpe Diem

備忘録

Envoy の Adaptive Concurrency で動的に流量制限をする

概要

christina04.hatenablog.com

で説明したように、Circuit Breakerのパラメータ調整は大変です。

実際のキャパシティよりパラメータを小さくしてしまうと、リソースはまだ余裕があるのにOpenしていまいます。
逆にパラメータが大きすぎると、Openが遅れサービス側に過負荷がかかってしまいます。

また一度適切に設定してからも、実装によってキャパシティが変更されればそれに合わせてCircuit Breaker側も変更が必要になります。

そのようなCircuit Breakerの課題を解決するものとしてAdaptive Concurrencyというものがあるので紹介します。

環境

  • Envoy v1.22.0

Adaptive Concurrency Filter

概念

Concurrencyは並行して処理可能な数ですが、それを超えるリクエストの場合はqueueに入れられます。

なので同時に受け付けることのできるin-flight requestsは

 concurrency \ size + queue \ size

となります。

in-flight requestsのキャパシティ計算にはリトルの法則を用いるのが一般的です。

inflight \ requests =throughput * RTT

スループットはスケールアップやスケールアウトなどでシステムを増強しない限り固定なので、in-flight requestsが増えれば増えるほどリクエストは段々と捌けなくなりレイテンシ(RTT)が悪化します。
レイテンシが悪化が進めばリクエストはどんどんTimeoutします。そして最終的には詰まったリクエストによってOOMなどが発生します。

そこでin-flight requestsの流量を、レイテンシの変化に基づいて動的に制限しようというのがAdaptive Concurrencyです。

gradient

以下の式からレイテンシの変化をgradientとして定期的にチェックします。

 gradient = RTT_{noload} \div RTT_{actual}

負荷がないときの値÷現在値で出すということですね。

Envoyでは

  • RTT_noload = minRTT
  • RTT_actual = sampleRTT

と表現しています。

このgradientの値がどう変化するかによって、limitの値を変えていきます。

gradient値 状態
1 queueに余裕がありlimitを増やすことができる
1未満 過剰なqueueが形成されたため、limitを減らす必要がある

具体的な式は以下のようになります。

 Limit_{new} = Limit_{current} \times gradient + headroom

headroomは制限がどれだけ速く増加するかを決定するために使用されます。
Envoyでは調整できません。

Envoyの設定

すでにfilterが用意されているので、それをHTTP Filter Chainに追加します。

http_filters:
  - name: envoy.filters.http.adaptive_concurrency
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.http.adaptive_concurrency.v3.AdaptiveConcurrency
      gradient_controller_config:
        sample_aggregate_percentile:
          value: 90
        concurrency_limit_params:
          concurrency_update_interval: 0.1s
        min_rtt_calc_params:
          jitter:
            value: 10
          min_concurrency: 30
          interval: 30s
          request_count: 25
      enabled:
        default_value: true
        runtime_key: "adaptive_concurrency.enabled"
  - name: envoy.filters.http.router
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

パラメータ

触りそうなパラメータについて説明します。

パラメータ 説明
concurrency_update_interval sampleRTTを収集してLimitを更新するインターバル
sample_aggregate_percentile ↑の期間で収集されたレイテンシのpercentileを使用
min_rtt_calc_params.min_concurrency minRTTとsampleRTTで計算されるconcurrency limitの下限を設定する。
concurrencyが小さくなりすぎる問題を防ぐ
min_rtt_calc_params.interval minRTTを再計算するインターバル
min_rtt_calc_params.jitter minRTTの再計算を開始する際のランダムな遅延
min_rtt_calc_params.request_count request_count分リクエストサンプルを収集してminRTTを計算する。p90を使用する

ref: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/adaptive_concurrency_filter#runtime

動作検証

シナリオ

以下のシナリオで負荷をかけてみます。

  • 数分間キャパシティ以内のリクエストを流す
  • 次に1分間キャパシティを超えるリクエストを流す
  • 最後に数分間キャパシティ以内のリクエストを流す

結果

20rpsで一定に返していることが分かります。
そしてシナリオ通り途中からリクエストが増え、キャパシティを超えたことでdurationが急増していることが分かります。

しかしAdaptive Concurrency Filterにより、リクエストが遮断され503が返ることで、backend serviceに過負荷がかからないようになっています。

前述の計算式から並行で捌けるリクエストを絞っていることが確認できます。

その他

サンプルコード

今回の検証コードはこちら

github.com

KubeCon + CloudNativeCon Europe 2020での解説動画

こちらの動画で分かりやすく説明されています。

www.youtube.com

まとめ

Circuit Breakerの課題であるパラメータ調整を、レイテンシに応じて自動的に調整できるAdaptive Concurrencyを検証してみました。

IstioおよびAnthos Service Meshではまだ使えませんが、利用できるようになったらぜひ導入したいですね。

参考