Configuring Rate Limiting

In this guide, we’ll walk you through deploying an HTTPLocalRateLimitPolicy resource to rate-limit the traffic to a given service.

For more information about Linkerd’s rate limiting check the Rate Limiting feature doc and the HTTPLocalRateLimitPolicy reference doc.

Prerequisites

To use this guide you’ll only need a Kubernetes cluster running a Linkerd instance. You can follow the installing Linkerd Guide.

Setup

First inject and install the Emojivoto application, then scale-down the vote-bot workload to avoid it interfering with our testing:

  1. linkerd inject https://run.linkerd.io/emojivoto.yml | kubectl apply -f -
  2. kubectl -n emojivoto scale --replicas 0 deploy/vote-bot

Finally, deploy a workload with an Ubuntu image, open a shell into it and install curl:

  1. kubectl create deployment client --image ubuntu -- bash -c "sleep infinity"
  2. kubectl exec -it client-xxx -- bash
  3. root@client-xxx:/# apt-get update && apt-get install -y curl

Leave that shell open so we can use it below when sending requests.

Creating an HTTPLocalRateLimitPolicy resource

We need first to create a Server resource pointing to the web-svc service. Note that this Server has accessPolicy: all-unauthenticated, which means that traffic is allowed by default and we don’t require to declare authorization policies associated to it:

  1. kubectl apply -f - <<EOF
  2. ---
  3. apiVersion: policy.linkerd.io/v1beta3
  4. kind: Server
  5. metadata:
  6. namespace: emojivoto
  7. name: web-http
  8. spec:
  9. accessPolicy: all-unauthenticated
  10. podSelector:
  11. matchLabels:
  12. app: web-svc
  13. port: http
  14. proxyProtocol: HTTP/1
  15. EOF

Now we can apply the HTTPLocalRateLimitPolicy resource pointing to that Server. For now, we’ll just set a limit of 4 RPS per identity:

  1. kubectl apply -f - <<EOF
  2. ---
  3. apiVersion: policy.linkerd.io/v1alpha1
  4. kind: HTTPLocalRateLimitPolicy
  5. metadata:
  6. namespace: emojivoto
  7. name: web-http
  8. spec:
  9. targetRef:
  10. group: policy.linkerd.io
  11. kind: Server
  12. name: web-http
  13. identity:
  14. requestsPerSecond: 4
  15. EOF

Sending requests

In the Ubuntu shell, issue 10 concurrent requests to web-svc.emojivoto:

  1. root@client-xxx:/# results=$(for i in {1..10}; do curl -s -o /dev/null -w "%{http_code}\n" "http://web-svc.emojivoto" & done; wait)
  2. root@client-xxx:/# echo $results
  3. 200 200 200 429 429 429 429 200 429 429

We see that only 4 requests were allowed. The requests that got rate-limited receive a response with a 429 HTTP status code.

Overrides

The former client had no identity as it was deployed in the default namespace, where workloads are not injected by default.

Now let’s create a new Ubuntu workload in the emojivoto namespace, which will be injected by default, and whose identity will be associated to the default ServiceAccount in the emojivoto namespace:

  1. kubectl -n emojivoto create deployment client --image ubuntu -- bash -c "sleep infinity"
  2. kubectl -n emojivoto exec -it client-xxx -c ubuntu -- bash
  3. root@client-xxx:/# apt-get update && apt-get install -y curl

Before issuing requests, let’s expand the HTTPLocalRateLimitPolicy resource, adding an override for this specific client, that’ll allow it to issue requests up to 6 RPS:

  1. kubectl apply -f - <<EOF
  2. ---
  3. apiVersion: policy.linkerd.io/v1alpha1
  4. kind: HTTPLocalRateLimitPolicy
  5. metadata:
  6. namespace: emojivoto
  7. name: web-http
  8. spec:
  9. targetRef:
  10. group: policy.linkerd.io
  11. kind: Server
  12. name: web-http
  13. identity:
  14. requestsPerSecond: 4
  15. overrides:
  16. - requestsPerSecond: 6
  17. clientRefs:
  18. - kind: ServiceAccount
  19. namespace: emojivoto
  20. name: default
  21. EOF

And finally back in the shell we execute the requests:

  1. root@client-xxx:/# results=$(for i in {1..10}; do curl -s -o /dev/null -w "%{http_code}\n" "http://web-svc.emojivoto" & done; wait)
  2. root@client-xxx:/# echo $results
  3. 429 429 429 429 200 200 200 200 200 200

We see that now 6 requests were allowed. If we tried again with the former client, we could verify we would still be allowed to 4 requests only.