Skip to content

Commit d58836a

Browse files
committed
docs: add doc for global rate limiting
Signed-off-by: Tom <[email protected]>
1 parent b307a77 commit d58836a

File tree

1 file changed

+369
-0
lines changed

1 file changed

+369
-0
lines changed
Lines changed: 369 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,369 @@
1+
---
2+
title: Global Rate Limiting
3+
sidebar_position: 12
4+
---
5+
6+
This document provides a step-by-step guide on how to test the global rate limiting functionality of kmesh. It covers deploying the necessary components, configuring traffic rules with an external rate limiting service, and observing the rate limiting behavior across multiple proxy instances.
7+
8+
## Step 1. Deploy Kmesh and istiod (>=1.24)
9+
10+
Please read [Quick Start](https://kmesh.net/docs/setup/quick-start) to complete the deployment of kmesh.
11+
12+
## Step 2. Deploy sleep and httpbin
13+
14+
We will deploy `httpbin` as the backend service for receiving requests and `sleep` as the client for sending requests.
15+
16+
``` sh
17+
kubectl apply -f samples/sleep/sleep.yaml
18+
kubectl apply -f samples/httpbin/httpbin.yaml
19+
```
20+
21+
## Step 3. Deploy Redis for global rate limiting
22+
23+
Global rate limiting requires an external service to coordinate rate limits across multiple proxy instances. We'll use Redis for this purpose.
24+
25+
```sh
26+
kubectl apply -f -<<EOF
27+
apiVersion: apps/v1
28+
kind: Deployment
29+
metadata:
30+
name: redis
31+
namespace: default
32+
spec:
33+
replicas: 1
34+
selector:
35+
matchLabels:
36+
app: redis
37+
template:
38+
metadata:
39+
labels:
40+
app: redis
41+
spec:
42+
containers:
43+
- name: redis
44+
image: redis:7-alpine
45+
ports:
46+
- containerPort: 6379
47+
---
48+
apiVersion: v1
49+
kind: Service
50+
metadata:
51+
name: redis
52+
namespace: default
53+
spec:
54+
selector:
55+
app: redis
56+
ports:
57+
- port: 6379
58+
targetPort: 6379
59+
EOF
60+
```
61+
62+
## Step 4. Deploy rate limiting service
63+
64+
Deploy the Envoy rate limiting service that will communicate with Redis to enforce global rate limits.
65+
66+
```sh
67+
kubectl apply -f -<<EOF
68+
apiVersion: v1
69+
kind: ConfigMap
70+
metadata:
71+
name: ratelimit-config
72+
namespace: default
73+
data:
74+
config.yaml: |
75+
domain: httpbin-ratelimit
76+
descriptors:
77+
- key: header_match
78+
value: Service[httpbin.default]-User[none]-Id[3100861967]
79+
rate_limit:
80+
unit: second
81+
requests_per_unit: 1
82+
- key: header_match
83+
value: Service[httpbin.default]-User[none]-Id[4123289408]
84+
rate_limit:
85+
unit: second
86+
requests_per_unit: 3
87+
- key: generic_key
88+
value: default
89+
rate_limit:
90+
unit: second
91+
requests_per_unit: 10
92+
---
93+
apiVersion: apps/v1
94+
kind: Deployment
95+
metadata:
96+
name: ratelimit
97+
namespace: default
98+
spec:
99+
replicas: 1
100+
selector:
101+
matchLabels:
102+
app: ratelimit
103+
template:
104+
metadata:
105+
labels:
106+
app: ratelimit
107+
spec:
108+
containers:
109+
- name: ratelimit
110+
image: envoyproxy/ratelimit:master
111+
command: ["/bin/ratelimit"]
112+
env:
113+
- name: LOG_LEVEL
114+
value: debug
115+
- name: REDIS_SOCKET_TYPE
116+
value: tcp
117+
- name: REDIS_URL
118+
value: redis:6379
119+
- name: USE_STATSD
120+
value: "false"
121+
- name: RUNTIME_ROOT
122+
value: /data
123+
- name: RUNTIME_SUBDIRECTORY
124+
value: ratelimit
125+
ports:
126+
- containerPort: 8080
127+
- containerPort: 8081
128+
- containerPort: 6070
129+
volumeMounts:
130+
- name: config-volume
131+
mountPath: /data/ratelimit/config/config.yaml
132+
subPath: config.yaml
133+
readOnly: true
134+
volumes:
135+
- name: config-volume
136+
configMap:
137+
name: ratelimit-config
138+
---
139+
apiVersion: v1
140+
kind: Service
141+
metadata:
142+
name: ratelimit
143+
namespace: default
144+
spec:
145+
selector:
146+
app: ratelimit
147+
ports:
148+
- name: http
149+
port: 8080
150+
targetPort: 8080
151+
- name: grpc
152+
port: 8081
153+
targetPort: 8081
154+
- name: debug
155+
port: 6070
156+
targetPort: 6070
157+
EOF
158+
```
159+
160+
## Step 5. Deploy waypoint for httpbin
161+
162+
First, if you haven't installed the Kubernetes Gateway API CRDs, run the following command to install.
163+
164+
``` sh
165+
kubectl get crd gateways.gateway.networking.k8s.io &> /dev/null || \
166+
{ kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd/experimental?ref=444631bfe06f3bcca5d0eadf1857eac1d369421d" | kubectl apply -f -; }
167+
```
168+
169+
Next, create a dedicated Waypoint proxy for the `httpbin` service and label the service to direct its traffic through this Waypoint.
170+
171+
```sh
172+
kmeshctl waypoint apply -n default --name httpbin-waypoint --image ghcr.io/kmesh-net/waypoint:latest
173+
174+
kubectl label service httpbin istio.io/use-waypoint=httpbin-waypoint
175+
```
176+
177+
## Step 6. Deploy envoyFilter
178+
179+
This `EnvoyFilter` resource injects a global rate-limiting filter into the `httpbin` service's Waypoint proxy. The filter is configured with the following rules:
180+
181+
- A request with the header `quota: low` will be limited to **1 request per second** globally.
182+
- A request with the header `quota: medium` will be limited to **3 requests per second** globally.
183+
- Other requests will be subject to a default limit of **10 requests per second** globally.
184+
185+
The `workloadSelector` ensures that this filter is applied only to the `httpbin-waypoint` proxy.
186+
187+
```sh
188+
kubectl apply -f -<<EOF
189+
apiVersion: networking.istio.io/v1alpha3
190+
kind: EnvoyFilter
191+
metadata:
192+
name: httpbin.global-ratelimit
193+
namespace: default
194+
spec:
195+
configPatches:
196+
- applyTo: HTTP_FILTER
197+
match:
198+
context: SIDECAR_INBOUND
199+
listener:
200+
filterChain:
201+
filter:
202+
name: envoy.filters.network.http_connection_manager
203+
subFilter:
204+
name: envoy.filters.http.router
205+
proxy:
206+
proxyVersion: ^1.*
207+
patch:
208+
operation: INSERT_BEFORE
209+
value:
210+
name: envoy.filters.http.ratelimit
211+
typed_config:
212+
'@type': type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit
213+
domain: httpbin-ratelimit
214+
failure_mode_deny: true
215+
rate_limit_service:
216+
grpc_service:
217+
envoy_grpc:
218+
cluster_name: rate_limit_cluster
219+
transport_api_version: V3
220+
- applyTo: CLUSTER
221+
match:
222+
context: SIDECAR_INBOUND
223+
proxy:
224+
proxyVersion: ^1.*
225+
patch:
226+
operation: ADD
227+
value:
228+
name: rate_limit_cluster
229+
type: STRICT_DNS
230+
connect_timeout: 10s
231+
lb_policy: ROUND_ROBIN
232+
http2_protocol_options: {}
233+
load_assignment:
234+
cluster_name: rate_limit_cluster
235+
endpoints:
236+
- lb_endpoints:
237+
- endpoint:
238+
address:
239+
socket_address:
240+
address: ratelimit.default.svc.cluster.local
241+
port_value: 8081
242+
- applyTo: HTTP_ROUTE
243+
match:
244+
proxy:
245+
proxyVersion: ^1.*
246+
routeConfiguration:
247+
vhost:
248+
name: inbound|http|8000
249+
route:
250+
name: default
251+
patch:
252+
operation: MERGE
253+
value:
254+
route:
255+
rate_limits:
256+
- actions:
257+
- header_value_match:
258+
descriptor_value: Service[httpbin.default]-User[none]-Id[3100861967]
259+
headers:
260+
- name: quota
261+
exact_match: low
262+
- actions:
263+
- header_value_match:
264+
descriptor_value: Service[httpbin.default]-User[none]-Id[4123289408]
265+
headers:
266+
- name: quota
267+
exact_match: medium
268+
- actions:
269+
- generic_key:
270+
descriptor_value: default
271+
workloadSelector:
272+
labels:
273+
gateway.networking.k8s.io/gateway-name: httpbin-waypoint
274+
EOF
275+
```
276+
277+
## Step 7. View the envoy filter configuration in waypoint through istioctl
278+
279+
To verify the configuration, first get the name of the Waypoint pod, then use `istioctl` to inspect its configuration.
280+
281+
```sh
282+
export WAYPOINT_POD=$(kubectl get pod -l gateway.networking.k8s.io/gateway-name=httpbin-waypoint -o jsonpath='{.items[0].metadata.name}')
283+
istioctl proxy-config all $WAYPOINT_POD -ojson | grep ratelimit -A 20
284+
```
285+
286+
## Step 8. Find the following results, which means the configuration has been sent to waypoint
287+
288+
```sh
289+
"envoy.filters.http.ratelimit": {
290+
"@type": "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit",
291+
"domain": "httpbin-ratelimit",
292+
"failure_mode_deny": true,
293+
"rate_limit_service": {
294+
"grpc_service": {
295+
"envoy_grpc": {
296+
"cluster_name": "rate_limit_cluster"
297+
}
298+
},
299+
"transport_api_version": "V3"
300+
}
301+
}
302+
```
303+
304+
## Step 9. Access httpbin through sleep to see if the global rate limit is working
305+
306+
Now, let's send requests from the `sleep` pod to the `httpbin` service to test the global rate limit rules.
307+
308+
First, get the name of the `sleep` pod:
309+
310+
```sh
311+
export SLEEP_POD=$(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}')
312+
```
313+
314+
### Test Case 1: "medium" quota
315+
316+
The rule for `quota: medium` allows 3 requests per second globally. Rapid successive requests beyond this limit should be rate-limited.
317+
318+
```sh
319+
kubectl exec -it $SLEEP_POD -- curl -H 'quota:medium' http://httpbin:8000/headers
320+
kubectl exec -it $SLEEP_POD -- curl -H 'quota:medium' http://httpbin:8000/headers
321+
kubectl exec -it $SLEEP_POD -- curl -H 'quota:medium' http://httpbin:8000/headers
322+
kubectl exec -it $SLEEP_POD -- curl -H 'quota:medium' http://httpbin:8000/headers
323+
```
324+
325+
Expected behavior: The first 3 requests should succeed, and the fourth request should be rate-limited and return an HTTP 429 status code.
326+
327+
### Test Case 2: "low" quota
328+
329+
The rule for `quota: low` allows only 1 request per second globally. The second request within the same second should be rate-limited.
330+
331+
```sh
332+
kubectl exec -it $SLEEP_POD -- curl -H 'quota:low' http://httpbin:8000/headers
333+
kubectl exec -it $SLEEP_POD -- curl -H 'quota:low' http://httpbin:8000/headers
334+
```
335+
336+
Expected behavior: The first request should succeed, and the second request should be rate-limited and return an HTTP 429 status code.
337+
338+
### Test Case 3: Default quota
339+
340+
Without any quota header, requests are subject to the default limit of 10 requests per second globally.
341+
342+
```sh
343+
for i in {1..12}; do kubectl exec -it $SLEEP_POD -- curl http://httpbin:8000/headers; done
344+
```
345+
346+
Expected behavior: The first 10 requests should succeed, and the 11th and 12th requests should be rate-limited.
347+
348+
## Step 10. Verify rate limiting service logs
349+
350+
You can check the rate limiting service logs to see the rate limiting decisions being made:
351+
352+
```sh
353+
export RATELIMIT_POD=$(kubectl get pod -l app=ratelimit -o jsonpath='{.items[0].metadata.name}')
354+
kubectl logs $RATELIMIT_POD -f
355+
```
356+
357+
The logs will show rate limiting decisions and Redis interactions, providing visibility into the global rate limiting behavior.
358+
359+
## Key Differences from Local Rate Limiting
360+
361+
1. **Shared State**: Global rate limiting uses Redis to maintain shared state across all proxy instances, ensuring consistent rate limiting behavior regardless of which proxy handles the request.
362+
363+
2. **External Service**: Requires deployment of a separate rate limiting service (ratelimit) that communicates with the data store.
364+
365+
3. **Network Dependency**: Rate limiting decisions depend on network calls to the external service, which may introduce latency but provides consistency.
366+
367+
4. **Scalability**: Better suited for distributed environments where multiple proxy instances need to coordinate rate limiting decisions.
368+
369+
5. **Configuration**: Uses `envoy.filters.http.ratelimit` filter instead of `envoy.filters.http.local_ratelimit`, and requires additional cluster configuration for the rate limiting service.

0 commit comments

Comments
 (0)