[Nginx] Rate Limiting

반응형

이번글에서는 nginx의 Rate Limiting을 사용해 incoming connection을 제한하는 방법에 대해 알아보도록 하겠습니다.

1. Rate Limiting이란?

incoming connection에 대한 rate를 제한하는 기능입니다.

예를 들어 너무 많은 connection이 동시에 발생해 server에 부담이 가는 경우, 정의한 rate limit을 초과한 connection은 reject 할 수 있습니다.

rate limiting을 사용하면 특정 incoming connection을 제한하거나 manage 할 수 있습니다.

rate limiting을 사용하는 이유는 다음과 같습니다.

Security : 동일 ip에 대한 반복적인 incoming connection을 제한해 brute force attack과 같은 공격을 방지 할 수 있습니다.

Reliability : traffic spike를 방지해 server를 more reliable하게 관리할 수 있습니다.

Shaping : server에 접근가능한 user를 restrict해 service의 priority를 관리할 수 있습니다.

2. SIEGE

rate limiting을 테스트하기위해 SIEGE를 사용하겠습니다.

이 글(https://www.digitalocean.com/community/questions/how-to-installing-siege-stress-tester-on-a-centos-server)을 참고해 SIEGE를 다운로드 받습니다.

이번 글에서 사용할 nginx.conf 파일은 다음과 같습니다.

nginx.conf

worker_processes auto;

events {
    worker_connections  1024;
}


http {
        include mime.types;

        # Redirect all traffic to HTTPS
        server {
                listen 80;
                server_name 13.209.72.104;
                return 301 https://$host$request_uri;
        }

        server {

                # listen 80;
                listen 443 ssl http2;
                server_name 13.209.72.104;

                root /sites/demo;
                index index.html;

                ssl_certificate /etc/nginx/ssl/self.crt;
                ssl_certificate_key /etc/nginx/ssl/self.key;

                location / {
                        try_files $uri $uri/ =404;
                }

                location ~\.php$ {
                        # Pass php requests to the php-fpm service (fastcgi)
                        include fastcgi.conf;
                        fastcgi_pass unix:/run/php/php7.10fpm.sock;

                }
        }
}

2-1) before rate limiting

이제 동시에 5개의 connection을 2번에 걸쳐 request하는 아래의 test를 수행해보겠습니다.

# c5 * r2 = 10 request
siege -v -r 2 -c 5 https://13.209.72.104/thumb.png

결과값으로 아래와 같이 총 10개의 request가 정상적으로 수행되는 것을 확인할 수 있습니다. 만약 connection이 reject된다면 파란색이 아닌 붉은색으로 표시됩니다.

현재는 어떠한 rate limiting 구문도 추가하지 않았기 때문에 모든 connection은 정상적으로 response 됩니다.

2-2) basic rate limiting

이번에는 rate liming을 적용해보도록 하겠습니다.

nginx.conf을 다음과 같이 수정합니다.

nginx.conf

worker_processes auto;

events {
    worker_connections  1024;
}


http {
        include mime.types;

        # Define limit zone
        limit_req_zone $request_uri zone=MYZONE:10m rate=1r/s;


        # Redirect all traffic to HTTPS
        server {
                listen 80;
                server_name 13.209.72.104;
                return 301 https://$host$request_uri;
        }

        server {

                # listen 80;
                listen 443 ssl http2;
                server_name 13.209.72.104;

                root /sites/demo;
                index index.html;

                ssl_certificate /etc/nginx/ssl/self.crt;
                ssl_certificate_key /etc/nginx/ssl/self.key;

                location / {
                        # set limit zone
                        limit_req zone=MYZONE;

                        try_files $uri $uri/ =404;
                }

                location ~\.php$ {
                        # Pass php requests to the php-fpm service (fastcgi)
                        include fastcgi.conf;
                        fastcgi_pass unix:/run/php/php7.10fpm.sock;

                }
        }
}

추가된 코드를 살펴보면 다음과 같습니다.

limit_req_zone $request_uri zone=MYZONE:10m rate=1r/s :

✔ rate limit을 적용할 zone을 정의합니다. $server_name (per_server) / $binary_remote_addr (per user) / $request_uri (per uri)를 사용할 수 있습니다.

