MongoDB를 사용하고 있는가. 그렇다면 MongoDB에 대해서 얼마나 알고 있는가.
MongoDB를 알 수 있는 가장 좋은 방법은 결국 직접 실행해보는 것이다.
이번에는 MongoDB를 Debug Mode로 빌드하고 gdb를 사용해서 디버깅해보는 방법을 알아보자.
Overview
gdb를 사용해서 MongoDB를 디버그 모드로 실행해볼 수 있다.
Debug Mode로 빌드하기
Prerequisite
Server
MongoDB를 빌드, 실행할 수 있는 서버가 필요하다.
각자 알아서 서버를 잘 준비해보도록하자. 스토리지는 100GB 정도면 적절하다. ( 나는 그랬다. )
mongod를 빌드하기 위해서 필요한 최소한의 용량은 13GB라고 하지만, 실제로 수행했을 때에는 약 34GB 정도의 공간을 차지하는 것을 볼 수 있었다. ( 빌드에 필요한 도구 등을 전부 포함한 스토리지 )
OS는 Rocky 8.10 버전을 사용한다.
Dev tools
MongoDB를 빌드하기 위한 도구들을 다운로드 해야한다.
MongoDB 6.0 기준으로 진행한다. MongoDB 6.0을 기준으로 빌드하는 방법은 다음 문서에서도 확인할 수 있다.
https://github.com/mongodb/mongo/blob/v6.0/docs/building.md
MongoDB 버전에 따라서 최소로 필요한 조건들이 달라지는데, 리눅스 환경에서 MongoDB를 빌드하기 위해서 주로 필요한 것들은 다음과 같다.
Program | Version |
GCC | 8.2 or newer |
libcurl-devel | - |
Python | 3.7.x and Pip modules |
다음 명령을 통해서 시원~하게 설치할 수 있다.
yum install gcc-toolset-10
dnf install libcurl-devel
dnf install python39
Python을 다운로드 받은 뒤에는 python, python3 명령어의 기본 바이너리 환경을 변경해주어야한다.
다운로드 받은 Python3.9를 사용하도록 변경해주자.
update-alternatives --config python
# check version
python3 --version
Build MongoDB with debug symbol
MongoDB를 debug symbol을 포함하도록 빌드해야한다.
Install source code
소스코드를 다운로드 받자.
https://github.com/mongodb/mongo 에서 코드를 다운로드 받는다.
git clone https://github.com/mongodb/mongo
6.0 버전을 기준으로 빌드하기 위해서 branch를 checkout한다.
git branch -a | grep 6.0
git checkout remotes/origin/v6.0
Build MongoDB
빌드는 기본적으로 https://github.com/mongodb/mongo/blob/v6.0/docs/building.md 문서를 따라가면 되는데, 단지 Scons을 통해 빌드할 때 디버그 심볼들을 포함하도록 --dbg=on 플래그를 추가해준다.
# install requirements
python3 -m pip install -r etc/pip/compile-requirements.txt
# build with debug symbol
python3 buildscripts/scons.py install-mongod --dbg=on
빌드는 굉장히 시간이 오래 걸린다. 12Core 서버에서 약 1시간 정도 걸린 듯 하다.
빌드가 완료되면 다음과 같이 mongod가 생성되어있는 것을 볼 수 있다.
[bin]$ pwd
/xxx/mongo/build/install/bin
[bin]$ ls -al
total 2546968
drwxrwxr-x 2 xxx xxx 38 Dec 21 23:17 .
drwxrwxr-x 3 xxx xxx 100 Dec 21 23:13 ..
-rwxrwxr-x 2 xxx xxx 2608090960 Dec 21 23:17 mongod
-rwxrw-r-- 2 xxx xxx 1782 Dec 21 23:13 resmoke.py
Execute mongod
기본적으로 mongod를 실행시킬 수 있는 환경들을 전부 갖춰준다.
그리고 mongosh로 접속할 수 있도록 mongosh도 다운로드를 받아준다.
이후 mongod 바이너리를 실행시켜준 뒤 replica set initiate, 계정 생성등을 진행한다.
실행하는 환경에 따라서 다르므로, 알잘딱 설정한 뒤 mongod를 실행해준다.
Debug with gdb
What is gdb?
Chatgpt의 힘을 빌려보자.
gdb는 GNU Debugger의 약자로, C, C++, Fortran 등 다양한 언어로 작성된 프로그램을 디버깅하기 위한 도구입니다. gdb는 개발자가 프로그램 실행 중 발생하는 문제를 분석하고 해결하는 데 매우 유용합니다.
암튼 c, c++ 로 개발된 프로그램을 디버깅하기 위한 도구라는 것 같다.
Install gdb
보통 RHEL의 GCC Toolset을 다운로드 받으면 gdb가 포함되어 다운로드된다.
https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/8/html/developing_c_and_cpp_applications_in_rhel_8/additional-toolsets-for-development_developing-applications#specifics-of-binutils-in-gcc-toolset-9_gcc-toolset-9
sudo yum install gcc-toolset-10
Execute gdb
gdb의 기본적인 사용법은 아직 잘 모르지만, mongod에 붙어 디버깅할 수 있는 방법을 소개한다.
우선 debug symbol 과 함께 빌드된 mongod 프로세스를 실행중인지 확인하고 이를 gdb로 debug mode로 붙는다.
ps -ef | grep mongod
gdb -p `pidof mongod`
gdb에서 mongod를 전부 확인하는데 시간이 조금 걸리는 것 같다.
다음 명령어가 뜨면서 console이 뜨면 성공이다.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
0x00007f85ec89e47c in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
Missing separate debuginfos, use: yum debuginfo-install brotli-1.0.6-3.el8.x86_64 cyrus-sasl-lib-2.1.27-6.el8_5.x86_64 glibc-2.28-251.el8_10.5.x86_64 keyutils-libs-1.5.10-9.el8.x86_64 krb5-nbpj-libs-1.17-120.el8.x86_64 libcom_err-1.45.6-5.el8.x86_64 libcurl-7.61.1-34.el8_10.2.x86_64 libgcc-8.5.0-22.el8_10.x86_64 libidn2-2.2.0-1.el8.x86_64 libnghttp2-1.33.0-5.el8_8.x86_64 libpsl-0.20.2-6.el8.x86_64 libselinux-2.9-8.el8.x86_64 libssh-0.9.6-14.el8.x86_64 libstdc++-8.5.0-22.el8_10.x86_64 libunistring-0.9.9-3.el8.x86_64 libxcrypt-4.1.1-6.el8.x86_64 openldap-2.4.46-18.el8.x86_64 openssl-libs-1.1.1k-14.el8_10.x86_64 pcre2-10.32-3.el8_6.x86_64 xz-libs-5.2.4-4.el8_6.x86_64
(gdb)
다음은 breakpoint를 생성한다.
breakpoint를 생성하고 mongod 실행 중 해당 함수로 들어오게 된다면, hit 이라는 이벤트와 함께 breakpoint에서부터 순차적으로 함수 혹은 코드를 실행시켜볼 수 있다.
이번에는 예시이기 때문에 Watchdog Thread의 DirectoryCheck::run 함수에 breakpoint를 걸어보자.
DirectoryCheck::run 함수는 다음 코드에서 확인할 수 있다.
https://github.com/mongodb/mongo/blob/dc676b71192abba530fb7087f637a7d87bb18f89/src/mongo/watchdog/watchdog.cpp#L660-L684
(gdb) b DirectoryCheck::run
Breakpoint 1 at 0x55ad0f796890: file src/mongo/watchdog/watchdog.cpp, line 660.
breakpoint를 생성한 뒤에는 info break 명령어를 통해서 breakpoint가 잘 생성되었는지 확인할 수 있다.
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x000055ad0f796890 in mongo::DirectoryCheck::run(mongo::OperationContext*) at src/mongo/watchdog/watchdog.cpp:660
breakpoint가 잘 생성되었다면 process를 실행시켜준다.
gdb가 attach 되면 기본적으로 process가 interrupt가 된 상태가 된다. 즉, mongod가 기동하고 있지 않다.
mongod를 재기동 시켜주기 위해서 continue 명령어를 수행해준다.
(gdb) continue
Watchdog Thread는 60초에 1번씩 실행이 되므로, 60초 안에 hit가 발생한다.
[New Thread 0x7f85bcf0b700 (LWP 119472)]
[Switching to Thread 0x7f85d8ef4700 (LWP 117890)]
Thread 20 "watchdogCheck" hit Breakpoint 1, mongo::DirectoryCheck::run (this=0x7f85e300c2c0, opCtx=0x7f85df396460) at src/mongo/watchdog/watchdog.cpp:660
660 void DirectoryCheck::run(OperationContext* opCtx) {
이때부터 디버깅을 할 수 있도록 콘솔이 활성화된다.
where 명령을 통해서 함수의 callstack을 확인할 수 있다.
(gdb) where
#0 mongo::DirectoryCheck::run (this=0x7f85e300c2c0, opCtx=0x7f85df396460) at src/mongo/watchdog/watchdog.cpp:660
...
...
next 명령을 수행하면 실제 코드를 한줄씩 실행시켜볼 수 있다.
(gdb) next
662 boost::filesystem::path file = _directory;
이런 식으로 debug mode로 실행한 뒤 mognod를 실행시켜서 동작을 확인할 수 있다.
'Database > MongoDB' 카테고리의 다른 글
MongoDB Task Executor Pool (0) | 2025.01.17 |
---|---|
MongoDB - Collect slow query using Fluentbit and Opensearch (0) | 2025.01.10 |
WiredTiger 직접 빌드해서 MongoDB 데이터파일 살펴보기 (5) | 2024.11.09 |
MongoDB Journaling (1) | 2024.10.09 |
MongoDB Replication (1) | 2024.09.15 |