Enabling Rate Limits using Envoy

This task shows you how to use Envoy’s native rate limiting to dynamically limit the traffic to an Istio service. In this task, you will apply a global rate-limit for the productpage service through ingress gateway that allows 1 requests per minute across all instances of the service. Additionally, you will apply a local rate-limit for each individual productpage instance that will allow 10 requests per minute. In this way, you will ensure that the productpage service handles a maximum of 1 requests per minute through instance gateway, but each productpage instance can handle up to 10 requests per minute, allowing for any in-mesh traffic.

Before you begin

  1. Setup Istio in a Kubernetes cluster by following the instructions in the Installation Guide.

  2. Deploy the Bookinfo sample application.

Rate limits

Envoy supports two kinds of rate limiting: global and local. Global rate limiting uses a global gRPC rate limiting service to provide rate limiting for the entire mesh. Local rate limiting is used to limit the rate of requests per service instance. Local rate limiting can be used in conjunction with global rate limiting to reduce load on the global rate limiting service.

In this task you will configure Envoy to rate limit traffic to a specific path of a service using both global and local rate limits.

Global rate limit

Envoy can be used to set up global rate limits for your mesh. Global rate limiting in Envoy uses a gRPC API for requesting quota from a rate limiting service. A reference implementation of the API, written in Go with a Redis backend, is used below.

  1. Use the following configmap to configure the reference implementation to rate limit requests to the path /productpage at 1 req/min and all other requests at 100 req/min.

    1. apiVersion: v1
    2. kind: ConfigMap
    3. metadata:
    4. name: ratelimit-config
    5. data:
    6. config.yaml: |
    7. domain: productpage-ratelimit
    8. descriptors:
    9. - key: PATH
    10. value: "/productpage"
    11. rate_limit:
    12. unit: minute
    13. requests_per_unit: 1
    14. - key: PATH
    15. rate_limit:
    16. unit: minute
    17. requests_per_unit: 100
  2. Apply an EnvoyFilter to the ingressgateway to enable global rate limiting using Envoy’s global rate limit filter.

    The first patch inserts the envoy.filters.http.ratelimit global envoy filter filter into the HTTP_FILTER chain. The rate_limit_service field specifies the external rate limit service, rate_limit_cluster in this case.

    The second patch defines the rate_limit_cluster, which provides the endpoint location of the external rate limit service.

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: EnvoyFilter
    4. metadata:
    5. name: filter-ratelimit
    6. namespace: istio-system
    7. spec:
    8. workloadSelector:
    9. # select by label in the same namespace
    10. labels:
    11. istio: ingressgateway
    12. configPatches:
    13. # The Envoy config you want to modify
    14. - applyTo: HTTP_FILTER
    15. match:
    16. context: GATEWAY
    17. listener:
    18. filterChain:
    19. filter:
    20. name: "envoy.filters.network.http_connection_manager"
    21. subFilter:
    22. name: "envoy.filters.http.router"
    23. patch:
    24. operation: INSERT_BEFORE
    25. # Adds the Envoy Rate Limit Filter in HTTP filter chain.
    26. value:
    27. name: envoy.filters.http.ratelimit
    28. typed_config:
    29. "@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit
    30. # domain can be anything! Match it to the ratelimter service config
    31. domain: productpage-ratelimit
    32. failure_mode_deny: true
    33. rate_limit_service:
    34. grpc_service:
    35. envoy_grpc:
    36. cluster_name: rate_limit_cluster
    37. timeout: 10s
    38. transport_api_version: V3
    39. - applyTo: CLUSTER
    40. match:
    41. cluster:
    42. service: ratelimit.default.svc.cluster.local
    43. patch:
    44. operation: ADD
    45. # Adds the rate limit service cluster for rate limit service defined in step 1.
    46. value:
    47. name: rate_limit_cluster
    48. type: STRICT_DNS
    49. connect_timeout: 10s
    50. lb_policy: ROUND_ROBIN
    51. http2_protocol_options: {}
    52. load_assignment:
    53. cluster_name: rate_limit_cluster
    54. endpoints:
    55. - lb_endpoints:
    56. - endpoint:
    57. address:
    58. socket_address:
    59. address: ratelimit.default.svc.cluster.local
    60. port_value: 8081
    61. EOF
  3. Apply another EnvoyFilter to the ingressgateway that defines the route configuration on which to rate limit. This adds rate limit actions for any route from a virtual host named *.80.

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: EnvoyFilter
    4. metadata:
    5. name: filter-ratelimit-svc
    6. namespace: istio-system
    7. spec:
    8. workloadSelector:
    9. labels:
    10. istio: ingressgateway
    11. configPatches:
    12. - applyTo: VIRTUAL_HOST
    13. match:
    14. context: GATEWAY
    15. routeConfiguration:
    16. vhost:
    17. name: "*:80"
    18. route:
    19. action: ANY
    20. patch:
    21. operation: MERGE
    22. # Applies the rate limit rules.
    23. value:
    24. rate_limits:
    25. - actions: # any actions in here
    26. - request_headers:
    27. header_name: ":path"
    28. descriptor_key: "PATH"
    29. EOF

Local rate limit

Envoy supports local rate limiting of L4 connections and HTTP requests. This allows you to apply rate limits at the instance level, in the proxy itself, without calling any other service.

