Istio中的全局限流方案
在k8s网格(istio)环境中, 可以通过创建Envfoyfilter
的方式来配置限流。
在istio官方文档中,提供了两种限流方式:
- 本地限流
- 全局限流
本地限流的细节这里不再赘述, 主要讲解全局限流的配置方式。
基本原理
istio通过向pod中注入sidecar,并通过envoy来代理流量,完成对流量的控制。
sidecar中的envoy, 由polit-agent管理。通过监听k8s中资源的变化,并由XDS协议下发配置到envoy中。
本地限流之所以称之为本地,是因为它只能为单个pod限流。 它通过hash桶算法进行流量分配。优点是可以平滑流量。
全局限流解决的是一个服务有多个pod的情况下,如何进行统一的限流。全局限流是通过sidecar中的envoy调用ratelimit服务来实现的。
全局限流
全局限流通过外部服务ratelimit来进行流量的统一控制。
ratelimit
服务是一个独立的服务,它通过gRPC协议提供限流服务。envoy通过gRPC协议调用ratelimit服务,来获取限流的配置。
ratelimit
中的配置管理支持configmap
和XDS
两种方式。我们开发了单独的ratelimit-configserver
服务,用于管理、下发配置到ratelimit
服务中。
这里不赘述ratelimit-configserver
的实现, 主要讲解如何在istio中配置全局限流。
使用ConfigMap
的架构是这样的
ratelimit服务部署
具体的部署方式以及示例使用, 在istio的官方文档中有详细的介绍。这里不再赘述。文档地址https://istio.io/latest/zh/docs/tasks/policy-enforcement/rate-limit/#global-rate-limit-advanced-case
这里需要提的是, 官方文档中使用的镜像版本较老,不支持XDS
的方式获取配置, 建议自行拉取最新代码并打包镜像。
配置说明
配置的方式比较生涩, 难以理解, 这里会着重说明一下。
基础配置说明
- domain 限流策略的唯一标识,与
EnvoyFilter
中的domain
字段对应 - descriptors 用于声明详细的规则, 是一个数组, 可以配置多个规则
- descriptors.key 规则的标识, 可以简单理解为http请求中某个header的代号(这并不准确,暂时先这样理解)
- descriptors.value 规则的值, 用于匹配请求中的某个header的值。 不指定时则匹配所有请求
- descriptors.rate_limit.unit 限流的单位, 可以是
second
,minute
,hour
,day
- descriptors.rate_limit.requests_per_unit 每个单位时间内的请求数
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
上面的配置表示:
对于/productpage
路径的请求, 每分钟限流1次; 对于api
路径的请求, 每分钟限流2次; 对于其他请求, 每分钟限流100次。
注意: /productpage 和 api 的请求次数会计入到100次的限流中。
官方文档示例中, 除了创建configmap外, 还有两个envoyfilter。 一个是用于配置ratelimit服务的地址(filter-ratelimit), 另一个是用于配置策略与请求之间的关系(filter-ratelimit-svc), 如KeyA对应的是请求头中的哪一个。
filter-ratelimit
中我们主要关注typed_config
这一项。
yaml
# type 声明当前使用的API版本
"@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit
# domain与configmap中的配置匹配
domain: ratelimit
# failure_mode_deny 表示当ratelimit服务不可用时,是否拒绝请求。
# 建议设为false, 这样即使我们的限流服务出现问题也不会影响业务服务的正常运行
failure_mode_deny: true
# timeout 速率限制服务RPC的超时(以毫秒为单位)。如果没有设置,则默认为20毫秒。
# 超时时间建议设的短一些, 这样可以快速失败,不会影响业务服务的正常运行
timeout: 10s
rate_limit_service:
grpc_service:
envoy_grpc:
# 用于设置上游的ratelimit服务的端口号及地址
# 这里测试时用ip会有问题
cluster_name: outbound|8081||ratelimit.default.svc.cluster.local
# 会设置到authority标头中
authority: ratelimit.default.svc.cluster.local
# 用于设置请求携带的metadata
initial_metadata: {}
transport_api_version: V3
filter-ratelimit-svc
中我们主要关注rate_limits
这一项。
yaml
rate_limits:
- actions:
- request_headers:
// 这里说明PATH这个key,会从:path这个header中读取数据匹配value
header_name: ":path"
descriptor_key: "PATH"
- request_headers:
// 可以有多个, 也可通过其他方式匹配请求。如参数、正则等,具体查看istio文档
header_name: "x-header-key"
descriptor_key: "KeyA"
// action也可以有多个
高级配置
着重于ratelimit中的配置, 参考ratelimit官方文档
-
对database为user和database为default的请求分别限制
yamldomain: mongo_cps descriptors: - key: database value: users rate_limit: unit: second requests_per_unit: 500 - key: database value: default rate_limit: unit: second requests_per_unit: 500
-
每个号码每天只允许100条信息; 且对message_type为marketing的信息, 每天每个号码只允许5条(算上5条,共在100条)
yaml
domain: messaging
descriptors:
- key: message_type
value: marketing
descriptors:
- key: to_number
rate_limit:
unit: day
requests_per_unit: 5
- key: to_number
rate_limit:
unit: day
requests_per_unit: 100
- 每个原地址(客户)允许10qps,"50.0.0.5"加入黑名单拒绝访问
yaml
domain: edge_proxy_per_ip
descriptors:
- key: remote_address
rate_limit:
unit: second
requests_per_unit: 10
- key: remote_address
value: 50.0.0.5
rate_limit:
unit: second
requests_per_unit: 0
总之, descriptors字段中, 多个规则平级为and关系, 多个规则嵌套为or关系