트래픽이 많은 웹사이트를 위한 필수 Nginx 성능 튜닝 체크리스트
워커, 연결, 버퍼, 캐싱, 압축, 로그, 타임아웃, TLS, 정적 파일에 대한 실용적인 Nginx 성능 체크리스트입니다.
트래픽이 많은 웹사이트를 위한 필수 Nginx 성능 튜닝 체크리스트
Nginx 성능 튜닝은 추측 게임이 아닌 체크리스트처럼 접근할 때 가장 쉽습니다. Nginx가 수용할 수 있는 트래픽 양을 결정하는 제한부터 시작하여 버퍼링, 캐싱, 압축, 로깅, 타임아웃, TLS 및 그 뒤에 있는 백엔드 서비스로 확장해 나가세요.
여기에 있는 모든 지시어를 한 번에 프로덕션에 적용하지 마십시오. 좋은 Nginx 성능 튜닝 체크리스트는 무엇을 확인해야 하는지, 왜 중요한지, 그리고 과도하게 적용할 경우 무엇이 잘못될 수 있는지 결정하는 데 도움을 줍니다. 대부분 정적인 문서 사이트에 적합한 설정은 롱폴링 API나 파일 업로드 서비스에 적합한 설정과 다릅니다.
1. 워커 프로세스 및 연결 최적화
Nginx는 마스터-워커 프로세스 모델을 활용합니다. 마스터 프로세스는 구성을 읽고 워커 프로세스를 관리하며, 워커 프로세스는 실제 클라이언트 요청을 처리합니다. 이를 적절히 구성하면 동시성과 리소스 활용률을 획기적으로 개선할 수 있습니다.
worker_processes
이 지시어는 Nginx가 생성할 워커 프로세스 수를 결정합니다. 일반적으로 auto로 설정하면 Nginx가 CPU 코어 수를 감지하여 동일한 수의 워커 프로세스를 생성하므로 일반적인 모범 사례입니다.
worker_connections
단일 워커 프로세스가 열 수 있는 최대 동시 연결 수를 정의합니다. 이 설정은 worker_processes와 함께 Nginx가 처리할 수 있는 총 이론적 동시 연결 수(worker_processes * worker_connections)를 결정합니다.
multi_accept
워커 프로세스가 한 번에 여러 개의 새 연결을 수락할 수 있도록 하여 높은 부하에서 잠재적인 병목 현상을 방지합니다.
# /etc/nginx/nginx.conf
worker_processes auto; # 일반적으로 'auto' 또는 CPU 코어 수로 설정
events {
worker_connections 1024; # 서버 용량과 예상 부하에 따라 조정
multi_accept on;
}
팁: CPU가 지속적으로 높다면
worker_connections를 높이는 것만으로는 해결되지 않습니다. 먼저 CPU 사용량이 TLS 핸드셰이크, 압축, 로깅, 정규식 기반 라우팅 또는 업스트림 애플리케이션에서 발생하는지 확인하십시오.
2. 효율적인 연결 관리
Nginx가 네트워크 연결을 처리하는 방식을 최적화하면 오버헤드를 줄이고 응답성을 개선할 수 있습니다.
keepalive_timeout
keep-alive 클라이언트 연결이 유지되는 시간을 지정합니다. 연결을 재사용하면 새 TCP 연결 및 SSL 핸드셰이크 설정 오버헤드가 줄어듭니다. 애플리케이션의 상호작용성에 따라 일반적인 값은 15-65초입니다.
sendfile
파일 디스크립터 간 데이터 직접 전송을 활성화하여 사용자 공간 버퍼링을 우회합니다. 이는 정적 파일을 제공할 때 성능을 크게 향상시킵니다.
tcp_nopush
sendfile과 함께 작동합니다. Nginx는 HTTP 헤더와 파일의 시작 부분을 하나의 패킷으로 보내려고 시도합니다. 그 후에는 전체 패킷으로 데이터를 보냅니다. 이렇게 하면 전송되는 패킷 수가 줄어듭니다.
tcp_nodelay
Nginx가 버퍼링 없이 데이터를 사용 가능한 즉시 보내도록 지시합니다. 이는 처리량 극대화보다 낮은 지연 시간이 더 중요한 대화형 애플리케이션(예: 채팅 애플리케이션 또는 실시간 업데이트)에 유용합니다.
http {
keepalive_timeout 65; # 65초 동안 keep-alive 연결 유지
sendfile on;
tcp_nopush on; # sendfile on 필요
tcp_nodelay on; # 동적 콘텐츠 프록시에 유용
}
3. 버퍼 최적화
Nginx는 클라이언트 요청과 업스트림 서버(예: 애플리케이션 서버)의 응답을 처리하기 위해 버퍼를 사용합니다. 이러한 버퍼의 크기를 적절히 조정하면 불필요한 디스크 I/O를 방지하고 메모리 사용량을 줄이며 처리량을 개선할 수 있습니다.
클라이언트 버퍼
client_body_buffer_size: 클라이언트 요청 본문의 버퍼 크기입니다. 본문이 이 크기를 초과하면 임시 파일에 기록됩니다.client_header_buffer_size: 클라이언트 요청의 첫 번째 줄과 헤더의 버퍼 크기입니다.large_client_header_buffers: 클라이언트 요청 헤더를 읽기 위한 더 큰 버퍼의 수와 크기를 정의합니다. 쿠키가 많거나 리퍼러 헤더가 긴 요청에 유용합니다.
프록시 버퍼(리버스 프록시 설정용)
proxy_buffers: 프록시 서버의 응답을 읽는 데 사용되는 버퍼의 수와 크기입니다.proxy_buffer_size: 응답을 읽기 위한 첫 번째 버퍼의 크기입니다. 일반적으로 헤더만 포함하므로 더 작습니다.proxy_busy_buffers_size: 주어진 시간에 '사용 중' 상태(클라이언트로 활발히 전송 중)일 수 있는 응답 버퍼의 최대 양입니다.
FastCGI 버퍼(PHP-FPM 등용)
fastcgi_buffers: FastCGI 서버의 응답을 읽는 데 사용되는 버퍼의 수와 크기입니다.fastcgi_buffer_size: 응답을 읽기 위한 첫 번째 버퍼의 크기입니다.
http {
# 클라이언트 버퍼
client_body_buffer_size 1M; # 예상 요청 본문 크기(예: 파일 업로드)에 따라 조정
client_header_buffer_size 1k;
large_client_header_buffers 4 8k; # 4개의 버퍼, 각각 8KB
# 프록시 버퍼(Nginx가 리버스 프록시 역할을 하는 경우)
proxy_buffers 8 16k; # 8개의 버퍼, 각각 16KB
proxy_buffer_size 16k; # 첫 번째 버퍼 16KB
proxy_busy_buffers_size 16k; # 최대 16KB의 사용 중 버퍼
# FastCGI 버퍼(Nginx가 PHP-FPM과 함께 작동하는 경우)
fastcgi_buffers 16 16k; # 많은 PHP-FPM 앱의 시작점
fastcgi_buffer_size 16k; # 첫 번째 버퍼 16KB
}
경고: 버퍼를 너무 작게 설정하면 디스크 I/O 및 성능 저하가 발생할 수 있습니다. 너무 크게 설정하면 과도한 메모리를 소비할 수 있습니다. 테스트를 통해 균형을 찾으십시오.
4. 강력한 캐싱 전략 구현
캐싱은 성능을 개선하고 백엔드 서버의 부하를 줄이는 가장 효과적인 방법 중 하나입니다. Nginx는 강력한 콘텐츠 캐시 역할을 할 수 있습니다.
proxy_cache_path
캐시 디렉토리의 경로, 크기, 하위 디렉토리 수준 수 및 비활성 항목이 캐시에 남아 있는 시간을 정의합니다.
proxy_cache
proxy_cache_path에 정의된 영역을 참조하여 주어진 location 블록에 대한 캐싱을 활성화합니다.
proxy_cache_valid
Nginx가 특정 HTTP 상태 코드로 응답을 캐시해야 하는 시간을 설정합니다.
proxy_cache_revalidate
활성화되면 Nginx는 If-Modified-Since 및 If-None-Match 헤더를 사용하여 백엔드에서 캐시된 콘텐츠의 유효성을 재검증하여 대역폭 사용량을 줄입니다.
proxy_cache_use_stale
백엔드 서버가 다운되었거나, 응답하지 않거나, 오류가 발생한 경우 Nginx가 오래된 캐시 콘텐츠를 제공하도록 지시합니다. 이는 가용성을 크게 향상시킵니다.
expires
정적 파일의 클라이언트 측 캐싱을 위해 Cache-Control 및 Expires 헤더를 설정합니다. 이렇게 하면 Nginx에 대한 반복 요청이 최소화됩니다.
http {
# http 블록에 프록시 캐시 영역 정의
proxy_cache_path /var/cache/nginx/my_cache levels=1:2 keys_zone=my_cache:10m inactive=60m max_size=10g;
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://my_upstream_backend;
proxy_cache my_cache; # 이 위치에 대해 캐싱 활성화
proxy_cache_valid 200 302 10m; # 성공적인 응답을 10분 동안 캐시
proxy_cache_valid 404 1m; # 404 응답을 1분 동안 캐시
proxy_cache_revalidate on;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cache-Status $upstream_cache_status; # 디버깅에 도움
}
# 브라우저에서 정적 파일을 더 오래 캐시
location ~* \.(jpg|jpeg|gif|png|css|js|ico|woff|woff2|ttf|svg|eot)$ {
expires 30d; # 30일 동안 캐시
add_header Cache-Control "public, no-transform";
# 정적 파일의 경우 프록시되지 않으면 Nginx에서 직접 제공하는 것을 고려
root /var/www/html;
}
}
}
5. Gzip 압축 활성화
클라이언트로 보내기 전에 응답을 압축하면 특히 텍스트 기반 콘텐츠의 경우 대역폭 사용량을 크게 줄이고 페이지 로드 시간을 개선할 수 있습니다.
gzip on
gzip 압축을 활성화합니다.
gzip_comp_level
압축 수준(1-9)을 설정합니다. 레벨 1은 가장 빠르지만 압축률이 낮고, 레벨 9는 가장 느리지만 최대 압축률을 제공합니다. 레벨 6은 일반적으로 좋은 균형을 제공합니다.
gzip_types
압축해야 하는 MIME 유형을 지정합니다. 일반적인 텍스트, CSS, JavaScript 및 JSON 유형을 포함하십시오.
gzip_min_length
압축을 활성화해야 하는 최소 응답 길이(바이트)를 설정합니다. 작은 파일은 큰 이점이 없으며 압축 오버헤드로 인해 더 느려질 수 있습니다.
gzip_proxied
프록시된 응답인 경우에도 Nginx가 압축하도록 지시합니다. any가 일반적인 값입니다.
gzip_vary
응답에 Vary: Accept-Encoding 헤더를 추가하여 프록시에 응답이 Accept-Encoding 요청 헤더에 따라 다를 수 있음을 알립니다.
http {
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6; # 압축 수준 1-9 (6이 좋은 균형)
gzip_buffers 16 8k; # 16개의 버퍼, 각각 8KB
gzip_http_version 1.1; # 압축을 위한 최소 HTTP 버전
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
gzip_min_length 1000; # 1KB보다 큰 응답만 압축
}
6. 로깅 최적화
로그는 모니터링 및 문제 해결에 필수적이지만, 과도하거나 최적화되지 않은 로깅은 특히 트래픽이 많은 사이트에서 상당한 디스크 I/O를 유발할 수 있습니다.
access_log
- 정적 자산에 대해 비활성화: 액세스 빈도가 높은 정적 콘텐츠(이미지, CSS, JS)의 경우
access_log를 비활성화하면 많은 I/O를 절약할 수 있습니다. - 버퍼링: Nginx는 디스크에 쓰기 전에 메모리에 로그 항목을 버퍼링하여 디스크 쓰기 빈도를 줄일 수 있습니다. 여기서
buffer및flush매개변수가 사용됩니다.
error_log
적절한 로깅 수준(crit, error, warn, info, debug)을 설정합니다. 프로덕션의 경우 warn 또는 error가 일반적으로 로그를 범람시키지 않으면서 중요한 문제를 포착하기에 충분합니다.
http {
server {
# 동적 콘텐츠의 기본 액세스 로그
access_log /var/log/nginx/access.log main;
location ~* \.(jpg|jpeg|gif|png|css|js|ico|woff|woff2|ttf|svg|eot)$ {
access_log off; # 일반적인 정적 파일에 대한 로깅 비활성화
expires 30d;
}
}
# 기본 HTTP 컨텍스트에 대한 버퍼링된 액세스 로그 예
# access_log /var/log/nginx/access.log main buffer=16k flush=5s;
error_log /var/log/nginx/error.log warn; # 경고 이상만 로깅
}
7. 타임아웃 조정
적절하게 구성된 타임아웃은 Nginx가 비활성 연결을 너무 오래 유지하지 않도록 하여 리소스를 확보합니다.
클라이언트 측 타임아웃
client_body_timeout: Nginx가 클라이언트가 요청 본문을 보낼 때까지 기다리는 시간입니다.client_header_timeout: Nginx가 클라이언트가 요청 헤더를 보낼 때까지 기다리는 시간입니다.send_timeout: Nginx가 응답을 보낸 후 클라이언트가 수락할 때까지 기다리는 시간입니다.
프록시/FastCGI 타임아웃(해당하는 경우)
proxy_connect_timeout: 프록시 서버와 연결을 설정하기 위한 타임아웃입니다.proxy_send_timeout: 프록시 서버로 요청을 전송하기 위한 타임아웃입니다.proxy_read_timeout: 프록시 서버로부터 응답을 읽기 위한 타임아웃입니다.
http {
client_body_timeout 15s; # 클라이언트가 본문을 보낼 시간 15초
client_header_timeout 15s; # 클라이언트가 헤더를 보낼 시간 15초
send_timeout 15s; # Nginx가 클라이언트에 응답을 보낼 시간 15초
# 프록시 시나리오의 경우
proxy_connect_timeout 5s; # 업스트림에 연결하는 데 5초
proxy_send_timeout 15s; # 업스트림에 요청을 보내는 데 15초
proxy_read_timeout 15s; # 업스트림에서 응답을 읽는 데 15초
# FastCGI 시나리오의 경우
fastcgi_connect_timeout 5s;
fastcgi_send_timeout 15s;
fastcgi_read_timeout 15s;
}
8. SSL/TLS 최적화
HTTPS가 활성화된 사이트의 경우 SSL/TLS 설정을 최적화하는 것이 CPU 오버헤드를 줄이고 핸드셰이크 성능을 개선하는 데 중요합니다.
ssl_session_cache 및 ssl_session_timeout
동일한 클라이언트의 후속 연결에 대해 계산 비용이 많이 드는 전체 TLS 핸드셰이크를 피하기 위해 SSL 세션 캐싱을 활성화합니다.
ssl_protocols 및 ssl_ciphers
TLSv1.2 및 TLSv1.3과 같은 최신 TLS 프로토콜을 사용하십시오. 복사된 암호 문자열에 주의하십시오. TLS 1.3 암호는 이전 TLS 암호 제품군과 동일한 방식으로 제어되지 않으며, 배포 기본값이 오래된 가이드의 예제보다 더 안전한 경우가 많습니다.
ssl_stapling
OCSP 스테이플링을 활성화합니다. Nginx는 주기적으로 CA에서 OCSP 응답을 가져와 SSL/TLS 핸드셰이크에 "스테이플"합니다. 이는 별도의 OCSP 쿼리를 피하여 클라이언트 측 지연 시간을 줄입니다.
server {
listen 443 ssl;
ssl_certificate /etc/nginx/ssl/your_domain.crt;
ssl_certificate_key /etc/nginx/ssl/your_domain.key;
ssl_session_cache shared:SSL:10m; # 10MB 세션 데이터에 대한 공유 캐시
ssl_session_timeout 10m; # 세션은 10분 후 만료
ssl_protocols TLSv1.2 TLSv1.3; # 최신 보안 프로토콜 사용
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
ssl_prefer_server_ciphers on;
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s; # 환경에 승인된 리졸버 사용
resolver_timeout 5s;
}
9. 열린 파일 캐시
Nginx는 자주 액세스하는 파일의 파일 디스크립터를 캐시하여 파일을 열고 닫기 위한 반복적인 시스템 호출을 줄일 수 있습니다.
open_file_cache
최대 요소 수와 비활성 요소가 유지되는 시간을 지정하여 캐시를 활성화합니다.
open_file_cache_valid
캐시가 요소의 유효성을 확인해야 하는 빈도를 설정합니다.
open_file_cache_min_uses
파일이 캐시에 남아 있기 위해 inactive 시간 내에 액세스되어야 하는 최소 횟수를 지정합니다.
open_file_cache_errors
Nginx가 파일을 열 때 오류를 캐시해야 하는지 여부를 결정합니다.
http {
open_file_cache max=100000 inactive=60s; # 최대 100,000개의 파일 디스크립터를 60초 동안 캐시
open_file_cache_valid 80s; # 80초마다 유효성 확인
open_file_cache_min_uses 1; # 최소 한 번 사용된 파일 캐시
open_file_cache_errors on; # 파일 열기 관련 오류 캐시
}
10. 실제 트래픽 신호로 검증
체크리스트의 마지막 항목은 측정입니다. 변경 전에 요청 지연 시간, 5xx 비율, 활성 연결, CPU, 메모리, 디스크 I/O, 네트워크 처리량 및 업스트림 응답 시간과 같은 작은 기준을 캡처하십시오. 변경 후 동일한 숫자를 비교하십시오.
리버스 프록시의 경우 $request_time 및 $upstream_response_time이 특히 유용합니다. 둘 다 함께 상승하면 백엔드가 느린 것일 수 있습니다. $request_time이 높은 반면 업스트림 시간이 낮거나 비어 있으면 클라이언트 업로드 속도, 응답 전송 시간, 버퍼링, 압축 또는 정적 파일 전송을 확인하십시오. 두 메트릭 모두 문제를 설명하지 못하면 오류 로그와 운영 체제를 확인하십시오.
가장 안전한 튜닝 순서는 간단합니다. nginx -t로 구성을 테스트하고, 가능하면 재시작보다는 리로드를 사용하며, 로그를 모니터링하고, 지연 시간이나 오류가 잘못된 방향으로 움직이면 신속하게 롤백하십시오. Nginx는 많은 트래픽을 처리할 수 있지만, 그 제한, 커널 및 업스트림 애플리케이션이 서로 일치할 때만 가능합니다.