NGINX Gateway Fabric Rate Limit 설정 가이드

이번 포스트에서는 NGINX Gateway Fabric Rate Limit 을 Kubernetes 환경에서 효과적으로 구현하는 방법을 자세히 설명합니다. NGINX Gateway Fabric 2.4.2가 릴리스되었으며, 이전 버전(1.x)에서 2.0으로 업그레이드하려면 공식 업그레이드 가이드를 참고하세요. 1.x 문서는 이전 버전 문서를 확인하세요.

Rate Limiting(속도 제한)은 DDoS 공격 방어, API 과도 사용 방지, 서버 안정성 유지에 필수적인 기능입니다. NGINX Gateway Fabric에서는 RateLimitPolicy API를 통해 Gateway API 표준으로 Rate Limiting을 선언적으로 구성할 수 있어, 기존 NGINX 설정보다 훨씬 편리하고 유지보수하기 쉽습니다. 특히 noDelay 옵션 유무에 따라 지연 방식 vs 즉시 차단 방식을 선택할 수 있어 실무 적용성이 매우 높습니다.

목차

1. NGINX Gateway Fabric Rate Limiting 개요와 NGINX 원리
2. RateLimitPolicy API 특징과 장점
3. NGINX Gateway Fabric Rate Limiting 구성 방법
4. 실제 RateLimitPolicy 예시 (noDelay 옵션 포함)
4-1. 매니페스트 파일 구성 (Gateway, HTTPRoute, RateLimitPolicy)
4-2. noDelay 옵션 차이점과 테스트 결과
5. 결론

1. NGINX Gateway Fabric Rate Limit 개요와 NGINX 원리

NGINX Gateway Fabric Rate Limit 은 NGINX의 limit_reqlimit_req_zone 디렉티브를 Custom Resource로 변환한 기능입니다. 클라이언트 별로 요청 횟수를 제한하여 서버 과부하를 방지합니다.

핵심 원리: Leaky Bucket (구멍 난 양동이)

NGINX는 요청을 처리할 때 ‘Leaky Bucket’ 모델을 사용합니다.

  • Rate (구멍의 크기): 물이 빠져나가는 속도입니다. 5r/s라면 0.2초당 1개의 요청만 처리하도록 구멍이 설계된 것입니다.
  • Burst (양동이의 크기): 갑자기 물이 쏟아질 때 넘치지 않게 담아두는 ‘여유 공간’입니다. burst=5는 대기열에 5개까지 요청을 보관할 수 있다는 뜻입니다.

주요 동작 원리:

  • limit_req_zone: 키(예: $binary_remote_addr)별 토큰 버킷 생성, zoneSize로 메모리 할당
  • rate: 초당 허용 요청 수 (예: 5r/s)
  • burst: 버퍼 허용량
  • noDelay 옵션:
    • 미설정: burst 초과 시 지연 처리 (큐에 쌓아 rate 준수)
    • true: burst 초과 시 즉시 503 반환 (지연 없음)

Gateway 적용 시 http 컨텍스트, Route 적용 시 location 컨텍스트에 설정되며, 두 정책이 병합되어 누적 제한이 적용됩니다.

2. RateLimitPolicy API 특징과 장점

RateLimitPolicy는 Inherited PolicyAttachment로, Gateway 또는 Route에 붙여 사용할 수 있습니다.

  • Gateway 수준: 모든 Route에 공통 Rate Limiting 적용 (운영자용 기본 정책)
  • Route 수준: 애플리케이션별 세밀한 제한 (개발자용 맞춤 정책)
  • 정책 병합: Gateway + Route → 가장 엄격한 제한 우선
  • NGINX 매핑: limit_req, burst, noDelay, log_level, status 코드 등 지원
  • dry-run 모드 등 테스트 안전성 높음

장점: Kubernetes 네이티브 선언적 구성, NGINX 고성능 Rate Limiting 활용, 버전 관리 용이. 정책 중첩 시 주의 필요합니다.

3. NGINX Gateway Fabric Rate Limit 구성 방법

NGINX Gateway Fabric 설치 후 Gateway와 HTTPRoute 생성 → RateLimitPolicy CRD 적용. targetRefs로 대상 지정, local.rateLimit.rules에서 zoneSize, key, rate, burst, noDelay 설정.

noDelay 선택 가이드:

  • noDelay 없이: 사용자 체감 지연 발생 → 정상 트래픽 보호 우선
  • noDelay: true: 즉시 차단 → 보안(공격 차단) 우선

4. 실제 RateLimitPolicy 예시 (noDelay 옵션 포함)

ho-ngf 네임스페이스에서 nginx-example-1에 Route 단위 Rate Limiting 적용 (초당 5회, 버스트 5회).

