NGINX로 Python 성능 극대화하기, 1부: 웹 서비스 및 캐싱

NGINX | 2016년 3월 31일

소개 – Python에서 NGINX를 사용하는 방법

Python은 쉽고 재미있게 사용할 수 있고 소프트웨어 개발을 간소화하며, 다른 스크립트 언어보다 런타임 성능이 뛰어난 것으로 유명합니다. (다만 최신 PHP 버전인 PHP 7은 Python의 강력한 경쟁자가 될 수도 있습니다.)

누구나 자신의 웹사이트와 애플리케이션이 더 빠르게 실행되기를 바랍니다. 또한 트래픽이 증가하거나 갑자기 급증하는 모든 웹사이트는 성능 문제와 서비스 중단에 취약하며, 이러한 문제는 흔히 최악의 시점, 즉 가장 바쁜 시간대에 발생합니다. 트래픽이 꾸준히 증가하든 사용량이 갑자기 급증하든, 거의 모든 웹사이트는 성능 문제와 서비스 중단을 겪습니다.

바로 이때 NGINX와 NGINX Plus가 필요합니다. 이들은 다음과 같은 세 가지 방식으로 웹사이트 성능을 향상합니다:

  1. 웹 서버로 사용 – NGINX는 원래 C10K 문제 – 즉, 10,000개 이상의 동시 연결을 손쉽게 지원하는 문제를 해결하기 위해 개발되었습니다. NGINX를 Python 앱의 웹 서버로 사용하면 트래픽이 적을 때도 웹사이트 속도가 빨라집니다. 사용자가 수천 명에 달하는 경우에는 훨씬 더 높은 성능과 더 적은 충돌, 더 짧은 다운타임을 제공할 것이 거의 확실합니다. NGINX 웹 서버에서 정적 파일 캐싱이나 마이크로캐싱을 수행할 수도 있지만, 둘 다 별도의 NGINX 리버스 프록시 서버에서 실행할 때 더 효과적입니다(다음 단락 참조).
  2. 리버스 프록시 서버로 사용 – 현재 애플리케이션 서버 구성 앞단에 NGINX를 리버스 프록시 서버로 간단히 추가할 수 있습니다. NGINX는 웹 요청을 받아 애플리케이션 서버로 전달합니다. 이 간단한 방법만으로도 웹사이트 속도가 빨라지고, 가동 중단 시간이 줄어들며, 서버 리소스 사용량이 감소하고, 보안이 강화됩니다. 또한 리버스 프록시 서버에 정적 파일을 캐시하고(매우 효율적), 동적 콘텐츠 마이크로캐싱을 추가하여 애플리케이션 자체의 부하를 줄이는 등 다양한 작업을 수행할 수 있습니다.
  3. 여러 애플리케이션 서버를 위한 로드 밸런서 – 먼저 리버스 프록시 서버를 배포합니다. 그런 다음 여러 애플리케이션 서버를 병렬로 실행하고 NGINX 또는 NGINX Plus를 사용해 서버 간 트래픽 부하를 분산하여 확장합니다. 이러한 방식으로 배포하면 트래픽 요구 사항에 맞춰 웹사이트 성능을 손쉽게 확장하는 동시에 안정성과 가동 시간을 높일 수 있습니다. 특정 사용자 세션을 동일한 서버에서 계속 처리해야 하는 경우, 로드 밸런서가 세션 지속성을 지원하도록 구성합니다.

NGINX와 NGINX Plus는 Python 앱의 웹 서버, 리버스 프록시 서버, 로드 밸런서로 사용하거나 이 세 가지 용도로 모두 사용할 때 각각 이점을 제공합니다.

2부작 시리즈의 첫 번째 글에서는 NGINX 및 NGINX Plus를 웹 서버로 사용하는 방법, 정적 파일 캐싱을 구현하는 방법, 애플리케이션에서 생성된 파일을 마이크로캐싱하는 방법 등 Python 앱의 성능을 개선하기 위한 다섯 가지 팁을 소개합니다. 2부에서는 NGINX 및 NGINX Plus를 리버스 프록시 서버로 사용하고 여러 애플리케이션 서버의 로드 밸런서로 활용하는 방법을 설명합니다.

팁 1 – Python 성능 병목 지점 찾기

