HAProxy vs NGINX: 클라우드 프록시 성능 비교

이번 포스트에서는 HAProxy 및 NGINX의 개요와 장단점 및 테스트를 진행 후 비교하여 더 좋은 솔루션이 무엇인지 알아보겠습니다.

대부분의 성능 벤치마크는 최대 처리량 또는 초당 요청 수(RPS)를 측정하지만 이러한 지표는 실제 사이트의 성능 사례를 지나치게 단순화할 수 있습니다. 최고 처리량 또는 그에 가까운 속도로 서비스를 실행하는 조직은 거의 없습니다. 어느 쪽이든 성능이 10% 변경되면 큰 차이가 날 수 있습니다. 사이트에 필요한 처리량 또는 RPS는 무한하지 않지만 서비스를 제공해야 하는 동시 사용자 수와 각 사용자의 활동 수준과 같은 외부 요인에 의해 고정됩니다. 결국 가장 중요한 것은 사용자가 최고 수준의 서비스를 제공받는 것입니다. 최종 사용자는 얼마나 많은 사람들이 당신의 사이트를 방문하는지 신경 쓰지 않습니다. 그들은 단지 그들이 받는 서비스에만 관심이 있습니다.

이를 통해 가장 중요한 것은 조직이 부하가 높은 경우에도 모든 사용자에게 일관되고 지연 시간이 짧은 성능을 제공한다는 사실을 알 수 있습니다. Amazon Elastic Compute Cloud(EC2)에서 Reverse Proxy로 실행되는 NGINX와 HAProxy를 비교할 때 다음 두 가지 작업을 수행하기 시작했습니다.

  • 각 프록시가 편안하게 처리하는 부하 수준 결정
  • 사용자 경험과 가장 직접적인 상관관계가 있는 지표인 지연 시간 백분위수 분포를 수집합니다.

목차

1. 테스트 프로토콜 및 지표 수집
2. 테스트 방법론
2-1. Client
2-2. HAProxy : 구성 및 버전 관리
2-3. NGINX : 구성 및 버전 관리
3. HAProxy vs NGINX 성능 비교 결과
4. HAProxy vs NGINX 결론

1. 테스트 프로토콜 및 지표 수집

부하 생성(load-generation) 프로그램 wrk2를 사용하여 클라이언트를 에뮬레이트 하여 지정된 기간 동안 HTTPS를 통해 지속적인 요청을 수행했습니다. 테스트 대상 시스템인 HAProxy 또는 NGINX는 Reverse Proxy 역할을 하여 wrk 스레드에 의해 시뮬레이션된 클라이언트와 암호화된 연결을 설정하고 요청을 NGINX Plus R22를 실행하는 Backend 웹 서버로 전달하고 웹 서버(파일)에서 생성된 응답을 클라이언트로 반환합니다.

세 가지 구성 요소(클라이언트, Reverse Proxy 및 웹 서버) 각각은 EC2의 c5n.2xlarge Amazon Machine Image(AMI)에서 Ubuntu 20.04.1 LTS를 실행했습니다.

언급한 바와 같이 각 테스트 실행에서 전체 지연 시간 백분위수 분포를 수집했습니다. 지연 시간은 클라이언트가 요청을 생성하고 응답을 받는 사이의 시간으로 정의됩니다. 지연 시간 백분위수 분포는 테스트 기간 동안 수집된 지연 시간 측정값을 가장 높은 시간(가장 짧은 지연 시간)에서 가장 낮은 시간 순으로 정렬합니다.

2. 테스트 방법론

2-1. Client

wrk2(버전 4.0.0)를 사용하여 Amazon EC2 인스턴스에서 다음 스크립트를 실행했습니다.

taskset -c 0-3 wrk -t 4 -c 100 -d 30s -R requests_per_second --latency https://adc.domain.com:443/

