본문 바로가기
WIKI 기술 지식 베이스

RateLimit

원문 보기 위키 갱신

출처: Traefik RateLimit Documentation

본문

RateLimit

rateLimit 미들웨어는 서비스가 공정한 양의 요청을 받도록 보장하며, 무엇이 공정한지 정의할 수 있게 해 줍니다.

이것은 토큰 버킷(token bucket) 구현을 기반으로 해요. 이 비유에서 average와 period 파라미터는 버킷이 다시 채워지는 속도를 정의하고, burst는 버킷의 크기(용량)입니다.

Rate와 Burst

rate는 average를 period로 나눠 정의합니다. 1 req/s 미만의 rate가 필요하면 period를 1초보다 크게 정의하면 돼요.

구성 예시

구조화된 (YAML)

# 여기서는 초당 평균 100개의 요청이 허용됩니다.
# 추가로 200개의 요청 버스트가 허용됩니다.
# Redis 분산 rate limiting이 사용 가능한 모든 옵션으로 구성됩니다.
http:
  middlewares:
    test-ratelimit:
      rateLimit:
        average: 100
        period: 1s
        burst: 200
        redis:
          endpoints:
            - "redis-primary.example.com:6379"
            - "redis-replica.example.com:6379"
          username: "ratelimit-user"
          password: "secure-password"
          db: 2
          poolSize: 50
          minIdleConns: 10
          maxActiveConns: 200
          readTimeout: 3s
          writeTimeout: 3s
          dialTimeout: 5s
          tls:
            ca: "/etc/ssl/redis-ca.crt"
            cert: "/etc/ssl/redis-client.crt"
            key: "/etc/ssl/redis-client.key"
            insecureSkipVerify: false

구조화된 (TOML)

# 여기서는 초당 평균 100개의 요청이 허용됩니다.
# 추가로 200개의 요청 버스트가 허용됩니다.
# Redis 분산 rate limiting이 사용 가능한 모든 옵션으로 구성됩니다.
[http.middlewares]
  [http.middlewares.test-ratelimit.rateLimit]
    average = 100
    period = "1s"
    burst = 200
    [http.middlewares.test-ratelimit.rateLimit.redis]
      endpoints = ["redis-primary.example.com:6379", "redis-replica.example.com:6379"]
      username = "ratelimit-user"
      password = "secure-password"
      db = 2
      poolSize = 50
      minIdleConns = 10
      maxActiveConns = 200
      readTimeout = "3s"
      writeTimeout = "3s"
      dialTimeout = "5s"
      [http.middlewares.test-ratelimit.rateLimit.redis.tls]
        ca = "/etc/ssl/redis-ca.crt"
        cert = "/etc/ssl/redis-client.crt"
        key = "/etc/ssl/redis-client.key"
        insecureSkipVerify = false

Labels

# 여기서는 초당 평균 100개의 요청이 허용됩니다.
# 추가로 200개의 요청 버스트가 허용됩니다.
# Redis 분산 rate limiting이 사용 가능한 모든 옵션으로 구성됩니다.
labels:
  - "traefik.http.middlewares.test-ratelimit.ratelimit.average=100"
  - "traefik.http.middlewares.test-ratelimit.ratelimit.period=1s"
  - "traefik.http.middlewares.test-ratelimit.ratelimit.burst=200"
  - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.endpoints=redis-primary.example.com:6379,redis-replica.example.com:6379"
  - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.username=ratelimit-user"
  - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.password=secure-password"
  - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.db=2"
  - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.poolSize=50"
  - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.minIdleConns=10"
  - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.maxActiveConns=200"
  - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.readTimeout=3s"
  - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.writeTimeout=3s"
  - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.dialTimeout=5s"
  - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.ca=/etc/ssl/redis-ca.crt"
  - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.cert=/etc/ssl/redis-client.crt"
  - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.key=/etc/ssl/redis-client.key"
  - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.insecureSkipVerify=false"

Tags

