[Nginx] 반복적인 요청 제한하기 (Rate Limiting)

JNETiii·2024년 3월 26일
1

Backend

목록 보기
3/3

배경

클라이언트에서 특정 api 요청이 반복적으로 오는 케이스를 발견했다.
몇천개씩 요청이 왔는데, 서버에서는 따로 처리가 되어있지 않아서 모든 요청을 다 받아주었다.
이런 경우, 429 에러(too many request) 를 발생시켜줘야 서버에 지장이 가지 않아서 해서 nginx에 추가해주었다.

방법

[nginx 공식 문서]
https://tech.sadaalomma.com/nginx/429-too-many-requests-nginx/#Proactive_Monitoring_and_Alerts

요청 속도를 제한하는 방법은 매우 간단하다.
아래처럼 추가하기만 하면 된다.

limit_req_zone $binary_remote_addr zone=limit:10m rate=10r/s;

server {
	limit_req zone=limit burst=20 nodelay;
    limit_req_status 429;
}

아래에서 모든 설명은 해당 코드 기반으로 설명을 하겠다.



한줄씩 설명하자면,
limit_req_zone $binary_remote_addr zone=limit:10m rate=10r/s;

limit_req_zone을 설정한다.

$binary_remote_addr
IP주소를 binary 형태로 표현한 것이다. 동일 IP에 대해서 요청 속도를 제한하도록 기준을 설정한다.

zone=limit:10m
IP주소의 상태, 요청 제한이 걸려있는 URL에 접근한 빈도 등을 저장할 때 필요한 메모리를 나타낸다. 위 코드에서는 10MB를 limit이라는 변수에 저장한다.

rate=10r/s
1초에 몇개의 요청을 허용하는지 나타낸다. (r: request, s: seconds)


server {
	limit_req zone=limit burst=20 nodelay;
    limit_req_status 429;
}

limit_req에서는 zone에 위에서 설정한 limit 값을 넣어준다.
limit_req_status는 limit을 넘었을 때 429(too many requests) 에러를 발생시킨다는 의미이다. (따로 작성하지 않으면 503에러를 발생시킨다.)

더 자세히 알아보자.

burst
허용 가능한 요청 수보다 더 많은 요청이 들어왔을 경우, 허용 가능한 요청 이외에 요청들을 대기열 큐에 넣어놓는다. 이때, 대기열에 넣어놓을 요청 수를 burst에 설정한다.
먼저 들어온 요청을 처리 한 후, 대기열에 있는 요청을 순차적으로 처리한다.
예를 들어, 1초에 30개의 요청이 왔다고 치자. rate 설정으로는 1초에 10개의 요청만을 처리할 수 있으니 나머지 20개의 요청은 실패한다.
하지만 위 코드상으로는, 실패할 20개의 요청을 대기열에 넣어둔다.
10개의 요청을 다 처리한 후에 대기열 20개의 요청을 순차적으로 처리하는 것이다.

burst를 사용할 경우, 대기열에 있는 요청을 순차적으로 처리하기 때문에 속도가 느려지는 현상이 발생한다. 이를 해결하기 위해서는 아래 소개될 nodelay를 사용해야한다.

nodelay
burst로 인해 속도가 느려질 때 nodelay를 사용하면 이 문제를 해결할 수 있다.
nodelay는 말그대로 딜레이가 없다. 순차적으로 처리하던 요청을 즉각적으로 바로 처리하는 것이다.
나는 이 설명을 보고 그럼 rate는 의미가 없는 것 아닌가? 라고 생각했지만 chatGPT에게 물어보니 평소에는 1초에 10개의 요청을 허용하지만, 더 많은 요청이 왔을 때는 일시적으로 1초에 burst 값(20) 만큼의 요청을 허용한다고 한다.

테스트

테스트는 아래 블로그를 참고했다.
[참고]
https://minholee93.tistory.com/entry/Nginx-Rate-Limiting

siege는 성능, 부하 테스트를 수행할 수 있는 도구이다.

설치

나는 brew를 통해 설치했다.

brew install siege

테스트

동시에 20번의 요청을 2번에 걸쳐 보내보겠다. (총 40번)

siege -v -r 2 -c 20 [URL]

결과

아래 이미지처럼 처음 요청은 성공하고, 그 뒤 요청들은 429 에러가 난 것을 확인 할 수 있다. (40번의 요청이지만 성공/실패를 보여주기 위해 일부만 캡쳐했다.)
method 뒤에 경로가 표시되는데 보안상 지웠다.

결론

이제 요청이 반복적으로 와도 서버 부하가 오지 않도록 설정해두어서 조금 안심이 된다.
nginx에 요청 제한을 추가하면서 nginx에 대해 더 공부해야겠다고 느꼈다. 아자!

profile
도전자 | 개발자

0개의 댓글