平常的工做中,會收到一堆CPU使用率太高的告警郵件,遇到某臺服務的CPU被佔滿了
,這時候咱們就要去查看是什麼進程將服務器的CPU資源佔用滿了。一般咱們會經過top
或者htop
來快速的查看佔據CPU最高的那個進程,以下圖:php
這裏是經過一個普通的服務器作演示使用,如圖所示當前服務器佔用CPU最高的是一個叫作kube-apiserver
命令運行的一個進程,該進程的PID爲25633
,固然你可能遇到一個服務器上運行有多個服務,想快速知道佔用率最高的那幾個進程的話,你可使用如下命令:html
ps aux|head -1;ps -aux | sort -k3nr | head -n 10 //查看前10個最佔用CPU的進程 ps aux|head -1;ps -aux | sort -k4nr | head -n 10 //查看前10個最佔用內存的進程
可是經過以上的方法獲取到服務器佔用資源的進程以後,仍是不知道CPU使用究竟耗時在哪裏
,不清楚瓶頸在哪裏,此時就能夠經過Linux
系統的性能分析工具perf
分析,分析其返回的正在消耗CPU的函數以及調用棧。而後能夠經過解析perf
採集的數據,渲染到火焰圖?,就清楚的知道究竟佔用系統CPU資源的罪魁禍首了。java
在製做火焰圖以前,須要先來講說這個Linux性能分析工具perf
,該工具是一個相對簡單易上手的性能分析工具,是Performance
單詞的縮寫,經過其perf
的命令選項完成系統事件的採集到解析,咱們來簡單的認識一下:linux
Perf
perf
我目前的服務器發行版是Ubuntu 16.04.6 LTS
所以須要先安裝perf才能使用,該工具由linux-tools-common
提供,可是它須要安裝後面的依賴。git
#安裝 root@master:~# apt install linux-tools-common linux-tools-4.4.0-142-generic linux-cloud-tools-4.4.0-142-generic -y root@master:~# perf -v #顯示perf的版本 perf version 4.4.167
在安裝完成時候,咱們就能夠對上圖CPU使用率最高的進程ID爲25633
的進程進行採樣分析。github
首選咱們採集一下該進程的調用棧
信息:api
root@master:~# sudo perf record -F 99 -p 25633 -g -- sleep 30 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.039 MB perf.data (120 samples) ]
這個命令會產生一個大的數據文件,取決與你採集的進程與CPU的配置,若是一臺服務器有16個 CPU,每秒抽樣99次,持續30秒,就獲得 47,520 個調用棧,長達幾十萬甚至上百萬行。上面的命令中,perf record
表示記錄,-F 99
表示每秒99次,-p 25633
是進程號,即對哪一個進程進行分析,-g
表示記錄調用棧,sleep 30
則是持續30秒,參數信息能夠視狀況調整。生成的數據採集文件在當前目錄下,名稱爲perf.data
。瀏覽器
perf record
命令能夠從高到低排列統計每一個調用棧出現的百分比,顯示結果以下圖所示:bash
root@master:~# sudo perf report -n --stdio
這樣的效果對使用者來講仍是不那麼直觀易讀,這時候,火焰?圖也就真正的派上用途了。服務器
火焰?圖並不是必定就是火焰系列的顏色主題,只是經過?色系更能表達出含義。火焰圖常見的類型有 On-CPU, Off-CPU, 還有 Memory, Hot/Cold, [Differential](http://www.brendangregg.com/blog/2014-11-09/differential-flame-graphs.html "Differential") 等等.
on-CPU/off-cpu`的區別就是一個是用於CPU是性能瓶頸,一個是IO是性能瓶頸,當你不知道當前的服務器的性能瓶頸到底是什麼的時候,你可使用這兩種類型進行對比,經過兩種火焰圖的差異是比較大的,若是兩張火焰圖長得差很少, 那麼一般認爲CPU被其它進程搶佔了.
另一種狀況就是若是沒法肯定當前的系統瓶頸, 能夠經過壓測工具來確認 : 經過壓測工具看看可否讓CPU使用率趨於飽和, 若是能那麼使用 On-CPU
火焰圖, 若是無論怎麼壓, CPU 使用率始終上不來, 那麼多半說明程序被 IO
或鎖卡住了, 此時適合使用 Off-CPU
火焰圖. 你能夠經過壓測工具進行測試,目前比較經常使用的就是ab
和wrk
,我建議嘗試使用諸如 wrk 之類更現代的壓測工具.
若是選擇ab
的話, 那麼務必記得開啓-k
選項, 以免耗盡系統的可用端口
Github上有Brendan D. Gregg
的 Flame Graph
工程實現了一套生成火焰圖的腳本.咱們能夠直接克隆下來直接用。
cd && git clone https://github.com/brendangregg/FlameGraph.git
生成火焰?圖,咱們通常都遵循如下流程
捕獲堆棧
: 使用perf
捕捉進程運行堆棧信息摺疊堆棧
: 對抓取的系統和程序運行每一時刻的堆棧信息進行分析組合, 將重複的堆棧累計在一塊兒, 從而體現出負載和關鍵路徑,經過stackcollapse
腳本完成生成火焰圖
:分析 stackcollapse 輸出的堆棧信息渲染成火焰圖Flame Graph
中提供了抓取不一樣信息的腳本,能夠按需使用。下面咱們須要對捕獲到的進程堆棧信息perf.data
進行摺疊,生成摺疊的堆棧信息:
root@master:~# perf script -i /root/perf.data &> /root/perf.unfold
用 stackcollapse-perf.pl
將 perf 解析出的內容 perf.unfold
中的符號進行摺疊
root@master:~/FlameGraph# ls aix-perf.pl docs example-perf.svg pkgsplit-perf.pl stackcollapse-aix.pl stackcollapse-go.pl stackcollapse-ljp.awk stackcollapse-pmc.pl stackcollapse-vsprof.pl test.sh demos example-dtrace-stacks.txt files.pl range-perf.pl stackcollapse-bpftrace.pl stackcollapse-instruments.pl stackcollapse-perf.pl stackcollapse-recursive.pl stackcollapse-vtune.pl dev example-dtrace.svg flamegraph.pl README.md stackcollapse-elfutils.pl stackcollapse-java-exceptions.pl stackcollapse-perf-sched.awk stackcollapse-sample.awk stackcollapse-xdebug.php difffolded.pl example-perf-stacks.txt.gz jmaps record-test.sh stackcollapse-gdb.pl stackcollapse-jstack.pl stackcollapse.pl stackcollapse-stap.pl test root@master:~/FlameGraph# ./stackcollapse-perf.pl /root/perf.unfold &> /root/perf.folded root@master:~/FlameGraph#
最後就是生成火焰?圖了
root@master:~/FlameGraph# ./flamegraph.pl /root/perf.folded > /root/perf.svg
固然也能夠經過管道符|
將整個過程簡化:
cd && perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > process.svg
最後在谷歌瀏覽器上打開該火焰圖:
火焰圖是基於stack
信息生成的SVG
圖片, 用來展現 CPU 的調用棧。
y
軸表示調用棧, 每一層都是一個函數. 調用棧越深, 火焰就越高, 頂部就是正在執行的函數, 下方都是它的父函數.x
軸表示抽樣數, 若是一個函數在 x 軸佔據的寬度越寬, 就表示它被抽到的次數多, 即執行的時間長. 注意, x 軸不表明時間, 而是全部的調用棧合併後, 按字母順序排列的.火焰圖就是看頂層的哪一個函數佔據的寬度最大. 只要有"平頂"(plateaus)
, 就表示該函數可能存在性能問題。顏色沒有特殊含義, 由於火焰圖表示的是 CPU 的繁忙程度, 因此通常選擇暖色調.
當調用棧不完整
調用棧過深時,某些系統只返回前面的一部分(好比前10層);當函數名缺失
,函數沒有名字,編譯器只用內存地址來表示(好比匿名函數),因此使用火焰圖也是存在分析不到的地方。你也能夠經過如下腳本進行採集分析火焰圖:
if [ $# -ne 1 ];then echo "Usage: $0 seconds" exit 1 fi perf record -a -g -o perf.data & PID=`ps aux| grep "perf record"| grep -v grep| awk '{print $2}'` if [ -n "$PID" ]; then sleep $1 kill -s INT $PID fi # wait until perf exite sleep 1 perf script -i perf.data &> perf.unfold perl stackcollapse-perf.pl perf.unfold &> perf.folded perl flamegraph.pl perf.folded >perf.svg