头图

这是开源的 K8s Gateway API 实现 FSM Gateway 实践系列的第三篇:


网关的限速功能是一种关键的网络流量管理策略,用于控制通过网关的数据传输速率,对于确保网络的稳定性和效率至关重要。。

FSM Gateway 的限速功能可以基于多种标准实施,包括端口、域名和路由。

  • 基于端口的限速:控制端口数据传输的速率,确保流量不超过设定的阈值。这通常用于防止网络拥堵和服务器过载。
  • 基于域名的限速:针对特定域名设置请求速率限制。这种策略通常用于控制对特定服务或应用的访问频率,以防止过载和保证服务质量。
  • 基于路由的限速:针对特定路由或 URL 路径设置请求速率限制。这种方式更加细致,允许对单个应用内的不同部分进行差异化流量控制。

限速配置

  • 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 头来告诉客户端限速的配置。还可以提供关于当前限速策略或如何联系系统管理员的其他有用信息。可选字段。

前置条件

  • Kubernetes 集群
  • kubectl 工具

环境准备

安装 FSM Gateway

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 指定端口 8000
    • bps 设置每秒的字节数为 2k
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: 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
  • backlog 积压的请求设为 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 %)

Flomesh
1 声望0 粉丝