효율적인 문제 해결을 위한 Nginx 로그 분석 마스터하기
Nginx 액세스 및 오류 로그를 마스터하여 효율적인 문제 해결을 수행하세요. 이 가이드에서는 중요한 타이밍 메트릭을 캡처하기 위해 사용자 정의 로그 형식을 구성하는 방법을 자세히 설명하여 Nginx 또는 업스트림 애플리케이션 서버 내의 성능 병목 현상을 정확히 찾아낼 수 있도록 도와줍니다. 오류 로그 심각도 수준을 사용하여 502 및 504 오류와 같은 중요한 문제를 즉시 진단하고 강력한 셸 명령어(`grep`, `awk`)를 활용하여 트래픽 패턴을 빠르게 필터링, 계산 및 분석하는 방법을 배우세요.
효율적인 문제 해결을 위한 Nginx 로그 분석 마스터하기
Nginx 로그는 일반적으로 "사이트가 다운되었습니다"라는 상황을 구체적인 문제로 전환하는 가장 빠른 방법입니다. 액세스 로그는 클라이언트가 무엇을 요청했고 어떤 상태를 받았는지 알려줍니다. 오류 로그는 Nginx가 수행할 수 없었던 작업(업스트림 연결, 인증서 읽기, 파일 열기, 구성 파싱, 백엔드 응답 대기 시간 초과 등)을 알려줍니다.
효과적인 Nginx 로그 분석은 파일을 응시하며 의심스러운 점을 찾는 것이 아닙니다. 좁은 질문을 하고, 빠르게 필터링하며, 액세스 로그와 오류 로그 및 업스트림 애플리케이션 로그를 상호 연관시키는 것입니다. 액세스 로그의 502는 증상입니다. 일치하는 오류 로그 라인이 일반적으로 답의 시작입니다.
1. Nginx 로그 기본 사항: 액세스 vs. 오류
Nginx는 각각 중요하고 별개의 기능을 수행하는 두 가지 유형의 로그를 유지 관리합니다.
1.1 액세스 로그 (access.log)
액세스 로그는 Nginx가 처리하는 모든 요청에 대한 세부 정보를 기록합니다. 사용자 행동을 이해하고, 트래픽 흐름을 모니터링하며, 응답 시간을 평가하는 데 필수적입니다.
기본 위치: 일반적으로 /var/log/nginx/access.log
목적: 클라이언트 상호 작용, 성공적인 요청, 클라이언트 오류, Nginx를 통해 반환된 서버 오류, 전송된 바이트, 사용자 에이전트 및 구성된 경우 요청 타이밍 추적.
1.2 오류 로그 (error.log)
오류 로그는 Nginx 처리 수명 주기 동안 발생하는 내부 문제, 운영 실패 및 통신 문제를 추적합니다. 이 로그는 백엔드 연결 문제 및 서버 구성 오류를 해결하기 위한 결정적인 소스입니다.
기본 위치: 일반적으로 /var/log/nginx/error.log
목적: 서버 측 오류, 경고 및 시스템 이벤트(5xx 오류, 구성 파일 파싱 실패) 추적.
오류 로그 심각도 수준
Nginx는 8가지 심각도 수준을 사용합니다. 문제 해결 시 일반적으로 error 수준 이상부터 시작하는 것이 좋습니다. 심각도 수준은 error_log 지시문을 사용하여 구성됩니다.
# 최소 심각도 수준을 'warn'으로 설정
error_log /var/log/nginx/error.log warn;
| 수준 | 설명 | 우선순위 |
|---|---|---|
| crit | 심각한 런타임 오류와 같은 중요 조건 | 가장 높음 |
| error | 요청 제공을 방해하는 오류 발생 | 높음 |
| warn | 예상치 못한 일이 발생했지만 작업은 계속됨 | 중간 |
| notice | 정상적이지만 중요한 조건(예: 서버 재시작) | 낮음 |
| info | 정보 메시지 | 가장 낮음 |
emerg, alert 및 debug 수준도 있습니다. debug는 매우 많은 양의 로그를 생성할 수 있으며 일반적으로 디버그 지원이 포함된 Nginx 빌드가 필요합니다. 일반 프로덕션 설정이 아닌 대상 문제 해결에 사용하십시오.
2. 성능 분석을 위한 액세스 로그 사용자 정의
종종 combined라고 불리는 기본 Nginx 액세스 로그 형식은 유용하지만 중요한 성능 타이밍 변수가 누락되었습니다. 지연 문제를 효과적으로 해결하려면 Nginx가 요청을 처리하는 데 소요된 시간과 업스트림 서버가 소요한 시간을 캡처하는 사용자 정의 형식을 정의해야 합니다.
2.1 성능 로그 형식 정의
log_format 지시문(일반적으로 nginx.conf에 정의)을 사용하여 timing_log와 같은 사용자 정의 형식을 만듭니다.
log_format timing_log '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time';
server {
listen 80;
server_name example.com;
# 여기에 사용자 정의 형식 적용
access_log /var/log/nginx/timing_access.log timing_log;
# ... 나머지 구성
}
| 변수 | 설명 | 문제 해결 가치 |
|---|---|---|
| $request_time | 첫 번째 바이트 수신부터 마지막 바이트 전송까지 경과된 총 시간. | 높은 값은 느린 네트워크, 느린 Nginx 또는 느린 백엔드를 나타냅니다. |
| $upstream_response_time | 업스트림 서버(예: 애플리케이션 서버)의 응답을 기다리는 데 소요된 시간. | 여기서 높은 값은 백엔드 애플리케이션이 병목 현상임을 나타냅니다. |
| $status | 클라이언트에 반환된 HTTP 상태 코드. | 오류(4xx, 5xx) 필터링에 필수적입니다. |
로그가 중앙 집중식 시스템으로 전송될 때 JSON 로그 형식을 사용하는 것을 고려하십시오. JSON은 사람이 읽기에는 더 어렵지만 도구가 안정적으로 구문 분석하기에는 훨씬 쉽습니다. 일반 텍스트 로그를 유지하는 경우 awk 필드 번호가 사용자 에이전트, 요청 경로 또는 따옴표로 묶인 필드에 공백이 포함될 때 깨질 수 있음을 인식하십시오.
또한 요청 ID 기록을 고려하십시오. 로드 밸런서 또는 애플리케이션이 이미 요청 ID 헤더를 보내는 경우 이를 전달하고 기록하십시오.
log_format timing_log '$remote_addr [$time_local] '
'"$request" $status $body_bytes_sent '
'request_time=$request_time '
'upstream_time=$upstream_response_time '
'request_id=$request_id '
'upstream=$upstream_addr';
요청 ID를 사용하면 하나의 느린 공개 요청을 하나의 애플리케이션 로그 항목에 연결할 수 있습니다. 이것이 없으면 타임스탬프, 경로 및 클라이언트 IP로 일치시켜야 하며 가능하지만 훨씬 덜 편리합니다.
3. 액세스 로그 항목 해석
사용자 정의 형식을 사용하는 일반적인 항목은 다음과 같습니다(끝에 타이밍 값 추가).
192.168.1.10 - - [10/May/2024:14:30:05 +0000] "GET /api/data HTTP/1.1" 200 450 "-" "Mozilla/5.0" 0.534 0.528
진단:
- 상태 코드 (200): 성공.
- 요청 시간 (0.534초): 총 시간은 0.5초입니다.
- 업스트림 시간 (0.528초): 거의 모든 시간이 백엔드 애플리케이션을 기다리는 데 소요되었습니다(
0.534 - 0.528 = 0.006초는 Nginx 오버헤드).
진단: 이 요청의 경우 백엔드 애플리케이션이 500ms 지연의 가능한 원인입니다. Nginx 오버헤드는 작아 보입니다.
한 줄만으로 일반화하지 마십시오. 느린 요청 샘플을 살펴보십시오. 대부분의 느린 요청에 $upstream_response_time이 높으면 앱 또는 업스트림 네트워크에 집중하십시오. $upstream_response_time은 낮은데 $request_time이 높으면 지연은 클라이언트 업로드 시간, 느린 클라이언트 다운로드, 버퍼링 동작 또는 Nginx 측 작업 때문일 수 있습니다.
상태 코드를 사용한 문제 해결
| 상태 코드 범위 | 의미 | 일반적인 조치/로그 소스 |
|---|---|---|
| 4xx (클라이언트 오류) | 클라이언트가 잘못되었거나 권한이 없는 요청을 보냈습니다. | 높은 빈도에 대해 액세스 로그를 확인하십시오. 404 Not Found(파일 없음) 또는 403 Forbidden(권한 문제)을 찾으십시오. |
| 5xx (서버 오류) | Nginx 또는 업스트림 서버가 유효한 요청을 이행하지 못했습니다. | 즉시 오류 로그에서 해당 항목을 확인하십시오. |
| 502 Bad Gateway | Nginx가 업스트림 애플리케이션으로부터 응답을 받을 수 없습니다. | 오류 로그에 세부 정보가 표시됩니다(연결 거부, 시간 초과). |
| 504 Gateway Timeout | 업스트림 서버가 구성된 프록시 제한 내에서 응답하는 데 너무 오래 걸렸습니다. | 오류 로그에 시간 초과 경고가 표시됩니다. 시간 초과를 늘리기 전에 백엔드 지연 시간을 조사하십시오. |
proxy_read_timeout을 높이면 사용자가 여전히 너무 오래 기다리는 동안 증상을 숨길 수 있습니다. 장기 실행 엔드포인트, 스트리밍 또는 알려진 느린 작업에는 유효하지만 일반 API 요청의 경우 먼저 백엔드 조사를 트리거해야 합니다.
4. 오류 로그에서 중요한 문제 진단
요청이 5xx 오류를 발생시키면 액세스 로그는 오류가 발생했다는 것만 알려줍니다. 오류 로그는 이유를 알려줍니다.
사례 연구: 502 Bad Gateway
502 오류는 Nginx를 리버스 프록시로 사용할 때 가장 일반적인 문제 중 하나입니다. 거의 항상 백엔드 애플리케이션이 다운되었거나, 과부하되었거나, 연결할 수 없음을 나타냅니다.
오류 로그에서 다음 특정 메시지를 찾으십시오.
4.1 연결 거부 (백엔드 다운)
이는 Nginx가 백엔드 포트에 연결을 시도했지만 수신 대기 중인 것이 없음을 나타내며, 이는 애플리케이션 서버(예: PHP-FPM, Gunicorn)가 중지되었거나 잘못 구성되었음을 의미합니다.
2024/05/10 14:35:10 [error] 12345#0: *1 connect() failed (111: Connection refused) while connecting to upstream, client: 192.168.1.10, server: example.com, request: "GET /test"
- 조치: 백엔드 서비스가 실행 중인지, 예상 포트 또는 Unix 소켓에서 수신 대기 중인지, Nginx가 동일한 주소를 가리키는지 확인하십시오. 중지된 이유를 이해한 후에만 다시 시작하십시오.
4.2 업스트림 연결이 조기에 종료됨 (백엔드 충돌)
이는 Nginx가 연결을 설정했지만 백엔드 서버가 전체 HTTP 응답을 보내기 전에 연결을 종료할 때 발생합니다. 이는 종종 애플리케이션 코드의 치명적인 오류 또는 충돌을 암시합니다.
2024/05/10 14:38:22 [error] 12345#0: *2 upstream prematurely closed connection while reading response header from upstream, client: 192.168.1.10, server: example.com, request: "POST /submit"
- 조치: 특정 치명적인 오류에 대해 애플리케이션 서버의 기본 오류 로그(예: PHP-FPM 로그, Node.js 로그)를 확인하십시오.
경고: Nginx가 시작 시 구성 파일을 읽지 못하는 경우 오류는 구성된
error.log위치가 아닌 표준 오류 또는 부트스트랩 로그 파일에 직접 덤프되는 경우가 많습니다. Nginx가 시작되지 않으면 항상journalctl -xe또는 시스템 로그를 확인하십시오.
사례 연구: 403 Forbidden
액세스 로그의 403은 애플리케이션 권한 부여, Nginx 액세스 규칙, 파일 시스템 권한 또는 디렉토리 인덱스 동작으로 인해 발생할 수 있습니다. 액세스 로그만으로는 어느 것인지 알 수 없습니다.
오류 로그에서 다음과 같은 줄을 찾으십시오.
2024/05/10 15:02:01 [error] 12345#0: *12 directory index of "/var/www/site/" is forbidden
이는 Nginx가 디렉토리에 도달했지만 제공할 인덱스 파일이 없고 디렉토리 목록이 비활성화되었음을 의미합니다. 해결 방법은 예상되는 index.html을 생성하거나, index 지시문을 조정하거나, 요청을 애플리케이션으로 라우팅하는 것일 수 있습니다.
권한 문제의 경우 다음이 표시될 수 있습니다.
2024/05/10 15:04:44 [error] 12345#0: *15 open() "/var/www/site/private.txt" failed (13: Permission denied)
파일 소유권, 디렉토리 실행 권한, 해당되는 경우 SELinux 또는 AppArmor 정책, Nginx 작업자가 실행되는 사용자를 확인하십시오.
사례 연구: 499 클라이언트 요청 종료
Nginx 관련 상태 499는 Nginx가 응답을 완료하기 전에 클라이언트가 연결을 종료했음을 의미합니다. 사용자가 탐색을 떠나거나, 모바일 클라이언트의 연결이 끊어지거나, 업스트림이 너무 오래 걸려 클라이언트가 포기할 때 일반적입니다.
모든 499를 Nginx 버그로 취급하지 마십시오. 타이밍을 살펴보십시오. 많은 499의 요청 시간이 높고 느린 업스트림과 일치하면 사용자가 느린 요청을 포기하는 것일 수 있습니다. 하나의 클라이언트 또는 네트워크에서 즉시 발생하면 클라이언트 동작일 수 있습니다.
5. 로그 분석을 위한 실용적인 셸 명령어
프로덕션 환경에서는 강력한 로그 모니터링 시스템을 권장하지만, Linux 명령줄은 빠르고 실시간 문제 해결을 위한 강력한 도구를 제공합니다.
5.1 실시간 모니터링
요청이 들어올 때 로그를 모니터링합니다(특히 수정 사항을 배포하거나 새 기능을 테스트한 후에 유용).
tail -f /var/log/nginx/access.log
# 또는 오류만 보려면
tail -f /var/log/nginx/error.log
로테이트되고 압축된 로그의 경우 zgrep을 사용하십시오.
zgrep '" 50[0-9] ' /var/log/nginx/access.log*.gz
로그 로테이션은 사고 검토 중에 중요합니다. 오류가 자정 직전 또는 로테이션 작업이 어제 파일을 압축하기 직전에 발생했을 수 있습니다.
5.2 오류 필터링 및 계산
지난 1시간 또는 하루 동안 가장 빈번한 5xx 오류를 빠르게 찾아 계산합니다.
# 모든 5xx 요청 찾기
grep '" 50[0-9] ' /var/log/nginx/access.log | less
# 5xx 오류 분포 계산(예: 502 vs 504 개수)
grep '" 50[0-9] ' /var/log/nginx/access.log | awk '{print $9}' | sort | uniq -c | sort -nr
설명: awk '{print $9}'는 HTTP 상태 코드를 분리합니다(상태가 9번째 필드인 기본 또는 결합 로그 형식 가정).
사용자 정의 로그 형식을 사용하는 경우 개수를 신뢰하기 전에 필드 번호를 확인하십시오. 더 안전한 빠른 확인은 구문 분석된 몇 줄을 출력하는 것입니다.
awk '{print NR, $0; if (NR == 3) exit}' /var/log/nginx/access.log
JSON 로그의 경우 필드 번호 대신 jq를 사용하십시오.
jq -r 'select(.status >= 500) | .status' /var/log/nginx/access.json \
| sort | uniq -c | sort -nr
5.3 느린 요청 식별 (사용자 정의 로그 형식 필요)
timing_log 형식을 구현한 경우($request_time이 끝에서 두 번째 필드이거나 예제에서 16번째 필드인 경우):
# 가장 느린 10개 요청 찾기(예: 1초 이상 걸리는 요청)
awk '($16 > 1.0) {print $16, $7}' /var/log/nginx/timing_access.log | sort -nr | head -10
설명: 이 명령은 1.0초 이상 걸린 요청에 대해 요청 시간과 URI($7)를 내림차순으로 정렬하여 출력합니다.
더 읽기 쉬운 일반 텍스트 타이밍 형식은 request_time=0.534와 같은 명명된 값을 사용합니다. 그런 다음 필드 번호 문제는 적지만 덜 우아하게 느린 범위를 grep할 수 있습니다. 심각한 분석을 위해서는 구조화된 로그를 로그 시스템으로 보내고 경로별로 백분위수를 쿼리하십시오.
5.4 상위 요청 IP 주소 식별
잠재적인 DoS 시도, 트래픽 급증 또는 의심스러운 활동을 발견하는 데 유용합니다.
# 요청을 보낸 상위 20개 IP 찾기
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -20
상위 IP는 시작점일 뿐이며 남용의 증거는 아닙니다. 회사 NAT, CDN 에지 또는 로드 밸런서는 많은 사용자를 하나의 소스로 보이게 할 수 있습니다. Nginx가 프록시 뒤에 있는 경우 real_ip_header 및 신뢰할 수 있는 프록시 범위를 사용하여 실제 클라이언트 IP를 신중하게 구성하고 기록하십시오. 공개 인터넷의 임의 X-Forwarded-For 헤더를 절대 신뢰하지 마십시오.
실용적인 문제 해결 흐름
사용자의 증상과 시간 창으로 시작하십시오. "체크아웃이 14:35 UTC 경에 502를 반환했습니다"는 "Nginx가 고장났습니다"보다 훨씬 유용합니다.
먼저 상태를 계산합니다.
grep '10/May/2024:14:3' /var/log/nginx/access.log \
| awk '{print $9}' | sort | uniq -c | sort -nr
일반 텍스트 로그를 사용한 날짜 필터링은 어색하며 정확한 명령은 로그 형식에 따라 다릅니다. 빠른 사고 확인을 위해 대략적인 필터링만으로도 문제가 주로 502, 504, 403 또는 404인지 보여줄 수 있습니다.
다음으로, 일치하는 요청 몇 개를 가져옵니다.
grep '" 502 ' /var/log/nginx/access.log | tail -20
타임스탬프, URI, 업스트림 시간 및 요청 ID(있는 경우)를 기록합니다. 그런 다음 동일한 타임스탬프 주변의 오류 로그를 검색합니다.
grep '14:35' /var/log/nginx/error.log
오류에 connect() failed (111: Connection refused)라고 표시되면 업스트림 서비스와 해당 포트를 검사하십시오. upstream timed out이라고 표시되면 백엔드 지연 시간 및 대기열을 검사하십시오. no live upstreams라고 표시되면 업스트림 상태, DNS 또는 로드 밸런서 구성을 검사하십시오.
마지막으로, 동일한 요청 ID 또는 타임스탬프를 사용하여 백엔드 로그를 확인하십시오. Nginx는 종종 핸드오프가 실패한 위치를 알려주지만, 백엔드 로그는 애플리케이션이 그렇게 동작한 이유를 알려줍니다.
중단 전에 로그를 유용하게 만들기
로깅을 개선하기 가장 나쁜 시기는 중단 중입니다. 필요하기 전에 요청 타이밍, 업스트림 타이밍, 업스트림 주소 및 요청 ID를 추가하십시오. 하나의 서버가 여러 애플리케이션을 호스팅하는 경우 사이트별로 액세스 및 오류 로그를 분리하여 유지하십시오. 로테이션이 실제로 조사하는 사고에 대해 충분한 기록을 유지하는지 확인하십시오.
무언가 고장 났을 때, 로그를 쌍으로 읽으십시오: 무슨 일이 일어났는지 액세스 로그, Nginx가 무엇을 할 수 없었는지 오류 로그, 업스트림이 다음에 무엇을 했는지 애플리케이션 로그. 이 습관은 문제 해결에 집중하게 하고 일반적으로 시간 초과를 변경하거나 서비스를 무작위로 다시 시작하는 것보다 실제 실패 지점에 더 빨리 도달하게 합니다.