분산 환경에서의 고성능 API Rate Limiting 처리 (Nginx + Lua + Redis)

진성대·2025년 3월 26일
0

사이드 프로젝트

목록 보기
4/7
post-thumbnail

배경 및 필요성

대규모 트래픽을 처리하는 이커머스 시스템에서 특정 API에 대한 과도한 호출은 시스템의 성능 저하와 서비스 장애를 유발할 수 있습니다.
이러한 문제를 방지하고 안정적인 서비스 운영을 보장하기 위해서는 효율적이고 확장 가능한 Rate Limiting 전략이 필수적입니다.

본 프로젝트에서는 이를 해결하기 위해 다음과 같은 기술 스택을 활용하여 Rate Limiting을 구현했습니다.

  • Nginx(OpenResty) + Lua (경량 스크립트 기반 요청 필터링)
  • Redis (빠른 데이터 접근, 분산 환경에서의 요청 집계 및 관리)

기존 Rate Limiting 방식의 한계

기존의 Nginx 내장 방식만을 사용한 Rate Limiting은 다음과 같은 문제점이 존재합니다.

문제점내용
단일 서버 종속성Nginx 내장 모듈을 사용하면 각 인스턴스마다 독립적으로 동작해 분산 환경에서 통합된 제한이 어려움
유연성 부족URI별, 메소드별 세부 제어가 어렵고, 동적으로 변경이 어려움
확장성 한계트래픽 증가 시 개별 Nginx 인스턴스의 설정 관리가 비효율적

Redis + Lua를 활용한 분산 환경 Rate Limiting 설계

위 문제를 해결하기 위해 Nginx(OpenResty) 환경에서 Lua 스크립트로 Redis를 연동하여 요청 빈도를 관리하는 분산 Rate Limiting을 도입하였습니다.

구성 요소역할 및 특징
OpenResty (Nginx + Lua)Lua를 통해 요청이 들어올 때마다 Redis로 접근하여 빈도를 계산하고 제한 여부 판단
Redis각 API 요청에 대한 빈도 카운트(INCR) 및 TTL 기반의 만료 관리(EXPIRE)

구현 방식 및 동작 원리

  1. Nginx + Lua 요청 필터
  • Nginx의 access_by_lua_file 지시자를 통해 모든 요청을 Lua 스크립트로 전달
  • Lua 스크립트에서 요청 정보를 기반으로 Redis의 요청 횟수를 카운팅 및 확인
  1. Redis 기반 카운트 및 제한
  • Redis는 각 요청을 식별하기 위한 고유한 키를 (메소드 + URI + IP)로 생성하여 사용
  • TTL(유효 시간)을 설정하여 특정 시간(window) 내 요청 횟수(count)를 관리
  • 요청 횟수가 설정된 limit 초과 시 HTTP 상태 코드 429 (Too Many Requests) 반환하여 제한 적용

핵심 코드(Lua + Redis)

local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)

-- Redis 연결
local ok, err = red:connect("redis", 6379)
if not ok then
  ngx.log(ngx.ERR, "Redis 연결 실패: ", err)
  return ngx.exit(500)
end

-- GET 요청은 제한에서 제외
if ngx.req.get_method() == "GET" then
  return
end

-- 요청을 구분하는 고유 키 생성 (메소드 + URI + 클라이언트 IP)
local key = "ratelimit:" .. ngx.req.get_method() .. ":" .. ngx.var.uri .. ":" .. ngx.var.binary_remote_addr

-- 제한 기준 설정 (예: 10초에 최대 5)
local limit = 5
local window = 10

-- 요청 횟수 증가 및 체크
local count, err = red:incr(key)
if not count then
  ngx.log(ngx.ERR, "Redis 카운트 증가 실패: ", err)
  return ngx.exit(500)
end

-- TTL이 없는 경우 새로 설정
local ttl, err = red:ttl(key)
if ttl == -1 then
  red:expire(key, window)
end

-- 요청 제한 초과 시 429 반환
if count > limit then
  ngx.status = 429
  ngx.say("Rate limit exceeded.")
  return ngx.exit(429)
end

적용 결과 및 성능 효과

개선 항목효과
분산 Rate Limiting 지원Redis를 통해 여러 Nginx 서버에서 통합적으로 제한 관리 가능
실시간성Redis의 TTL 기반 캐싱으로 최신 상태의 빠른 조회 및 정확한 제한 적용
유연성 및 확장성메소드, URI, 클라이언트 IP 기반으로 제한을 매우 세부적이고 동적으로 관리 가능

  • 실제 부하 테스트(K6)를 통해 99.9% 정확도로 제한이 적용
  • Grafana로 Redis 상태 모니터링 환경까지 구축하여 실시간으로 요청 현황과 캐시 상태를 모니터링 가능.

profile
주니어 개발자

0개의 댓글