
NGINX 디버깅
디버깅 Binary, 디버그 로깅 및 코어 덤프를 사용하여 NGINX 또는 NGINX Plus 배포에서 문제를 해결하고 버그를 추적할 수 있습니다.
목차
1. 소개
1-1. 디버깅을 위한 NGINX Binary 구성
1-2. NGINX Plus Binary 구성
1-3. NGINX Open Source Binary 컴파일하기
1-4. NGINX 및 디버깅 기호
2. NGINX 구성에서 디버그 로깅 활성화하기
2-1. 디버깅 로그를 파일에 쓰기
2-2. 메모리에 디버깅 로그 쓰기
2-3. 선택한 IP에 대한 디버그 로그
2-4. 각 가상 호스트에 대한 디버그 로그
3. 코어 덤프 활성화
3-1. 운영 체제에서 코어 덤프 활성화하기
3-2. NGINX 구성에서 코어 덤프 활성화하기
4. 코어 덤프 파일에서 Backtrace 가져오기
5. 실행 중인 프로세스에서 NGINX 구성 덤프
1. 소개
디버깅은 문제가 발생했을 때 프로그램 코드의 버그를 식별하는 데 도움이 됩니다. 일반적으로 타사 또는 실험용 모듈을 개발하거나 테스트할 때 사용됩니다.
NGINX Plus 디버깅 기능에는 디버깅 로그와 추가 Backtrace이 가능한 코어 덤프 파일 생성이 포함됩니다.
1-1. 디버깅을 위한 NGINX Binary 구성
먼저 NGINX Binary에서 디버깅을 활성화해야 합니다. NGINX Plus 는 이미 nginx-debug Binary를 제공하지만 NGINX Open Source는 다시 컴파일해야 합니다.
1-2. NGINX Plus Binary 구성
릴리스 8부터 NGINX Plus 는 표준 Binary와 함께 nginx-debug Binary를 제공합니다. NGINX Plus 에서 디버깅을 사용하려면 nginx에서 nginx-debug Binary로 전환해야 합니다. 터미널을 열고 다음 명령을 실행합니다.
$ service nginx stop && service nginx-debug start
완료되면 구성 파일에서 디버깅 로그를 활성화합니다.
1-3. NGINX Open Source Binary 컴파일하기
NGINX Open Source에서 디버깅을 활성화하려면 구성 스크립트에 지정된 --with-debug
Flag를 사용하여 다시 컴파일해야 합니다.
디버그 지원을 갖춘 NGINX Open Source를 컴파일하려면:
1. NGINX Source 파일을 다운로드하여 압축을 풀고 Source 파일이 있는 디렉터리로 이동합니다.
2. NGINX 구성 인자 목록을 가져옵니다. 다음 명령을 실행합니다.
$ nginx -V 2>&1 | grep arguments
3. 구성 명령 목록에 --with-debug
옵션을 추가하고 구성 스크립트를 실행합니다.
$ ./configure --with-debug <other configure arguments>
4. NGINX를 컴파일하고 설치합니다.
$ sudo make
$ sudo make install
5. NGINX를 다시 시작합니다.
1-4. NGINX 및 디버깅 기호
디버그 기호는 함수, 변수, 데이터 구조, Source 파일 및 줄 번호 정보 등 디버깅을 위한 추가 정보를 얻는 데 도움이 됩니다.
기본적으로 NGINX는 디버그 기호를 포함하는 “-g” Flag를 사용하여 컴파일됩니다.
그러나 Backtrace를 실행할 때 “No symbol table info available” 오류가 발생하면 디버깅 기호가 누락된 것이므로 디버깅 기호를 지원하도록 NGINX를 다시 컴파일해야 합니다.
컴파일러 Flag의 정확한 세트는 컴파일러에 따라 다릅니다. 예를 들어 GCC 컴파일러 시스템의 경우.
- “-g” Flag와 함께 디버깅 기호를 포함합니다.
- “-O0” Flag를 사용하여 컴파일러 최적화를 비활성화하면 디버거 출력을 더 쉽게 이해할 수 있습니다.
$ ./configure --with-debug --with-cc-opt='-O0 -g' ...
2. NGINX 구성에서 디버그 로깅 활성화하기
디버깅 로그는 오류 및 모든 디버깅 관련 정보를 기록하며 기본적으로 비활성화되어 있습니다. 디버깅 로그를 활성화하려면 디버깅을 지원하도록 NGINX가 컴파일되었는지 확인한 다음(디버깅을 위해 NGINX Binary 구성하기 참조), error_log
지시문의 debug
매개변수를 사용하여 NGINX 구성 파일에서 디버깅을 활성화합니다. 디버깅 로그는 파일, 메모리에 할당된 buffer, stderr 출력 또는 syslog에 기록될 수 있습니다.
무슨 일이 일어나고 있는지 전체 상황을 파악하려면 NGINX 구성의 “main
” Level에서 디버깅 로그를 활성화하는 것이 좋습니다.
2-1. 디버깅 로그를 파일에 쓰기
디버깅 로그를 파일에 쓰면 부하가 높을 때 성능이 느려질 수 있습니다. 또한 파일 크기가 매우 커져 디스크 공간을 빠르게 차지할 수 있습니다. 부정적인 영향을 줄이려면 디버깅 로그를 메모리 Buffer에 기록하도록 구성하거나 특정 IP 주소에 대한 디버깅 로그를 설정할 수 있습니다. 자세한 내용은 메모리에 디버깅 로그 쓰기 및 선택한 IP에 대한 디버그 로그를 참조하세요.
디버깅 로그를 파일에 쓸 수 있도록 설정합니다.
1. NGINX가 --with-debug
구성 옵션으로 구성되어 있는지 확인합니다. 다음 명령을 실행하고 출력에 --with-debug
줄이 포함되어 있는지 확인합니다.
$ nginx -V 2>&1 | grep -- '--with-debug'
2. NGINX 구성 파일을 엽니다.
$ sudo vi /etc/nginx/nginx.conf
3. 기본적으로 main
컨텍스트에 있는 error_log
지시문을 찾아 로깅 Level을 debug
로 변경합니다. 필요한 경우 로그 파일의 경로를 변경합니다.
error_log /var/log/nginx/error.log debug;
4. 구성을 저장하고 구성 파일을 종료합니다.
2-2. 메모리에 디버깅 로그 쓰기
디버깅 로그는 Cyclic Buffer를 사용하여 메모리에 기록할 수 있습니다. 디버그 Level에서 로깅하면 부하가 높을 때 성능에 큰 영향을 미치지 않는다는 장점이 있습니다.
디버그 로그를 메모리에 쓸 수 있도록 설정합니다.
1. NGINX가 --with-debug
구성 옵션으로 구성되어 있는지 확인합니다. 다음 명령을 실행하고 출력에 --with-debug
줄이 포함되어 있는지 확인합니다.
$ nginx -V 2>&1 | grep -- '--with-debug'
2. NGINX 구성 파일에서 main
컨텍스트에 지정된 error_log
지시문을 사용하여 디버그 로깅을 위한 메모리 Buffer를 활성화합니다.
error_log memory:32m debug;
...
http {
...
}
메모리에서 디버그 로그 추출하기.
로그는 GDB 디버거에서 실행되는 스크립트를 사용하여 메모리 Buffer에서 추출할 수 있습니다.
메모리에서 디버깅 로그를 추출합니다.
1. NGINX Worker Process의 PID를 가져옵니다.
$ ps axu |grep nginx
2. GDB 디버거를 실행합니다.
$ sudo gdb -p <nginx PID obtained at the previous step>
3. 스크립트를 복사하여 GDB에 붙여넣고 “Enter”를 누릅니다. 스크립트가 현재 디렉터리에 있는 debug_log.txt 파일에 로그를 저장합니다.
set $log = ngx_cycle->log
while $log->writer != ngx_log_memory_writer
set $log = $log->next
end
set $buf = (ngx_log_memory_buf_t *) $log->wdata
dump binary memory debug_log.txt $buf->start $buf->end
4. CTRL+D를 눌러 GDB를 종료합니다.
5. 현재 디렉터리에 있는 “debug_log.txt” 파일을 엽니다.
$ sudo less debug_log.txt
2-3. 선택한 IP에 대한 디버그 로그
특정 IP 주소 또는 IP 주소 범위에 대해 디버깅 로그를 활성화할 수 있습니다. 특정 IP를 로깅하면 성능에 부정적인 영향을 미치지 않으므로 Production 환경에서 유용할 수 있습니다. IP 주소는 events
블록 내의 debug_connection
지시문에 지정되며, 지시문은 두 번 이상 정의할 수 있습니다.
error_log /path/to/log;
...
events {
debug_connection 192.168.1.1;
debug_connection 192.168.10.0/24;
}
2-4. 각 가상 호스트에 대한 디버그 로그
일반적으로 error_log
지시문은 main
컨텍스트에 지정되므로 server
와 location
을 포함한 다른 모든 컨텍스트에 적용됩니다. 그러나 특정 server
또는 location
블록 내에 다른 error_log
지시문이 지정되어 있는 경우 전역 설정이 재정의되고 해당 error_log
지시문은 로그 파일의 경로와 로깅 수준을 자체적으로 설정합니다.
특정 가상 호스트에 대한 디버깅 로그를 설정하려면 특정 server
블록 내에 error_log
지시문을 추가하여 로그 파일의 새 경로와 debug
로깅 Level을 설정합니다.
error_log /path1/to/log debug;
...
http {
...
server {
error_log /path2/to/log debug;
...
}
}
특정 가상 호스트 별로 디버깅 로그를 사용하지 않으려면 특정 server
블록 내에 error_log
지시문을 지정하고 로그 파일의 경로만 지정하세요.
error_log /path/to/log debug;
...
http {
...
server {
error_log /path/to/log;
...
}
}
3. 코어 덤프 활성화
코어 덤프 파일은 NGINX 충돌로 이어지는 문제를 식별하고 수정하는 데 도움이 됩니다. 코어 덤프 파일에는 비밀번호 및 Private Key와 같은 민감한 정보가 포함될 수 있으므로 안전하게 처리해야 합니다.
코어 덤프는 두 가지 방법으로 활성화할 수 있습니다.
- 운영 체제에서
- NGINX 구성 파일에서
3-1. 운영 체제에서 코어 덤프 활성화하기
운영 체제에서 다음 단계를 수행합니다.
1. 코어 덤프 파일을 저장할 작업 디렉터리를 지정합니다(예: “/tmp/cores”).
$ mkdir /tmp/cores
2. 디렉터리가 NGINX Worker Process에서 쓰기 가능한지 확인합니다.
$ sudo chown root:root /tmp/cores
$ sudo chmod 1777 /tmp/cores
3. 코어 덤프 파일의 최대 크기 제한을 비활성화합니다.
$ ulimit -c unlimited
“Cannot modify limit: operation not permitted”라는 메시지가 표시되면 다음 명령을 실행합니다.
$ sudo sh -c "ulimit -c unlimited && exec su $LOGNAME"
4. setuid 및 setgid 프로세스에 대한 코어 덤프를 활성화합니다.
CentOS 7.0, Debian 8.2, Ubuntu 14.04의 경우 다음 명령을 실행합니다.
$ echo "/tmp/cores/core.%e.%p" | sudo tee /proc/sys/kernel/core_pattern
$ sudo sysctl -w fs.suid_dumpable=2
$ sysctl -p
FreeBSD의 경우 다음 명령을 실행합니다.
$ sudo sysctl kern.sugid_coredump=1
$ sudo sysctl kern.corefile=/tmp/cores/%N.core.%P
3-2. NGINX 구성에서 코어 덤프 활성화하기
운영 체제에서 이미 코어 덤프 파일 생성을 구성한 경우 이 단계를 건너뜁니다.
NGINX 구성 파일에서 코어 덤프를 활성화합니다.
1. NGINX 구성 파일을 엽니다.
$ sudo vi /usr/local/etc/nginx/nginx.conf
2. working_directory
지시문을 사용하여 핵심 덤프 파일을 보관할 디렉터리를 정의합니다. 이 지시문은 main 구성 Level에서 지정됩니다.
working_directory /tmp/cores/;
3. 디렉터리가 존재하고 NGINX Worker Process에서 쓰기 가능한지 확인합니다. 터미널을 열고 다음 명령을 실행합니다.
$ sudo chown root:root /tmp/cores
$ sudo chmod 1777 /tmp/cores
4. worker_rlimit_core
지시문을 사용하여 코어 덤프 파일의 가능한 최대 크기를 지정합니다. 이 지시문은 main
구성 Level에서도 지정할 수 있습니다. 코어 덤프 파일 크기가 이 값을 초과하면 코어 덤프 파일이 생성되지 않습니다.
worker_rlimit_core 500M;
예제:
worker_processes auto;
error_log /var/log/nginx/error.log debug;
working_directory /tmp/cores/;
worker_rlimit_core 500M;
events {
...
}
http {
...
}
이 설정을 사용하면 코어 덤프 파일이 “/tmp/cores/” 디렉터리에 생성되며, 그 크기가 500MB를 초과하지 않는 경우에만 생성됩니다.
4. 코어 덤프 파일에서 Backtrace 가져오기
Backtrace는 코어 덤프 파일에서 프로그램 충돌 시 무엇이 잘못되었는지에 대한 정보를 제공합니다.
코어 덤프 파일에서 Backtrace를 가져오려면:
1. 패턴을 사용하여 GDB 디버거로 코어 덤프 파일을 엽니다.
$ sudo gdb <nginx_executable_path> <coredump_file_path>
2. backtrace 명령어를 입력하여 충돌 시점부터 스택 추적을 가져옵니다.
(gdb) backtrace
“backtrace” 명령에 “No symbol table info available” 메시지가 표시되면 디버깅 기호를 포함하도록 NGINX Binary를 다시 컴파일해야 합니다.
5. 실행 중인 프로세스에서 NGINX 구성 덤프
메모리의 Master Process에서 현재 NGINX 구성을 추출할 수 있습니다. 이 기능은 유용하게 사용할 수 있습니다.
- 로드된 구성을 확인합니다.
- 디스크의 버전을 실수로 제거하거나 덮어쓴 경우 이전 구성을 복구할 수 있습니다.
구성 덤프는 NGINX에 디버그 지원이 있는 경우 GDB 스크립트를 사용하여 얻을 수 있습니다.
1. 디버그 지원(구성 인수 목록에 있는 --with-debug
configure 옵션)을 사용하여 NGINX가 빌드되었는지 확인합니다. 명령을 실행하고 출력에 --with-debug
줄이 포함되어 있는지 확인합니다.
$ nginx -V 2>&1 | grep -- '--with-debug'
2. NGINX Worker Process의 PID를 가져옵니다.
$ ps axu | grep nginx
3. GDB 디버거를 시작합니다.
$ sudo gdb -p <nginx PID obtained at the previous step>
4. 스크립트를 복사하여 GDB에 붙여넣고 “Enter”를 누릅니다. 스크립트가 현재 디렉터리의 nginx_conf.txt 파일에 구성을 저장합니다.
set $cd = ngx_cycle->config_dump
set $nelts = $cd.nelts
set $elts = (ngx_conf_dump_t*)($cd.elts)
while ($nelts-- > 0)
set $name = $elts[$nelts]->name.data
printf "Dumping %s to nginx_conf.txt\n", $name
append memory nginx_conf.txt \
$elts[$nelts]->buffer.start $elts[$nelts]->buffer.end
end
5. CTRL+D를 눌러 GDB를 종료합니다.
6. 현재 디렉터리에 있는 nginx_conf.txt 파일을 엽니다.
$ sudo vi nginx.conf.txt