Envoy を使用したレート制限の有効化
このタスクでは、Envoy のネイティブレート制限を使用して、Istio サービスへのトラフィックを動的に制限する方法を示します。このタスクでは、サービス全体のすべてのインスタンスに対して 1 分あたり 1 リクエストを許可する、イングレスゲートウェイを介したproductpage
サービスに対するグローバルレート制限を適用します。さらに、1 分あたり 4 リクエストを許可する、個々のproductpage
インスタンスごとのローカルレート制限を適用します。このようにして、productpage
サービスがイングレスゲートウェイを介して 1 分あたり最大 1 リクエストを処理することを保証しますが、各productpage
インスタンスは最大 1 分あたり 4 リクエストを処理でき、メッシュ内のすべてのトラフィックに対応できます。
開始する前に
レート制限
Envoy は、グローバルとローカルの2種類のレート制限をサポートしています。グローバルレート制限は、メッシュ全体のレート制限を提供するためにグローバル gRPC レート制限サービスを使用します。ローカルレート制限は、サービスインスタンスごとのリクエストレートを制限するために使用されます。ローカルレート制限は、グローバルレート制限サービスへの負荷を軽減するために、グローバルレート制限と組み合わせて使用できます。
このタスクでは、グローバルとローカルの両方のレート制限を使用して、サービスへの特定のパスへのトラフィックをレート制限するように Envoy を構成します。
グローバルレート制限
Envoy は、メッシュのグローバルレート制限を設定するために使用できます。Envoy におけるグローバルレート制限は、レート制限サービスからのクォータを要求するために gRPC API を使用します。以下では、Redis バックエンドを使用した Go で記述された API のリファレンス実装を使用します。
次の ConfigMap を使用して、パス
/productpage
へのリクエストを 1 req/min、後続の高度な例で使用する値api
、その他すべてのリクエストを 100 req/min にレート制限するようにリファレンス実装を構成します。$ kubectl apply -f - <<EOF apiVersion: v1 kind: ConfigMap metadata: name: ratelimit-config data: config.yaml: | domain: ratelimit descriptors: - key: PATH value: "/productpage" rate_limit: unit: minute requests_per_unit: 1 - key: PATH value: "api" rate_limit: unit: minute requests_per_unit: 2 - key: PATH rate_limit: unit: minute requests_per_unit: 100 EOF
Envoy のレート制限サービスプロトコルを実装するグローバルレート制限サービスを作成します。リファレンスとして、Envoy が提供するリファレンス実装に基づいたデモ構成はこちらにあります。
$ kubectl apply -f @samples/ratelimit/rate-limit-service.yaml@
ingressgateway
にEnvoyFilter
を適用して、Envoy のグローバルレート制限フィルターを使用してグローバルレート制限を有効にします。このパッチは、
envoy.filters.http.ratelimit
グローバル Envoy フィルターをHTTP_FILTER
チェーンに挿入します。rate_limit_service
フィールドは、この例では外部レート制限サービスであるoutbound|8081||ratelimit.default.svc.cluster.local
を指定します。$ kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: filter-ratelimit namespace: istio-system spec: workloadSelector: # select by label in the same namespace labels: istio: ingressgateway configPatches: # The Envoy config you want to modify - applyTo: HTTP_FILTER match: context: GATEWAY listener: filterChain: filter: name: "envoy.filters.network.http_connection_manager" subFilter: name: "envoy.filters.http.router" patch: operation: INSERT_BEFORE # Adds the Envoy Rate Limit Filter in HTTP filter chain. value: name: envoy.filters.http.ratelimit typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit # domain can be anything! Match it to the ratelimter service config domain: ratelimit failure_mode_deny: true timeout: 10s rate_limit_service: grpc_service: envoy_grpc: cluster_name: outbound|8081||ratelimit.default.svc.cluster.local authority: ratelimit.default.svc.cluster.local transport_api_version: V3 EOF
レート制限を適用するルーティング構成を定義する別の
EnvoyFilter
をingressgateway
に適用します。これにより、bookinfo.com:80
という名前の仮想ホストからのすべてのルートに対してレート制限アクションが追加されます。$ kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: filter-ratelimit-svc namespace: istio-system spec: workloadSelector: labels: istio: ingressgateway configPatches: - applyTo: VIRTUAL_HOST match: context: GATEWAY routeConfiguration: vhost: name: "" route: action: ANY patch: operation: MERGE # Applies the rate limit rules. value: rate_limits: - actions: # any actions in here - request_headers: header_name: ":path" descriptor_key: "PATH" EOF
グローバルレート制限の高度なケース
この例では、/api/*
uri
に一致させるために正規表現を使用し、VirtualService http 名を使用してルートレベルで挿入されるレート制限アクションを定義します。前の例で挿入された PATH 値 api
がここで重要になります。
プレフィックス
/api/v1/products
がapi
というルートに移動するように VirtualService を変更します。$ kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: bookinfo spec: gateways: - bookinfo-gateway hosts: - '*' http: - match: - uri: exact: /productpage - uri: prefix: /static - uri: exact: /login - uri: exact: /logout route: - destination: host: productpage port: number: 9080 - match: - uri: prefix: /api/v1/products route: - destination: host: productpage port: number: 9080 name: api EOF
1から99の任意の製品について、ルートレベルでレート制限アクションを追加する EnvoyFilter を適用します。
$ kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: filter-ratelimit-svc-api namespace: istio-system spec: workloadSelector: labels: istio: ingressgateway configPatches: - applyTo: HTTP_ROUTE match: context: GATEWAY routeConfiguration: vhost: name: "*:8080" route: name: "api" patch: operation: MERGE value: route: rate_limits: - actions: - header_value_match: descriptor_key: "PATH" descriptor_value: "api" headers: - name: ":path" safe_regex_match: google_re2: {} regex: "/api/v1/products/[1-9]{1,2}" EOF
ローカルレート制限
Envoy は、L4接続およびHTTPリクエストのローカルレート制限をサポートしています。これにより、他のサービスを呼び出すことなく、プロキシ自体でインスタンスレベルでレート制限を適用できます。
次の EnvoyFilter
は、productpage
サービスを通過するすべてのトラフィックに対してローカルレート制限を有効にします。HTTP_FILTER
パッチは、envoy.filters.http.local_ratelimit
ローカル Envoy フィルターをHTTP接続マネージャーフィルターチェーンに挿入します。ローカルレート制限フィルターのトークンバケットは、4リクエスト/分を許可するように構成されています。このフィルターは、ブロックされたリクエストに x-local-rate-limit
レスポンスヘッダーを追加するように構成されています。
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: filter-local-ratelimit-svc
namespace: istio-system
spec:
workloadSelector:
labels:
app: productpage
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_INBOUND
listener:
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.local_ratelimit
typed_config:
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
type_url: type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
value:
stat_prefix: http_local_rate_limiter
token_bucket:
max_tokens: 4
tokens_per_fill: 4
fill_interval: 60s
filter_enabled:
runtime_key: local_rate_limit_enabled
default_value:
numerator: 100
denominator: HUNDRED
filter_enforced:
runtime_key: local_rate_limit_enforced
default_value:
numerator: 100
denominator: HUNDRED
response_headers_to_add:
- append: false
header:
key: x-local-rate-limit
value: 'true'
EOF
上記の構成は、すべての仮想ホスト/ルートにローカルレート制限を適用します。あるいは、特定のルートに制限することもできます。
次の EnvoyFilter
は、productpage
サービスのポート 9080 へのすべてのトラフィックに対してローカルレート制限を有効にします。前の構成とは異なり、HTTP_FILTER
パッチには token_bucket
は含まれていません。token_bucket
は、代わりに、仮想ホスト inbound|http|9080
へのルートに対して、envoy.filters.http.local_ratelimit
ローカル Envoy フィルターの typed_per_filter_config
を含む2番目(HTTP_ROUTE
)のパッチで定義されています。
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: filter-local-ratelimit-svc
namespace: istio-system
spec:
workloadSelector:
labels:
app: productpage
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_INBOUND
listener:
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.local_ratelimit
typed_config:
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
type_url: type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
value:
stat_prefix: http_local_rate_limiter
- applyTo: HTTP_ROUTE
match:
context: SIDECAR_INBOUND
routeConfiguration:
vhost:
name: "inbound|http|9080"
route:
action: ANY
patch:
operation: MERGE
value:
typed_per_filter_config:
envoy.filters.http.local_ratelimit:
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
type_url: type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
value:
stat_prefix: http_local_rate_limiter
token_bucket:
max_tokens: 4
tokens_per_fill: 4
fill_interval: 60s
filter_enabled:
runtime_key: local_rate_limit_enabled
default_value:
numerator: 100
denominator: HUNDRED
filter_enforced:
runtime_key: local_rate_limit_enforced
default_value:
numerator: 100
denominator: HUNDRED
response_headers_to_add:
- append: false
header:
key: x-local-rate-limit
value: 'true'
EOF
結果の検証
グローバルレート制限の検証
Bookinfo サンプルにトラフィックを送信します。Webブラウザーで http://$GATEWAY_URL/productpage
にアクセスするか、次のコマンドを発行します。
$ for i in {1..2}; do curl -s "http://$GATEWAY_URL/productpage" -o /dev/null -w "%{http_code}\n"; sleep 3; done
200
429
$ for i in {1..3}; do curl -s "http://$GATEWAY_URL/api/v1/products/${i}" -o /dev/null -w "%{http_code}\n"; sleep 3; done
200
200
429
/productpage
の場合、最初の要求は通過しますが、1分以内の後続のすべての要求は429応答を受け取ります。また、/api/v1/products/*
の場合は、1分以内に429応答を受け取るまで、1〜99の間の任意の数を使用して2回ヒットする必要があります。
ローカルレート制限の検証
ingress gateway のグローバルレート制限では productpage
サービスへのリクエストを 1 req/min に制限していますが、productpage
インスタンスのローカルレート制限では 4 req/min が許可されています。これを確認するには、次の curl
コマンドを使用して、ratings
ポッドから内部 productpage
リクエストを送信します。
$ kubectl exec "$(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}')" -c ratings -- bash -c 'for i in {1..5}; do curl -s productpage:9080/productpage -o /dev/null -w "%{http_code}\n"; sleep 1; done'
200
200
200
200
429
productpage
インスタンスごとに、4 req/min を超えるリクエストは通過しないはずです。
クリーンアップ
$ kubectl delete envoyfilter filter-ratelimit -nistio-system
$ kubectl delete envoyfilter filter-ratelimit-svc -nistio-system
$ kubectl delete envoyfilter filter-ratelimit-svc-api -nistio-system
$ kubectl delete envoyfilter filter-local-ratelimit-svc -nistio-system
$ kubectl delete cm ratelimit-config
$ kubectl delete -f @samples/ratelimit/rate-limit-service.yaml@