The following EnvoyFilter enables local rate limiting for any traffic through the productpage service. The HTTP_FILTER patch inserts the envoy.filters.http.local_ratelimit local envoy filter into the HTTP connection manager filter chain. The local rate limit filter’s token bucket is configured to allow 10 requests/min. The filter is also configured to add an x-local-rate-limit response header to requests that are blocked.

  1. $ kubectl apply -f - <<EOF
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: EnvoyFilter
  4. metadata:
  5. name: filter-local-ratelimit-svc
  6. namespace: istio-system
  7. spec:
  8. workloadSelector:
  9. labels:
  10. app: productpage
  11. configPatches:
  12. - applyTo: HTTP_FILTER
  13. listener:
  14. filterChain:
  15. filter:
  16. name: "envoy.filters.network.http_connection_manager"
  17. patch:
  18. operation: INSERT_BEFORE
  19. value:
  20. name: envoy.filters.http.local_ratelimit
  21. typed_config:
  22. "@type": type.googleapis.com/udpa.type.v1.TypedStruct
  23. type_url: type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
  24. value:
  25. stat_prefix: http_local_rate_limiter
  26. token_bucket:
  27. max_tokens: 10
  28. tokens_per_fill: 10
  29. fill_interval: 60s
  30. filter_enabled:
  31. runtime_key: local_rate_limit_enabled
  32. default_value:
  33. numerator: 100
  34. denominator: HUNDRED
  35. filter_enforced:
  36. runtime_key: local_rate_limit_enforced
  37. default_value:
  38. numerator: 100
  39. denominator: HUNDRED
  40. response_headers_to_add:
  41. - append: false
  42. header:
  43. key: x-local-rate-limit
  44. value: 'true'
  45. EOF

The above configuration applies local rate limiting to all vhosts/routes. Alternatively, you can restrict it to a specific route.

The following EnvoyFilter enables local rate limiting for any traffic to port 80 of the productpage service. Unlike the previous configuration, there is no token_bucket included in the HTTP_FILTER patch. The token_bucket is instead defined in the second (HTTP_ROUTE) patch which includes a typed_per_filter_config for the envoy.filters.http.local_ratelimit local envoy filter, for routes to virtual host inbound|http|9080.

  1. $ kubectl apply -f - <<EOF
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: EnvoyFilter
  4. metadata:
  5. name: filter-local-ratelimit-svc
  6. namespace: istio-system
  7. spec:
  8. workloadSelector:
  9. labels:
  10. app: productpage
  11. configPatches:
  12. - applyTo: HTTP_FILTER
  13. listener:
  14. filterChain:
  15. filter:
  16. name: "envoy.filters.network.http_connection_manager"
  17. patch:
  18. operation: INSERT_BEFORE
  19. value:
  20. name: envoy.filters.http.local_ratelimit
  21. typed_config:
  22. "@type": type.googleapis.com/udpa.type.v1.TypedStruct
  23. type_url: type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
  24. value:
  25. stat_prefix: http_local_rate_limiter
  26. - applyTo: HTTP_ROUTE
  27. match:
  28. context: SIDECAR_OUTBOUND
  29. routeConfiguration:
  30. vhost:
  31. name: "inbound|http|9080"
  32. route:
  33. action: ANY
  34. patch:
  35. operation: MERGE
  36. value:
  37. typed_per_filter_config:
  38. envoy.filters.http.local_ratelimit:
  39. "@type": type.googleapis.com/udpa.type.v1.TypedStruct
  40. type_url: type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
  41. value:
  42. stat_prefix: http_local_rate_limiter
  43. token_bucket:
  44. max_tokens: 10
  45. tokens_per_fill: 10
  46. fill_interval: 60s
  47. filter_enabled:
  48. runtime_key: local_rate_limit_enabled
  49. default_value:
  50. numerator: 100
  51. denominator: HUNDRED
  52. filter_enforced:
  53. runtime_key: local_rate_limit_enforced
  54. default_value:
  55. numerator: 100
  56. denominator: HUNDRED
  57. response_headers_to_add:
  58. - append: false
  59. header:
  60. key: x-local-rate-limit
  61. value: 'true'
  62. EOF

Verify the results

Verify global rate limit

Send traffic to the Bookinfo sample. Visit http://$GATEWAY_URL/productpage in your web browser or issue the following command:

  1. $ curl "http://$GATEWAY_URL/productpage"

$GATEWAY_URL is the value set in the Bookinfo example.

You will see the first request go through but every following request within a minute will get a 429 response.

Verify local rate limit

Although the global rate limit at the ingress gateway limits requests to the productpage service at 1 req/min, the local rate limit for productpage instances allows 10 req/min. To confirm this, send internal productpage requests, from the ratings pod, using the following curl command:

  1. $ kubectl exec "$(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}')" -c ratings -- curl -sS productpage:9080/productpage | grep -o "<title>.*</title>"
  2. <title>Simple Bookstore App</title>

You should see no more than 10 req/min go through per productpage instance.

See also

App Identity and Access Adapter

Using Istio to secure multi-cloud Kubernetes applications with zero code changes.

Mixer and the SPOF Myth

Improving availability and reducing latency.

Mixer Adapter Model

Provides an overview of Mixer’s plug-in architecture.

Observability

Describes the telemetry and monitoring features provided by Istio.

Security

Describes Istio’s authorization and authentication functionality.