4-1. 매니페스트 파일 구성 (Gateway, HTTPRoute, RateLimitPolicy)

백엔드 Deployment & Service:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-example-1
spec:
replicas: 2
selector:
matchLabels:
app: nginx-example-1
template:
metadata:
labels:
app: nginx-example-1
spec:
containers:
- name: nginx-example-1
image: nginxstore/webapp-example-1:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-example-1
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
selector:
app: nginx-example-1
Gateway:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: ho-ngf-gateway
namespace: ho-ngf
spec:
gatewayClassName: nginx
listeners:
- name: http
port: 80
protocol: HTTP
hostname: "*.devopsshin.com"
HTTPRoute:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: ho-ngf-httproute
namespace: ho-ngf
spec:
parentRefs:
- name: ho-ngf-gateway
sectionName: http
hostnames:
- "ho-ngf.devopsshin.com"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: nginx-example-1
port: 80

RateLimitPolicy (noDelay 미설정 – 지연 방식):

apiVersion: gateway.nginx.org/v1alpha1
kind: RateLimitPolicy
metadata:
name: route-rate-limit
namespace: ho-ngf
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: ho-ngf-httproute
rateLimit:
local:
rules:
- zoneSize: 10m
key: "$binary_remote_addr"
rate: 5r/s
burst: 5

RateLimitPolicy (noDelay: true – 즉시 차단):

apiVersion: gateway.nginx.org/v1alpha1
kind: RateLimitPolicy
metadata:
name: route-rate-limit-nodelay
namespace: ho-ngf
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: ho-ngf-httproute
rateLimit:
local:
rules:
- zoneSize: 10m
key: "$binary_remote_addr"
rate: 5r/s
burst: 5
noDelay: true

4-2. noDelay 옵션 차이점과 테스트 결과

Rate Limit Request Python Code:
import requests
import threading
import time
from datetime import datetime
URL = "http://example.com" # 테스트 주소
TOTAL = 30
INTERVAL = 1.0 / TOTAL # 1초 동안 30개 균등 발사 (약 0.033초 간격)
results = []
lock = threading.Lock()
def send(idx):
sent_at = datetime.now()
try:
r = requests.get(URL, timeout=5)
status = r.status_code
except:
status = "ERR"
resp_at = datetime.now()
with lock:
results.append({
'id': idx,
'sent': sent_at,
'resp': resp_at,
'status': status,
'latency': (resp_at - sent_at).total_seconds()
})
# 실행부
print(f"🚀 테스트 시작: 1초 동안 {TOTAL}개의 요청을 보냅니다...")
threads = [threading.Thread(target=send, args=(i,)) for i in range(TOTAL)]
for t in threads:
t.start()
time.sleep(INTERVAL)
for t in threads:
t.join()
# 결과 정렬 및 출력
results.sort(key=lambda x: x['id'])
print("\n" + "="*95)
print(f"{'ID':<4} | {'Sent At':<12} | {'Resp At':<12} | {'Latency':<10} | {'Status':<8} | {'Note'}")
print("-" * 95)
for r in results:
s_time = r['sent'].strftime('%H:%M:%S.%f')[:12]
r_time = r['resp'].strftime('%H:%M:%S.%f')[:12]
latency = f"{r['latency']:.3f}s"
# 상태별 노트 및 아이콘
if r['status'] == 200:
if r['latency'] > 0.1: # 0.1초 이상 걸리면 지연된 것으로 간주
note = f"⏳ 지연 처리 (Queueing)"
status_text = "✅ 200"
else:
note = "⚡ 즉시 처리 (Pass)"
status_text = "✅ 200"
else:
note = "🚫 거절됨 (Rejected)"
status_text = f"❌ {r['status']}"
print(f"{r['id']:02d} | {s_time:<12} | {r_time:<12} | {latency:<10} | {status_text:<8} | {note}")
print("="*95)
  • 핵심: “차례대로 보내주기”
  • 동작: 바구니에 담긴 요청들을 설정된 속도(rate=5r/s, 즉 0.2초 간격)에 맞춰 줄을 세워 하나씩 서버로 보냅니다.
  • 결과: 사용자는 성공(200)하더라도 뒤로 갈수록 응답이 느려지는(지연) 현상을 겪습니다.
