Nginx 성능 튜닝: 워커 프로세스와 연결 최적화

추측 없이 Nginx 워커 프로세스, 워커 연결, 파일 제한, keepalive 동작 및 업스트림 용량을 조정하세요.

Nginx 성능 튜닝: 워커 프로세스와 연결 최적화

Nginx 성능 튜닝은 종종 두 가지 설정으로 시작됩니다: 워커 프로세스와 워커 연결. 이 설정들은 서버가 동시에 처리할 수 있는 요청 수를 결정하므로, 작은 실수도 트래픽 급증 시 느린 페이지, 다운로드 중단 또는 연결 오류로 나타날 수 있습니다.

좋은 소식은 무작정 추측할 필요가 없다는 것입니다. Nginx의 워커 모델을 CPU, 파일 제한, 트래픽 패턴 및 업스트림 애플리케이션 동작에 맞춰 조정할 수 있습니다.

Nginx 워커가 트래픽을 처리하는 방법

Nginx는 마스터 프로세스와 하나 이상의 워커 프로세스를 사용합니다. 마스터 프로세스는 구성을 읽고, 워커를 시작하며, 리로드를 처리합니다. 워커는 실제 요청 처리를 담당합니다.

각 워커는 Nginx가 이벤트 기반 모델을 사용하기 때문에 많은 연결을 처리할 수 있습니다. 이는 요청당 하나의 프로세스나 스레드가 필요했던 기존 웹 서버와 다릅니다. 운영 체제가 허용하는 경우 하나의 Nginx 워커는 수천 개의 유휴 keepalive 연결을 유지할 수 있습니다.

두 가지 핵심 지시문은 일반적으로 /etc/nginx/nginx.conf에 위치합니다:

worker_processes auto;

events {
    worker_connections 1024;
}

worker_processes auto;는 Nginx가 사용 가능한 각 CPU 코어에 대해 하나의 워커를 생성하도록 지시합니다. 대부분의 최신 Linux 서버에서 이것이 올바른 시작점입니다. 가상 머신 크기를 조정할 때 잘못될 수 있는 값을 하드코딩하는 것을 방지합니다.

worker_connections은 각 워커가 열 수 있는 최대 동시 연결 수를 설정합니다. 대략적인 상한선은 다음과 같습니다:

worker_processes * worker_connections

4개의 워커와 4096개의 워커 연결이 있는 경우 이론적 최대값은 16,384개의 연결입니다. 실제로는 리버스 프록시 트래픽이 클라이언트 측과 업스트림 측 연결을 모두 사용할 수 있기 때문에 사용 가능한 수는 더 낮습니다.

예를 들어, Nginx가 Node.js 앱으로 트래픽을 프록시하는 경우 하나의 사용자 요청이 하나의 클라이언트 연결과 하나의 업스트림 연결을 소비할 수 있습니다. 즉, 16,384개의 열린 연결은 keepalive 및 요청 타이밍에 따라 약 8,000개의 활성 프록시 요청을 지원할 수 있습니다.

워커 프로세스 및 연결 값 선택

특별한 이유가 없는 한 worker_processes auto로 시작하십시오. 이 값을 CPU 수보다 높게 수동으로 설정해도 도움이 되지 않는 경우가 많습니다. 컨텍스트 스위칭을 증가시키고 부하 시 성능을 저하시킬 수 있습니다.

그런 다음 예상 동시성을 기반으로 worker_connections을 조정하십시오. 조용한 내부 도구는 1024로 충분할 수 있습니다. 로드 밸런서 뒤에 있는 공개 웹사이트는 4096, 8192 이상이 필요할 수 있습니다.

많은 프로덕션 서버에 대한 실용적인 기준은 다음과 같습니다:

worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 4096;
    multi_accept on;
}