Python 애플리케이션의 성능이 중요한 상황은 크게 두 가지입니다. 첫째는 일상적인 수준의 ‘적정한’ 사용자 수를 처리할 때이고, 둘째는 부하가 많이 걸릴 때입니다. 많은 사이트 운영자는 부하가 적을 때의 성능을 지나치게 가볍게 여기지만, 저희 생각에는 응답 시간을 0.1초라도 줄이기 위해 온 힘을 쏟아야 합니다. 응답 시간을 몇 밀리초 단축하는 일은 어렵고 알아주는 이도 드물지만, 사용자의 만족도를 높이고 더 나은 비즈니스 성과로 이어집니다.

하지만 이 블로그 게시물과 함께 제공되는 2부에서는 모두가 우려하는 상황, 즉 사이트에 트래픽이 몰릴 때 발생하는 심각한 성능 저하나 장애 같은 성능 문제에 초점을 맞춥니다. 또한 많은 해커 공격은 사용자 수가 갑자기 급증할 때와 비슷한 영향을 일으키므로, 사이트 성능을 개선하는 것은 흔히 공격에 대응하는 데에도 중요한 단계입니다.

Apache HTTP Server처럼 사용자당 일정량의 메모리를 할당하는 시스템에서는 사용자가 계속 늘어날수록 물리적 메모리에 과부하가 발생합니다. 그러면 서버가 디스크 스와핑을 시작하면서 성능이 급격히 저하되고, 결국 성능 문제와 충돌이 발생합니다. 이 블로그 게시물에서 설명한 대로 NGINX로 전환하면 이 문제를 해결하는 데 도움이 됩니다.

Python은 일반적으로 다른 스크립팅 언어보다 작업을 수행하는 데 더 많은 메모리를 사용하기 때문에(그 결과 작업을 더 빠르게 실행함) 메모리 관련 성능 문제가 특히 발생하기 쉽습니다. 따라서 다른 모든 조건이 같다면 Python 기반 앱은 다른 언어로 작성된 앱보다 더 적은 사용자 부하에서도 “다운”될 수 있습니다.

앱을 최적화하면 어느 정도 도움이 될 수 있지만, 일반적으로 트래픽과 관련된 사이트 성능 문제를 해결하는 가장 효과적이거나 빠른 방법은 아닙니다. 이 블로그 게시물과 함께 제공되는 2부에서 설명하는 단계가 트래픽 관련 성능 문제를 해결하는 가장 효과적이고 빠른 방법입니다. 여기에서 제시한 단계를 모두 수행한 후에는 앱을 개선하거나 마이크로서비스 아키텍처를 사용하도록 다시 작성해도 좋습니다.

팁 2 – 단일 서버 또는 다중 서버 배포 선택

소규모 웹사이트는 단일 서버에 배포해도 원활하게 운영됩니다. 대규모 웹사이트에는 여러 대의 서버가 필요합니다. 하지만 그 중간 규모에 해당하거나 사이트가 소규모에서 대규모로 성장하고 있다면, 몇 가지 중요한 선택을 해야 합니다.

단일 서버로 배포한 경우 트래픽이 급증하거나 전체 트래픽이 빠르게 증가하면 상당한 위험에 처할 수 있습니다. 확장성에 한계가 있으며, 이를 해결하려면 앱을 개선하거나, 웹 서버를 NGINX로 전환하거나, 더 크고 빠른 서버를 도입하거나, 스토리지를 콘텐츠 전송 네트워크(CDN)로 이전하는 등의 방법을 고려할 수 있습니다. 이러한 방법은 모두 구현하는 데 시간이 걸리고 비용이 들며, 구현 과정에서 버그나 문제가 발생할 위험이 있습니다.

또한 단일 서버로 배포하면 사이트에는 필연적으로 단일 장애 지점이 생기며, 사이트를 중단시킬 수 있는 많은 문제는 빠르고 간단하게 해결할 수 없습니다.

NGINX와 Python은 함께 작동하며 NGINX의 웹 서비스, 부하 분산 및 캐싱 기능을 통해 뛰어난 성능을 제공합니다.
애플리케이션 서버 “앞단에 NGINX 배치”

단일 서버 배포 환경에서 서버를 NGINX로 전환하는 경우 NGINX Open Source와 NGINX Plus 중에서 자유롭게 선택할 수 있습니다. NGINX Plus에는 엔터프라이즈급 지원과 추가 기능이 포함되어 있습니다. 추가 기능 중 실시간 활동 모니터링과 같은 기능은 단일 서버 배포 환경에 적합하며, 로드 밸런싱 및 세션 지속성과 같은 기능은 다중 서버 배포 환경에서 NGINX Plus를 리버스 프록시 서버로 사용할 때 활용할 수 있습니다.

