文章背景在日常工作中,我们会收到一堆CPU占用率过高的告警邮件,而当某个服务的CPU被占满时,我们会去看看是什么进程正在占用服务器的CPU资源。通常我们使用top或者htop来快速查看占用CPU最高的进程,如下图所示:下面是使用普通服务器的演示。如图所示,当前占用CPU最高的服务器是一个叫kube-apiserver的命令。一个PID为25633的进程。当然,你可能会遇到在一个服务器上运行多个服务的情况。如果想快速知道占用率最高的进程,可以使用如下命令:psaux|head-1;ps-aux|排序-k3nr|head-n10//查看CPU占用率最高的前10个进程psaux|head-1;ps-aux|排序-k4nr|head-n10//查看CPU占用率最高的前10个进程占用内存的进程但是通过以上方法获取到服务器上占用资源的进程后,还是不知道CPU占用耗时在哪里瓶颈在哪里。这时候我可以使用linux系统的性能分析工具perf来分析分析它返回的是正在消耗CPU的函数和调用栈。然后,通过分析perf采集到的数据,渲染成火焰图,就可以清楚的知道占用系统CPU资源的罪魁祸首。在制作火焰图之前,我们需要先说说Linux性能分析工具perf。该工具是一款比较简单易用的性能分析工具。它是性能一词的缩写。它通过其perf命令选项完成对系统事件的收集和分析,简单了解一下:linux上的性能分析工具Perf安装perf我目前的服务器发行版是Ubuntu16.04.6LTS,所以使用前需要先安装perf。该工具由linux-tools-common提供,但需要安装后面的Dependencies。#installroot@master:~#aptinstalllinux-tools-commonlinux-tools-4.4.0-142-genericlinux-cloud-tools-4.4.0-142-generic-yroot@master:~#perf-v#显示perfversionperfversion4.4.167安装完成后,我们可以对上图中CPU占用率最高的进程ID为25633的进程进行采样分析。首先我们收集进程的调用栈信息:root@master:~#sudoperfrecord-F99-p25633-g--sleep30[perfrecord:Wokenup1timestowritedata][perfrecord:Captured并写入0.039MBperf.data(120samples)]此命令将生成一个大数据文件,这取决于您收集的进程和CPU配置。如果一个服务器有16个CPU,它会每秒采样99次,持续30秒,你会得到47,520个调用堆栈,高达数十万甚至数百万行。上面命令中perfrecord表示记录,-F99表示每秒99次,-p25633是进程号,分析哪个进程,-g表示记录调用堆栈,sleep30表示30秒,参数信息可以根据情况调整。生成的数据采集文件在当前目录下,名称为perf.data。perfrecord命令可以从高到低排列统计每个调用栈的百分比,显示结果如下图:root@master:~#sudoperfreport-n--stdio效果不是很好为了用户直观易读,这个时候,火焰图就真的能派上用场了。制造火焰?图片火焰?画面不一定是火焰系列的色彩主题,但通过色系可以更好的表达意思。常见的火焰图类型有On-CPU、Off-CPU和Memory、Hot/Cold、[Differential](http://www.brendangregg.com/blog/2014-11-09/differential-flame-graphs。html“差分”)等。on-CPU/off-cpu`的区别在于,一个用于CPU作为性能瓶颈,一个用于IO作为性能瓶颈。当你不知道当前服务器的性能瓶颈是什么时,可以使用这两种进行比较。两张火焰图的区别比较大。如果两个火焰图看起来很相似,通常认为CPU被其他进程抢占了。另一种情况是,如果不能确定当前系统瓶颈,可以通过压测工具来确认:使用压测工具看CPU使用率是否可??以饱和,如果可以,则使用On-CPU火焰图,如果不能压力再大,CPU占用率也不会增加吧,那么很多时候就是程序被IO或者lock卡住了,这时候适合使用Off-CPU火焰图。您可以使用压力测试工具对其进行测试。目前常用的有ab和wrk。我建议尝试使用类似wrk的更现代的压力测试工具。如果选择ab,那么一定要记得启用-k选项,以免耗尽系统的可用端口。BrendanD.Gregg在Github上的火焰图项目实现了一组生成火焰图的脚本。我们可以直接克隆直接使用。cd&&gitclonehttps://github.com/brendangregg/FlameGraph.git生成火焰图,我们一般按照以下流程抓栈:使用perf抓取进程运行栈信息折叠栈:运行每一个抓取的系统和程序的分析合并某一时刻的栈信息,将重复的栈累加在一起,反映负载和关键路径,通过stackcollapse脚本生成火焰图:渲染分析stackcollapse输出的栈信息成火焰图。FlameGraph提供了不同消息的捕获脚本,可以按需使用。接下来,我们需要将捕获到的进程栈信息进行折叠perf.data来生成折叠后的栈信息:root@master:~#perfscript-i/root/perf.data&>/root/perf.unfoldusestackcollapse-perf.pl将perfroot@master解析的内容的perf.unfold中的symbol折叠起来:~/FlameGraph#lsaix-perf.pldocsexample-perf.svgpkgsplit-perf.plstackcollapse-aix.plstackcollapse-go.plstackcollapse-ljp.awkstackcollapse-pmc.plstackcollapse-vsprof.pltest.shdemosexample-dtrace-stacks.txtfiles.plrange-perf.plstackcollapse-bpftrace.plstackcollapse-instruments.plstackcollapse-perf.plstackcollapse-recursive.plstackcollapse-vtune.pldev示例-dtrace.svgflamegraph.plREADME.mdstackcollapse-elfutils.plstackcollapse-java-exceptions.plstackcollapse-perf-sched.awkstackcollapse-sample.awkstackcollapse-xdebug.phpplifffolded.pl示例-perf-stacks.txt.gzjmapsrecord-test.shstackcol失效-gdb.plstackcollapse-jstack.plstackcollapse.plstackcollapse-stap.pltestroot@master:~/FlameGraph#./stackcollapse-perf.pl/root/perf.unfold&>/root/perf.foldedroot@master:~/FlameGraph#最后是生成火焰?Root@master:~/FlameGraph#./flamegraph.pl/root/perf.folded>/root/perf.svg当然也可以通过管道符|简化整个过程:cd&&perfscript|火焰图/stackcollapse-perf.pl|FlameGraph/flamegraph.pl>process.svg最后在谷歌浏览器上打开火焰图:火焰图是根据堆栈信息生成的SVG图像,用于显示CPU的调用堆栈,y轴代表调用堆栈,每一个层是一个函数。调用栈越深,火焰越高,最上面是正在执行的函数,最下面是它的父函数。x轴表示样本数,如果一个函数在x轴的宽度越宽,说明它被绘制的次数越多,也就是执行时间越长。请注意,x轴不代表时间,而是将所有调用堆栈合并并按字母顺序排列。火焰图就是看最顶层哪个函数占用的宽度最大。只要有一个“平台期”,就意味着该函数可能存在性能问题。颜色没有特殊含义,因为火焰图表示CPU的繁忙程度,所以一般选择暖色。当调用栈不完整,调用栈太深时,有些系统只返回前面的部分(比如前10层);当函数名缺失时,函数没有名字,编译器只用内存地址来表示(比如匿名函数),所以在使用火焰图时会有无法分析的地方。您还可以使用以下脚本来收集和分析火焰图:if[$#-ne1];thenecho"Usage:$0seconds"exit1fiperfrecord-a-g-operf.data&PID=`psaux|grep“性能记录”|grep-vgrep|awk'{print$2}'`if[-n"$PID"];然后sleep$1kill-sINT$PIDfi#waituntilperfexitsleep1perfscript-iperf.data&>perf.unfoldperlstackcollapse-perf.plperf.unfold&>perf.foldedperlflamegraph.plperf.folded>perf.svg