✔ 사용할 zone 이름과 memory에 저장할 zone의 size를 정의합니다.

✔ limit rate를 정의합니다. 60r/m과 1r/s은 동일한 frequecny를 의미합니다.

limit_req zone=MYZONE : location에서 사용할 zone을 명시합니다.

저장후 동일한 test를 수행해보겠습니다.

rate limiting을 적용 후 아래와 같이 처음 1개의 connection을 제외한 나머지 connection은 503 code와 함께 reject 된 것을 확인할 수 있습니다.

503 error는 "service is not unavailable"을 의미합니다.

위의 결과를 자세히 살펴보면 다음과 같습니다. 먼저 server는 5개의 connections을 동시에 요청받았습니다.

이때 우리는 rate limiting을 통해 1초에 1개의(1r/s) connection만 허용했으므로, 나머지 4개의 connection은 즉시 reject 되었습니다. 또한 이러한 reject은 굉장히 빠른 시간내에 수행된 것을 확인할 수 있습니다. (0.01초)

따라서 다음의 2번째 5 connections가 도착했을때에도 아직 이전의 1 connection가 처리될 수 있는 1s가 지나지 않았기 때문에, 이때의 5개의 connections도 동일하게 503 code와 함께 reject 되게 됩니다.

2-3) burst

이번에는 rate limiting에 burst를 적용해보겠습니다.

burst를 적용하면, rate limiting을 초과하는 connection을 즉시 reject하지 않고 wait하게 만들 수 있습니다.

예를 들어 위와 같이 burst에 5를 정의하면, 1r/s 초과하는 나머지 5 connection은 즉시 reject 되지 않고 해당 connection가 수행될 수 있을때까지 대기하게 됩니다.

burst를 적용하기위해 nginx.conf의 limit_req zone을 다음과 같이 수정해보겠습니다.

# before burst
limit_req zone=MYZONE;

# after burst
limit_req zone=MYZONE burst=5;

이후 동일한 test를 수행해보면 아래와 같이 10개의 connections 모두가 정상적으로 response 된 것을 확인할 수 있습니다.

1r/s +5 burst이므로 첫 5(1+4)개의 connections은 정상적으로 transaction을 수행하고, 뒤이어 도착한 5(1+4)개의 connections 또한 정상적으로 transactions을 수행하게 됩니다.

만약 test를 아래와 같이 동시에 15개의 connections을 요청하도록 변경한다면

# c15 * r1 = 15 request
siege -v -r 1 -c 15 https://13.209.72.104/thumb.png

1r/s + 5 burst를 초과하는 9개의 connections은 즉시 reject 되며 wait을 하는 5 burst는 순차적으로 response 되는 것을 확인할 수 있습니다.

2-4) nodelay

burst를 적용한 zone에는 nodelay도 함께 적용할 수 있습니다.

nodelay를 사용하기위해 아래와 같이 nginx.conf의 limit_req zone을 다음과 같이 수정합니다.

# nodelay
limit_req zone=MYZONE burst=5 nodelay;

nodeplay를 사용하면 허용된 burst 수만큼의 connections은 rate limiting을 무시하고 즉시 response 하게 합니다.

주의점으로는 즉시 response 되었다고 하더라도, 다음의 new request에 관해서는 동일한 rate limit을 적용합니다.

예를 들어 동시에 6 connections을 2번 수행하면 아래와 같이 처음 6(1r/s+5 burst)는 즉시 response 되었지만, rate limit은 여전히 6이므로 2번째 request의 일부는 reject 되는 것을 확인할 수 있습니다.


참고 자료 : https://www.udemy.com/course/nginx-fundamentals/


반응형

'Nginx' 카테고리의 다른 글

[Nginx] Hardening Nginx  (0) 2020.08.31
[Nginx] Basic Auth  (0) 2020.08.31
[Nginx] HTTPS (SSL)  (0) 2020.08.31
[Nginx] HTTP/2  (0) 2020.08.31
[Nginx] FastCGI Cache  (0) 2020.08.31

댓글

Designed by JB FACTORY