모든 사항을 고려할 때, 사이트가 앞으로도 오랫동안 소규모로 유지되고 다운타임이 큰 문제가 되지 않을 것이라고 확신하지 않는 한 단일 서버 배포에는 어느 정도 위험이 따릅니다. 다중 서버 배포는 사실상 원하는 만큼 확장할 수 있습니다. 단일 장애 지점을 제거하도록 설계할 수 있고, 필요에 따라 성능을 조정할 수 있으며, 용량도 신속하게 추가할 수 있습니다.

팁 3 – 웹 서버를 NGINX로 변경하세요

웹 초창기에는 “Apache”라는 이름이 곧 “웹 서버”를 의미했습니다. 하지만 2000년대 초에 개발된 NGINX는 꾸준히 인기를 얻고 있으며, 이미 전 세계 상위 1위 웹 서버 1,000개, 10,000개, 100,000개 및 [ngx_snippet name=’proportion-top-sites’] 사이트에서 가장 많이 사용되는 웹 서버입니다.

NGINX는 C10K 문제를 해결하기 위해 개발되었습니다. 즉, 주어진 메모리 한도 내에서 10,000개가 넘는 동시 연결을 처리하기 위한 것입니다. 다른 웹 서버는 연결마다 일정량의 메모리가 필요하므로 수천 명의 사용자가 동시에 사이트에 접속하려 하면 물리적 메모리가 고갈되어 속도가 느려지거나 서버가 중단됩니다. NGINX는 각 요청을 개별적으로 처리하며 훨씬 더 많은 사용자도 원활하게 수용할 수 있습니다. (또한 아래에서 설명하겠지만, 그 밖의 용도로도 매우 유용합니다.)

아래는 NGINX 아키텍처의 개략적인 개요입니다.

웹 서비스, 부하 분산 및 캐싱에서 NGINX 기능을 활용하기 위한 Python 구성 지침 아키텍처
NGINX 아키텍처, 출처: 오픈 소스 애플리케이션의 아키텍처, 제2권

다이어그램에서 Python 애플리케이션 서버는 백엔드의 애플리케이션 서버 블록에 해당하며, FastCGI를 통해 액세스되는 것으로 표시되어 있습니다. NGINX는 Python을 실행하는 방법을 "알지" 못하므로, Python을 실행할 수 있는 환경으로 연결해 주는 게이트웨이가 필요합니다. FastCGI는 PHP, Python 및 기타 언어에서 널리 사용되는 인터페이스입니다.

하지만 Python과 NGINX 간 통신에는 웹 서버 게이트웨이 인터페이스(WSGI)가 더 널리 사용됩니다. WSGI는 멀티스레드 및 멀티프로세스 환경에서 작동하므로 이 블로그 게시물에서 언급한 모든 배포 옵션에서 원활하게 확장할 수 있습니다.

웹 서버를 NGINX로 전환하려는 경우, 필요한 각 단계를 안내하는 다양한 자료를 활용할 수 있습니다:

  • gunicorn 구성하기 – 널리 사용되는 WSGI 서버인 “Green Unicorn”을 NGINX와 함께 사용하도록 구성합니다.
  • uWSGI 구성 – NGINX와 함께 사용할 수 있는 또 다른 인기 WSGI 서버입니다. uWSGI는 NGINX를 직접 지원합니다.
  • uWSGI, NGINX 및 Django 사용하기 – 널리 사용되는 Python 웹 프레임워크입니다.

이 코드 조각은 uWSGI와 함께 사용하도록 NGINX를 구성하는 방법을 보여줍니다. 여기서는 Python 프레임워크인 Django를 사용하는 프로젝트를 예로 듭니다:

http {    # ...
    upstream django {
       server 127.0.0.1:29000;
    }

    server {
        listen 80;
        server_name myapp.example.com;

        root /var/www/myapp/html;

        location / {
            index index.html;
        }

        location /static/  {
            alias /var/django/projects/myapp/static/;
        }

        location /main {
            include /etc/nginx/uwsgi_params;
            uwsgi_pass django;

            uwsgi_param Host $host;
            uwsgi_param X-Real-IP $remote_addr;
            uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
            uwsgi_param X-Forwarded-Proto $http_x_forwarded_proto;
        }
    }
}

팁 4 – 정적 파일 캐싱 구현

정적 콘텐츠 캐싱은 자주 변경되지 않는 파일(몇 시간마다 변경되거나 전혀 변경되지 않을 수도 있음)의 사본을 애플리케이션 서버가 아닌 다른 위치에 보관하는 것을 의미합니다. 정적 콘텐츠의 대표적인 예로는 웹 페이지의 일부로 표시되는 JPEG 이미지를 들 수 있습니다.

