Openresty繪製火焰圖
參考連結:https://7byte.github.io/2016/10/02/trytouse-FlameGraph/
火焰圖就像是給一個軟體系統拍的 X 光照片,可以很自然地把時間和空間兩個維度上的資訊融合在一張圖上,以非常直觀的形式展現出來,從而反映系統在效能方面的很多定量的統計規律。 ——動態追蹤技術漫談
下面介紹下火焰圖相關工具的安裝和使用。
-
首先需要安裝核心開發包和除錯包。檢視當前系統的核心版本:
1
2
$ uname -r
3.10.0-327.28.2.el7.x86_64
-
然後進入 http://debuginfo.centos.org/ ,可以看到有 4/ 5/ 6/ 7/ 這樣的目錄,這些目錄分別對應了centos系統的大版本。找到並下載自己的系統核心版本對應的包:
1
2
$ wget "http://debuginfo.centos.org/7/x86_64/kernel-debuginfo-($version).rpm"
$ wget "http://debuginfo.centos.org/7/x86_64/kernel-debuginfo-common-($version).rpm"
-
安裝上面的開發包和除錯包,並安裝核心探測工具 systemtap
1
2
3
4
$ sudo rpm -ivh kernel-debuginfo-common-($version).rpm
$ sudo rpm -ivh kernel-debuginfo-($version).rpm
$ sudo yum install kernel-devel-($version)
$ sudo yum install systemtap
-
下載火焰圖繪製相關工具
1
2
$ git clone https://github.com/openresty/nginx-systemtap-toolkit.git
$ git clone https://github.com/brendangregg/FlameGraph.git
-
然後測試下有沒有安裝成功
通過ps命令檢視當前 nginx worker 程序的PID,如果有多個worker選擇其中一個即可,下面是我測試時查詢到的結果 PID=144891
2
3
$ ps -ef | grep nginx
root 1580 1 0 11:36 ? 00:00:00 nginx: master process /usr/local/openresty/nginx/sbin/nginx -c /home/7byte/openresty-test/conf/nginx.conf
nobody 14489 1580 0 13:46 ? 00:00:00 nginx: worker process
這裡我選用了工具包裡的 sample-bt 來驗證,它抓取的是C級別的執行狀態,引數 -p 表示要抓取的程序id,-t是探測的時間,單位是秒,-u表示抓取使用者空間,對應的-k表示核心空間,探測結果輸出到 a.bt
1
2
3
4
$ mkdir svg
$ sudo nginx-systemtap-toolkit/./sample-bt -p 14489 -t 20 -u > svg/a.bt
WARNING: Tracing 14489 (/usr/local/openresty/nginx/sbin/nginx) in user-space only...
WARNING: Time's up. Quitting now...(it may take a while)
得到 a.bt 後再用 FlameGraph 提供的兩個指令碼處理輸出結果
1
2
$ FlameGraph/stackcollapse-stap.pl svg/a.bt > svg/a.cbt
$ FlameGraph/flamegraph.pl svg/a.cbt > svg/a.svg
最後生成的 a.svg 就是火焰圖,用瀏覽器開啟即可,效果如下圖:
每個框代表一個棧裡的一個函式;
Y軸代表棧深度(棧楨數)。最頂端的框顯示正在執行的函式,這之下的框都是呼叫者。在下面的函式是上面函式的父函式;
X軸代表取樣總量。從左到右並不代表時間變化,從左到右也不具備順序性;
框的寬度代表佔用CPU總時間。寬的框代表的函式可能比窄的執行慢,或者被呼叫了更多次數。框的顏色深淺也沒有任何意義;
如果是多執行緒同時取樣,取樣總數會超過總時間。 -
然後再測試下抓取lua級別的火焰圖,這裡需要使用另外一個工具 ngx-sample-lua-bt , –luajit20 表示nginx使用luajit2.0,如果使用的是標準lua則使用 –lua51
1
2
3
4
$ sudo nginx-systemtap-toolkit/./ngx-sample-lua-bt -p 2834 --luajit20 -t 20 > svg/a.bt
WARNING: missing unwind/symbol data for module 'kernel'
WARNING: Tracing 2836 (/usr/local/openresty/nginx/sbin/nginx) for LuaJIT 2.0...
WARNING: Time's up. Quitting now...
對輸出檔案 a.bt 的後續處理與上文基本相同。另外可以預處理一下輸出檔案,增強可讀性:
1
2
3
$ nginx-systemtap-toolkit/./fix-lua-bt svg/a.bt > svg/tmp.bt
$ FlameGraph/stackcollapse-stap.pl svg/tmp.bt > svg/a.cbt
$ FlameGraph/flamegraph.pl svg/a.cbt > svg/a.svg
遇到的問題:
1
2
3
4
5
6
7
8
9
10
11
12
$ sudo nginx-systemtap-toolkit/./ngx-sample-lua-bt -p 2039 --luajit20 -t 5 > svg/a.bt
WARNING: cannot find module /usr/local/openresty/luajit/lib/libluajit-5.1.so.2.1.0 debuginfo: No DWARF information found [man warning::debuginfo]
WARNING: Bad $context variable being substituted with literal 0: identifier '$L' at <input>:17:30
source: lua_states[my_pid] = $L
^
semantic error: type definition 'TValue' not found in '/usr/local/openresty/luajit/lib/libluajit-5.1.so.2.1.0': operator '@cast' at :62:12
source: return @cast(tvalue, "TValue", "/usr/local/openresty/luajit/lib/libluajit-5.1.so.2.1.0")->fr->tp->ftsz
^
Pass 2: analysis failed. [man error::pass2]
Number of similar warning messages suppressed: 100.
Rerun with -v to see them.
在使用 ngx-sample-lua-bt 探測lua級別的火焰圖時遇到了失敗的情況,從錯誤資訊來看是找不到 DWARF 除錯資訊。查詢官網資料,貌似在很早以前 openresty 自帶的 LuaJIT 2.0 預設就會啟用 DWARF 除錯資訊,那我用的最新版怎麼就是沒有除錯資訊呢?折騰了好久,最後我到 luajit 官網下載了當前 openresty 版本對應的 luajit 原始碼,然後編譯
1
$ make CCDEBUG=-g -B -j8
備份然後替換 /usr/local/openresty/luajit 目錄下的兩個檔案,重啟 openresty之後問題解決。
1
2
3
4
5
6
7
$ cd /usr/local/openresty/luajit/bin/
$ sudo cp luajit-2.1.0-beta2 luajit-2.1.0-beta2_20160829
$ sudo cp ~/LuaJIT-2.1.0-beta2/src/luajit/luajit luajit-2.1.0-beta2
$ cd /usr/local/openresty/luajit/lib/
$ sudo cp libluajit-5.1.so.2.1.0 libluajit-5.1.so.2.1.0_20160829
$ sudo cp ~/LuaJIT-2.1.0-beta2/src/libluajit.so libluajit-5.1.so.2.1.0
從上面的分析可以看出,使用火焰圖可以精確地定位 nginx + lua 潛在的效能問題,對CPU佔用率低、吐吞量低的情況也可以使用火焰圖的方式排查程式中是否有阻塞呼叫導致整個架構的吞吐量低下。
根據官網的說明,火焰圖本身對系統性能的影響較小,每秒請求數會下降11%:The overhead exposed on the target process is usually small. For example, the throughput (req/sec) limit of an nginx worker process doing simplest “hello world” requests drops by only 11% (only when this tool is running), as measured by ab -k -c2 -n100000 when using Linux kernel 3.6.10 and systemtap 2.5. The impact on full-fledged production processes is usually smaller than even that, for instance, only 6% drop in the throughput limit is observed in a production-level Lua CDN application.