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

Traefik Distributed RateLimit 미들웨어

원문 보기 위키 갱신

출처: Traefik Distributed RateLimit 미들웨어 (Distributed RateLimit)

본문

Distributed RateLimit

Traefik Hub 기능

이 미들웨어는 Traefik Hub에서만 독점적으로 사용할 수 있어요. Traefik Hub의 고급 기능에 대해 자세히 알아보세요.

Distributed RateLimit 미들웨어는 개별 프록시에서만이 아니라 클러스터 전체에 걸쳐 시간에 따라 요청이 제한되도록 보장해요.

토큰 버킷(token bucket) 구현을 기반으로 해요.

설정 예시

아래는 Redis 백엔드를 사용해 클러스터 전체에 걸친 속도 제한을 가능하게 하는 Distributed RateLimit 미들웨어의 고급 구성이에요.

Middleware Distributed Rate Limit

# 여기서는 초당 100개의 요청 한도가 허용됩니다.
# 또한 200개의 요청 버스트가 허용됩니다.
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: test-distributedratelimit
  namespace: traefik
spec:
  plugin:
    distributedRateLimit:
      burst: 200
      limit: 100
      period: 1s
      denyOnError: false
      responseHeaders: true
      sourceCriterion:
        ipStrategy:
          excludedIPs:
            - 172.20.176.201
      store:
        redis:
          endpoints:
            - my-release-redis-master.default.svc.cluster.local:6379
          # 같은 namespace에 있는 Secret redis의 password 필드 사용
          password: urn:k8s:secret:redis:password
          timeout: 500ms

Kubernetes Secret

apiVersion: v1
kind: Secret
metadata:
  name: redis
  namespace: traefik
stringData:
  password: mysecret12345678

Rate와 Burst

Rate는 limit를 period로 나눠 정의돼요. 1 req/s보다 낮은 rate의 경우 period를 1초보다 크게 정의하세요.

미들웨어는 토큰 버킷 구현을 기반으로 해요. 이 비유에서 limit와 period 파라미터는 버킷이 다시 채워지는 rate를 정의하고, burst는 버킷의 크기(용량)예요.

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: test-ratelimit
spec:
  plugin:
    distributedRateLimit:
      burst: 100
      period: 1m
      limit: 6

위 예시에서 미들웨어는 최대 100개의 병렬 연결(burst)을 허용해요. 각 연결은 토큰 하나를 소비하고, 100개의 토큰이 모두 소비되면 버킷에 토큰이 하나 이상 생길 때까지 나머지 요청은 차단돼요.

버킷이 가득 차 있지 않을 때, 토큰 하나가 10초마다 생성돼요 (1분마다 6개, period / limit).

설정 옵션