정적 파일 캐싱은 애플리케이션 성능을 향상하는 일반적인 방법이며, 실제로 여러 계층에서 이루어집니다:

  • 사용자의 브라우저에서
  • 여러 계층의 인터넷 제공업체에서 – 기업 내부 네트워크부터 인터넷 서비스 제공업체(ISP)까지
  • 여기에서 설명할 웹 서버에서

웹 서버에 정적 파일 캐싱을 구현하면 두 가지 이점이 있습니다:

  • 사용자에게 더 빠르게 제공 – NGINX는 정적 파일 캐싱에 최적화되어 있어 애플리케이션 서버보다 훨씬 빠르게 정적 콘텐츠 요청을 처리합니다.
  • 애플리케이션 서버의 부하 감소 – 캐시된 정적 파일에 대한 요청은 웹 서버에서 처리하므로 애플리케이션 서버까지 전달되지 않습니다.

정적 파일 캐싱은 단일 서버 구현에서도 원활하게 작동하지만, 기반 하드웨어는 여전히 웹 서버와 애플리케이션 서버가 공유합니다. 웹 서버가 캐시된 파일을 가져오느라 하드웨어를 사용 중이라면 – 아무리 효율적으로 처리하더라도 – 해당 하드웨어 리소스를 애플리케이션에서 사용할 수 없으므로 애플리케이션의 속도가 어느 정도 느려질 수 있습니다.

브라우저 캐싱을 지원하려면 정적 파일의 HTTP 헤더를 올바르게 설정하세요. HTTP Cache-Control 헤더(특히 max-age 설정), Expires 헤더 및 Entity 태그를 고려하세요. 이 주제에 대한 유용한 입문 자료는 NGINX Plus 관리자 가이드의 uWSGI 및 Django와 함께 NGINX 및 NGINX Plus를 애플리케이션 게이트웨이로 사용하기를 참조하세요.

다음 코드는 JPEG 파일, GIF, PNG 파일, MP4 동영상 파일, PowerPoint 파일을 비롯한 다양한 정적 파일을 캐시하도록 NGINX를 구성합니다. www.example.com을 웹 서버의 URL로 바꾸세요.

server {    # substitute your web server's URL for "www.example.com"
    server_name www.example.com;
    root /var/www/example.com/htdocs;
    index index.php;
    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log;

   location / {
        try_files $uri $uri/ /index.php?$args;
    }

   location ~ .php$ {
        try_files $uri =404;
        include fastcgi_params;
        # substitute the socket, or address and port, of your Python server
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        #fastcgi_pass 127.0.0.1:9000;
 	}  

   location ~* .(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg
                  |jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid
                  |midi|wav|bmp|rtf)$ {
        expires max;
        log_not_found off;
        access_log off;
    }
}

팁 5 – 마이크로캐싱 구현

마이크로캐싱을 활용하면 Python, PHP 및 기타 언어를 실행하는 애플리케이션 서버의 성능을 크게 향상할 수 있습니다. 캐싱 관점에서 웹 페이지는 세 가지 유형으로 나눌 수 있습니다:

  • 정적 파일 – 이러한 파일은 팁 4에 설명된 대로 캐시할 수 있습니다.
  • 애플리케이션에서 생성된 비개인화 페이지 – 이러한 페이지는 최신 상태를 유지해야 하므로 일반적으로 캐시하는 것이 적절하지 않습니다. 예를 들어 로그인하지 않은 전자상거래 사용자에게 제공되는 페이지가 이에 해당합니다(다음 항목 참조). 판매 중인 제품, 추천 유사 제품 등이 계속 바뀔 수 있으므로 최신 페이지를 제공하는 것이 중요합니다. 하지만 가령 0.1초 뒤에 다른 사용자가 방문한다면 이전 사용자에게 제공한 것과 동일한 페이지를 보여줘도 괜찮을 수 있습니다.
  • 애플리케이션에서 생성된 개인화 페이지 – 사용자별로 제공되는 페이지이고 동일한 사용자가 같은 개인화 페이지를 두 번 볼 가능성도 낮으므로, 캐시해도 실질적인 이점이 없습니다. 로그인한 사용자에게 표시되는 전자상거래 페이지가 그 예로, 같은 페이지를 다른 사용자에게 표시할 수 없습니다.

정적 파일과 애플리케이션에서 생성된 비개인화 파일은 캐시할 수 있습니다

