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