본문 바로가기
server/💻Server

Nginx로 Rate limit 설정

by 발개발자 2023. 1. 4.
반응형

  멍청함엔 끝이 없다  

머리가 멍청하면 몸이 고생하더라,,, 어르신들 틀린게 하나 없다.

 

Nginx Webserver 구축하면서 rate limit 설정이 있는 확인했다.

기존에 Application단에서 Filter 구현하기 전에 nginx 구현 가능한지 찾아봤으면 이렇게 일하는 일이 없을 텐데 이렇게 몸이 고생하는 것도 능력이다. 하하하

 

Java filter로 rate limit 구현은 아래 링크를 확인하면 된다.

https://foot-develop.tistory.com/52

 

그럼 바로 Nginx에서 rate limit 어떻게 다루는지 알아보자.


 

Nginx에서 rate limit 구현하기 위해 네트워킹에서 많이 사용 되는 누수 버킷(leaky bucket) 알고리즘을 사용한다.

 

  누수 버킷? (leaky bucket)  

위에서 물이 쏟아지고 바닥은 새는 양동이를 비유한 것이다.

물이 쏟아지는 속도가 누출되는 속도를 초과하면 양동이가 넘치게 되는데, 상황은 rate limit으로 표현하면 아래와 같다.

 

  • 쏟아지는 물 - client 요청
  • 양동이 - 요청을 받는 FIFO(선입선출) 큐
  • 새는 물 - 큐에 쌓여있다가 서버로 전송된 client 요청
  • 넘치는 물 - 큐의 사이즈보다 오버 플로되어 block되는 client 요청

 

그렇다면 rate limit 구성해보자.


  Rate limit 기본 구성

default.conf

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

