使用 Envoy 启用速率限制

此任务向您展示如何使用 Envoy 的本地速率限制来动态地将流量限制到 Istio 服务。 在本任务中,您将通过允许的入口网关为 productpage 服务应用全局速率限制在服务的所有实例中, 每分钟 1 次请求。此外,您将为每个项目应用一个本地速率限制,允许 productpage 实例每分钟处理 4 个请求。 通过这种方式,您将确保 productpage 服务通过入口网关每分钟最多处理一个请求,但是每个 productpage 实例可以处理每分钟最多 4 个请求,允许任何网格内通信。

开始之前

  1. 参照安装指南,在 Kubernetes 集群中安装 Istio。

  2. 部署 Bookinfo 示例应用程序。

速率限制

Envoy 支持两种速率限制:全局和本地。全局速率使用全局 gRPC 速率限制服务为整个网格提供速率限制。 本地速率限制用于限制每个服务实例的请求速率。本地速率限制可以与全局速率限制一起使用, 以减少负载全局速率限制服务。

在本任务中,您将使用全局速率限制和本地速率限制来配置 Envoy,以对特定路径的服务流量进行速率限制。

全局速率限制

Envoy 可以为您的网格设置全局速率限制。 Envoy 中的全局速率限制使用 gRPC API 向速率限制服务请求配额。 下文使用的参考实现后端,是用 Go 语言编写并使用 Redis 作为缓存。

  1. 参考下面的 ConfigMap 配置限流规则, 将 /productpage 的限制速率为每分钟 1 次,用于后续高级示例的 api 值, 并将其他所有请求的速率限制为每分钟 100 次。

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: v1
    3. kind: ConfigMap
    4. metadata:
    5. name: ratelimit-config
    6. data:
    7. config.yaml: |
    8. domain: ratelimit
    9. descriptors:
    10. - key: PATH
    11. value: "/productpage"
    12. rate_limit:
    13. unit: minute
    14. requests_per_unit: 1
    15. - key: PATH
    16. value: "api"
    17. rate_limit:
    18. unit: minute
    19. requests_per_unit: 2
    20. - key: PATH
    21. rate_limit:
    22. unit: minute
    23. requests_per_unit: 100
    24. EOF
  2. 创建一个全局速率限制服务,它实现 Envoy 的速率限制服务协议。 作为参考,可以在这里找到一个演示配置, 它是基于 Envoy 提供的参考实现

    Zip

    1. $ kubectl apply -f @samples/ratelimit/rate-limit-service.yaml@
  3. ingressgateway 应用 EnvoyFilter,以使用 Envoy 的全局速率限制过滤器来启用全局速率限制。

    此 patch 将 envoy.filters.http.ratelimit Envoy 全局限流过滤器插入到 HTTP_FILTER 链中。rate_limit_service 字段指定外部速率限制服务,在本例中为 outbound|8081||ratelimit.default.svc.cluster.local

    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. # 同一个命名空间内通过 label 匹配
    10. labels:
    11. istio: ingressgateway
    12. configPatches:
    13. # 您要修改的 Envoy 配置
    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. # 在 HTTP 过滤器链中添加 Envoy 速率限制过滤器。
    26. value:
    27. name: envoy.filters.http.ratelimit
    28. typed_config:
    29. "@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit
    30. # 域名可以是任何东西!将其与 ratelitter 服务配置相匹配
    31. domain: ratelimit
    32. failure_mode_deny: true
    33. timeout: 10s
    34. rate_limit_service:
    35. grpc_service:
    36. envoy_grpc:
    37. cluster_name: outbound|8081||ratelimit.default.svc.cluster.local
    38. authority: ratelimit.default.svc.cluster.local
    39. transport_api_version: V3
    40. EOF
  4. 对定义限速路由配置的 ingressgateway 应用另一个 EnvoyFilter。 对于来自名为 bookinfo.com: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: ""
    18. route:
    19. action: ANY
    20. patch:
    21. operation: MERGE
    22. # 应用速率限制规则。
    23. value:
    24. rate_limits:
    25. - actions: # 此处的任何操作
    26. - request_headers:
    27. header_name: ":path"
    28. descriptor_key: "PATH"
    29. EOF