웹 애플리케이션에 액세스하는 많은 클라이언트를 시뮬레이트 하기 위해 Reverse Proxy에 대한 100개의 연결을 함께 설정하는 4개의 wrk 스레드가 생성되었습니다. 30초 테스트 실행 동안 스크립트는 지정된 수의 RPS를 생성했습니다. 이러한 매개변수는 다음 wrk2 옵션에 해당합니다.

  • -t 옵션 – 생성할 스레드 수(4)
  • -c 옵션 – 생성할 TCP 연결 수(100)
  • -d 옵션 – 테스트 기간의 수(30초)
  • -R 옵션 – 클라이언트가 발행한 RPS 수
  • –latency 옵션 – 출력에는 수정된 지연 시간 백분위수 정보가 포함됩니다.

Proxy 중 하나가 CPU 사용률 100%에 도달할 때까지 테스트 실행에 걸쳐 RPS 수를 점진적으로 늘렸습니다. 자세한 내용은 성능 결과를 참조하십시오.

클라이언트와 Proxy 간의 모든 연결은 TLSv1.3을 사용하는 HTTPS를 통해 이루어졌습니다. 우리는 256 Bit 키 크기의 ECC, Perfect Forward Secrecy 및 TLS_AES_256_GCM_SHA384 암호화 제품군을 사용했습니다. (TLSv1.2는 여전히 인터넷에서 일반적으로 사용되기 때문에 테스트도 다시 실행했습니다. 결과는 TLSv1.3의 결과와 매우 유사하여 여기에 포함하지 않습니다.)

2-2. HAProxy : 구성 및 버전 관리

HAProxy 버전 2.3(안정적인 버전)을 Reverse Proxy로 프로비저닝했습니다.

인기 있는 웹사이트의 동시 사용자 수는 엄청날 수 있습니다. 많은 양의 트래픽을 처리하려면 Reverse Proxy가 여러 CPU 코어를 활용하도록 확장할 수 있어야 합니다. 확장에는 두 가지 기본 방법인 다중 처리 및 다중 스레딩이 있습니다. NGINX와 HAProxy는 모두 다중 처리를 지원하지만 중요한 차이점이 있습니다. HAProxy의 구현에서는 프로세스가 메모리를 공유하지 않습니다(NGINX에서는 공유합니다). 프로세스 간에 상태를 공유할 수 없으면 HAProxy에 몇 가지 결과가 발생합니다.

  • 구성 매개 변수 – 제한, 통계 및 속도를 포함하여 각 프로세스에 대해 별도로 정의해야 합니다.
  • 성능 지표는 프로세스별로 수집됩니다. 이를 결합하려면 추가 구성이 필요하며 이는 상당히 복잡할 수 있습니다.
  • 각 프로세스는 상태 점검을 개별적으로 처리하므로 대상 서버는 예상대로 서버별이 아닌 프로세스별로 Probe 됩니다.
  • 세션 지속성을 사용할 수 없습니다.
  • HAProxy Runtime API를 통한 동적 구성 변경은 단일 프로세스에 적용되므로 각 프로세스에 대해 API 호출을 반복해야 합니다.

이러한 문제 때문에 HAProxy는 다중 처리 구현의 사용을 권장하지 않습니다. HAProxy 구성 설명서에서 직접 인용한 문장입니다.

” 여러 프로세스를 사용하는 것은 디버깅하기가 더 어렵고 정말 권장되지 않습니다. “

HAProxy는 멀티 처리의 대안으로 버전 1.8에서 멀티 스레딩(Multi‑Threading)을 도입했습니다. 멀티 스레딩은 대부분 상태 공유 문제를 해결하지만 성능 결과에서 논의한 것처럼 멀티 스레드 모드에서 HAProxy는 멀티 프로세스 모드만큼 성능이 좋지 않습니다.

HAProxy 구성에는 다중 스레드 모드(HAProxy MT) 및 다중 프로세스 모드(HAProxy MP) 모두에 대한 프로비저닝이 포함되었습니다. 테스트 중에 각 RPS 수준에서 모드를 번갈아 사용하기 위해 적절한 라인에 주석을 달고 주석을 제거하고 구성이 적용되도록 HAProxy를 다시 시작했습니다.

$ sudo service haproxy restart

