当登录到一台表现不佳的Linux系统中后,我们可以进行以下步骤来排查问题。
- 查看平均负载
- 查看系统日志
- 查看cpu饱和度
- 查看磁盘饱和度
- 查看内存饱和度
- 查看网络饱和度
- 跟踪进程
步骤1:查看平均负载
[root@VM-12-4-centos ~]# uptime
16:49:58 up 7 days, 19:49, 4 users, load average: 0.94, 0.66, 0.50
最后三个数字是1,5,15分钟内的平均负载,通过比较这三个数字,你可以判断负载在15分钟内(或者其他时间段)是在上升,下降还是平稳。
平均负载代表了对CPU资源的需求,通过汇总正在运行的线程数(使用率)和正在排队等待运行的线程数(饱和度)计算得出。计算平均负载的一个新方法是把使用率加上线程调度器延时得出,而不是去取样队列长度,从而提高精度。
这个值的意义为,平均负载大于CPU数量表示CPU不足以服务线程,有些线程在等待,如果平均负载小于CPU数量,这(很有可能)代表还有一些余量,线程可以在它们想要的时候在CPU上运行。
举一个现代化的例子,一个有64颗CPU的系统的平均负载为128。这意味着平均每个CPU上有一个线程在运行,还有一个线程在等待。而同样的系统,如果平均负载为10,则代表还有很大的余量,在所有CPU跑满前还可以运行54个CPU消耗型线程。
平均负载最理想的情况是等于 CPU 个数。所以在评判平均负载时,首先你要知道系统有几个 CPU,这可以通过 top 命令或者从文件 /proc/cpuinfo 中读取,比如
[root@VM-12-4-centos ~]# grep ‘model name’ /proc/cpuinfo | wc -l
2
有了 CPU 个数,我们就可以判断出,当平均负载比 CPU 个数还大的时候,系统已经出现了过载。
当平均负载高于 CPU 数量 70% 的时候,你就应该分析排查负载高的问题了。一旦负载过高,就可能导致进程响应变慢,进而影响服务的正常功能。
但 70% 这个数字并不是绝对的,最推荐的方法,还是把系统的平均负载监控起来,然后根据更多的历史数据,判断负载的变化趋势。当发现负载有明显升高趋势时,比如说负载翻倍了,你再去做分析和调查。
系统的平均负载值还是一个优秀指标,可以作为系统基准数据的一部分进行跟踪。如果知道您的系统在普通工作日的系统平均负载,而在有问题的同一天也处于同样的范围,这就是暗示您应该看看别的方面(如网络)来确定性能问题的原因。如果平均负载超出预期的正常水平,则表明您应该看看系统自身所运行的进程。
在linux上最好通过一些其他的指标料及CPU负载,例如vmstat和mpstat提供的一些数据。
步骤2:查看系统日志
[root@VM-12-4-centos ~]# dmesg |tail
[ 576.408049] atkbd serio0: Unknown key pressed (translated set 2, code 0x0 on isa0060/serio0).
[ 576.408749] atkbd serio0: Use ‘setkeycodes 00 ‘ to make it known.
[ 576.738079] atkbd serio0: Unknown key released (translated set 2, code 0x0 on isa0060/serio0).
[ 576.738874] atkbd serio0: Use ‘setkeycodes 00 ‘ to make it known.
[452528.220798] hrtimer: interrupt took 4380755 ns
[2505242.669943] conntrack: generic helper won’t handle protocol 47. Please consider loading the specific helper module.
[3040185.594969] python3[26454]: segfault at a9 ip 0000000000549478 sp 00007ffcabffe870 error 4 in python3.7[400000+29e000]
[8552308.399053] TCP: request_sock_TCP: Possible SYN flooding on port 22. Sending cookies. Check SNMP counters.
[21887551.894806] AliSecGuard : 1818d2bba043a57cfe9bbe06fb701e2825c6e66d
[21887552.136283] AliSecGuard : 1818d2bba043a57cfe9bbe06fb701e2825c6e66d
这个命令显示过去10条系统日志,如果有的话,注意在这里寻找可能导致性能问题的错误。
我们还可以查看下/var/log/message 的内容。
步骤3:查看CPU饱和度
mpstat -P ALL 1
这个命令将每个CPU分解到各个状态下的时间打印出来。选项-P ALL 用来打印每个CPU的报告,mpstat默认只打印系统级别的总结信息。
输出列如下:
- CPU:逻辑CPU ID或者all表示总结信息
- %usr:用户态时间
- %nice:以nice优先级运行的进程用户态时间
- %sys:系统态时间(内核)
- %iowait:I/O等待
- %irq:硬件中断CPU用量
- %sort:软件中断CPU用量
- %steal:耗费在服务其他租户的时间
- %guest:花在访客虚拟机的时间
- %idle:空闲
重要的有%usr,%sys,%idle。这些显示了每个CPU的用量以及用户态和内核态的时间比例。这同样也能表明”热“CPU—那些跑到100%使用率(%usr+%sys)的CPU,而其他CPU并未跑满—–可能是由单线程应用程序的负载或者设备中断映射造成。
CPU花在执行用户态应用程序代码的时间称为用户态时间,而执行内核态代码的时间称为内核态时间。内核态时间包括了系统调用,内核线程和中断的时间。当在整个系统范围内进行测量时,用户态时间和内核态时间之比揭示了运行的负载类型。
计算密集的应用程序几乎会把大量的时间用在用户态代码上,用户态/内核态时间之比接近99/1。这类例子有图像处理,基因组学和数据分析。
I/O密集型的应用程序的系统调用频率较高,通过执行内核代码执行I/O操作。例如,一个进行网络I/O的Web服务器的用户态/内核态时间比大约为70/30。
这些数字依赖许多因素,只是用来表示预期的比例。
对于比较高的%iowait时间也要注意,可以使用磁盘I/O工具进一步分析。如果出现较高的%sys值,可以使用系统调用(syscall)跟踪和内核跟踪,以及CPU剖析手段进一步分析。
pidstat 1 (查出是哪些进程占用了更多的CPU)
直接运行 pidstat
默认显示所有进程的 CPU 使用信息,等效于 `pidstat -u -p ALL。
输出列如下:
- PID:进程ID
- %usr:进程在用户级(应用程序)执行时所使用的CPU百分比
- %system:进程在系统级执行时所使用的CPU百分比(内核)。
- %guest:进程在虚拟机(运行虚拟处理器)中消耗的CPU百分比。
- %CPU:进程使用的CPU时间的总百分比
- CPU:进程所在的CPU编号。
- Command:进程名
pidstat(1)命令按每个进程展示CPU的使用情况。top(1)命令虽然也很流行,但是pidstat(1) 默认支持滚动打印输出,这样可以采集到不同时间段的数据变化。
步骤4:查看磁盘饱和度
iostat -xz 1
这个工具显示了存储设备的I/O指标。
要检查的列包括如下几个:
- r/s,w/s,rkB/s,wkB/s:这些是每秒向设备发送的读,写次数以及读,写字节数。可以用这些指标对业务负载画像。某些性能问题仅仅是因为超过了能够承受的最大负载导致的。
- await:I/O的平均响应时间,以毫秒为单位。这是应用需要承受的时间,它同时包含了I/O队列时间和服务时间。超过预期的平均响应时间,可看作设备已饱和或者设备层面有问题的表征。
- avgqu-sz: 设备请求队列的平均长度,比1大的值有可能是发生饱和的表征(不过对有些设备,尤其是对基于多块磁盘的虚拟设备来说,通常以并行方式处理请求)。
- %util:设备使用率,这是设备繁忙程度的百分比,显示了每秒设备开展实际工作的时间占比,不过它展示的并不是容量规划意义下的使用率,因为设备可以并行处理请求。大于60%的值通常会导致性能变差(可以通过await字段确认),不过这也取决于具体设备,接近100%的值通常代表了设备达到饱和状态。
pidstat -d (查出是哪些进程占用了更多的磁盘)
输出列如下:
- UID:用户ID
- PID:进程ID
- kB_rd/s: 每秒进程从磁盘读取的数据量 KB 单位
- kB_wr/s: 每秒进程向磁盘写的数据量 KB 单位
- kB_ccwr/s: 每秒进程向磁盘写入,但是被取消的数据量
- iodelay: 块I/O延迟
- Command: 进程名
步骤5:查看内存饱和度
free -m
这个输出显示了用MB作为单位的可用内存。检查可用内存(available)是否接近0,这个值显示了在系统中还有多少实际剩余内存可用,包括缓冲区和页缓存区。将一些内存用于缓存可以提升文件系统的性能。
vmstat 1
输出如下,单位为KB:
- swap: 交换出的内存量
- free:空闲的可用内存
- buff:用于缓冲缓存的内存
- cache:用于页缓存的内存
- si:换入的内存(换页)
- so:换出的内存
如果si和so列一直非0,那么系统正存在内存压力并换页到交换设备或文件。我们可以使用pidstat工具来查看具体是什么在消耗内存。
pidstat -r 2 10 (查出是哪些进程占用了更多的内存)
输出列如下:
- UID:用户ID
- PID:进程ID
- Minflt/s : 每秒次缺页错误次数 (minor page faults),虚拟内存地址映射成物理内存地址产生的 page fault 次数
- Majflt/s : 每秒主缺页错误次数 (major page faults), 虚拟内存地址映射成物理内存地址时,相应 page 在 swap 中
- VSZ virtual memory usage : 该进程使用的虚拟内存 KB 单位
- RSS : 该进程使用的物理内存 KB 单位
- %MEM : 内存使用率
- Command : 该进程的命令 task name
步骤6:查看网络饱和度
系统活动报告工具sar(1)可以观测当前活动并且能配置为保存和报告历史统计数据。
Linux版本用以下选项提供网络统计信息。
- -n DEV:网络接口统计信息。
- -n EDEV:网络接口错误。
- -n IP:IP 数据报统计信息。
- -n EIP:IP 错误统计信息。
- -n TCP:TCP 统计信息。
- -n ETCP:TCP 错误统计信息。
- -n SOCK:套接字使用。
sar -n DEV 1
将不同的指标进行组合,sar(1)工具具有不同的运行模式。在这个例子中,我们使用它来看网络设备指标。通过接口吞吐量信息rxkB/s和txkB/s来检查是否有指标达到了上限。
sar -n TCP,ETCP 1
现在我们使用sar(1)工具来查看TCP指标和TCP错误信息,相关的字段包括如下几个:
- active/s:每秒本地发起的TCP连接的数量(通过调用connect()创建)
- passive/s:每秒远端发起的TCP连接的数量(通过调用accept()创建)
- retrans/s:每秒TCP重传的数量
主动和被动连接计数对业务负载画像很有用。重传则是网络或者远端主机有问题的征兆。
netstat
基于使用的选项,netstat(8)命令能报告多种类型的网络统计数据,就像具有多种功能的组合工具。
选项介绍如下:
- (默认):列出连接的套接字。
- -a:列出所有套接字的信息。
- -s:网络栈统计信息。
- -i:网络接口信息。
- -r:列出路由表。
[root@VM-12-4-centos ~]# netstat -i
数据列包括网络接口(Iface)、MTU,以及一系列接收(RX-)和传输(TX-)的指标。
- OK:成功传输的数据包。
- ERR:错误数据包。
- DRP:丢包。
- OVR:超限。
丢包和超限是网络接口饱和的指针。-c 连续模式能与-i 一并使用,每秒输出这些累积的计数器。它提供计算数据包速率的数据。
步骤7:跟踪进程
strace
strace常用来跟踪进程执行时的系统调用和所接收的信号。
在Linux世界,进程不能直接访问硬件设备,当进程需要访问硬件设备(比如读取磁盘文件,接收网络数据等等)时,必须由用户态模式切换至内核态模式,通过系统调用访问硬件设备。
strace可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间。
-f表示跟踪子进程和子线程,-T表示显示系统调用的时长,-tt表示显示跟踪时间。
$ strace -f -T -tt -p pid
perf
perf利用Linux的trace特性,可以用于实时跟踪,统计event计数(perf stat);或者使用采样(perf record),报告(perf report|script|annotate)的使用方式进行诊断。
$ perf top -g -p pid
按方向键切换到 php-fpm,再按下回车键展开 php-fpm 的调用关系,你会发现,调用关系最终到了BF_crypt。接下来看下哪个地方调用了这个库,把对应的代码改下就可以了。
参考资料:
《性能之巅 洞悉系统,企业与云计算》
《BPF性能之巅》
《UNIX/Linux 系统管理技术手册》