/Traefik Cluster Rate Limiter

Traefik Cluster Rate Limiter

3
v1.1.1

traefik-cluster-ratelimit

Traefik comes with a default rate limiter middleware, but the rate limiter doesn't share a state if you are using several instance of Traefik (think kubernetes HA deployment for example).

This plugin is here to solve this issue: using a Redis as a common state, this plugin implement the token bucket algorithm.

Configuration

You need to setup the static and dynamic configuration

The following declaration (given here in YAML) defines the plugin:

# Static configuration
experimental:
plugins:
clusterRatelimit:
moduleName: "github.com/nzin/traefik-cluster-ratelimit"
version: "v1.1.1"

Here is an example of a file provider dynamic configuration (given here in YAML), where the interesting part is the http.middlewares section:

# Dynamic configuration
http:
routers:
my-router:
rule: host(`demo.localhost`)
service: service-foo
entryPoints:
- web
middlewares:
- my-middleware
services:
service-foo:
loadBalancer:
servers:
- url: http://127.0.0.1:5000
middlewares:
my-middleware:
plugin:
clusterRatelimit:
average: 50
burst: 100

With a kubernetesingress provider:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: clusterratelimit
namespace: ingress-traefik
spec:
clusterRatelimit:
average: 100
burst: 200
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
namespace: ingress-traefik
annotations:
traefik.ingress.kubernetes.io/router.middlewares: ingress-traefik-clusterratelimit@kubernetescrd
spec:
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example-service
port:
number: 80

Extra configuration

The average and the burst are the number of allowed connection per second, there are other variables:

VariableDescriptiondefault
periodthe period (in seconds) of the rate limiter window1
averageallowed requests per "period" ( 0 = unlimited)
burstallowed burst requests per "period"
redisAddressaddress of the redis serverredis:6379
redisDbredis db to use0
redisPasswordredis authentication (if any)
sourceCriterion.*defines what criterion is used to group requests. See nextipStrategy
sourceCriterion.ipStrategyclient IP based source
sourceCriterion.ipStrategy.depthtells Traefik to use the X-Forwarded-For header and select the IP located at the depth position
sourceCriterion.ipStrategy.excludedIPslist of X-Forwarded-For IPs that are to be excluded
sourceCriterion.requestHostbased source on request host
sourceCriterion.requestHeaderNameName of the header used to group incoming requests
breakerThresholdnumber of failed connection before pausing Redis3
breakerReattemptnb seconds before attempting to reconnect to Redis15
redisConnectionTimeoutredis connection timeout (in seconds)2

Notes:

  • for more information about sourceCriteron check the Traefik ratelimit page
  • regarding redispassword, if you dont want to set it in clear text in the traefik configuration, you can specify a variable name starting with '$'. For example $REDIS_PASSWORD will use the REDIS_PASSWORD environment variable

A full example would be

# Dynamic configuration
http:
...
middlewares:
my-middleware:
plugin:
clusterRatelimit:
average: 5
burst: 10
period: 10
sourceCriterion:
ipStrategy:
depth: 2
excludedIPs:
- 127.0.0.1/32
- 192.168.1.7
redisAddress: redis:6379
redisPassword: $REDIS_AUTH_PASSWORD
redisConnectionTimeout: 2

Circuit-breaker

If the Redis server is not available, we will stop talking to it, and let pass through. As mentionned above there are 2 variables you can use to change the default behaviour: breakerThreshold and breakerReattempt. Usually you dont need to tweak that.

Benchmark

You can test traefik with the rate limiter with some tools. For example with vegeta (you probably need to install it):

docker-compose up -d
echo "GET http://localhost:8000/" | vegeta attack -duration=5s -rate=200 | tee results.bin | vegeta report