HAProxy MT가 프로비저닝된 구성은 다음과 같습니다. 한 프로세스에서 4개의 스레드가 생성되고 각 스레드가 CPU에 고정됩니다. HAProxy MP(여기에 설명)의 경우 CPU에 각각 4개의 프로세스가 고정되어 있습니다.


    cpu-map auto:1/1-4 0-3
 
    #Multi-process mode
    #nbproc 4
    #cpu-map 1 0
    #cpu-map 2 1
    #cpu-map 3 2
    #cpu-map 4 3        
  
    ssl-server-verify none
    log /dev/log    local0
    log /dev/log    local1 notice
    chroot /var/lib/haproxy
    maxconn 4096
    
defaults
    log    global
    option httplog
    option http-keep-alive
      
frontend Local_Server
    bind 172.31.12.25:80
    bind 172.31.12.25:443 ssl crt /etc/ssl/certs/bundle-hapee.pem
    redirect scheme https code 301 if !{ ssl_fc } 
    default_backend Web-Pool
    http-request set-header Connection keep-alive
    
backend Web-Pool
    mode http
    server server1 backend.workload.1:80 check

2-3. NGINX : 구성 및 버전 관리

NGINX Open Source 버전 1.18.0을 Reverse Proxy로 배포했습니다.

시스템에서 사용 가능한 모든 코어(이 경우 4개)를 사용하기 위해 우리는 Worker_Processes 지시문에 auto 매개 변수를 포함시켰습니다. 이는 저장소에서 배포된 기본 nginx.conf 파일의 설정이기도 합니다. 또한 각 작업자 프로세스를 CPU에 고정하기 위해 worker_cpu_affinity 지시문이 포함되었습니다(두 번째 매개변수의 각 1은 시스템의 CPU를 나타냅니다).

user                nginx;
worker_processes    auto;
worker_cpu_affinity auto 1111;

error_log /var/log/nginx/error.log warn;
pid       /var/run/nginx.pid;


events {
    worker_connections 1024;
}

http {
    include      /etc/nginx/mime.types;
    default_type application/octet-stream;

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log  main;

    sendfile           on;
    keepalive_timeout  65;
    keepalive_requests 100000;

    server {
        listen              443 ssl reuseport;
        ssl_certificate     /etc/ssl/certs/hapee.pem;
        ssl_certificate_key /etc/ssl/private/hapee.key;
        ssl_protocols       TLSv1.3;    

        location / {
            proxy_set_header   Connection ' ';
            proxy_http_version 1.1;
            proxy_pass         http://backend;
        }
    }

    upstream backend {
        server    backend.workload.1:80;
        keepalive 100;
    }
}

3. HAProxy vs NGINX 성능 비교 결과

Reverse Proxy가 애플리케이션의 Frontend 역할을 하는 경우 성능이 매우 중요합니다.

각 Reverse Proxy(NGINX, HAProxy MP 및 HAProxy MT) 중 하나가 CPU 사용률이 100%에 도달할 때까지 점점 더 많은 RPS를 테스트했습니다. 세 가지 모두 CPU가 소진되지 않은 RPS 수준에서 유사한 성능을 발휘했습니다.

CPU 사용률이 100% 도달하는 것은 HAProxy MT에서 85,000RPS로 처음 발생했으며 그 시점에서 HAProxy MT와 HAProxy MP 모두에서 성능이 크게 저하되었습니다. 여기서는 해당 Load Level에서 각 Reverse Proxy의 지연 시간 백분위수 분포를 나타냅니다. 차트는 GitHub에서 사용할 수 있는 HdrHistogram 프로그램을 사용하여 wrk 스크립트의 출력에서 구성되었습니다.

85,000 RPS에서 HAProxy MT의 지연 시간은 90번째 백분위수까지 갑자기 증가한 다음 약 1,100 ms에서 점차 적으로 감소합니다.

HAProxy MP는 HAProxy MT보다 더 나은 성능을 발휘합니다. 지연 시간은 99번째 백분위수까지 더 느린 속도로 상승하며, 이 지점에서 대략 400ms에서 평준화되기 시작합니다. (HAProxy MP가 더 효율적이라는 확인으로 모든 RPS 수준에서 HAProxy MT가 HAProxy MP보다 약간 더 많은 CPU를 사용하는 것을 관찰했습니다.)