// 여기서는 초당 평균 100개의 요청이 허용됩니다.
// 추가로 200개의 요청 버스트가 허용됩니다.
// Redis 분산 rate limiting이 사용 가능한 모든 옵션으로 구성됩니다.
{
  "Tags": [
    "traefik.http.middlewares.test-ratelimit.ratelimit.average=100",
    "traefik.http.middlewares.test-ratelimit.ratelimit.period=1s",
    "traefik.http.middlewares.test-ratelimit.ratelimit.burst=200",
    "traefik.http.middlewares.test-ratelimit.ratelimit.redis.endpoints=redis-primary.example.com:6379,redis-replica.example.com:6379",
    "traefik.http.middlewares.test-ratelimit.ratelimit.redis.username=ratelimit-user",
    "traefik.http.middlewares.test-ratelimit.ratelimit.redis.password=secure-password",
    "traefik.http.middlewares.test-ratelimit.ratelimit.redis.db=2",
    "traefik.http.middlewares.test-ratelimit.ratelimit.redis.poolSize=50",
    "traefik.http.middlewares.test-ratelimit.ratelimit.redis.minIdleConns=10",
    "traefik.http.middlewares.test-ratelimit.ratelimit.redis.maxActiveConns=200",
    "traefik.http.middlewares.test-ratelimit.ratelimit.redis.readTimeout=3s",
    "traefik.http.middlewares.test-ratelimit.ratelimit.redis.writeTimeout=3s",
    "traefik.http.middlewares.test-ratelimit.ratelimit.redis.dialTimeout=5s",
    "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.ca=/etc/ssl/redis-ca.crt",
    "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.cert=/etc/ssl/redis-client.crt",
    "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.key=/etc/ssl/redis-client.key",
    "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.insecureSkipVerify=false"
  ]
}

Kubernetes

# 여기서는 초당 평균 100개의 요청이 허용됩니다.
# 추가로 200개의 요청 버스트가 허용됩니다.
# Redis 분산 rate limiting이 사용 가능한 모든 옵션으로 구성됩니다.
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: test-ratelimit
spec:
  rateLimit:
    average: 100
    period: 1s
    burst: 200
    redis:
      endpoints:
        - "redis-primary.example.com:6379"
        - "redis-replica.example.com:6379"
      secret: redis-credentials
      db: 2
      poolSize: 50
      minIdleConns: 10
      maxActiveConns: 200
      readTimeout: 3s
      writeTimeout: 3s
      dialTimeout: 5s
      tls:
        caSecret: redis-ca
        certSecret: redis-client-cert
        insecureSkipVerify: false

---
apiVersion: v1
kind: Secret
metadata:
  name: redis-credentials
  namespace: default
data:
  username: cmF0ZWxpbWl0LXVzZXI=  # base64 encoded "ratelimit-user"
  password: c2VjdXJlLXBhc3N3b3Jk  # base64 encoded "secure-password"

---
apiVersion: v1
kind: Secret
metadata:
  name: redis-ca
  namespace: default
data:
  tls.ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t...

---
apiVersion: v1
kind: Secret
metadata:
  name: redis-client-cert
  namespace: default
data:
  tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t...
  tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0t...

구성 옵션

