使用 Envoy 启用速率限制
此任务向您展示如何使用 Envoy 的原生速率限制来动态限制 Istio 服务的流量。在此任务中,您将通过入口网关为 productpage
服务应用全局速率限制,该限制允许每分钟跨服务所有实例发送 1 个请求。此外,您还将为每个单独的 productpage
实例应用本地速率限制,该限制将允许每分钟发送 4 个请求。这样,您可以确保 productpage
服务通过入口网关每分钟处理最多 1 个请求,但每个 productpage
实例每分钟最多可以处理 4 个请求,从而允许任何网内流量。
开始之前
速率限制
Envoy 支持两种速率限制:全局和本地。全局速率限制使用全局 gRPC 速率限制服务来为整个服务网格提供速率限制。本地速率限制用于限制每个服务实例的请求速率。本地速率限制可以与全局速率限制结合使用,以减少全局速率限制服务的负载。
在本任务中,您将配置 Envoy 使用全局和本地速率限制来限制对服务特定路径的流量。
全局速率限制
Envoy 可用于 设置服务网格的全局速率限制。Envoy 中的全局速率限制使用 gRPC API 从速率限制服务请求配额。下面使用了 API 的参考实现,该实现使用 Go 编写并使用 Redis 作为后端。
使用以下 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
创建一个全局速率限制服务,该服务实现 Envoy 的 速率限制服务协议。作为参考,可以在 此处 找到一个演示配置,该配置基于 Envoy 提供的 参考实现。
$ kubectl apply -f @samples/ratelimit/rate-limit-service.yaml@
将
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
将另一个
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
将发挥作用。
更改 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
应用 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_bucket
。token_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@