🚀 테스트 시작: 1초 동안 30개의 요청을 보냅니다...
===============================================================================================
ID | Sent At | Resp At | Latency | Status | Note
-----------------------------------------------------------------------------------------------
00 | 11:14:35.922 | 11:14:35.942 | 0.020s | ✅ 200 | ⚡ 즉시 처리 (Pass)
01 | 11:14:35.958 | 11:14:36.139 | 0.182s | ✅ 200 | ⏳ 지연 처리 (Queueing)
02 | 11:14:35.993 | 11:14:36.336 | 0.343s | ✅ 200 | ⏳ 지연 처리 (Queueing)
03 | 11:14:36.029 | 11:14:36.537 | 0.508s | ✅ 200 | ⏳ 지연 처리 (Queueing)
04 | 11:14:36.066 | 11:14:36.738 | 0.672s | ✅ 200 | ⏳ 지연 처리 (Queueing)
05 | 11:14:36.103 | 11:14:36.938 | 0.835s | ✅ 200 | ⏳ 지연 처리 (Queueing)
06 | 11:14:36.140 | 11:14:37.137 | 0.997s | ✅ 200 | ⏳ 지연 처리 (Queueing)
07 | 11:14:36.177 | 11:14:36.182 | 0.006s | ❌ 503 | 🚫 거절됨 (Rejected)
08 | 11:14:36.213 | 11:14:36.219 | 0.006s | ❌ 503 | 🚫 거절됨 (Rejected)
09 | 11:14:36.250 | 11:14:36.256 | 0.006s | ❌ 503 | 🚫 거절됨 (Rejected)
10 | 11:14:36.284 | 11:14:36.292 | 0.008s | ❌ 503 | 🚫 거절됨 (Rejected)
11 | 11:14:36.323 | 11:14:36.328 | 0.005s | ❌ 503 | 🚫 거절됨 (Rejected)
12 | 11:14:36.359 | 11:14:37.337 | 0.978s | ✅ 200 | ⏳ 지연 처리 (Queueing)
13 | 11:14:36.396 | 11:14:36.402 | 0.006s | ❌ 503 | 🚫 거절됨 (Rejected)
14 | 11:14:36.433 | 11:14:36.438 | 0.005s | ❌ 503 | 🚫 거절됨 (Rejected)
15 | 11:14:36.469 | 11:14:36.475 | 0.006s | ❌ 503 | 🚫 거절됨 (Rejected)
16 | 11:14:36.506 | 11:14:36.512 | 0.006s | ❌ 503 | 🚫 거절됨 (Rejected)
17 | 11:14:36.543 | 11:14:37.540 | 0.997s | ✅ 200 | ⏳ 지연 처리 (Queueing)
18 | 11:14:36.579 | 11:14:36.585 | 0.005s | ❌ 503 | 🚫 거절됨 (Rejected)
19 | 11:14:36.615 | 11:14:36.622 | 0.006s | ❌ 503 | 🚫 거절됨 (Rejected)
20 | 11:14:36.652 | 11:14:36.657 | 0.006s | ❌ 503 | 🚫 거절됨 (Rejected)
21 | 11:14:36.688 | 11:14:36.694 | 0.006s | ❌ 503 | 🚫 거절됨 (Rejected)
22 | 11:14:36.724 | 11:14:36.731 | 0.007s | ❌ 503 | 🚫 거절됨 (Rejected)
23 | 11:14:36.761 | 11:14:37.742 | 0.981s | ✅ 200 | ⏳ 지연 처리 (Queueing)
24 | 11:14:36.799 | 11:14:36.806 | 0.008s | ❌ 503 | 🚫 거절됨 (Rejected)
25 | 11:14:36.835 | 11:14:36.841 | 0.006s | ❌ 503 | 🚫 거절됨 (Rejected)
26 | 11:14:36.872 | 11:14:36.880 | 0.009s | ❌ 503 | 🚫 거절됨 (Rejected)
27 | 11:14:36.910 | 11:14:36.917 | 0.008s | ❌ 503 | 🚫 거절됨 (Rejected)
28 | 11:14:36.948 | 11:14:37.937 | 0.989s | ✅ 200 | ⏳ 지연 처리 (Queueing)
29 | 11:14:36.986 | 11:14:36.994 | 0.008s | ❌ 503 | 🚫 거절됨 (Rejected)
===============================================================================================

noDelay: true (즉시 차단):

  • 핵심: “일단 바로 보내주기”
  • 동작: 바구니에 자리가 있다면 담자마자 기다리지 않고 즉시 서버로 보냅니다. 단, 바구니 칸은 여전히 0.2초마다 한 칸씩 비워집니다.
  • 결과: 사용자는 성공(200)하는 모든 요청에 대해 지연 없이 빠른 응답을 받습니다.