| Field | Description | Default | Required | | average | period를 사용해 rate를 정의하는 요청 수. 0이면 rate limiting 없음. 자세한 내용은 여기. | 0 | No | | period | rate를 정의하는 데 사용하는 시간 기간. 자세한 내용은 여기. | 1s | No | | burst | 정확히 같은 순간에 통과할 수 있는 최대 요청 수. 자세한 내용은 여기. | 1 | No | | sourceCriterion.requestHost | 요청 호스트를 소스로 간주할지 여부. sourceCriterion에 대한 자세한 내용은 여기. | false | No | | sourceCriterion.requestHeaderName | 들어오는 요청을 그룹화하는 데 사용하는 헤더 이름. sourceCriterion에 대한 자세한 내용은 여기. | "" | No | | sourceCriterion.ipStrategy.depth | X-Forwarded-For 헤더에서 선택할 IP의 깊이 위치(오른쪽부터 시작). 0이면 깊이 없음. X-Forwarded-For의 총 IP 수보다 크면 클라이언트 IP는 비어 있게 돼요. 0보다 크면 excludedIPs 옵션은 평가되지 않습니다. sourceCriterion, ipStrategy, depth에 대한 자세한 내용은 아래를 참고하세요. | 0 | No | | sourceCriterion.ipStrategy.excludedIPs | X-Forwarded-For 헤더를 훑어 목록에 없는 첫 IP를 선택할 수 있게 해요. depth가 지정되면 excludedIPs는 무시됩니다. sourceCriterion, ipStrategy, excludedIPs에 대한 자세한 내용은 아래를 참고하세요. | | No | | sourceCriterion.ipStrategy.ipv6Subnet | ipv6Subnet이 제공되고 선택된 IP가 IPv6라면, 그 IP는 속한 서브넷의 첫 IP로 변환됩니다. sourceCriterion, ipStrategy.ipv6Subnet에 대한 자세한 내용은 아래를 참고하세요. | | No | | redis | redis 구성은 여러 Traefik 인스턴스에 걸쳐 rate limit 토큰을 저장하기 위해 Redis를 사용해 분산 rate limiting을 활성화해요. 이를 통해 Traefik 프록시 클러스터 전반에 일관된 rate limit을 강제할 수 있습니다. Redis가 구성되지 않으면 Traefik은 rate limiting에 인메모리 저장소를 사용하는데, 이는 개별 Traefik 인스턴스에서만 동작해요. | | No | | redis.endpoints | 분산 rate limiting을 위한 Redis 서버 엔드포인트 목록. Redis 클러스터나 고가용성 구성에서는 여러 엔드포인트를 지정할 수 있어요. | "localhost:6379" | No | | redis.username | Redis 인증용 사용자 이름. | "" | No | | redis.password | Redis 인증용 비밀번호. Kubernetes에서는 secrets를 통해 제공할 수 있어요. | "" | No | | redis.db | 선택할 Redis 데이터베이스 번호. | 0 | No | | redis.poolSize | 풀의 기본 소켓 연결 수를 정의해요. 0으로 설정하면 runtime.GOMAXPROCS가 보고하는 CPU 코어당 10개 연결로 기본 설정됩니다. 풀에 충분한 연결이 없으면 poolSize를 넘어 maxActiveConns까지 새 연결이 할당됩니다. | 0 | No | | redis.minIdleConns | 풀에서 유지할 최소 유휴 연결 수. 새 연결을 맺는 것이 느릴 때 유용해요. 0이면 유휴 연결이 자동으로 닫히지 않습니다. | 0 | No | | redis.maxActiveConns | 풀이 주어진 순간에 할당할 수 있는 최대 연결 수. 0이면 제한 없음을 의미해요. | 0 | No | | redis.readTimeout | 소켓 읽기 타임아웃. 도달하면 명령이 차단하는 대신 타임아웃으로 실패합니다. 0이면 타임아웃 없음. | 3s | No | | redis.writeTimeout | 소켓 쓰기 타임아웃. 도달하면 명령이 차단하는 대신 타임아웃으로 실패합니다. 0이면 타임아웃 없음. | 3s | No | | redis.dialTimeout | 새 연결을 맺기 위한 타임아웃. 0이면 타임아웃 없음. | 5s | No | | redis.tls.ca | Redis에 대한 보안 연결에 사용하는 인증 기관의 경로. 시스템 번들로 기본 설정됩니다. | "" | No | | redis.tls.cert | Redis에 대한 보안 연결에 사용하는 공개 인증서의 경로. 이 옵션이 설정되면 key 옵션이 필요해요. | "" | No | | redis.tls.key | Redis에 대한 보안 연결에 사용하는 개인 키의 경로. 이 옵션이 설정되면 cert 옵션이 필요해요. | "" | No | | redis.tls.insecureSkipVerify | insecureSkipVerify가 true면, Redis로의 TLS 연결은 서버가 제시하는 인증서를 호스트 이름과 관계없이 모두 수락해요. | false | No |

sourceCriterion

sourceCriterion 옵션은 어떤 기준으로 요청을 공통 소스에서 온 것으로 그룹화할지 정의합니다. 여러 전략이 동시에 정의되면 오류가 발생해요. 아무것도 설정하지 않으면 기본값은 요청의 원격 주소(remote address) 필드를 사용합니다(ipStrategy로).

