使用 Envoy 启用速率限制

此任务向您展示如何使用 Envoy 的原生速率限制来动态限制 Istio 服务的流量。在此任务中,您将通过入口网关为 productpage 服务应用全局速率限制,该限制允许每分钟跨服务所有实例发送 1 个请求。此外,您还将为每个单独的 productpage 实例应用本地速率限制,该限制将允许每分钟发送 4 个请求。这样,您可以确保 productpage 服务通过入口网关每分钟处理最多 1 个请求,但每个 productpage 实例每分钟最多可以处理 4 个请求,从而允许任何网内流量。

开始之前

  1. 按照安装指南中的说明在 Kubernetes 集群中设置 Istio。

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

速率限制

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

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

全局速率限制

Envoy 可用于 设置服务网格的全局速率限制。Envoy 中的全局速率限制使用 gRPC API 从速率限制服务请求配额。下面使用了 API 的参考实现,该实现使用 Go 编写并使用 Redis 作为后端。

  1. 使用以下 ConfigMap 配置参考实现,以限制对路径 /productpage 的请求速率为每分钟 1 个请求,为即将到来的高级示例设置值 api,并将所有其他请求的速率限制为每分钟 100 个请求。

    $ 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
    
  2. 创建一个全局速率限制服务,该服务实现 Envoy 的 速率限制服务协议。作为参考,可以在 此处 找到一个演示配置,该配置基于 Envoy 提供的 参考实现

    压缩
    $ kubectl apply -f @samples/ratelimit/rate-limit-service.yaml@
    
  3. EnvoyFilter 应用于 ingressgateway 以使用 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
    
  4. 将另一个 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 将发挥作用。

  1. 更改 VirtualService,以便将前缀 /api/v1/products 移动到名为 api 的路由。

    $ 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
    
  2. 应用 EnvoyFilter 以在任何 1 到 99 产品的路由级别添加速率限制操作。

    $ 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_buckettoken_bucket 而是定义在第二个(HTTP_ROUTE)补丁中,该补丁包含 envoy.filters.http.local_ratelimit 本地 Envoy 过滤器的 typed_per_filter_config,用于路由到虚拟主机 inbound|http|9080

$ 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,您将看到第一个请求通过,但在一分钟内之后的每个请求都将收到 429 响应。对于 /api/v1/products/*,您需要点击两次,并在 1 到 99 之间使用任何数字,直到在一分钟内收到 429 响应。

验证本地速率限制

尽管入口网关处的全局速率限制将对 productpage 服务的请求限制为每分钟 1 个请求,但 productpage 实例的本地速率限制允许每分钟 4 个请求。要确认这一点,请使用以下 curl 命令从 ratings Pod 发送内部 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 个请求通过。

清理

压缩
$ 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@
这些信息是否有用?
您是否有任何改进建议?

感谢您的反馈!