Nginx 성능 병목 현상 식별 및 해결: 문제 해결 가이드
로그, 상태 메트릭, 시스템 점검 및 CPU, 지연 시간, 메모리, 연결에 대한 실용적인 수정을 통해 Nginx 병목 현상을 진단합니다.
Nginx 성능 병목 현상 식별 및 해결: 문제 해결 가이드
Nginx 성능 문제는 보통 간단하게 나타납니다: 페이지가 느려지고, API 호출이 타임아웃되기 시작하며, CPU 사용량이 증가하거나, 사용자에게 502 및 504 오류가 표시되기 시작합니다. 어려운 점은 Nginx가 병목 현상인지, 아니면 불평할 만큼 충분히 큰 첫 번째 서비스인지 파악하는 것입니다.
Nginx 문제를 해결할 때 저는 지시문을 변경하는 것부터 시작하지 않으려고 합니다. 먼저 몇 가지 간단한 질문을 합니다. 모든 경로에서 지연 시간이 증가했습니까, 아니면 하나의 업스트림에 도달하는 경로에서만 증가했습니까? 정적 파일도 느립니까? 배포, 트래픽 급증, 인증서 변경 또는 로깅 변경 후에 오류가 시작되었습니까? 이러한 컨텍스트는 일반적으로 오래된 게시물에서 튜닝 블록을 복사하는 것보다 더 많은 시간을 절약합니다.
Nginx 성능 메트릭 이해
문제 해결에 뛰어들기 전에 성능 병목 현상을 구성하는 요소와 주요 지표가 무엇인지 이해하는 것이 중요합니다. 병목 현상은 시스템의 한 구성 요소가 전체 용량이나 속도를 제한할 때 발생합니다. Nginx의 경우 이는 요청 처리, 연결 관리 또는 콘텐츠 효율적인 제공 능력과 관련이 있는 경우가 많습니다.
모니터링해야 할 주요 메트릭은 다음과 같습니다:
- 활성 연결: Nginx가 현재 처리 중인 클라이언트 연결 수입니다.
- 초당 요청 수(RPS): Nginx가 요청을 제공하는 속도입니다.
- 요청 지연 시간: Nginx가 클라이언트 요청에 응답하는 데 걸리는 시간입니다.
- CPU 사용량: Nginx 작업자 프로세스가 소비하는 CPU 리소스의 백분율입니다.
- 메모리 사용량: Nginx 프로세스에서 사용하는 RAM의 양입니다.
- 네트워크 I/O: Nginx 서버 안팎으로의 데이터 전송 속도입니다.
- 디스크 I/O: Nginx가 정적 파일을 직접 제공하거나 광범위하게 로깅하는 경우 관련이 있습니다.
진단을 위한 내장 Nginx 도구
Nginx는 운영 상태를 모니터링하고 성능 데이터를 수집하는 데 도움이 되는 여러 기능을 제공합니다.
stub_status 모듈 사용
stub_status 모듈은 Nginx의 현재 상태에 대한 기본적이면서도 중요한 정보를 제공합니다. 서버 활동에 대한 빠른 개요를 확인할 수 있는 훌륭한 첫 번째 단계입니다.
stub_status 활성화
stub_status를 활성화하려면 nginx.conf(일반적으로 모니터링 엔드포인트의 server 블록 내)에 다음 구성 블록을 추가합니다:
server {
listen 80;
server_name monitoring.example.com;
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1; # 로컬 호스트에서만 액세스 허용
deny all;
}
}
구성을 수정한 후 Nginx를 다시 로드합니다:
sudo nginx -t # 구성 테스트
sudo nginx -s reload # Nginx 다시 로드
stub_status 출력 해석
상태 페이지(예: http://localhost/nginx_status)에 액세스하면 다음과 유사한 출력이 표시됩니다:
Active connections: 291
server accepts handled requests
1162447 1162447 4496426
Reading: 6 Writing: 17 Waiting: 268
각 메트릭의 의미는 다음과 같습니다:
Active connections:Reading,Writing및Waiting연결을 포함한 현재 활성 클라이언트 연결 수입니다.accepts: Nginx가 수락한 총 연결 수입니다.handled: Nginx가 처리한 총 연결 수입니다. 이상적으로accepts와handled는 같아야 합니다.handled가 현저히 낮으면 리소스 제한(예:worker_connections제한)을 나타낼 수 있습니다.requests: Nginx가 처리한 총 클라이언트 요청 수입니다.Reading: Nginx가 현재 요청 헤더를 읽고 있는 연결 수입니다.Writing: Nginx가 현재 클라이언트에 응답을 쓰고 있는 연결 수입니다.Waiting: 요청을 기다리는 유휴 클라이언트 연결(예:keep-alive연결) 수입니다. 여기서 숫자가 높으면 효율적인keep-alive사용을 나타낼 수 있지만, 활성 연결이 적고 리소스가 제한된 경우 작업자 프로세스가 대기 중이라는 의미일 수도 있습니다.
고급 메트릭을 위한 Nginx Plus API 활용
Nginx Plus 사용자의 경우 Nginx Plus API는 모니터링을 위한 보다 상세한 실시간 JSON 인터페이스를 제공합니다. 이 API는 영역, 서버, 업스트림, 캐시 등에 대한 세분화된 메트릭을 제공하므로 심층적인 성능 분석 및 모니터링 대시보드와의 통합에 매우 유용합니다.
Nginx Plus API 활성화
Nginx Plus 구성에서 API 위치를 구성합니다:
http {
server {
listen 8080;
location /api {
api write=on;
allow 127.0.0.1; # 보안을 위해 액세스 제한
deny all;
}
location /api.html {
root /usr/share/nginx/html;
}
}
}
Nginx를 다시 로드하고 http://localhost:8080/api에 액세스하여 JSON 출력을 확인합니다. 이 API는 stub_status보다 훨씬 세분화된 문제 해결을 가능하게 하는 광범위한 데이터(상세 연결 통계, 요청 처리 시간, 업스트림 상태, 캐시 성능 포함)를 제공합니다.
Nginx 액세스 및 오류 로그
Nginx 로그는 성능 문제 해결을 위한 정보의 보고입니다. 모든 요청과 발생한 모든 오류를 기록합니다.
상세 로깅 구성
log_format을 사용자 정의하여 요청 처리 시간($request_time) 및 업스트림 응답 시간($upstream_response_time)과 같은 유용한 성능 메트릭을 포함할 수 있습니다.
http {
log_format perf_log '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'request_time:$request_time upstream_response_time:$upstream_response_time '
'upstream_addr:$upstream_addr';
access_log /var/log/nginx/access.log perf_log;
error_log /var/log/nginx/error.log warn;
# 임계값보다 느린 요청을 기록하는 예
# 이것은 좀 더 고급 기능이며 사용자 정의 모듈이나 별도의 구문 분석 도구가 필요할 수 있습니다.
# 느린 요청에 대해 기본 access_log를 구문 분석하는 것이 더 쉬운 경우가 많습니다.
}
느린 요청 및 오류 식별
- 느린 요청:
grep또는awk와 같은 도구를 사용하여 특정$request_time또는$upstream_response_time임계값을 초과하는 요청에 대해 액세스 로그를 구문 분석합니다. 이는 문제가 있는 애플리케이션이나 외부 서비스를 식별하는 데 도움이 됩니다.
이렇게 하면 고정된 로그 필드 번호에 의존하지 않으므로 요청 경로, 사용자 에이전트 또는 리퍼러에 공백이 포함되는 즉시 중단됩니다.awk 'match($0, /request_time:([0-9.]+)/, m) && m[1] > 1.0 {print $0}' /var/log/nginx/access.log - 오류: "upstream timed out", "no live upstreams" 또는 "too many open files"와 같은 중요한 문제에 대해
error.log를 모니터링합니다. 이러한 오류는 백엔드 문제 또는 Nginx 리소스 제한을 직접적으로 가리킵니다.
외부 시스템 모니터링 도구
Nginx 성능은 종종 기본 서버의 리소스와 연결됩니다. 시스템 수준 모니터링은 중요한 컨텍스트를 제공합니다.
- CPU 사용량 (
top,htop,mpstat): Nginx 작업자 프로세스의 높은 CPU 사용량은 복잡한 구성(정규식, SSL 핸드셰이크), 비효율적인 코드 또는 단순히 높은 부하를 나타낼 수 있습니다.top -c # CPU 사용량별로 정렬된 프로세스 표시 - 메모리 사용량 (
free -h,htop): 과도한 메모리 소비는 큰 버퍼 크기(proxy_buffers), 메모리 누수 또는 비정상적으로 많은 활성 연결 수를 나타낼 수 있습니다.free -h # 사람이 읽을 수 있는 메모리 사용량 표시 - 디스크 I/O (
iostat,iotop): Nginx가 정적 콘텐츠를 많이 제공하거나 광범위하게 로깅하는 경우 관련이 있습니다. 높은 디스크 I/O는 스토리지의 병목 현상 또는 과도한 로깅을 의미할 수 있습니다.iostat -x 1 10 # 1초마다 10번 확장 디스크 통계 표시 - 네트워크 I/O (
netstat,ss,iftop): 포화 또는 과도한 재전송에 대한 네트워크 트래픽을 모니터링합니다. 이는 네트워크 병목 현상 또는 Nginx와 클라이언트/업스트림 간의 문제를 나타낼 수 있습니다.netstat -antp | grep nginx # Nginx 연결 표시
일반적인 Nginx 성능 병목 현상 및 해결 방법
모니터링 데이터를 바탕으로 일반적인 문제와 해결 방법을 살펴보겠습니다.
1. 높은 CPU 사용량
증상: top은 Nginx 작업자 프로세스가 적당한 부하에서도 CPU의 많은 비율을 소비하고 있음을 보여줍니다.
원인:
- 멀티 코어 CPU에 비해 작업자 프로세스가 너무 적음: Nginx가 사용 가능한 모든 코어를 활용하지 못할 수 있습니다.
- 복잡한
if문 또는 정규 표현식: 지나치게 복잡한 정규식이나 구성의 많은if문은 CPU 집약적일 수 있습니다. - 비효율적인 SSL/TLS 구성: 더 많은 CPU가 필요한 약한 암호를 사용하거나 사용 가능한 하드웨어 가속을 활용하지 않습니다.
- 과도한 로깅: 특히 복잡한
log_format규칙을 사용하여 디스크에 너무 많은 데이터를 씁니다. - TLS, 압축 또는 요청 처리 오버헤드: 값비싼 TLS 핸드셰이크, 높은 압축 수준, 무거운 재작성 규칙 또는 매우 큰 요청 헤더는 CPU를 높일 수 있습니다.
해결 방법:
worker_processes최적화:worker_processes auto;(권장) 또는 CPU 코어 수로 설정합니다. 각 작업자 프로세스는 단일 스레드이며 하나의 CPU 코어를 완전히 활용할 수 있습니다.worker_processes auto;- 구성 단순화:
if문과 정규식을 검토합니다. 더 간단한 논리를 위해map지시문 또는try_files사용을 고려하십시오. - SSL/TLS 최적화: 최신 TLS 설정을 사용하고 적절한 경우
ssl_session_cache및ssl_session_timeout을 활성화하여 반복적인 핸드셰이크 작업을 줄입니다. - 로깅 제어: 버퍼링된 액세스 로그를 사용하거나 요청별 레코드가 필요하지 않은 경우 시끄러운 정적 자산에 대한 액세스 로그를 비활성화합니다.
- 백엔드 조사: Nginx가 대기 중이면 병목 현상은 업스트림에 있습니다. 백엔드 애플리케이션을 최적화합니다.
2. 느린 응답 시간
증상: 로그에 높은 $request_time 또는 $upstream_response_time; 페이지 로딩 속도가 느립니다.
원인:
- 업스트림(백엔드) 서버 문제: 가장 일반적인 원인입니다. 애플리케이션 서버가 응답을 생성하는 속도가 느립니다.
- 적절한 최적화 없이 대용량 파일 전송:
sendfile또는gzip없이 대용량 정적 파일을 제공합니다. - 네트워크 지연 시간: 클라이언트와 Nginx 사이 또는 Nginx와 업스트림 사이의 느린 네트워크.
- 캐싱 부족: 동적 콘텐츠를 반복적으로 가져옵니다.
해결 방법:
- 업스트림 상태 확인 및 시간 초과 최적화:
proxy_read_timeout,proxy_connect_timeout및proxy_send_timeout을 구성합니다. 업스트림 서버에 대한 상태 확인을 구현합니다.location / { proxy_pass http://backend_app; proxy_read_timeout 90s; # 필요에 따라 조정 proxy_connect_timeout 5s; } gzip압축 활성화: 텍스트 기반 콘텐츠의 경우gzip은 전송 크기를 크게 줄입니다.gzip on; gzip_comp_level 5; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;sendfile및tcp_nodelay활성화: 효율적인 정적 파일 제공을 위해.sendfile on; tcp_nodelay on;- 캐싱 구현: 동적 콘텐츠에는
proxy_cache를 사용하거나 정적 자산에는expires헤더를 설정합니다.# 정적 자산 예시 location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { expires 30d; log_not_found off; }
3. 연결 오류 / 최대 연결 수 초과
증상: 클라이언트가 연결 실패, 502 또는 504 응답 또는 간헐적인 시간 초과를 수신합니다. stub_status는 수락된 연결이 빠르게 증가하고 있음을 보여줄 수 있으며, 오류 로그는 worker_connections are not enough, too many open files 또는 업스트림 연결 실패를 언급할 수 있습니다.
원인:
worker_connections제한 도달: Nginx가 새 연결을 수락할 수 없습니다.- 너무 많은 열린 파일(ulimit): 파일 디스크립터에 대한 운영 체제 제한에 도달했습니다.
- 백엔드 포화: 업스트림 서버가 압도되어 연결을 수락하지 않습니다.
- DDoS 또는 비정상적으로 높은 합법적 트래픽.
해결 방법:
worker_connections증가:events블록 내에서 이 지시문을 높은 값(예:10240이상)으로 설정합니다. 이는 작업자 프로세스당 최대 연결 수입니다.events { worker_connections 10240; }- 파일 디스크립터 제한 조정: 운영 체제의 열린 파일 제한을 늘립니다. 적절한 경우
nginx.conf에worker_rlimit_nofile 65535;를 추가하고 대부분의 최신 Linux 배포판에서LimitNOFILE=65535를 사용하여 systemd를 통해 서비스 제한을 설정합니다. keepalive_timeout최적화: 긴keep-alive시간 초과는 클라이언트가 연결을 재사용하지 않는 경우 불필요하게 작업자 프로세스를 묶어둘 수 있습니다.Waiting연결이 많고requests가 적으면 시간을 줄이십시오.keepalive_timeout 15s; # 기본값은 75초입니다.- 로드 밸런싱 및 확장 구현: 여러 백엔드 서버에 트래픽을 분산합니다. Nginx의 로드 밸런싱 기능(라운드 로빈, 최소 연결, IP 해시)을 고려하십시오.
- 속도 제한:
limit_req또는limit_conn모듈을 사용하여 단일 클라이언트의 과도한 요청 또는 연결로부터 서버를 보호합니다.
4. 높은 메모리 사용량
증상: Nginx 작업자 프로세스가 상당한 RAM을 소비합니다. 서버가 과도하게 스왑할 수 있습니다.
원인:
- 큰 버퍼 크기:
proxy_buffers,client_body_buffer_size,fastcgi_buffers가 너무 높게 구성되었습니다. - 광범위한 캐싱: 큰
proxy_cache_path크기. - 많은 활성 연결: 각 연결에는 약간의 메모리가 필요합니다.
해결 방법:
- 버퍼 크기 조정: 로그에 실제 버퍼 문제(예: 구성된 프록시 또는 FastCGI 버퍼에 비해 응답 헤더가 너무 큼)가 표시되는 경우에만 버퍼 크기를 늘리십시오.
413 Request Entity Too Large는 프록시 응답 버퍼가 아닌client_max_body_size와 같은 요청 본문 제한에 의해 제어됩니다.proxy_buffer_size 4k; proxy_buffers 8 8k; - 캐싱 최적화: 캐시 크기 및 제거 정책(
proxy_cache_path매개변수)을 관리합니다. keepalive_timeout검토: 앞서 언급했듯이 과도하게 긴keepalive_timeout은 유휴 연결에 대해 작업자 프로세스와 관련 메모리를 활성 상태로 유지할 수 있습니다.
성능을 위한 Nginx 구성 모범 사례
특정 문제 해결 외에도 이러한 일반적인 모범 사례는 최적의 Nginx 성능을 유지하는 데 도움이 됩니다:
worker_processes auto;: 모든 CPU 코어를 활용합니다.worker_connections: 예상 동시성 및 파일 디스크립터 제한과 일치하는 값을 설정합니다.4096또는8192는 사용량이 많은 서버의 일반적인 시작점이지만 올바른 값은 워크로드에 따라 다릅니다.sendfile on;: 효율적인 정적 파일 제공을 위해.tcp_nodelay on;: 작은 패킷의 즉각적인 전송을 보장하여 대화형 서비스의 지연 시간을 개선합니다.keepalive_timeout: 클라이언트 동작에 따라 조정합니다. 15-30초가 종종 좋은 균형입니다.gzip on;: 텍스트 기반 콘텐츠에 대해 압축을 활성화합니다.proxy_buffering on;: 일반적으로 버퍼링을 켜 둡니다. 이를 통해 Nginx는 업스트림의 응답을 디스크(필요한 경우)로 스풀링하고 가능한 한 빨리 클라이언트로 보내 업스트림을 확보할 수 있습니다. 실시간 저지연 스트리밍이 절대적으로 중요하고 그 영향을 이해하는 경우에만 비활성화하십시오.expires헤더: 클라이언트 측에서 정적 콘텐츠를 적극적으로 캐시합니다.if문 및 정규식 최소화: 더 나은 성능을 위해map지시문 또는try_files를 선택하십시오.- 정적 파일에
access_log off;사용: 로깅이 엄격히 필요하지 않은 경우 자주 액세스하는 정적 자산에 대한 디스크 I/O를 줄입니다. - HTTP/2: 최신 브라우저에 대해 HTTP/2를 활성화하여 HTTPS를 통한 멀티플렉싱 및 헤더 압축을 개선합니다.
listen 443 ssl http2;
문제 해결 워크플로 및 전략
성능 문제에 직면했을 때는 체계적인 접근 방식을 따르십시오:
- 기준 정의: 정상 기간 동안의 정상 운영 메트릭(CPU, 메모리, 연결, RPS, 지연 시간)을 이해합니다.
- 증상 모니터링: 특정 증상(예: 높은 CPU, 느린 요청, 연결 오류)을 식별하고 도구(
stub_status, 로그,top)를 사용하여 확인합니다. - 가설 수립: 증상을 기반으로 근본 원인에 대한 가설을 공식화합니다(예: "높은 CPU는 비효율적인 정규식 때문입니다").
- 테스트 및 분석: 변경 사항을 구현하고(예: 정규식 단순화) 메트릭에 미치는 영향을 모니터링합니다. 새 로그 항목 또는
stub_status출력을 분석합니다. - 반복: 문제가 지속되면 가설을 구체화하고 프로세스를 반복합니다.
- 문서화: 향후 참조를 위해 변경 사항과 그 효과에 대한 기록을 유지합니다.
최고의 Nginx 성능 수정은 일반적으로 지루합니다: 지연이 발생한 위치를 증명하고, 한 가지를 변경하고, 그 후에 동일한 메트릭을 관찰합니다. $upstream_response_time이 높으면 Nginx를 비난하기 전에 앱 경로를 조정하십시오. 업스트림 시간이 비어 있는 동안 정적 파일이 느리면 디스크, 네트워크, 압축 및 정적 파일 설정을 확인하십시오. 오류가 파일 디스크립터 또는 작업자 연결을 언급하면 해당 제한을 쌍으로 수정하십시오. 이 습관은 문제 해결을 민속학이 아닌 증거에 기반하도록 유지합니다.