ipStrategy

ipStrategy 옵션은 Traefik이 클라이언트 IP를 어떻게 결정할지 구성하는 세 가지 파라미터를 정의합니다: depth, excludedIPs, ipv6Subnet.

미들웨어로서 rate limiting은 실제 백엔드로의 프록시가 일어나기 전에 실행됩니다. 게다가 이전 네트워크 홉은 프록시의 마지막 단계, 즉 이미 rate limiting을 통과한 뒤에만 X-Forwarded-For에 추가돼요. 따라서 rate limiting 중에는 이전 네트워크 홉이 아직 X-Forwarded-For에 없으므로, 이를 찾거나 의존할 수 없습니다.

sourceCriterion.ipStrategy.ipv6Subnet

이 전략은 Depth와 RemoteAddr 전략에만 적용됩니다. ipv6Subnet이 제공되고 선택된 IP가 IPv6라면, 그 IP는 속한 서브넷의 첫 IP로 변환됩니다.

새 IPv6를 얻어 이 미들웨어를 우회하는 것을 막기 위해 IPv6 주소를 서브넷으로 그룹화할 때 유용해요.

  • ipv6Subnet 값이 0-128 범위 밖이면 무시됩니다.

ipv6Subnet 예시

ipv6Subnet이 제공되면 IP는 다음과 같이 변환됩니다.

| IP | ipv6Subnet | clientIP | | "::abcd:1111:2222:3333" | 64 | "::0:0:0:0" | | "::abcd:1111:2222:3333" | 80 | "::abcd:0:0:0:0" | | "::abcd:1111:2222:3333" | 96 | "::abcd:1111:0:0:0" |

sourceCriterion.ipStrategy.depth

depth가 2로 설정되고 요청의 X-Forwarded-For 헤더가 "10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"이라면, "실제" 클라이언트 IP는 "10.0.0.1"(depth 4 지점)이지만 기준으로 사용되는 IP는 "12.0.0.1"(depth=2)입니다.

| X-Forwarded-For | depth | clientIP | | "10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1" | 1 | "13.0.0.1" | | "10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1" | 3 | "11.0.0.1" | | "10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1" | 5 | "" |

sourceCriterion.ipStrategy.excludedIPs

이름이 암시하는 것과 달리, 이 옵션은 rate limiter에서 IP를 제외하는 것이 아니므로, 일부 IP의 rate limiting을 비활성화하는 데 사용할 수 없습니다.

excludedIPs는 다소 구별되는 두 부류의 사용 사례를 다루기 위한 것입니다:

  • 같은 (집합의) 리버스 프록시 뒤에 있는 IP들을 구별해서, 각각이 다른 것과 독립적으로 자신의 rate-limit "버킷"(토큰 버킷 참고)에 기여하도록 합니다. 이 경우, 실제 clientIP를 찾기 위해 제외할 X-Forwarded-For IP 목록과 일치하도록 excludedIPs를 설정해야 합니다.

각 IP를 구별된 소스로 사용하는 예시:

| X-Forwarded-For | excludedIPs | clientIP | | "10.0.0.1,11.0.0.1,12.0.0.1" | "11.0.0.1,12.0.0.1" | "10.0.0.1" | | "10.0.0.2,11.0.0.1,12.0.0.1" | "11.0.0.1,12.0.0.1" | "10.0.0.2" |

  • IP 집합(역시 공통 리버스 프록시 집합 뒤에 있는)을 함께 그룹화해서 같은 소스로 간주하고, 모두 같은 rate-limit 버킷에 기여하도록 합니다.

IP를 같은 소스로 그룹화하는 예시:

| X-Forwarded-For | excludedIPs | clientIP | | "10.0.0.1,11.0.0.1,12.0.0.1" | "12.0.0.1" | "11.0.0.1" | | "10.0.0.2,11.0.0.1,12.0.0.1" | "12.0.0.1" | "11.0.0.1" | | "10.0.0.3,11.0.0.1,12.0.0.1" | "12.0.0.1" | "11.0.0.1" |

더 알아보기 (Learn more)