| 필드 | 설명 | 기본값 | 필수 | | limit | period를 사용해 rate를 정의하는 데 쓰이는 요청 수예요. 0은 속도 제한이 없다는 뜻이에요. 자세한 내용은 여기. | 0 | 아니요 | | period | rate를 정의하는 데 쓰이는 시간 기간이에요. 자세한 내용은 여기. | 1s | 아니요 | | burst | 정확히 같은 순간에 통과가 허용되는 최대 요청 수예요. 자세한 내용은 여기. | 1 | 아니요 | | denyOnError | rate limit 저장소(Redis)를 사용할 수 없을 때 요청을 거부할지 여부예요. false일 때 Redis에 연결할 수 없으면 요청이 통과되도록 허용돼요. | true | 아니요 | | responseHeaders | rate limit 헤더(X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset)를 응답에 주입할지 여부예요. | false | 아니요 | | store.redis.endpoints | 연결할 Redis 인스턴스의 엔드포인트예요 (예: redis.traefik-hub.svc.cluster.local:6379) | "" | 예 | | store.redis.username | Traefik Hub가 Redis에 연결할 때 사용할 사용자 이름이에요 | "" | 아니요 | | store.redis.password | Traefik Hub가 Redis에 연결할 때 사용할 비밀번호예요 | "" | 아니요 | | store.redis.database | Traefik Hub가 정보를 저장할 때 사용할 데이터베이스예요. Redis Cluster 모드에서는 사용할 수 없어요 (데이터베이스 0만 지원돼요). | 0 | 아니요 | | store.redis.timeout | Redis 연결의 dial, read, write 연산에 적용되는 타임아웃이에요. | "" | 아니요 | | store.redis.cluster | Redis Cluster 모드를 활성화해요. 활성화하려면 {}로 설정하고, 비활성화하려면 생략해요. store.redis.sentinel과 함께 사용할 수 없어요. | - | 아니요 | | store.redis.sentinel.masterSet | Redis Sentinel 마스터 셋의 이름이에요. store.redis.cluster와 함께 사용할 수 없어요. | "" | 예 (Sentinel 사용 시) | | store.redis.sentinel.username | Redis Sentinel 인증에 사용할 사용자 이름이에요. | "" | 아니요 | | store.redis.sentinel.password | Redis Sentinel 인증에 사용할 비밀번호예요. | "" | 아니요 | | store.redis.tls.ca | 커스텀 CA 번들이에요 | "" | 아니요 | | store.redis.tls.cert | TLS 인증서예요 | "" | 아니요 | | store.redis.tls.key | TLS 키예요 | "" | 아니요 | | store.redis.tls.insecureSkipVerify | TLS 검증 건너뛰기를 허용해요 | false | 아니요 | | sourceCriterion.requestHost | 요청 호스트를 소스로 간주할지 여부예요. sourceCriterion에 대한 자세한 내용은 여기. | false | 아니요 | | sourceCriterion.requestHeaderName | 들어오는 요청을 그룹화하는 데 사용되는 헤더 이름이에요. sourceCriterion에 대한 자세한 내용은 여기. | "" | 아니요 | | sourceCriterion.ipStrategy.depth | X-Forwarded-For 헤더에서 선택할 IP의 depth 위치예요 (오른쪽에서부터 시작). 0은 depth가 없다는 뜻이에요. X-Forwarded-For의 총 IP 수보다 크면 클라이언트 IP는 비어 있어요. 0보다 크면 excludedIPs 옵션은 평가되지 않아요. sourceCriterion, ipStrategy, depth에 대한 자세한 내용은 아래를 참고하세요. | 0 | 아니요 | | sourceCriterion.ipStrategy.excludedIPs | Traefik이 X-Forwarded-For 헤더를 스캔해 목록에 없는 첫 IP를 선택하도록 해요. depth가 지정되면 excludedIPs는 무시돼요. sourceCriterion, ipStrategy, excludedIPs에 대한 자세한 내용은 아래를 참고하세요. | | 아니요 |

sourceCriterion

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

ipStrategy

ipStrategy 옵션은 Traefik이 클라이언트 IP를 결정하는 방식을 구성하는 두 파라미터 depth와 excludedIPs를 정의해요.

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

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들을 구분해서, 각 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" |

store

Distributed Rate Limit 미들웨어는 데이터를 저장하기 위해 영구 KV 저장소를 사용해요.

Redis 연결을 구성하려면 redis 옵션을 참고하세요.

Redis 서버에 대한 연결 파라미터는 Middleware 배포에 첨부돼요.

다음 Redis 모드가 지원돼요:

  • 단일 인스턴스 모드

  • Redis Cluster

  • Redis Sentinel

Redis에 대한 자세한 내용은 공식 Redis 문서를 권장해요.

정보

단일 인스턴스 모드나 Redis Sentinel에서 Redis를 사용한다면 database 필드를 구성할 수 있어요. Redis Cluster를 사용하면 이 값은 고려되지 않아요 (데이터베이스 0만 사용 가능해요). 이 경우 경고가 표시되고 값은 무시돼요.

더 알아보기 (Learn more)