server {
    location / {

            limit_req zone=mylimit;
            limit_req_status 429;

            proxy_pass $service_url;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
    }

…

 

위와 같이 nginx config를 구성하면 1초에 10개의 요청을 받을 있다.

 

코드 한땀 한땀 장인의 마인드로 알아보자.

 

limit_req_zone

: 속도 제한에 대한 매개 변수를 정의하고 해당 변수를 사용하는 구역에서 속도 제한을 활성화 시킨다.

 

binary_remote_addr$remote_addr

: 클라이언트 IP 주소를 이진 표현하는 NGINX변수이다. , IP 주소를 값으로 요청 속도를 제한한다.

 

zone=mylimit:10m

: IP 주소의 상태와 요청 제한 URL 접근한 빈도를 저장하는 사용되는 공유메모리 영역을 정의한다. 본인은 mylimit이라는 명칭을 사용하여 10mb 메모리를 할당하였다.

 

rate=10r/s

: 최대 요청 속도를 설정한다. 여기서는 초당 10개의 요청을 초과할 없게 설정하였다. 주의해야할 점이 있는데 NGINX 밀리초 단위로 요청을 추적하기 때문에 100ms마다 1개의 요청에 해당된다. 현재 설정에서 버스트를 허용하지 않기때문에 이전 요청보다 100ms 이내에 요청이 오면 거부가 된다는 것이다.

, 1초에 요청을 5개를 보내도 100ms 안에 5개를 한번에 보내면 1개만 요청을 받고 나머지는 거부가 된다는 점이다. 부분 때문에 약간의 사프질을 했다,,, 헤헷

 

limit_req zone=mylimit

: 해당 URL요청이 왔을 rate limit 요청하는 구문이다.

 

limit_req_status 429

: 만일 rate limit 초과했을 경우 429 (Too many requests)에러를 뱉어주게 된다.


 

  버스트 처리

아까 설명했다싶이, 100ms안에 많은 요청이 오거나, 1초에 10개의 요청이 넘어가게 되는 경우 limit 초과하게 되어 상태코드 에러를 뱉어 주게 된다.

우리가 사용하는 어플리케이션에서도 분명 rate limit의 요청 개수는 넘지 않더라도 요청 딜레이가 너무 짧아서 limit 에러를 뱉어주는 상황이 온다.(실화바탕 본인 이야기임)

상황을 해결해줄 있는 방법이 바로 버스트처리이다.

버스트처리는 rate limit 초과요청에 대해 따로 대기열에 대기 시켜놓고 적시에 서비스를 제공하는 것이다.

 

 location / {

            limit_req zone=mylimit burst=20;
            limit_req_status 429;
  ...
}

위와 같은 코드를 통해 버스트 처리를 있으며 20개의 초과요청을 담을 있는 대기열을 만들어 주는 것이다.

 

버스트 처리를 하게 되면, 하나의 IP에서 1초에 21개의 요청이 동시에 도착할 때(100ms이내) NGINX 번째 요청을 서버에 전달하고 나머지 20개는 버스트 생성한 큐에 넣는다. 그리고 100ms 마다 대기 중인 요청을 서버에 전달하고 추가로 들어오는 요청으로 인해 대기 중인 요청 수가 20개가 초과하는 경우에만 limit 에러를 뱉어주게 된다.

 


  Nodelay

위와 같이 버스트처리를 해줄 경우, 버퍼에 쌓인 20개의 초과 요청이 서버까지 전달되는데 2초가 걸리게 된다. 이는 트래픽은 원활하지만 사이트가 느리게 나타날 있으므로 실용적이지 않게 된다. 상황을 해결하기 위해 nodelay라는 변수를 추가해준다.

 

location / {
            limit_req zone=mylimit burst=20 nodelay;
            limit_req_status 429;
...
}

 

Nodelay 적용하면 20개의 초과 요청을 버퍼에 쌓아 놓지만, 서버로 전달은 즉시 하게 된다.

, 서버로 모든 요청은 전달해놓고, 버퍼를 채워놓아 100ms마다 버퍼에 있는 1개의 요청을 지워주는 것이다. 따라서 버퍼에 쌓인 요청은 즉시 처리하면서 rate limit 유지할 있게 되는 것이다.


  허용 목록 (White List)

설정을 통해 특정 ip 대해 rate limit 제외할 있다.

 

geo $limit {
    default 1;
    10.0.0.0/16 0;  # 내부 네트워크 대역 10.10.*.*에 대해 rate limit 제외
    101.245.78.124 0;  # 해당 외부 IP에 대해 rate limit 제외
}
 
map $limit $limit_key {
    0 "";
    1 $binary_remote_addr;
}
 
limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;
 
server {
    location / {
        limit_req zone=req_zone burst=10 nodelay;
        ...


Geo 모듈에서 작성한 1, 0 같은 숫자들이 map에서 매핑되어 적용되게 된다.

즉, default $binary_remote_addr; 이런식으로 작성이 된다.

limit_req_zone $limit_key

이렇게 해당 white list를 적용 시키면

빈문자열로 적용되는 10.0.0.0/16 101.245.78.124 white list 적용되어 rate limit에서 제외되게 된다.

 


  허용 목록 Rate Limit 적용

아래와 같이 작성하면 $limit_key 허용 목록에도 별도로 rate limit 있게된다.

즉, 변수 설정과 구문 제어를 통해 다양한 컨트롤이 가능해진다.

 

 ...
    limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;
    limit_req_zone $binary_remote_addr zone=req_zone_wl:10m rate=15r/s;
 
    server {
        # ...
        location / {
            limit_req zone=req_zone burst=10 nodelay;
            limit_req zone=req_zone_wl burst=20 nodelay;
            # ...
        }
...

 


 

  로깅

기본적으로 Nginx에서 rate limit으로 인해 지연되거나 삭제된 요청을 기록할 있다.

코드와 같이 로그 레벨도 설정할 수 있다.

 

location /login/ {
    limit_req zone=mylimit burst=20 nodelay;
    limit_req_log_level error;
 
    proxy_pass $service_url;
}

 

로그 항목의 필드는 아래와 같다.

  • 2015/06/13 04:20:00 – 로그 항목이 작성된 날짜 및 시간
  • [error] – 심각도 수준
  • 120315#0 – NGINX 작업자의 프로세스 ID 및 스레드 ID(기호로 구분)#
  • *32086 – 속도가 제한된 프록시된 연결의 ID
  • limiting requests – 로그 항목이 속도 제한을 기록하는 표시기
  • excess – 이 요청이 나타내는 구성된 속도에 대한 밀리초당 요청 수
  • zone – 부과 된 속도 제한을 정의하는 영역
  • client – 요청하는 클라이언트의 IP 주소
  • server – 서버의 IP 주소 또는 호스트 이름
  • request – 클라이언트의 실제 HTTP 요청
  • host – HTTP 헤더의 값Host

error.log를 확인하면 rate limit으로 block된 요청들의 log를 확인할 수 있다.

 

이렇게 성공적으로 filter단에서 설정한 rate limit을 nginx에서 하는 것으로 변경하였다.

 

참고

https://www.nginx.com/blog/rate-limiting-nginx/

반응형

댓글