===============================================================================================
ID | Sent At | Resp At | Latency | Status | Note
-----------------------------------------------------------------------------------------------
00 | 11:18:30.654 | 11:18:30.665 | 0.011s | ✅ 200 | ⚡ 즉시 처리 (Pass)
01 | 11:18:30.689 | 11:18:30.699 | 0.010s | ✅ 200 | ⚡ 즉시 처리 (Pass)
02 | 11:18:30.725 | 11:18:30.736 | 0.011s | ✅ 200 | ⚡ 즉시 처리 (Pass)
03 | 11:18:30.762 | 11:18:30.772 | 0.010s | ✅ 200 | ⚡ 즉시 처리 (Pass)
04 | 11:18:30.799 | 11:18:30.809 | 0.010s | ✅ 200 | ⚡ 즉시 처리 (Pass)
05 | 11:18:30.834 | 11:18:30.843 | 0.010s | ✅ 200 | ⚡ 즉시 처리 (Pass)
06 | 11:18:30.870 | 11:18:30.880 | 0.010s | ✅ 200 | ⚡ 즉시 처리 (Pass)
07 | 11:18:30.907 | 11:18:30.914 | 0.008s | ❌ 503 | 🚫 거절됨 (Rejected)
08 | 11:18:30.943 | 11:18:30.950 | 0.007s | ❌ 503 | 🚫 거절됨 (Rejected)
09 | 11:18:30.980 | 11:18:30.987 | 0.007s | ❌ 503 | 🚫 거절됨 (Rejected)
10 | 11:18:31.017 | 11:18:31.023 | 0.007s | ❌ 503 | 🚫 거절됨 (Rejected)
11 | 11:18:31.053 | 11:18:31.060 | 0.006s | ❌ 503 | 🚫 거절됨 (Rejected)
12 | 11:18:31.090 | 11:18:31.097 | 0.007s | ✅ 200 | ⚡ 즉시 처리 (Pass)
13 | 11:18:31.126 | 11:18:31.132 | 0.006s | ❌ 503 | 🚫 거절됨 (Rejected)
14 | 11:18:31.163 | 11:18:31.168 | 0.005s | ❌ 503 | 🚫 거절됨 (Rejected)
15 | 11:18:31.199 | 11:18:31.205 | 0.006s | ❌ 503 | 🚫 거절됨 (Rejected)
16 | 11:18:31.235 | 11:18:31.244 | 0.009s | ❌ 503 | 🚫 거절됨 (Rejected)
17 | 11:18:31.273 | 11:18:31.283 | 0.010s | ✅ 200 | ⚡ 즉시 처리 (Pass)
18 | 11:18:31.310 | 11:18:31.317 | 0.007s | ❌ 503 | 🚫 거절됨 (Rejected)
19 | 11:18:31.347 | 11:18:31.355 | 0.007s | ❌ 503 | 🚫 거절됨 (Rejected)
20 | 11:18:31.385 | 11:18:31.391 | 0.007s | ❌ 503 | 🚫 거절됨 (Rejected)
21 | 11:18:31.422 | 11:18:31.429 | 0.007s | ❌ 503 | 🚫 거절됨 (Rejected)
22 | 11:18:31.459 | 11:18:31.467 | 0.009s | ✅ 200 | ⚡ 즉시 처리 (Pass)
23 | 11:18:31.496 | 11:18:31.503 | 0.007s | ❌ 503 | 🚫 거절됨 (Rejected)
24 | 11:18:31.530 | 11:18:31.537 | 0.007s | ❌ 503 | 🚫 거절됨 (Rejected)
25 | 11:18:31.567 | 11:18:31.570 | 0.003s | ❌ 503 | 🚫 거절됨 (Rejected)
26 | 11:18:31.602 | 11:18:31.605 | 0.003s | ❌ 503 | 🚫 거절됨 (Rejected)
27 | 11:18:31.637 | 11:18:31.641 | 0.004s | ❌ 503 | 🚫 거절됨 (Rejected)
28 | 11:18:31.673 | 11:18:31.682 | 0.010s | ✅ 200 | ⚡ 즉시 처리 (Pass)
29 | 11:18:31.709 | 11:18:31.715 | 0.006s | ❌ 503 | 🚫 거절됨 (Rejected)
===============================================================================================

5. 결론

NGINX Gateway Fabric Rate Limit 은 RateLimitPolicy API로 간편하고 강력하게 구현할 수 있습니다. noDelay 옵션 선택으로 서비스 성격에 맞는 보호 전략을 세우세요. 초기에는 지연 방식으로 시작해 사용자 영향을 최소화하고, 점진적으로 튜닝하는 것이 좋습니다.

더 자세한 내용은 NGINX 공식 Rate Limit Policy 문서를 확인하세요.

NGINX Gateway Fabric이나 RateLimitPolicy 관련 질문 있으시면 언제든 NGINX STORE에 문의 주세요.

NGINX STORE를 통한 솔루션 도입 및 기술지원 무료 상담 신청

* indicates required