• 启用速率限制
    • 开始之前
    • 速率限制
    • 有条件的速率限制
    • 理解速率限制
    • 清理

    启用速率限制

    这一任务展示了如何使用 Istio 动态的对服务通信进行速率限制。

    开始之前

    • 按照安装指南在 Kubernetes 集群上设置 Istio。

    • 部署 Bookinfo 示例应用。

    Bookinfo 例子中需要部署三个版本的 reviews 服务:

    • v1 版本不会调用 ratings 服务。
    • v2 版本调用 ratings 服务,并用 1 到 5 个黑色图标显示评级信息。
    • v3 版本调用 ratings 服务,并用 1 到 5 个红色图标显示评级信息。
      这里需要设置一个到某版本的缺省路由,否则当发送请求到 reviews 服务的时候,Istio 会随机路由到某个版本,有时候显示评级图标,有时不显示。
    • 将所有服务的默认版本设置为 v1。

    Zip

    1. $ kubectl apply -f @samples/bookinfo/networking/virtual-service-all-v1.yaml@

    速率限制

    在此任务中,您将 Istio 配置为根据 IP 地址将流量限制到访问 productpage 的用户。您将使用 X-Forwarded-For 请求 http header 作为客户端 IP 地址。您还将使用免除登录用户的条件速率限制。

    为方便起见,您可以配置 memquota 适配器启用速率限制。但是,在生产系统上,你需要 Redis ,然后配置 redisquota 适配器。 memquotaredisquota 适配器都支持 quota template,因此,在两个适配器上启用速率限制的配置是相同的。

    • 速率限制配置分为两部分。

      • 客户端
        • QuotaSpec 定义客户端应该请求的配额名称和大小。
        • QuotaSpecBinding 有条件地将 QuotaSpec 与一个或多个服务相关联。
      • Mixer 端
        • quota instance 定义了 Mixer 如何确定配额的大小。
        • memquota adapter 定义了 memquota 适配器配置。
        • quota rule 定义何时将配额实例分派给 memquota 适配器。
          运行以下命令以使用 memquota 启用速率限制:

    Zip

    1. $ kubectl apply -f @samples/bookinfo/policy/mixer-rule-productpage-ratelimit.yaml@

    或者

    将以下 yaml 文件另存为 redisquota.yaml 。替换 rate_limit_algorithm,redis_server_url包含配置值。

    1. apiVersion: "config.istio.io/v1alpha2"
    2. kind: redisquota
    3. metadata:
    4. name: handler
    5. namespace: istio-system
    6. spec:
    7. redisServiceUrl: <redis_server_url>
    8. connectionPoolSize: 10
    9. quotas:
    10. - name: requestcount.quota.istio-system
    11. maxAmount: 500
    12. validDuration: 1s
    13. bucketDuration: 500ms
    14. rateLimitAlgorithm: <rate_limit_algorithm>
    15. # The first matching override is applied.
    16. # A requestcount instance is checked against override dimensions.
    17. overrides:
    18. # The following override applies to 'reviews' regardless
    19. # of the source.
    20. - dimensions:
    21. destination: reviews
    22. maxAmount: 1
    23. # The following override applies to 'productpage' when
    24. # the source is a specific ip address.
    25. - dimensions:
    26. destination: productpage
    27. source: "10.28.11.20"
    28. maxAmount: 500
    29. # The following override applies to 'productpage' regardless
    30. # of the source.
    31. - dimensions:
    32. destination: productpage
    33. maxAmount: 2
    34. ---
    35. apiVersion: "config.istio.io/v1alpha2"
    36. kind: quota
    37. metadata:
    38. name: requestcount
    39. namespace: istio-system
    40. spec:
    41. dimensions:
    42. source: request.headers["x-forwarded-for"] | "unknown"
    43. destination: destination.labels["app"] | destination.workload.name | "unknown"
    44. destinationVersion: destination.labels["version"] | "unknown"
    45. ---
    46. apiVersion: config.istio.io/v1alpha2
    47. kind: rule
    48. metadata:
    49. name: quota
    50. namespace: istio-system
    51. spec:
    52. # quota only applies if you are not logged in.
    53. # match: match(request.headers["cookie"], "session=*") == false
    54. actions:
    55. - handler: handler.redisquota
    56. instances:
    57. - requestcount.quota
    58. ---
    59. apiVersion: config.istio.io/v1alpha2
    60. kind: QuotaSpec
    61. metadata:
    62. name: request-count
    63. namespace: istio-system
    64. spec:
    65. rules:
    66. - quotas:
    67. - charge: 1
    68. quota: requestcount
    69. ---
    70. apiVersion: config.istio.io/v1alpha2
    71. kind: QuotaSpecBinding
    72. metadata:
    73. name: request-count
    74. namespace: istio-system
    75. spec:
    76. quotaSpecs:
    77. - name: request-count
    78. namespace: istio-system
    79. services:
    80. - name: productpage
    81. namespace: default
    82. # - service: '*' # Uncomment this to bind *all* services to request-count
    83. ---

    运行以下命令以使用 redisquota 启用速率限制:

    1. $ kubectl apply -f redisquota.yaml
    • 检查 memquota 的创建情况:
    1. $ kubectl -n istio-system get memquota handler -o yaml
    2. apiVersion: config.istio.io/v1alpha2
    3. kind: memquota
    4. metadata:
    5. name: handler
    6. namespace: istio-system
    7. spec:
    8. quotas:
    9. - name: requestcount.quota.istio-system
    10. maxAmount: 500
    11. validDuration: 1s
    12. overrides:
    13. - dimensions:
    14. destination: reviews
    15. maxAmount: 1
    16. validDuration: 5s
    17. - dimensions:
    18. destination: productpage
    19. maxAmount: 2
    20. validDuration: 5s

    memquota 处理程序定义了 3 种不同的速率限制方案。在没有 overrides 生效的缺省情况下,每秒限制请求为 500 次。还定义了两个 overrides 条目:

    • 如果 destination 值为 reviews ,限制值为每 5 秒 1 次。
    • 如果 destination 值为 productpage ,限制值为每 5 秒 2 次。
      处理请求时,Istio 会选择第一条符合条件的 overrides(读取顺序为从上到下)应用到请求上。

    或者

    确认已创建 redisquota handler :

    1. $ kubectl -n istio-system get redisquota handler -o yaml
    2. apiVersion: config.istio.io/v1alpha2
    3. kind: redisquota
    4. metadata:
    5. name: handler
    6. namespace: istio-system
    7. spec:
    8. connectionPoolSize: 10
    9. quotas:
    10. - name: requestcount.quota.istio-system
    11. maxAmount: 500
    12. validDuration: 1s
    13. bucketDuration: 500ms
    14. rateLimitAlgorithm: ROLLING_WINDOW
    15. overrides:
    16. - dimensions:
    17. destination: reviews
    18. maxAmount: 1
    19. - dimensions:
    20. destination: productpage
    21. source: 10.28.11.20
    22. maxAmount: 500
    23. - dimensions:
    24. destination: productpage
    25. maxAmount: 2

    redisquota handler 定义了 4 种不同的速率限制方案。在没有 overrides 生效的缺省情况下,每秒限制请求为 500次。它使用 ROLLING_WINDOW 算法进行配额检查,因此为 ROLLING_WINDOW 算法定义了 500ms 的 bucketDuration。还定义了 overrides 条目:

    • 如果 destination 的值为 reviews是 那么最大请求配额为 1
    • 如果 destination 的值为 productpage 并且来源是 10.28.11.20 那么最大请求配额为 500
    • 如果 destination 的值为 productpage 那么最大请求配额为 2
      处理请求时,Istio 会选择第一条符合条件的 overrides(读取顺序为从上到下)应用到请求上。

    确认 quota instance 的创建情况:

    1. $ kubectl -n istio-system get quotas requestcount -o yaml
    2. apiVersion: config.istio.io/v1alpha2
    3. kind: quota
    4. metadata:
    5. name: requestcount
    6. namespace: istio-system
    7. spec:
    8. dimensions:
    9. source: request.headers["x-forwarded-for"] | "unknown"
    10. destination: destination.labels["app"] | destination.service.host | "unknown"
    11. destinationVersion: destination.labels["version"] | "unknown"

    quota 模板定义了 memquotaredisquota 使用的三个维度,用于设置匹配某些属性的请求。 destination 将被设置为 destination.labels["app"]destination.service.host"unknown" 中的第一个非空值。有关表达式的更多信息,请参阅表达式语言文档中获取更多表达式方面的内容。

    • 确认 quota rule 的创建情况:
    1. $ kubectl -n istio-system get rules quota -o yaml
    2. apiVersion: config.istio.io/v1alpha2
    3. kind: rule
    4. metadata:
    5. name: quota
    6. namespace: istio-system
    7. spec:
    8. actions:
    9. - handler: handler.memquota
    10. instances:
    11. - requestcount.quota

    rule 通知 Mixer,使用 Instance requestcount.quota 构建对象并传递给上面创建的 handler.memquota。这一过程使用 quota 模板将 dimensions 数据映射给 memquota 进行处理。

    • 确认 QuotaSpec 的创建情况:
    1. $ kubectl -n istio-system get QuotaSpec request-count -o yaml
    2. apiVersion: config.istio.io/v1alpha2
    3. kind: QuotaSpec
    4. metadata:
    5. name: request-count
    6. namespace: istio-system
    7. spec:
    8. rules:
    9. - quotas:
    10. - charge: "1"
    11. quota: requestcount

    QuotaSpec 为上面创建的 quota 实例(requstcount)设置了 charge 值为 1。

    • 确认 QuotaSpecBinding 的创建情况:
    1. $ kubectl -n istio-system get QuotaSpecBinding request-count -o yaml
    2. kind: QuotaSpecBinding
    3. metadata:
    4. name: request-count
    5. namespace: istio-system
    6. spec:
    7. quotaSpecs:
    8. - name: request-count
    9. namespace: istio-system
    10. services:
    11. - name: productpage
    12. namespace: default
    13. # - service: '*'

    QuotaSpecBinding 把前面的 QuotaSpec 绑定到需要应用限流的服务上。因为 QuotaSpecBinding 所属命名空间和这些服务是不一致的,所以这里必须定义每个服务的 namespace

    • 在浏览器中刷新 productpage 页面。

    request-count 配额适用于 productpage ,每 5 秒允许 2 个请求。如果你不断刷新页面,你会看到 RESOURCE_EXHAUSTED:Quota is exhausted for: requestcount

    有条件的速率限制

    在前面的例子中,ratings 服务受到的速率限制并没有考虑没有 dimension 属性的情况。还可以在配额规则中使用任意属性进行匹配,从而完成有条件的配额限制。

    例如下面的配置:

    1. $ kubectl -n istio-system edit rules quota
    2. apiVersion: config.istio.io/v1alpha2
    3. kind: rule
    4. metadata:
    5. name: quota
    6. namespace: istio-system
    7. spec:
    8. match: match(request.headers["cookie"], "session=*") == false
    9. actions:
    10. - handler: handler.memquota
    11. instances:
    12. - requestcount.quota

    只有当请求中没有 session = <sessionid> cookie 时,才会调度 memquotaredisquota 适配器。这可确保登录用户不受此配额的约束。

    • 验证速率限制不适用于登录用户。

    jason 身份登录并反复刷新 productpage。现在你应该能够毫无问题地做到这一点。

    • 验证速率限制在未登录时适用

    注销 jason 并反复刷新 productpage

    您应该再次看到 RESOURCE_EXHAUSTED:Quota is exhausted for: requestcount

    理解速率限制

    在前面的例子中演示了 Mixer 根据条件对请求实施速率限制的过程。

    每个有名称的 Quota 实例,例如前面的 requestcount,都代表了一套计数器。这一个集合就是所有 Quota dimensions 的笛卡尔积定义的。如果上一个 expiration 区间内的请求数量超过了 maxAmount,Mixer 就会返回 RESOURCE_EXHAUSTED 信息给 Proxy。Proxy 则返回 HTTP 429 给调用方。

    memquota 适配器使用一个为亚秒级分辨率的滑动窗口来实现速率限制。

    适配器配置中的 maxAmount 设置了关联到 Quota 实例中的所有计数器的缺省限制。如果所有 overrides 条目都无法匹配到一个请求,就只能使用 maxAmount 限制了。memquota 会选择适合请求的第一条 overrideoverride 条目无需定义所有 quota dimension, 例如例子中的 0.2 qps 条目在 4 条 quota dimensions 中只选用了三条。

    如果要把上面的策略应用到某个命名空间而非整个 Istio 网格,可以把所有 istio-system 替换成为给定的命名空间。

    清理

    • 如果使用 memquota ,删除 memquota 速率限制相关的配置:

    Zip

    1. $ kubectl delete -f @samples/bookinfo/policy/mixer-rule-productpage-ratelimit.yaml@

    或者

    如果使用 redisquota ,删除 redisquota 速率限制相关的配置:

    1. $ kubectl delete -f redisquota.yaml
    • 删除应用路由规则:

    Zip

    1. $ kubectl delete -f @samples/bookinfo/networking/virtual-service-all-v1.yaml@
    • 如果不准备尝试后续任务,可参考 Bookinfo 清理 的介绍关停应用。