NGINX는 어떤 백분위수에서도 지연 시간이 거의 없습니다. 상당한 수의 사용자가 경험할 수 있는 가장 높은 지연 시간(99.9999번째 백분위수)은 약 8ms입니다.

이러한 결과는 사용자 경험에 대해 무엇을 알려줍니까? 서론에서 언급했듯이 실제로 중요한 측정 기준은 테스트 중인 시스템의 서비스 시간이 아니라 최종 사용자 관점의 응답 시간입니다.

분포의 평균 지연 시간이 사용자 경험을 가장 잘 나타낸다는 것은 일반적인 오해입니다. 실제로 중앙값은 응답 시간의 약 절반이 더 나쁜 숫자입니다. 사용자는 일반적으로 페이지 로드당 많은 요청을 발행하고 많은 리소스에 액세스하므로 일부 요청은 차트의 상위 백분위수(99~99.9999번째)에서 지연 시간을 경험할 수밖에 없습니다. 사용자는 낮은 성능을 참지 못하기 때문에 백분위수가 높은 지연 시간을 가장 쉽게 알아차릴 수 있습니다.

이렇게 생각해 보세요. 식료품점에서 계산하는 경험은 계산원이 물건을 받는 데 걸리는 시간이 아니라 계산대에 선 순간부터 가게를 떠나는 데 걸리는 시간에 따라 결정됩니다. 예를 들어 앞에 있는 고객이 항목 가격에 대해 이의를 제기하고 출납원이 확인을 위해 누군가에게 요청해야 하는 경우 전반적인 체크아웃 시간은 평소보다 훨씬 길어집니다.

지연 시간 결과에서 이를 고려하려면 조정 누락이라는 것을 수정해야 합니다. 여기서(wrk2 README 끝에 있는 참고에 설명된 대로) “높은 지연 시간 응답으로 인해 부하 생성기가 서버를 조정하여 지연 시간이 긴 기간 동안 측정을 피하십시오.” 다행스럽게도 wrk2는 기본적으로 조정 누락을 수정합니다(조정 누락에 대한 자세한 내용은 README 참조하십시오).

HAProxy MT가 85,000RPS에서 CPU를 소진하면 많은 요청에서 지연 시간이 길어집니다. 조정 누락을 수정하고 있기 때문에 데이터에 정당하게 포함됩니다. 지연 시간이 긴 요청을 한두 번만 하면 페이지 로드가 지연되어 성능이 저하된 것으로 인식됩니다. 실제 시스템이 한 번에 여러 사용자에게 서비스를 제공하고 있다는 점을 감안할 때 요청의 1%만 긴 지연 시간(99번째 백분위수 값)이 있더라도 많은 사용자가 잠재적으로 영향을 받을 수 있습니다.

4. HAProxy vs NGINX 결론

성능 벤치마킹의 주요 포인트 중 하나는 애플리케이션이 사용자를 만족시키고 계속해서 재방문할 수 있을 만큼 충분히 반응하는지 확인하는 것입니다.

NGINX와 HAProxy는 모두 소프트웨어 기반이며 이벤트 기반 아키텍처를 가지고 있습니다. HAProxy MP는 HAProxy MT보다 더 우수한 성능을 제공하지만 HAProxy: 구성 및 버전 관리에서 자세히 설명한 것처럼 프로세스 간 상태 공유가 부족하여 관리가 더 복잡해집니다. HAProxy MT는 이러한 제한 사항을 해결하지만 결과에 나타난 것처럼 낮은 성능이 나오게 됩니다.

NGINX를 사용하면 프로세스가 상태를 공유하므로 멀티 스레드 모드가 필요하지 않습니다. HAProxy의 사용을 방해하는 제한 없이 다중 처리의 우수한 성능을 얻을 수 있습니다.

NGINX 를 Proxy로 사용하거나 테스트 및 사용해 보려면 지금 30일 무료 평가판을 신청하거나 사용 사례에 대해 최신 소식을 빠르게 전달받고 싶으시면 아래 뉴스레터를 구독하세요.