全局速率限制高级示例

此示例使用正则表达式来匹配 /api/* uri 并使用 VirtualService http 名称来定义在路由级别插入的速率限制操作。 前面示例中插入的 PATH 值 api 开始发挥作用。

  1. 更改 VirtualService,前缀 /api/v1/products 被移动到名为 api 的路由中:

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1
    3. kind: VirtualService
    4. metadata:
    5. name: bookinfo
    6. spec:
    7. gateways:
    8. - bookinfo-gateway
    9. hosts:
    10. - '*'
    11. http:
    12. - match:
    13. - uri:
    14. exact: /productpage
    15. - uri:
    16. prefix: /static
    17. - uri:
    18. exact: /login
    19. - uri:
    20. exact: /logout
    21. route:
    22. - destination:
    23. host: productpage
    24. port:
    25. number: 9080
    26. - match:
    27. - uri:
    28. prefix: /api/v1/products
    29. route:
    30. - destination:
    31. host: productpage
    32. port:
    33. number: 9080
    34. name: api
    35. EOF
  2. 应用 EnvoyFilter 在结果为 1 到 99 的任一路由级别添加速率限制操作:

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: EnvoyFilter
    4. metadata:
    5. name: filter-ratelimit-svc-api
    6. namespace: istio-system
    7. spec:
    8. workloadSelector:
    9. labels:
    10. istio: ingressgateway
    11. configPatches:
    12. - applyTo: HTTP_ROUTE
    13. match:
    14. context: GATEWAY
    15. routeConfiguration:
    16. vhost:
    17. name: "*:8080"
    18. route:
    19. name: "api"
    20. patch:
    21. operation: MERGE
    22. value:
    23. route:
    24. rate_limits:
    25. - actions:
    26. - header_value_match:
    27. descriptor_key: "PATH"
    28. descriptor_value: "api"
    29. headers:
    30. - name: ":path"
    31. safe_regex_match:
    32. google_re2: {}
    33. regex: "/api/v1/products/[1-9]{1,2}"
    34. EOF

本地速率限制

Envoy 支持 L4 连接和 HTTP 请求的 本地速率限制

这允许您在代理本身的实例级应用速率限制,而无需调用任何其他服务。

下面的 EnvoyFilter 为通过 productpage 服务的任何流量启用了本地速率限制。 HTTP_FILTER patch 会插入 envoy.filters.http.local_ratelimit 本地 Envoy 过滤器 到 HTTP 连接管理器过滤器链中。本地速率限制过滤器的令牌桶 被配置为允许每分钟 4 个请求。该过滤器还配置为添加 x-local-rate-limit 响应头到被阻塞的请求。

Envoy 速率限制页面 中提到的统计数据默认是禁用的,您可以在部署期间使用以下注解启用它们:

  1. template:
  2. metadata:
  3. annotations:
  4. proxy.istio.io/config: |-
  5. proxyStatsMatcher:
  6. inclusionRegexps:
  7. - ".*http_local_rate_limit.*"
  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. match:
  14. context: SIDECAR_INBOUND
  15. listener:
  16. filterChain:
  17. filter:
  18. name: "envoy.filters.network.http_connection_manager"
  19. patch:
  20. operation: INSERT_BEFORE
  21. value:
  22. name: envoy.filters.http.local_ratelimit
  23. typed_config:
  24. "@type": type.googleapis.com/udpa.type.v1.TypedStruct
  25. type_url: type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
  26. value:
  27. stat_prefix: http_local_rate_limiter
  28. token_bucket:
  29. max_tokens: 4
  30. tokens_per_fill: 4
  31. fill_interval: 60s
  32. filter_enabled:
  33. runtime_key: local_rate_limit_enabled
  34. default_value:
  35. numerator: 100
  36. denominator: HUNDRED
  37. filter_enforced:
  38. runtime_key: local_rate_limit_enforced
  39. default_value:
  40. numerator: 100
  41. denominator: HUNDRED
  42. response_headers_to_add:
  43. - append: false
  44. header:
  45. key: x-local-rate-limit
  46. value: 'true'
  47. EOF

上述配置对所有 vhosts/routes 都进行本地速率限制。或者,您可以将其限制为特定的路由。

下面的 EnvoyFilterproductpage 服务的 9080 端口的任何流量启用了本地速率限制。 与前面的配置不同,HTTP_FILTER patch 中不包含 token_buckettoken_bucket 被定义在第二个(HTTP_ROUTE)patch 中,其中包含 envoy.filters.http.local_ratelimit 本地 Envoy 过滤器所用的 typed_per_filter_config,用于路由到虚拟主机 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. match:
  14. context: SIDECAR_INBOUND
  15. listener:
  16. filterChain:
  17. filter:
  18. name: "envoy.filters.network.http_connection_manager"
  19. patch:
  20. operation: INSERT_BEFORE
  21. value:
  22. name: envoy.filters.http.local_ratelimit
  23. typed_config:
  24. "@type": type.googleapis.com/udpa.type.v1.TypedStruct
  25. type_url: type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
  26. value:
  27. stat_prefix: http_local_rate_limiter
  28. - applyTo: HTTP_ROUTE
  29. match:
  30. context: SIDECAR_INBOUND
  31. routeConfiguration:
  32. vhost:
  33. name: "inbound|http|9080"
  34. route:
  35. action: ANY
  36. patch:
  37. operation: MERGE
  38. value:
  39. typed_per_filter_config:
  40. envoy.filters.http.local_ratelimit:
  41. "@type": type.googleapis.com/udpa.type.v1.TypedStruct
  42. type_url: type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
  43. value:
  44. stat_prefix: http_local_rate_limiter
  45. token_bucket:
  46. max_tokens: 4
  47. tokens_per_fill: 4
  48. fill_interval: 60s
  49. filter_enabled:
  50. runtime_key: local_rate_limit_enabled
  51. default_value:
  52. numerator: 100
  53. denominator: HUNDRED
  54. filter_enforced:
  55. runtime_key: local_rate_limit_enforced
  56. default_value:
  57. numerator: 100
  58. denominator: HUNDRED
  59. response_headers_to_add:
  60. - append: false
  61. header:
  62. key: x-local-rate-limit
  63. value: 'true'
  64. EOF

验证结果

验证全局速率限制

向 Bookinfo 示例发送通信流。在您的浏览器访问 http://$GATEWAY_URL/productpage 网站或使用以下命令:

  1. $ for i in {1..2}; do curl -s "http://$GATEWAY_URL/productpage" -o /dev/null -w "%{http_code}\n"; sleep 3; done
  2. 200
  3. 429
  1. $ 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
  2. 200
  3. 200
  4. 429

$GATEWAY_URL 是在 Bookinfo 示例中设置的值。

对于 /productpage,您将看到第一个请求通过,但随后的每个请求都将在一分钟内得到 429 响应。 而对于 /api/v1/products/*,您需要触发两次 1 到 99 之间的任意数字,直到在一分钟内收到 429 响应。

验证本地速率限制

虽然入口网关的全局速率限制将对 productpage 服务的请求限制在每分钟 1 个请求,但 productpage 实例的本地速率限制允许每分钟 4 个请求。

为了确认这一点,使用下面的 curl 命令从 ratings Pod 发送内部 productpage 请求:

  1. $ 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'
  2. 200
  3. 200
  4. 200
  5. 200
  6. 429

您应该看到每个 productpage 实例的请求次数每分钟不超过 4 个。

清理

Zip

  1. $ kubectl delete envoyfilter filter-ratelimit -nistio-system
  2. $ kubectl delete envoyfilter filter-ratelimit-svc -nistio-system
  3. $ kubectl delete envoyfilter filter-ratelimit-svc-api -nistio-system
  4. $ kubectl delete envoyfilter filter-local-ratelimit-svc -nistio-system
  5. $ kubectl delete cm ratelimit-config
  6. $ kubectl delete -f @samples/ratelimit/rate-limit-service.yaml@