worker_rlimit_nofile은 Nginx 워커가 사용할 수 있는 파일 디스크립터 제한을 높입니다. 모든 네트워크 소켓이 파일 디스크립터를 사용하기 때문에 중요합니다. 운영 체제 제한이 낮게 유지되면 worker_connections만 늘리는 것은 도움이 되지 않습니다.

서비스 관리자 제한도 확인해야 합니다. systemd 시스템에서 Nginx는 다음과 같은 재정의가 필요할 수 있습니다:

[Service]
LimitNOFILE=65535

systemd 제한을 변경한 후 systemd를 리로드하고 Nginx를 다시 시작하십시오. 더 광범위한 명령 참조는 Nginx 서비스 제어 명령을 참조하십시오.

multi_accept on에 주의하십시오. 이를 통해 워커는 준비 알림을 받은 후 가능한 한 많은 새 연결을 수락할 수 있습니다. 이는 버스트 시 도움이 될 수 있지만 마법은 아닙니다. 업스트림 앱이 느린 경우 연결을 더 빨리 수락해도 큐가 더 빨리 채워질 수 있습니다.

Nginx에 영향을 미치는 운영 체제 제한

Nginx 설정은 Linux 제한 위에 있습니다. 이러한 제한이 너무 작으면 Nginx 자체 구성이 넉넉해도 한계에 도달합니다.

튜닝 시 다음 영역을 확인하십시오:

  • Nginx 프로세스의 열린 파일 제한
  • 커널 네트워크 백로그 설정
  • 많은 프록시 트래픽에 대한 임시 포트 가용성
  • 업스트림 keepalive 동작
  • 로드 밸런서 유휴 시간 초과 값

열린 파일 제한이 가장 일반적인 장애물입니다. Nginx가 worker_connections are not enough 또는 too many open files와 같은 메시지를 기록하는 경우 Nginx와 systemd 제한을 모두 살펴봐야 합니다.

많은 클라이언트가 동시에 연결될 때 백로그 설정이 중요합니다. 커널 수락 큐가 가득 차면 CPU 사용량이 정상으로 보여도 사용자에게 연결 시간 초과가 발생할 수 있습니다. net.core.somaxconnnet.ipv4.tcp_max_syn_backlog와 같은 값은 높은 트래픽 튜닝 중에 자주 검토됩니다.

테스트 없이 임의의 예제에서 큰 커널 값을 복사하지 마십시오. 하나의 API 서버를 실행하는 소규모 팀은 CDN 에지 노드와 동일한 설정이 필요하지 않습니다. 단계적으로 조정하고, 측정하고, 기록을 유지하십시오.

사람들을 혼란스럽게 하는 또 다른 세부 사항이 있습니다: Nginx 연결 제한이 경로의 유일한 연결 제한은 아닙니다. 클라우드 로드 밸런서에는 유휴 시간 초과가 있습니다. 컨테이너 런타임에는 네트워크 주소 변환 제한이 있을 수 있습니다. 백엔드 앱에는 자체 워커 풀 또는 데이터베이스 연결 풀이 있을 수 있습니다. Nginx가 20,000개의 연결을 수락할 수 있지만 앱이 200개의 동시 요청만 처리할 수 있다면 사용자는 여전히 기다려야 합니다.

이것이 연결 튜닝 변경에 빠른 종단 간 확인이 포함되어야 하는 이유입니다. 서버 외부의 호스트에서 소규모 부하 테스트를 실행하고, Nginx 활성 연결을 관찰하고, 백엔드도 관찰하십시오. Nginx가 안정적인 동안 백엔드 지연 시간이 급격히 증가하면 프록시가 제 역할을 하고 있으며 다음 제한은 그 뒤에 있습니다.

리버스 프록시 워크로드 튜닝

많은 Nginx 서버가 애플리케이션 서버 앞에서 리버스 프록시 역할을 합니다. 그 역할에서 업스트림 동작은 Nginx 용량만큼 중요합니다.

Nginx가 동일한 백엔드 풀과 반복적으로 통신할 때 업스트림 keepalive를 사용하십시오:

upstream app_backend {
    server 127.0.0.1:3000;
    keepalive 32;
}

server {
    location / {
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_pass http://app_backend;
    }
}

이렇게 하면 새 백엔드 연결을 여는 비용이 줄어듭니다. 대시보드의 API 호출과 같이 앱이 많은 소규모 요청을 받을 때 특히 유용합니다.

또한 시간 초과 값을 확인하십시오. 매우 긴 프록시 시간 초과는 클라이언트가 사라지거나 애플리케이션이 응답을 중지한 후에도 워커 연결을 점유할 수 있습니다. 매우 짧은 시간 초과는 합법적인 느린 요청을 중단시킬 수 있습니다. 모든 곳에서 하나의 기본값을 사용하는 대신 워크로드에 맞게 시간 초과 값을 조정하십시오.

실용적인 시나리오: 사이트가 하루 중 대부분 빠르지만 뉴스레터 전송 중에 느려집니다. CPU는 35%에 불과하지만 Nginx 로그에 연결 경고가 표시됩니다. 이는 원시 CPU가 아닌 연결 제한, 파일 디스크립터 또는 업스트림 큐잉을 가리킵니다. 워커 연결을 늘리면 도움이 될 수 있지만 앱과 OS가 추가 부하를 지원할 수 있는 경우에만 가능합니다.

또 다른 일반적인 시나리오는 각 브라우저 탭에서 많은 소규모 API 호출을 만드는 대시보드 앱입니다. 10명이 수백 개의 짧은 요청을 생성할 수 있습니다. 이 경우 단순히 worker_connections을 높이는 것보다 업스트림 keepalive가 더 중요한 경우가 많습니다. 백엔드에 대한 반복적인 TCP 설정이 불필요한 오버헤드가 되기 때문입니다.

파일 다운로드 서비스의 경우 상황이 다릅니다. 소수의 사용자가 대용량 파일을 다운로드하는 동안 오랫동안 연결을 열어 둘 수 있습니다. 장기 전송을 위한 충분한 워커 연결이 필요할 수 있지만 sendfile, 디스크 처리량, 네트워크 처리량 및 클라이언트 시간 초과 동작도 확인해야 합니다.

WebSocket 또는 롱 폴링 앱의 경우 유휴 연결이 정상입니다. 높은 Waiting 수가 자동으로 나쁜 것은 아닙니다. 문제는 이러한 유휴 연결이 새 요청에 충분한 용량을 남겨두는지와 메모리 사용량이 예측 가능하게 유지되는지 여부입니다.

튜닝 중 stub_status 읽기

stub_status 모듈은 연결 동작에 대한 빠른 보기를 제공합니다:

Active connections: 291
server accepts handled requests
 1162447 1162447 4496426
Reading: 6 Writing: 17 Waiting: 268

Reading은 Nginx가 요청 헤더를 읽고 있음을 의미합니다. 지속적으로 높은 수는 느린 클라이언트, 큰 헤더 또는 공격 패턴을 가리킬 수 있습니다. Writing은 Nginx가 응답을 보내고 있음을 의미합니다. 클라이언트가 데이터를 느리게 수신하거나 응답이 큰 경우 상승할 수 있습니다. Waiting은 유휴 keepalive 연결을 의미합니다. 이 수는 정상적인 사이트에서 높을 수 있습니다.

acceptshandled 카운터는 일반적으로 함께 움직여야 합니다. 수락된 연결이 증가하지만 처리된 연결이 뒤처지거나 오류가 나타나면 워커 제한 및 파일 디스크립터 제한을 확인하십시오. 또한 Nginx가 처리하기 전에 커널이 연결을 드롭하는지 확인하십시오.