마이크로캐싱은 위에서 설명한 두 번째 페이지 유형, 즉 애플리케이션에서 생성되지만 개인화되지 않은 페이지에 유용합니다. 여기서 ‘마이크로’는 매우 짧은 시간을 의미합니다. 사이트에서 동일한 페이지를 초당 여러 번 생성하는 경우, 해당 페이지를 1초 동안 캐시하더라도 페이지의 최신성에는 큰 영향을 주지 않을 수 있습니다. 하지만 이렇게 짧은 캐싱만으로도 특히 트래픽이 급증할 때 애플리케이션 서버의 부하를 크게 줄일 수 있습니다. 캐시 제한 시간 동안 동일한 콘텐츠의 페이지를 10개, 20개 또는 100개 생성하는 대신, 특정 페이지를 한 번만 생성한 후 캐시하여 여러 사용자에게 제공합니다.

그 효과는 거의 기적에 가깝습니다. 초당 수십 건의 요청을 처리할 때 느리던 서버도 단 하나의 요청만 처리하면 상당히 빨라집니다. (물론 개인화된 페이지에 대한 요청은 별도로 처리해야 합니다.) Owen Garrett이 작성한 블로그 게시물에서는 구성 코드와 함께 마이크로캐싱의 이점을 자세히 설명합니다. 핵심 변경 사항인 제한 시간이 1초인 프록시 캐시 설정에는 몇 줄의 구성 코드만 필요합니다.

proxy_cache_path /tmp/cache keys_zone=cache:10m levels=1:2 inactive=600s max_size=100m;server {
    proxy_cache cache;
    proxy_cache_valid 200 1s;
    # ...
}

더 많은 구성 예시는 Tyler Hicks‑Wright의 다음 블로그 글을 참조하세요: NGINX를 활용한 Python 및 uWSGI.

결론

1부에서는 단일 서버 Python 구현의 성능을 높이는 방법과 캐싱을 살펴보았습니다. 캐싱은 단일 서버 구현에 배포하거나 리버스 프록시 서버 또는 별도의 캐싱 서버에서 실행할 수 있습니다. (캐싱은 별도의 서버에서 더 효과적으로 작동합니다.) 다음 편인 2부에서는 두 대 이상의 서버가 필요한 성능 개선 방법을 설명합니다.

지원, 실시간 활동 모니터링, 즉각적인 재구성 등 애플리케이션을 위한 NGINX Plus의 고급 기능을 살펴보고 싶다면, 지금 30일 무료 평가판을 시작하거나 문의하여 사용 사례에 대해 상담해 보세요.


공유

작성자 소개

관련 블로그 게시물

시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric
NGINX | 2023. 11. 30.

시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric

이 영상에서는 KubeCon의 NGINX 부스를 찾아가, 저희가 공개한 Kubernetes Gateway API 구현을 살펴봅니다.

F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법
NGINX | 2023. 03. 23.

F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법

앱 보안을 강화하는 두 가지 방법을 소개합니다. NGINX Management Suite를 통해 WAF 운영에 대한 가시성을 높이고, NGINX App Protect WAF를 활용해 코드형 보안을 구현하는 것입니다.

Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션
NGINX | 2021. 09. 27.

Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션

개발 주체, 개발 철학, 프로덕션 환경 준비도, 보안 및 지원을 기준으로 어떤 NGINX Ingress 컨트롤러가 가장 적합한지 알아보세요.

진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법
NGINX | 2021. 07. 06.

진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법

NGINX App Protect Denial of Service(DoS)가 계층 7 DoS 공격을 방어하기 위해 완화 방법을 실시간으로 조정하는 방식을 설명합니다. 정상적인 사용자 행동과 서버 상태에 대한 모델을 생성하여 공격을 나타내는 이상 징후를 감지하고 맞춤형 완화 시그니처를 적용할 수 있습니다.

NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리
NGINX | 2021. 06. 30.

NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리

NGINX Instance Manager 버전 1.0에는 두 가지 새로운 기능이 도입되었습니다. NGINX 인스턴스에 태그와 사용자 역할을 지정하면 전체 그룹에 구성을 한 번에 적용할 수 있습니다. 인증서 검사는 만료 예정인 인증서를 감지하여 서비스 중단을 방지할 수 있도록 인증서를 교체할 수 있게 해줍니다.

Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유
NGINX | 2015. 03. 25.

Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유

Netflix가 글로벌 콘텐츠 전송 네트워크인 Open Connect의 스트리밍 미디어 기술로 NGINX를 선택한 이유를 NGINX 개발자 Gleb Smirnoff에게서 알아보세요.