这是开源的 K8s Gateway API 实现 FSM Gateway 实践系列的第三篇:
网关的限速功能是一种关键的网络流量管理策略,用于控制通过网关的数据传输速率,对于确保网络的稳定性和效率至关重要。。
FSM Gateway 的限速功能可以基于多种标准实施,包括端口、域名和路由。
targetRef
是对应用策略的目标资源的引用,这里设置的是端口粒度的限速,因此引用的是 Gateway
资源 simple-fsm-gateway
bps
:端口的默认限速,每秒通过的字节数config
:L7 的限速配置ports
port
指定端口bps
设置每秒的字节数hostnames
hostname
:域名config
:L7 的限速配置http
match
:
headers
:HTTP 请求匹配method
:HTTP method 匹配config
:L7 的限速配置L7 的限速配置:
backlog
积压值是指在达到限速阈值时,系统允许排队的请求数量。这是一个重要的字段,尤其是在系统突然遭受大量请求时,这些请求可能会超过设置的限速阈值。积压值为系统提供了一定的缓冲,可以处理超出限速阈值的请求,但在积压值上限内。一旦达到积压上限,任何新的请求都会立即被拒绝,无需等待。可选字段,默认是 10
。requests
请求值是指在限速时间窗口内允许的访问次数。这是限速策略的核心参数,它确定了在特定的时间窗口内可以接受多少请求。设置此值的目的是为了确保在给定的时间窗口内,后端系统不会受到超过它能处理的请求。必须字段,最小值是 1
。statTimeWindow
限速时间窗口(单位:秒)定义了统计请求数量的时间段。限速策略通常基于滑动窗口或固定窗口来实现。StatTimeWindow 定义了这个窗口的大小。比如,如果 statTimeWindow
设置为 60 秒,并且 requests
为 100,则意味着每 60 秒内最多只能有 100 个请求。必须字段。burst
: 爆发值表示在短时间内允许的最大请求次数。这是一个可选字段,它主要用于处理短时间的请求高峰。爆发值通常大于请求值,允许在短时间内接受的请求数量超过平均速率。可选字段。responseStatusCode
: 发生限速时,返回给客户端的 HTTP 状态码。这个状态码告诉客户端请求被拒绝的原因是因为达到了限速阈值。常见的状态码是 429(Too Many Requests)
,但可以根据需要自定义。必须字段。responseHeadersToAdd
: 发生限速时,要添加到响应中的 HTTP 头部信息。这可以用来通知客户端有关限速策略的更多信息。例如,可以添加一个 RateLimit-Limit
头来告诉客户端限速的配置。还可以提供关于当前限速策略或如何联系系统管理员的其他有用信息。可选字段。FSM Gateway 的安装,可以参考 安装文档。这里选择 CLI 的方式安装。
下载 FSM CLI。
system=$(uname -s | tr '[:upper:]' '[:lower:]')
arch=$(uname -m | sed -E 's/x86_/amd/' | sed -E 's/aarch/arm/')
release=v1.2.0
curl -L https://github.com/flomesh-io/fsm/releases/download/$release/fsm-$release-$system-$arch.tar.gz | tar -vxzf -
./$system-$arch/fsm version
sudo cp ./$system-$arch/fsm /usr/local/bin/fsm
在安装 FSM 时启用 FSM Gateway,默认情况是不启用的。
fsm install \
--set=fsm.fsmGateway.enabled=true
接下来部署示例应用,使用常用的 httpbin 服务,并创建 网关(Gateway) 和 HTTP 路由(HttpRoute)。
kubectl create namespace httpbin
kubectl apply -n httpbin -f https://raw.githubusercontent.com/flomesh-io/fsm-docs/main/manifests/gateway/http-routing.yaml
检查网关和 HTTP 路由,可以看到创建了两个不同域名的路由。
kubectl get gateway,httproute -n httpbin
NAME CLASS ADDRESS PROGRAMMED AGE
gateway.gateway.networking.k8s.io/simple-fsm-gateway fsm-gateway-cls Unknown 3s
NAME HOSTNAMES AGE
httproute.gateway.networking.k8s.io/http-route-foo ["foo.example.com"] 2s
httproute.gateway.networking.k8s.io/http-route-bar ["bar.example.com"] 2s
访问应用验证 HTTP 路由是否生效。
export GATEWAY_IP=$(kubectl get svc -n httpbin -l app=fsm-gateway -o jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}')
curl http://$GATEWAY_IP:8000/headers -H 'host:foo.example.com'
{
"headers": {
"Accept": "*/*",
"Connection": "keep-alive",
"Host": "10.42.0.15:80",
"User-Agent": "curl/7.81.0"
}
}
创建一个 8k 大小的文件。
dd if=/dev/zero of=payload bs=1K count=8
测试发送文件到服务的耗时仅需 1s。
time curl -s -X POST -T payload http://$GATEWAY_IP:8000/status/200 -H 'host:foo.example.com'
real 0m1.018s
user 0m0.001s
sys 0m0.014s
接下来设置限速策略:
targetRef
是对应用策略的目标资源的引用,这里设置的是端口粒度的限速,因此引用的是 Gateway
资源 simple-fsm-gateway
ports
port
指定端口 8000bps
设置每秒的字节数为 2kkubectl apply -n httpbin -f - <<EOF
apiVersion: gateway.flomesh.io/v1alpha1
kind: RateLimitPolicy
metadata:
name: ratelimit-sample
spec:
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: simple-fsm-gateway
namespace: httpbin
ports:
- port: 8000
bps: 2048
EOF
待上面的策略生效后,还是发送 8k 的文件,现在限速的策略起了作用,耗时 4 秒。
time curl -s -X POST -T payload http://$GATEWAY_IP:8000/status/200 -H 'host:foo.example.com'
real 0m4.016s
user 0m0.007s
sys 0m0.005s
在测试基于域名的限速之前,删除上面创建的策略。
kubectl delete ratelimitpolicies -n httpbin ratelimit-sample
然后使用 forito 来生成负载:1 个并发以 200 qps 发送 1000 个请求。
fortio load -quiet -c 1 -n 1000 -qps 200 -H 'host:foo.example.com' http://$GATEWAY_IP:8000/status/200
Code 200 : 1000 (100.0 %)
接下来设置限速策略:
foo.example.com
1
60s
内的最大请求数未 200
429
并添加响应头 RateLimit-Limit: 200
kubectl apply -n httpbin -f - <<EOF
apiVersion: gateway.flomesh.io/v1alpha1
kind: RateLimitPolicy
metadata:
name: ratelimit-sample
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: http-route-foo
namespace: httpbin
hostnames:
- hostname: foo.example.com
config:
backlog: 1
requests: 100
statTimeWindow: 60
responseStatusCode: 429
responseHeadersToAdd:
- name: RateLimit-Limit
value: "100"
EOF
策略生效后,还是生成相同的负载来测试。可以看到成功的响应有 200,被限速的响应有 798。
-1
是 fortio 在 read timeout 时设定的错误码。这是因为 fortio 默认的 timeout 为3s
,限速策略设置积压的请求是 1。FSM Gateway 默认为 2 个线程,因此超时的请求有 2 个。
fortio load -quiet -c 1 -n 1000 -qps 200 -H 'host:foo.example.com' http://$GATEWAY_IP:8000/status/200
Code -1 : 2 (0.2 %)
Code 200 : 200 (19.9 %)
Code 429 : 798 (79.9 %)
但是访问 bar.example.com
则不会被限速。
fortio load -quiet -c 1 -n 1000 -qps 200 -H 'host:bar.example.com' http://$GATEWAY_IP:8000/status/200
Code 200 : 1000 (100.0 %)
同样地,在开始下面的测试之前删除上面创建的策略。
kubectl delete ratelimitpolicies -n httpbin ratelimit-sample
在配置访问策略之前,在 HTTP 路由 foo.example.com
下我们添加一条路径前缀为 /headers
的路由,方便为其设置访问控制策略。
kubectl apply -n httpbin -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: http-route-foo
spec:
parentRefs:
- name: simple-fsm-gateway
port: 8000
hostnames:
- foo.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /status/200
backendRefs:
- name: httpbin
port: 8080
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: httpbin
port: 8080
EOF
更新限速策略,添加路由匹配规则:前缀为 /status/200
,其他的配置保持不限。
kubectl apply -n httpbin -f - <<EOF
apiVersion: gateway.flomesh.io/v1alpha1
kind: RateLimitPolicy
metadata:
name: ratelimit-sample
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: http-route-foo
namespace: httpbin
http:
- match:
path:
type: PathPrefix
value: /status/200
config:
backlog: 1
requests: 100
statTimeWindow: 60
responseStatusCode: 429
responseHeadersToAdd:
- name: RateLimit-Limit
value: "100"
EOF
应用策略后,发送同样的负载。从结果可以看到只有 200 个请求成功。
fortio load -quiet -c 1 -n 1000 -qps 200 -H 'host:foo.example.com' http://$GATEWAY_IP:8000/status/200
Code -1 : 2 (0.2 %)
Code 200 : 200 (20.0 %)
Code 429 : 798 (79.8 %)
当路径 /status/204
则不会受限速的限制。
fortio load -quiet -c 1 -n 1000 -qps 200 -H 'host:foo.example.com' http://$GATEWAY_IP:8000/status/204
Code 204 : 1000 (100.0 %)