使用新調試功能探測Nginx內核
Nginx 是由 Igor Sysoev 為俄羅斯訪問量第二的 Rambler.ru 站點開發的一個高性能HTTP和反向代理服務器,第一個公開版本0.1.0發布于2004年10月4日,最新的版本已經到1.9.3。Nginx也是一個 IMAP/POP3/SMTP 服務器,還可作為 Web 服務器、負載均衡服務器。Nginx將源代碼以類BSD許可證的形式發布,因它的穩定性、豐富的功能集、示例配置文件和低系統資源的消耗而聞名。
從Nginx關于調試工具的 官方網頁 來看,在Nginx最近的版本中,增加了很多有用的調試功能,通過使用GDB從運行的服務器中提取信息。雖然現在并不建議在生產環境的Nginx上使用GDB,但其在開發或測試環境下是非常有用的。
本文要介紹的新的功能包括了建立Nginx 時的--with-debug配置選項。為了確定文件是否內置了這個選項,運行nginx -V命令:
# nginx -V nginx version nginx/1.9.3 built by gcc 5.1.1 20150618 (Red Hat 5.1.1-4) (GCC) configure arguments: --with-debug --prefix-/opt/nginx-debug
在內存中編寫調試日志
第一個新功能是在內存中的調試日志。Nginx調試日志對于挖掘復雜的問題非常有用。在以前的版本當中,調試日志存儲在磁盤空中,但由于日志文件可能會迅速變得非常大,導致占用大量的磁盤空間。
Nginx 1.7.11增加了使用循環緩沖區(cyclic memory buffer)直接訪問內存的能力,使得調試日志完全不用使用磁盤存儲。有關詳細信息,請參閱Nginx文檔中<a href="http://nginx.org/en/docs/debugging_log.html?& _ga=1.228176989.1157264337.1438403117#memory">循環內存緩沖區</a>。要啟用一個 32 MB的緩沖區調試日志記錄,在Nginx配置文件的主環境下,使用error_log指令:
error_log memory:32m debug;
可以使用GDB從內存中提取日志。 同時可以到<a >此處< /a>下載一個GitHub Gist,里面包含了文章中介紹的例子所需要的所有東西。下載并保存為Nginx.gdb,或在home目錄下重新命名為.gdbinit,讓其自動加載。
首先,運行以下命令可以顯示Nginx工作進程的進程ID:
# pgrep -f "nginx: worker"
找到探測對象的進程ID。在這個例子中,有一個ID為20192的工作進程,要啟動GDB并加載工作進程,運行以下命令(請注意,當GDB運行的時候,工作進程被暫停):
# sudo gdb --pid 20192
加載從GitHub Gist上下載的腳本并轉儲調試日志,運行以下命令:
(gdb) source nginx.gdb (gdb) ddl
GDB創建了一個名為debug_log.txt的文件,里面包含了使用error_log指令分配的內存轉儲,因此在這個例子中的文件大小為32 MB。可以輕松使用下面的sed命令截斷它。在大多數情況下,不需要這樣做,如果日志文件已經打包,該命令無效:
# sed -i 's/[[:space:]]*$//' debug_log.txt
顯然,在探測日志的時候讓工作進程暫停并不總是一個好辦法。所以,可以將暫停的時間限制得非常短,如果超出時間限制,告訴GDB要探測日志并讓工作進程立刻停止,這種辦法很常用,更多細節可以參考<a >這里</a>。
# gdb --pid 20192 -iex "source nginx.gdb" -ex "ddl" –batch
轉儲活動Nginx配置
在Nginx 1.9.2及以后的版本中,當Nginx使用--with-debug配置選項進行建立的時候,整個配置存儲在內存中,從而可以使用GDB從主進程中提取配置。這可能是很有用的,可以用來核實哪些配置已經被加載,如果磁盤上的版本已經被意外刪除或覆蓋的時候可以幫助恢復之前的配置。
和前面一樣,<a >GitHub Gist</a>中的nginx.gdb文件也包含了運行內存轉儲的功能。因此,首先載入GDB:
# sudo gdb --pid `pgrep -f "nginx: master"`
然后,運行以下命令來轉儲配置。
(gdb) source nginx.gdb (gdb) dcfg
在轉儲它們的時候,GDB輸出文件的名稱。例如,這個例子中的輸出,每個文件名的末尾可能有一些亂碼,這是因為當字符串沒有NUL終止的時候,GDB的printf函數不是一直運行得很好。最后的結果是一個包含完整活動配置的名為nginx_conf.txt的文件。
<img _href="img://null" _p="true" src="http://cdn2.infoqstatic.com/statics_s2_20150819-0313/resource/articles/Nginx-GDB/zh/resources/gdb-dcfg-output.png" width="550" />
在使用調試日志的時候,還可以使用批處理模式轉儲配置:
# gdb --pid `pgrep -f "nginx: master"` -iex "source nginx.gdb" -ex "dcfg" –batch
用core文件使用GDB
文章中涵蓋的所有東西都可以用來幫助使用core文件調試問題產生的原因:
# gdb --core core.9491 nginx
無論是在這篇文章中描述的dcfg函數還是ddl函數,都可以用作core文件。如果需要在core文件生成的時候找到NGINX服務器的配置,或者需要為導致core文件生成的事件找到調試信息,這可能會有用。
對于提取關于Nginx內核的信息,轉儲調試日志和配置都是非常有用的方法,當然也可以通過調整GDB的腳本,在文章介紹的技術的基礎上進行拓展。例如,轉儲配置時,可以轉儲每個加載的配置文件到一個單獨的輸出文件中,而不是轉儲所有東西到單獨的一個文件中。文件名的長度用文件名自身存儲,所以要使用它們的時候,應該有一種方法要么復制它們要么截斷它們,標準API腳本是一種非常好的方式,GDB最近的版本開始支持Python腳本,也提供了一種選擇。
值得重申的是,這些技術僅僅建議在開發和測試環境中使用。暫停Nginx過程不是一個好主意,尤其是在生產環境下。