Habilitar Límites de Tasa usando Envoy

Esta tarea muestra cómo usar la limitación de tasa nativa de Envoy para limitar dinámicamente el tráfico a un service de Istio. En esta tarea, aplicará un límite de tasa global para el service productpage a través del ingress gateway que permite 1 solicitud por minuto en todas las instancias del service. Además, aplicará un límite de tasa local para cada instancia individual de productpage que permitirá 4 solicitudes por minuto. De esta manera, se asegurará de que el service productpage maneje un máximo de 1 solicitud por minuto a través del ingress gateway, pero cada instancia de productpage puede manejar hasta 4 solicitudes por minuto, permitiendo cualquier tráfico dentro de la malla.

Antes de empezar

  1. Configure Istio en un cluster de Kubernetes siguiendo las instrucciones de la Guía de Instalación.

  2. Despliegue la application de ejemplo Bookinfo.

Límites de tasa

Envoy admite dos tipos de limitación de tasa: global y local. La limitación de tasa global utiliza un service de limitación de tasa gRPC global para proporcionar limitación de tasa para toda la malla. La limitación de tasa local se utiliza para limitar la tasa de solicitudes por instancia de service. La limitación de tasa local se puede utilizar junto con la limitación de tasa global para reducir la carga en el service de limitación de tasa global.

En esta tarea, configurará Envoy para limitar la tasa de tráfico a una ruta específica de un service utilizando límites de tasa tanto globales como locales.

Límite de tasa global

Envoy se puede utilizar para configurar límites de tasa globales para su malla. La limitación de tasa global en Envoy utiliza una API gRPC para solicitar cuota de un service de limitación de tasa. A continuación se utiliza una implementación de referencia de la API, escrita en Go con un backend de Redis.

  1. Utilice el siguiente configmap para configurar la implementación de referencia para limitar la tasa de solicitudes a la ruta /productpage a 1 solicitud/min, un valor api para el siguiente ejemplo avanzado, y todas las demás solicitudes a 100 solicitudes/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
  2. Cree un service de límite de tasa global que implemente el protocolo de service de límite de tasa de Envoy. Como referencia, se puede encontrar una configuración de demostración aquí, que se basa en una implementación de referencia proporcionada por Envoy.

    Zip
    $ kubectl apply -f @samples/ratelimit/rate-limit-service.yaml@
  3. Aplique un EnvoyFilter al ingressgateway para habilitar la limitación de tasa global utilizando el filtro de límite de tasa global de Envoy.

    El parche inserta el filtro global de Envoy envoy.filters.http.ratelimit en la cadena HTTP_FILTER. El campo rate_limit_service especifica el service de límite de tasa externo, outbound|8081||ratelimit.default.svc.cluster.local en este caso.

    $ 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. Aplique otro EnvoyFilter al ingressgateway que defina la configuración de ruta en la que se aplicará el límite de tasa. Esto agrega acciones de límite de tasa para cualquier ruta de un virtual host llamado 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

Caso avanzado de límite de tasa global

Este ejemplo utiliza regex para coincidir con /api/* uri y define una acción de límite de tasa insertada a nivel de ruta utilizando el nombre http de VirtualService. El valor PATH api insertado en el ejemplo anterior entra en juego.

  1. Cambie VirtualService para que el prefijo /api/v1/products se mueva a una ruta llamada 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. Aplique un EnvoyFilter para agregar la acción de límites de tasa a nivel de ruta en cualquier producto del 1 al 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

Límite de tasa local

Envoy admite la limitación de tasa local de conexiones L4 y solicitudes HTTP. Esto le permite aplicar límites de tasa a nivel de instancia, en el propio proxy, sin llamar a ningún otro service.

El siguiente EnvoyFilter habilita la limitación de tasa local para cualquier tráfico a través del service productpage. El parche HTTP_FILTER inserta el filtro local de Envoy envoy.filters.http.local_ratelimit en la cadena de filtros del administrador de conexiones HTTP. El cubo de tokens del filtro de límite de tasa local está configurado para permitir 4 solicitudes/min. El filtro también está configurado para agregar una cabecera de respuesta x-local-rate-limit a las solicitudes que se bloquean.

$ 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

La configuración anterior aplica la limitación de tasa local a todos los vhosts/rutas. Alternativamente, puede restringirla a una ruta específica.

El siguiente EnvoyFilter habilita la limitación de tasa local para cualquier tráfico al puerto 9080 del service productpage. A diferencia de la configuración anterior, no hay token_bucket incluido en el parche HTTP_FILTER. El token_bucket se define en el segundo parche (HTTP_ROUTE) que incluye un typed_per_filter_config para el filtro local de Envoy envoy.filters.http.local_ratelimit, para rutas al virtual host 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

Verificar los resultados

Verificar el límite de tasa global

Envíe tráfico a la muestra de Bookinfo. Visite http://$GATEWAY_URL/productpage en su navegador web o emita el siguiente comando:

$ 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

Para /productpage, verá que la primera solicitud pasa, pero cada solicitud siguiente dentro de un minuto obtendrá una respuesta 429. Y para /api/v1/products/* deberá realizar dos solicitudes, con cualquier número entre 1 y 99, hasta que obtenga la respuesta 429 dentro de un minuto.

Verificar el límite de tasa local

Aunque el límite de tasa global en el ingress gateway limita las solicitudes al service productpage a 1 solicitud/min, el límite de tasa local para las instancias de productpage permite 4 solicitudes/min. Para confirmar esto, envíe solicitudes internas de productpage, desde el pod ratings, usando el siguiente comando curl:

$ 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

No debería ver más de 4 solicitudes/min por instancia de productpage.

Limpieza

Zip
$ 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@
¿Fue útil esta información?
¿Tienes alguna sugerencia para mejorar?

¡Gracias por tus comentarios!