平常的工做中,会收到一堆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