이러한 카운터는 기본적이지만 연결 압력을 CPU 압력과 분리하기 때문에 유용합니다. 활성 연결이 낮고 CPU가 높으면 문제는 아마도 worker_connections이 아닙니다. 활성 연결이 높고 CPU가 낮으면 연결 제한, keepalive 동작, 업스트림 큐잉 또는 느린 클라이언트가 더 가능성이 높습니다.

안전한 기준 구성

소규모 프로덕션 서버의 경우 보수적으로 시작하고 측정하는 것이 좋습니다:

worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 4096;
    multi_accept on;
}

http {
    keepalive_timeout 30s;
    keepalive_requests 1000;
}

이것은 보편적인 최상의 구성이 아닙니다. 많은 일반적인 리버스 프록시 워크로드에 대한 합리적인 시작점입니다. 매우 작은 VM은 더 적게 필요할 수 있습니다. 바쁜 에지 프록시는 훨씬 더 많이 필요할 수 있습니다. 중요한 부분은 worker_connectionsworker_rlimit_nofile이 정렬되어 있다는 것입니다.

기준을 적용한 후 유사한 트래픽 중에 이전 및 이후 메트릭을 비교하십시오. 리로드 후 한 번의 운 좋은 순간으로 튜닝 변경을 판단하지 마십시오. 실제 동작을 확인하기 위해 충분한 시간 동안 p95 또는 p99 지연 시간, 오류율, CPU, 메모리 및 백엔드 큐잉을 살펴보십시오.

연결 튜닝을 무작위로 보이게 만드는 실수

첫 번째 실수는 연결 대신 요청을 계산하는 것입니다. 브라우저는 여러 요청에 대해 하나의 연결을 재사용할 수 있으며 HTTP/2는 하나의 연결을 통해 많은 요청을 멀티플렉싱할 수 있습니다. 느린 클라이언트는 유용한 작업을 거의 수행하지 않으면서 연결을 열어 둘 수 있습니다. 즉, "초당 500개의 요청만 받는다"는 것이 Nginx에 필요한 연결 수를 알려주지 않습니다.

두 번째 실수는 업스트림 연결을 잊는 것입니다. Nginx가 정적 파일을 제공하는 경우 대부분의 연결은 클라이언트 측입니다. Nginx가 앱에 프록시하는 경우 활성 요청에는 종종 백엔드 소켓도 필요합니다. 업스트림에 keepalive를 사용하는 경우 일부 백엔드 연결은 재사용을 위해 열려 있습니다. 이는 좋지만 양쪽에서 파일 디스크립터를 계속 소비합니다.

세 번째 실수는 애플리케이션을 확인하지 않고 Nginx 제한을 높이는 것입니다. Nginx가 이제 12,000개의 동시 연결을 수락할 수 있지만 애플리케이션에 16개의 워커 프로세스와 50개의 연결로 구성된 데이터베이스 풀이 있다고 가정해 보십시오. Nginx는 앱이 완료할 수 있는 것보다 더 많은 작업을 수락합니다. 사용자는 즉각적인 연결 오류를 더 적게 볼 수 있지만 요청이 큐에서 더 오래 대기하기 때문에 지연 시간이 더 나빠질 수 있습니다.

네 번째 실수는 모든 곳에서 긴 keepalive 시간 초과를 사용하는 것입니다. Keepalive는 반복적인 TCP 및 TLS 설정을 피하기 때문에 유용합니다. 그러나 매우 긴 시간 초과는 트래픽 급증 후 많은 유휴 소켓을 열어 둘 수 있습니다. 메모리가 풍부한 에지 프록시에서는 괜찮을 수 있습니다. 소규모 VM에서는 활성 작업을 밀어낼 수 있습니다. Waiting 수가 매우 높고 요청 재사용이 낮은 경우 더 짧은 keepalive_timeout을 시도하고 다시 측정하십시오.

문제 해결 예제

오류 로그에 worker_connections are not enough라고 표시되면 구성된 값, 워커 수 및 프로세스 파일 제한을 확인하십시오:

