This user guide shows how the kuadrant's control plane applies rate limit policy at Gateway API's Gateway level.
git clone https://github.com/Kuadrant/kuadrant-operator && cd kuadrant-operator
This step creates a containerized Kubernetes server locally using Kind, then it installs Istio, Kubernetes Gateway API and kuadrant.
make local-setup
kubectl -n kuadrant-system apply -f - <<EOF
---
apiVersion: kuadrant.io/v1beta1
kind: Kuadrant
metadata:
name: kuadrant-sample
spec: {}
EOF
kubectl apply -f examples/toystore/toystore.yaml
kubectl apply -f - <<EOF
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: toystore
labels:
app: toystore
spec:
parentRefs:
- name: istio-ingressgateway
namespace: istio-system
hostnames: ["*.toystore.com"]
rules:
- matches:
- path:
type: PathPrefix
value: "/toy"
method: GET
- path:
type: PathPrefix
value: "/free"
method: GET
- path:
type: Exact
value: "/admin/toy"
method: POST
backendRefs:
- name: toystore
port: 80
EOF
GET /toy
: no rate limiting
while :; do curl --write-out '%{http_code}' --silent --output /dev/null -H "Host: api.toystore.com" http://localhost:9080/toy | egrep --color "\b(429)\b|$"; sleep 1; done
POST /admin/toy
: no rate limiting
while :; do curl --write-out '%{http_code}' --silent --output /dev/null -H "Host: api.toystore.com" -X POST http://localhost:9080/admin/toy | egrep --color "\b(429)\b|$"; sleep 1; done
Note: This only works out of the box on linux environments. If not on linux, you may need to forward ports
kubectl port-forward -n istio-system service/istio-ingressgateway 9080:80
RateLimitPolicy applied for the toystore
HTTPRoute.
Endpoints | Rate Limits |
---|---|
POST /admin/toy |
5 reqs / 10 secs (0.5 rps) |
GET /toy |
8 reqs / 10 secs (0.8 rps) |
* |
30 reqs / 10 secs (3.0 rps) |
kubectl apply -f - <<EOF
---
apiVersion: kuadrant.io/v1beta1
kind: RateLimitPolicy
metadata:
name: toystore
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: toystore
rateLimits:
- rules:
- paths: ["/admin/toy"]
methods: ["POST"]
configurations:
- actions:
- generic_key:
descriptor_key: admin_operation
descriptor_value: "1"
limits:
- conditions:
- "admin_operation == '1'"
maxValue: 5
seconds: 10
variables: []
- rules:
- paths: ["/toy"]
methods: ["GET"]
configurations:
- actions:
- generic_key:
descriptor_key: get_operation
descriptor_value: "1"
limits:
- conditions:
- "get_operation == '1'"
maxValue: 8
seconds: 10
variables: []
- configurations:
- actions:
- generic_key:
descriptor_key: toystore
descriptor_value: "1"
limits:
- conditions: ["toystore == '1'"]
maxValue: 30
seconds: 10
variables: []
EOF
Validating the rate limit policy.
GET /toy
@ 1 rps (expected to be rate limited @ 8 reqs / 10 secs (0.8 rps))
while :; do curl --write-out '%{http_code}' --silent --output /dev/null -H "Host: api.toystore.com" http://localhost:9080/toy | egrep --color "\b(429)\b|$"; sleep 1; done
POST /admin/toy
@ 1 rps (expected to be rate limited @ 5 reqs / 10 secs (0.5 rps))
while :; do curl --write-out '%{http_code}' --silent --output /dev/null -H "Host: api.toystore.com" -X POST http://localhost:9080/admin/toy | egrep --color "\b(429)\b|$"; sleep 1; done
RateLimitPolicy applied for the Gateway.
Policy | Rate Limits |
---|---|
POST /* |
2 reqs / 10 secs (0.2 rps) |
Per remote IP | 25 reqs / 10 secs (2.5 rps) |
kubectl apply -f - <<EOF
---
apiVersion: kuadrant.io/v1beta1
kind: RateLimitPolicy
metadata:
name: kuadrant-gw
namespace: istio-system
spec:
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: istio-ingressgateway
rateLimits:
- rules:
- methods: ["POST"]
configurations:
- actions:
- generic_key:
descriptor_key: expensive_op
descriptor_value: "1"
limits:
- conditions: ["expensive_op == '1'"]
maxValue: 2
seconds: 10
variables: []
- configurations:
- actions:
- remote_address: {}
limits:
- conditions: []
maxValue: 25
seconds: 10
variables: ["remote_address"]
EOF
GET /toy
@ 1 rps (expected to be rate limited @ 8 reqs / 10 secs (0.8 rps))
while :; do curl --write-out '%{http_code}' --silent --output /dev/null -H "Host: api.toystore.com" http://localhost:9080/toy | egrep --color "\b(429)\b|$"; sleep 1; done
POST /admin/toy
@ 1 rps (expected to be rate limited @ 2 reqs / 10 secs (0.2 rps))
while :; do curl --write-out '%{http_code}' --silent --output /dev/null -H "Host: api.toystore.com" -X POST http://localhost:9080/admin/toy | egrep --color "\b(429)\b|$"; sleep 1; done
Stop all traffic.
GET /free
@ 3 rps (expected to be rate limited @ 25 reqs / 10 secs (2.5 rps))
while :; do curl --write-out '%{http_code}\n' --silent --output /dev/null -H "Host: api.toystore.com" http://localhost:9080/free -: --write-out '%{http_code}\n' --silent --output /dev/null -H "Host: api.toystore.com" http://localhost:9080/free -: --write-out '%{http_code}\n' --silent --output /dev/null -H "Host: api.toystore.com" http://localhost:9080/free | egrep --color "\b(429)\b|$"; sleep 1; done