案例:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?

记录《Linux性能优化》课程实践过程。

环境准备

  • 两台虚拟机(Ubuntu 18.04)
  • 机器配置:2 CPU,8GB 内存
  • 预先安装 docker、sysstat、perf、ab 等工具,如 apt install docker.io sysstat linux-tools-common apache2-utils

操作和分析

其中一台用作 Web 服务器,来模拟性能问题;另一台用作 Web 服务器的客户端,来给 Web 服务增加压力请求。

打开两个终端,分别 SSH 登录到两台机器上,并安装上面提到的工具。

默认以 root 用户运行所有命令,先运行 sudo su root 命令切换到 root 用户。

由于 Nginx 和 PHP 的配置比较麻烦, 课程提供了两个Docker 镜像,这样只需要运行两个容器,就可以得到模拟环境。

运行docker环境

首先,在第一个终端执行下面的命令来运行 Nginx 和 PHP 应用:

//注意每个案例使用的镜像区别
$ docker run --name nginx -p 10000:80 -itd feisky/nginx:sp
$ docker run --name phpfpm -itd --network container:nginx feisky/php-fpm:sp

下载过程可能比较缓慢… 下载完成后,执行 docker ps -a ,如图:

案例:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?

第二个终端使用 curl 访问 [http://[VM1] 的 IP]:10000,确认 Nginx 已正常启动。

# 192.168.139.139是第一台虚拟机的IP地址
$ curl http://192.168.139.139:10000/

如图:

案例:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?

压力测试一

在第二个终端运行下面的 ab 命令:

# 并发100个请求测试Nginx性能,总共测试1000个请求
$ ab -c 100 -n 1000 http://192.168.139.139:10000/

如图:

案例:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?

从 ab 的输出结果我们可以看到,Nginx 能承受的每秒平均请求数,只有 63 多一点,性能相对较差。如果暂时感觉不到什么,那尝试用 toppidstat 再做观察。

压力测试二

继续在第二个终端,运行 ab 命令:

//并发数设置为 5,同时把请求时长设置为 10 分钟(-t 600)
$ ab -c 5 -t 600 http://192.168.139.139:10000/

观察 top 输出信息

回到第一个终端运行 top 命令,并按下数字 1 ,切换到每个 CPU 的使用率:

案例:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?

这里可以观察下,进程列表里CPU使用率最高的也只是2.6%,并不是很多。然而,再看 %Cpu 这一行,你会发现,系统的整体 CPU 使用率是比较高的:用户 CPU 使用率(us)已经到了 77.6%,系统 CPU 为 16.8%,而空闲 CPU (id)则只有 0.3%。

这里开始有点疑惑,为何用户 CPU 使用率会这么高呢?重新分析下进程列表:

  • containerd-shim 应该是跟docker容器相关的, containerd-shim相关文章
  • Nginx 和 php-fpm 是运行 Web 服务的,它们会占用一些 CPU 也不意外,并且 2% 左右的 CPU 使用率也不算高;
  • 后面的几个 0.3% 的进程,也不太像是导致用户CPU使用率骤然升高的原因。

也就是说,目前 top 命令显示的信息不足以解答我们遇到的问题。那尝试 pidstat 命令吧,它可以用来分析进程的 CPU 使用情况。

观察 pidstat 输出信息

保持压力测试的情况下,在第一个终端运行以下命令,观察输出信息:

# 间隔1秒输出一组数据(按Ctrl+C结束)
$ pidstat 1

如图:

案例:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?

可以随机挑选两组数据,基本上总和不会超过25%,远远没有达到77%。问题似乎卡在这里了,明明用户CPU使用率很高,但却找不到具体进程,下一步该怎么分析呢?也许是遗漏了某些关键信息。

压力测试三

重新压测,然后在第一个终端运行 top 命令,持续观察一段时间(很重要),下图只是某个瞬间的状态:

案例:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?

重新观察 top 输出信息

仅分析此图,Tasks 这一行中,就绪队列中有 8 个 Running 状态的进程(8 running)。 8 running 似乎跟初始设置的并发数 5 没有相差太多,不过图中显示php-fpm、nginx等进程大部分时间都处于 S(休眠)状态,处于 R 状态更多的是 stress 进程。

那接下来就分析下具体的 stress 进程吧,

// -p 指定具体的进程 PID
pidstat -p XXX

如图:

案例:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?

发现这几个进程实际已经消失了,不过,在压测过程继续观察top界面,你还是可以看到有新的 stress 进程处于 R 状态。

案例:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?

案例:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?

是不是可以做个猜想,要么是这些进程在不停地崩溃、重启,要么就是全新的进程,具体分析:

  • 第一种情况,进程在不停地崩溃重启,比如因为段错误、配置错误等等,这时,进程在退出后可能又被监控系统自动重启了。
  • 第二种情况,这些进程都是短时进程,也就是在其他应用内部通过 exec 调用的外面命令。这些命令一般都只运行很短的时间就会结束,你很难用 top 这种间隔时间比较长的工具发现(如压测二)。

其实 stress,它是一个常用的压力测试工具。它的 PID 在不断变化中,看起来像是被其他进程调用的短时进程。要想继续分析下去,还得找到它们的父进程。

pstree 分析进程调用关系

// pstree  可以用树状形式显示所有进程之间的关系:
$ pstree | grep stress

如图:

案例:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?

可以看到,stress 是被 php-fpm 调用的子进程,而且很多时候不止一个(5* 代表5个),那么还是查看下 php-fpm 的应用源码吧。

# 拷贝源码到本地
$ docker cp phpfpm:/app .

# grep 查找看看是不是有代码在调用stress命令
$ grep stress -r app

如图:

案例:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?

发现 app/index.php 确实调用到了 stress 命令,再查看具体代码。如图:

案例:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?

根据代码注释,stress 会通过 write() 和 unlink() 对 I/O 进程进行压测,也就是说会对系统I/O造成压力,但回过头看之前 top 输出的数据,并没见到 iowait (wa)升高,反而是用户 CPU(us) 和系统 CPU (sy)升高。

检查输出日志信息

直觉来说,我会认为它根本没有走到最终调用 I/O 进程的地方。根据代码,传入参数 verbose=1 查看具体输出信息吧,如下:

curl http://192.168.139.139:10000?verbose=1

案例:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?

可以看到,提示 Permission denied 没有权限创建临时文件,同时提示 failed run 退出了。

至此,可以猜想一下,由于权限错误,大量的 stress 进程在启动时初始化失败,进而导致用户 CPU 使用率的升高。

perf 分析CPU性能事件

那么,怎么验证是否有大量的 stress 进程呢?既然 top、pidstat 工具无法解决问题,尝试 perf 工具吧,它可以用来分析 CPU 性能事件。如下:

# 记录性能事件,等待大约15秒后按 Ctrl+C 退出
$ perf record -g

# 查看报告
$ perf report

保持压力测试,在终端一运行命令,如图:

案例:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?

案例:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?

确实,stress 占了所有 CPU 时钟事件的 67%。你还可以通过 enter 键展开 + 号位置,查看 stress 的调用栈详情,如下:

案例:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?

找到问题的根源,随后的优化就很简单了,只要修复权限问题,并减少或删除 stress 的调用,就可以减轻系统的 CPU 压力。

结语

案例代码设计相对简单,工作中可能遇到更加复杂的问题。本文图例比较多,如果有不明白的,也可以在评论区留言。

同时,我会把更多实践案例归纳在 Linux 性能优化笔记 文章底部,欢迎阅读。:smile:

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 4年前 自动加精
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 2
mouyong

好棒的文章,谢谢 :)

4年前 评论
chenlixin (楼主) 4年前

分析过程很棒 不过 代码为什么用stress ?

4年前 评论
chenlixin (楼主) 4年前
swing07 (作者) 4年前
swing07 (作者) 4年前
chenlixin (楼主) 4年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!