grep -R "worker_connections\\|worker_processes\\|worker_rlimit_nofile" /etc/nginx/nginx.conf /etc/nginx/conf.d
cat /proc/$(pgrep -o nginx)/limits | grep "open files"

pgrep -o nginx 명령은 일반적으로 가장 오래된 Nginx 프로세스를 찾으며, 이는 종종 마스터입니다. 일부 시스템에서는 systemctl status nginx를 사용하여 기본 PID를 확인하는 것이 좋습니다.

오류 로그에 too many open files라고 표시되면 worker_connections만 높이지 마십시오. 프로세스가 디스크립터 제한에 도달한 것입니다. systemd 서비스에 LimitNOFILE을 추가하거나 조정하고, systemd를 리로드하고, Nginx를 다시 시작하여 새 제한이 실제로 적용되도록 하십시오:

sudo systemctl edit nginx
sudo systemctl daemon-reload
sudo systemctl restart nginx

사용자에게 시간 초과가 발생하지만 Nginx에 여유 CPU가 있고 연결 경고가 없는 경우 Nginx 뒤를 살펴보십시오. 액세스 로그에서 업스트림 응답 시간을 확인하십시오. 앱 워커 풀을 확인하십시오. 데이터베이스 연결을 확인하십시오. 리버스 프록시는 실제 병목 현상이 포화된 백엔드인 동안 트래픽을 원활하게 수락할 수 있습니다.

요청이 액세스 로그에 나타나기 전에 급증으로 인해 연결 재설정이 발생하면 문제는 Nginx 요청 처리보다 앞서 있을 수 있습니다. 커널 백로그 설정, 로드 밸런서 로그, 방화벽 상태 테이블 및 SYN 플러드 보호를 살펴보십시오. Nginx는 수신하지 않은 요청을 기록할 수 없습니다.

변경 사항을 안전하게 테스트하는 방법

편집하고 바라기만 하면서 프로덕션을 튜닝하지 마십시오. 먼저 구문을 테스트하십시오:

sudo nginx -t

그런 다음 Nginx를 리로드하여 활성 연결이 정상적으로 처리되도록 하십시오:

sudo systemctl reload nginx

모든 변경 후 오류 로그를 확인하십시오:

sudo tail -f /var/log/nginx/error.log

또한 요청 지연 시간, 4xx 및 5xx 비율, 활성 연결, CPU, 메모리 및 업스트림 응답 시간을 모니터링해야 합니다. 연결 용량을 증가시키지만 애플리케이션 지연 시간을 높이는 튜닝 변경은 실제 승리가 아닐 수 있습니다.

더 깊은 검증 단계는 Nginx 구성 테스트 및 상태 모니터링을 참조하십시오.

전문가를 불러야 할 때

기본 튜닝 후에도 Nginx 오류가 계속되거나, 트래픽 급증이 수익에 영향을 미치거나, 프로덕션 시스템에서 커널 네트워크 설정을 변경하는 경우 숙련된 DevOps 엔지니어 또는 웹 성능 전문가에게 문의하십시오. 결제 흐름, 로그인 시스템 또는 중요한 API 앞에서 Nginx를 튜닝하는 경우에도 동일하게 적용됩니다.

병목 현상이 명확하지 않은 경우 전문가의 도움이 유용합니다. 실제 문제가 느린 데이터베이스, 소진된 업스트림 앱 풀, 과부하된 TLS 종료 계층 또는 로드 밸런서 시간 초과 불일치인 경우 Nginx가 문제처럼 보일 수 있습니다.

핵심 요점은 간단합니다: 워커 프로세스를 CPU에 맞추고, 워커 연결을 동시성에 맞추고, Linux 파일 제한이 둘 다 지원하는지 확인하십시오. 한 번에 한 계층씩 변경하고, 리로드 전에 구성을 테스트하고, 이론적 최대값을 신뢰하는 대신 실제 트래픽을 측정하십시오.