github.com/projectcontour/contour@v1.28.2/site/content/docs/1.25/guides/global-rate-limiting.md (about) 1 --- 2 title: Global Rate Limiting 3 --- 4 5 Starting in version 1.13, Contour supports [Envoy global rate limiting][1]. 6 In global rate limiting, Envoy communicates with an external Rate Limit Service (RLS) over gRPC to make rate limit decisions for each request. 7 Envoy is configured to produce 1+ descriptors for incoming requests, containing things like the client IP, header values, and more. 8 Envoy sends descriptors to the RLS, and the RLS returns a rate limiting decision to Envoy based on the descriptors and the RLS's configured rate limits. 9 10 In this guide, we'll walk through deploying an RLS, configuring it in Contour, and configuring an `HTTPProxy` to use it for rate limiting. 11 12 **NOTE: you should not consider the RLS deployment in this guide to be production-ready.** 13 The instructions and example YAML below are intended to be a demonstration of functionality only. 14 Each user will have their own unique production requirements for their RLS deployment. 15 16 ## Prerequisites 17 18 This guide assumes that you have: 19 20 - A local KinD cluster created using [the Contour guide][2]. 21 - Contour installed and running in the cluster using the [quick start][3]. 22 23 ## Deploy an RLS 24 25 For this guide, we'll deploy the [Envoy rate limit service][4] as our RLS. 26 Per the project's README: 27 28 > The rate limit service is a Go/gRPC service designed to enable generic rate limit scenarios from different types of applications. 29 > Applications request a rate limit decision based on a domain and a set of descriptors. 30 > The service reads the configuration from disk via [runtime][10], composes a cache key, and talks to the Redis cache. 31 > A decision is then returned to the caller. 32 33 However, any service that implements the [RateLimitService gRPC interface][5] is supported by Contour/Envoy. 34 35 Create a config map with [the ratelimit service configuration][6]: 36 37 ```yaml 38 apiVersion: v1 39 kind: ConfigMap 40 metadata: 41 name: ratelimit-config 42 namespace: projectcontour 43 data: 44 ratelimit-config.yaml: | 45 domain: contour 46 descriptors: 47 48 # requests with a descriptor of ["generic_key": "foo"] 49 # are limited to one per minute. 50 - key: generic_key 51 value: foo 52 rate_limit: 53 unit: minute 54 requests_per_unit: 1 55 56 # each unique remote address (i.e. client IP) 57 # is limited to three requests per minute. 58 - key: remote_address 59 rate_limit: 60 unit: minute 61 requests_per_unit: 3 62 ``` 63 64 Create a deployment for the RLS that mounts the config map as a volume. 65 **This configuration is for demonstration purposes only and is not a production-ready deployment.** 66 ```yaml 67 apiVersion: apps/v1 68 kind: Deployment 69 metadata: 70 labels: 71 app: ratelimit 72 name: ratelimit 73 namespace: projectcontour 74 spec: 75 replicas: 1 76 selector: 77 matchLabels: 78 app: ratelimit 79 template: 80 metadata: 81 labels: 82 app: ratelimit 83 spec: 84 containers: 85 - name: redis 86 image: redis:alpine 87 env: 88 - name: REDIS_SOCKET_TYPE 89 value: tcp 90 - name: REDIS_URL 91 value: redis:6379 92 - name: ratelimit 93 image: docker.io/envoyproxy/ratelimit:6f5de117 94 ports: 95 - containerPort: 8080 96 name: http 97 protocol: TCP 98 - containerPort: 8081 99 name: grpc 100 protocol: TCP 101 volumeMounts: 102 - name: ratelimit-config 103 mountPath: /data/ratelimit/config 104 readOnly: true 105 env: 106 - name: USE_STATSD 107 value: "false" 108 - name: LOG_LEVEL 109 value: debug 110 - name: REDIS_SOCKET_TYPE 111 value: tcp 112 - name: REDIS_URL 113 value: localhost:6379 114 - name: RUNTIME_ROOT 115 value: /data 116 - name: RUNTIME_SUBDIRECTORY 117 value: ratelimit 118 - name: RUNTIME_WATCH_ROOT 119 value: "false" 120 # need to set RUNTIME_IGNOREDOTFILES to true to avoid issues with 121 # how Kubernetes mounts configmaps into pods. 122 - name: RUNTIME_IGNOREDOTFILES 123 value: "true" 124 command: ["/bin/ratelimit"] 125 livenessProbe: 126 httpGet: 127 path: /healthcheck 128 port: 8080 129 initialDelaySeconds: 5 130 periodSeconds: 5 131 volumes: 132 - name: ratelimit-config 133 configMap: 134 name: ratelimit-config 135 ``` 136 137 Create a service: 138 139 ```yaml 140 apiVersion: v1 141 kind: Service 142 metadata: 143 name: ratelimit 144 namespace: projectcontour 145 spec: 146 ports: 147 - port: 8081 148 name: grpc 149 protocol: TCP 150 selector: 151 app: ratelimit 152 type: ClusterIP 153 ``` 154 155 Check the progress of the deployment: 156 157 ```bash 158 $ kubectl -n projectcontour get pods -l app=ratelimit 159 NAME READY STATUS RESTARTS AGE 160 ratelimit-658f4b8f6b-2hnrf 2/2 Running 0 12s 161 ``` 162 163 Once the pod is `Running` with `2/2` containers ready, move onto the next step. 164 165 ## Configure the RLS with Contour 166 167 Create a Contour extension service for the RLS: 168 169 ```yaml 170 apiVersion: projectcontour.io/v1alpha1 171 kind: ExtensionService 172 metadata: 173 namespace: projectcontour 174 name: ratelimit 175 spec: 176 protocol: h2c 177 # The service name and port correspond to 178 # the service we created in the previous 179 # step. 180 services: 181 - name: ratelimit 182 port: 8081 183 timeoutPolicy: 184 response: 100ms 185 ``` 186 187 Update the Contour configmap to have the following RLS configuration: 188 189 ```yaml 190 apiVersion: v1 191 kind: ConfigMap 192 metadata: 193 name: contour 194 namespace: projectcontour 195 data: 196 contour.yaml: | 197 rateLimitService: 198 # extensionService is the <namespace>/<name> 199 # of the ExtensionService we created in the 200 # previous step. 201 extensionService: projectcontour/ratelimit 202 # domain corresponds to the domain in the 203 # projectcontour/ratelimit-config config map. 204 domain: contour 205 # failOpen is whether to allow requests through 206 # if there's an error connecting to the RLS. 207 failOpen: false 208 ``` 209 210 Restart Contour to pick up the new config map: 211 212 ```bash 213 $ kubectl -n projectcontour rollout restart deploy/contour 214 deployment.apps/contour restarted 215 ``` 216 217 ## Deploy a sample app 218 219 To demonstrate how to use global rate limiting in a `HTTPProxy` resource, we first need to deploy a simple echo application: 220 221 ```yaml 222 apiVersion: apps/v1 223 kind: Deployment 224 metadata: 225 name: ingress-conformance-echo 226 spec: 227 replicas: 1 228 selector: 229 matchLabels: 230 app.kubernetes.io/name: ingress-conformance-echo 231 template: 232 metadata: 233 labels: 234 app.kubernetes.io/name: ingress-conformance-echo 235 spec: 236 containers: 237 - name: conformance-echo 238 image: agervais/ingress-conformance-echo:latest 239 ports: 240 - name: http-api 241 containerPort: 3000 242 readinessProbe: 243 httpGet: 244 path: /health 245 port: 3000 246 --- 247 apiVersion: v1 248 kind: Service 249 metadata: 250 name: ingress-conformance-echo 251 spec: 252 ports: 253 - name: http 254 port: 80 255 targetPort: http-api 256 selector: 257 app.kubernetes.io/name: ingress-conformance-echo 258 ``` 259 260 This echo server will respond with a JSON object that reports information about the HTTP request it received, including the request headers. 261 262 Once the application is running, we can expose it to Contour with a `HTTPProxy` resource: 263 264 ```yaml 265 apiVersion: projectcontour.io/v1 266 kind: HTTPProxy 267 metadata: 268 name: echo 269 spec: 270 virtualhost: 271 fqdn: local.projectcontour.io 272 routes: 273 - conditions: 274 - prefix: / 275 services: 276 - name: ingress-conformance-echo 277 port: 80 278 - conditions: 279 - prefix: /foo 280 services: 281 - name: ingress-conformance-echo 282 port: 80 283 ``` 284 285 We can verify that the application is working by requesting any path: 286 287 ```bash 288 $ curl -k http://local.projectcontour.io/test/$((RANDOM)) 289 {"TestId":"","Path":"/test/22808","Host":"local.projectcontour.io","Method":"GET","Proto":"HTTP/1.1","Headers":{"Accept":["*/*"],"Content-Length":["0"],"User-Agent":["curl/7.75.0"],"X-Envoy-Expected-Rq-Timeout-Ms":["15000"],"X-Envoy-Internal":["true"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Request-Id":["8ecb85e1-271b-44b4-9cf0-4859cbaed7a7"],"X-Request-Start":["t=1612903866.309"]}} 290 ``` 291 292 ## Add global rate limit policies 293 294 Now that we have a working application exposed by a `HTTPProxy` resource, we can add add global rate limiting to it. 295 296 Edit the `HTTPProxy` that we created in the previous step to add rate limit policies to both routes: 297 298 ```yaml 299 apiVersion: projectcontour.io/v1 300 kind: HTTPProxy 301 metadata: 302 name: echo 303 spec: 304 virtualhost: 305 fqdn: local.projectcontour.io 306 routes: 307 - conditions: 308 - prefix: / 309 services: 310 - name: ingress-conformance-echo 311 port: 80 312 rateLimitPolicy: 313 global: 314 descriptors: 315 - entries: 316 - remoteAddress: {} 317 - conditions: 318 - prefix: /foo 319 services: 320 - name: ingress-conformance-echo 321 port: 80 322 rateLimitPolicy: 323 global: 324 descriptors: 325 - entries: 326 - remoteAddress: {} 327 - entries: 328 - genericKey: 329 value: foo 330 ``` 331 332 ## Make requests 333 334 Before making requests to our `HTTPProxy`, let's quickly revisit the `ratelimit-config` config map. 335 Here's what we defined: 336 337 ```yaml 338 ... 339 descriptors: 340 # requests with a descriptor of ["generic_key": "foo"] 341 # are limited to one per minute. 342 - key: generic_key 343 value: foo 344 rate_limit: 345 unit: minute 346 requests_per_unit: 1 347 348 # each unique remote address (i.e. client IP) 349 # is limited to three total requests per minute. 350 - key: remote_address 351 rate_limit: 352 unit: minute 353 requests_per_unit: 3 354 ``` 355 356 The first entry says that requests with a descriptor of `["generic_key": "foo"]` should be limited to one per minute. 357 The second entry says that each unique remote address (client IP) should be allowed three total requests per minute. 358 All relevant rate limits are applied for each request, and requests that result in a `429 (Too Many Requests)` count against limits. 359 360 So, we should be able to make: 361 - a first request to `local.projectcontour.io/foo` that get a `200 (OK)` response 362 - a second request to `local.projectcontour.io/foo` that gets a `429 (Too Many Requests)` response (due to the first rate limit) 363 - a third request to `local.projectcontour.io/bar`that gets a `200 (OK)` response 364 - a fourth request to `local.projectcontour.io/bar`that gets a `429 (Too Many Requests)` response (due to the second rate limit) 365 366 Let's try it out (remember, you'll need to make all of these requests within 60 seconds since the rate limits are per minute): 367 368 Request #1: 369 ``` 370 $ curl -I local.projectcontour.io/foo 371 372 HTTP/1.1 200 OK 373 content-type: application/json 374 date: Mon, 08 Feb 2021 22:25:06 GMT 375 content-length: 403 376 x-envoy-upstream-service-time: 4 377 vary: Accept-Encoding 378 server: envoy 379 ``` 380 381 Request #2: 382 383 ``` 384 $ curl -I local.projectcontour.io/foo 385 386 HTTP/1.1 429 Too Many Requests 387 x-envoy-ratelimited: true 388 date: Mon, 08 Feb 2021 22:59:10 GMT 389 server: envoy 390 transfer-encoding: chunked 391 ``` 392 393 Request #3: 394 395 ``` 396 $ curl -I local.projectcontour.io/bar 397 398 HTTP/1.1 200 OK 399 content-type: application/json 400 date: Mon, 08 Feb 2021 22:59:54 GMT 401 content-length: 404 402 x-envoy-upstream-service-time: 2 403 vary: Accept-Encoding 404 server: envoy 405 ``` 406 407 Request #4: 408 409 ``` 410 $ curl -I local.projectcontour.io/bar 411 412 HTTP/1.1 429 Too Many Requests 413 x-envoy-ratelimited: true 414 date: Mon, 08 Feb 2021 23:00:28 GMT 415 server: envoy 416 transfer-encoding: chunked 417 ``` 418 419 ## Wrapping up 420 421 For more information, see the [Contour rate limiting documentation][7] and the [API reference documentation][8]. 422 423 The YAML used in this guide is available [in the Contour repository][9]. 424 425 [1]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting 426 [2]: ../deploy-options/#kind 427 [3]: https://projectcontour.io/getting-started/#option-1-quickstart 428 [4]: https://github.com/envoyproxy/ratelimit 429 [5]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/ratelimit/v3/rls.proto 430 [6]: https://github.com/envoyproxy/ratelimit#configuration 431 [7]: ../config/rate-limiting/ 432 [8]: ../config/api/ 433 [9]: {{< param github_url>}}/tree/main/examples/ratelimit 434 [10]: https://github.com/lyft/goruntime