CPU高负载排查

本文将介绍如何排查 Linux 系统中 CPU 负载过高的问题。当系统的 CPU 负载过高时,可能会导致系统响应变慢、进程无响应等问题,影响系统的稳定性和性能。本文将从系统监控、进程分析、资源限制等方面介绍如何定位和解决 CPU 负载过高的问题。通过本文的介绍,读者将了解如何使用常用的工具和技术来排查 CPU 负载过高的问题,提高系统的稳定性和性能。

初级方法

进入系统中查看所有进程占用资源的详细情况,可以使用kubectl exec命令进入pod中的容器,并使用top或ps命令查看进程信息。

以下是具体步骤:

  1. 使用kubectl exec命令进入pod中的容器,例如:

    kubectl exec -it <pod-name> -c <container-name> -- /bin/bash

    这个命令可以进入pod中的指定容器,并打开一个交互式的bash终端。

  2. 在容器中使用top或ps命令查看进程信息,例如:

    top

    或者

    ps aux

    这些命令可以显示容器中所有进程的详细信息,包括进程ID、CPU和内存使用情况、进程状态等。

  3. 如果需要查看某个进程的详细信息,可以使用ps命令结合grep命令来查找进程,例如:

    ps aux | grep <process-name>

    这个命令可以查找容器中指定名称的进程,并显示其详细信息。

通过以上步骤,可以进入pod中查看所有进程占用资源的详细情况,并找出可能的性能瓶颈和优化方案。注意,在进入pod中查看进程信息时,需要确保容器中已经安装了相应的工具和命令,例如top和ps命令。

从监控上看cpu占用很高95%+,但是使用上面的top命令和ps aux都没有找到占用CPU比率超过5%的进程

如果从监控上看到CPU占用很高,但是使用top命令和ps aux命令没有找到占用CPU比率超过5%的进程,可能有以下几种情况:

  1. CPU占用率高的进程已经结束:在top命令和ps aux命令执行期间,可能有一些CPU占用率高的进程已经结束了,导致无法在进程列表中找到。可以使用历史监控数据来查看CPU占用率高的时间段,以找到可能的进程。
  2. CPU占用率高的进程是短暂的:有些进程可能只是短暂地占用了CPU资源,例如一些周期性的任务或者瞬时的请求处理。这些进程可能无法在top命令和ps aux命令中被捕捉到。可以使用更高级的监控工具来捕捉这些短暂的进程,例如perf和strace。
  3. CPU占用率高的进程是内核进程:有些CPU占用率高的进程可能是内核进程,例如中断处理、IO操作等。这些进程可能无法在用户空间的进程列表中被捕捉到。可以使用更高级的监控工具来查看内核进程的CPU占用率,例如sar和vmstat。
  4. CPU占用率高的进程是被隐藏的:有些恶意软件或者攻击可能会隐藏自己的进程,以避免被发现。这些进程可能无法在top命令和ps aux命令中被捕捉到。可以使用更高级的安全工具来检测和清除这些恶意进程,例如rkhunter和chkrootkit。

可以使用以下工具进一步跟踪:

  1. perf命令:perf是一个Linux性能分析工具,可以用来分析CPU、内存、磁盘和网络等方面的性能问题。可以使用以下命令来查看CPU占用率高的进程:

    perf top

    这个命令可以显示CPU占用率高的进程和函数,以及相应的CPU时间和调用次数等信息。

  2. strace命令:strace是一个Linux系统调用跟踪工具,可以用来跟踪进程的系统调用和信号处理等操作。可以使用以下命令来跟踪CPU占用率高的进程:

    strace -p <pid>

    这个命令可以显示进程的系统调用和信号处理等操作,以帮助你找出可能的性能瓶颈和优化方案。

  3. sar命令:sar是一个Linux系统性能监控工具,可以用来收集和分析系统的CPU、内存、磁盘和网络等性能数据。可以使用以下命令来查看CPU占用率高的时间段:

    sar -u <start-time> <end-time>

    这个命令可以显示系统的CPU使用率和负载情况,以帮助你找出可能的性能瓶颈和优化方案。

  4. vmstat命令:vmstat是一个Linux虚拟内存统计工具,可以用来收集和分析系统的CPU、内存、磁盘和网络等性能数据。可以使用以下命令来查看CPU占用率高的时间段:

    vmstat <interval> <count>

    这个命令可以显示系统的CPU使用率、内存使用率和磁盘IO情况等,以帮助你找出可能的性能瓶颈和优化方案。

  5. htop命令:htop是一个交互式的进程监控工具,可以用来查看进程的CPU、内存和IO等使用情况。可以使用以下命令来查看CPU占用率高的进程:

    htop

    这个命令可以显示进程的CPU使用率、内存使用率和IO情况等,以帮助你找出可能的性能瓶颈和优化方案。

htop 命令中,time+ 列显示的是进程的累计 CPU 时间,包括用户态和内核态的 CPU 时间。当进程的累计 CPU 时间超过1小时时,htop 会将时间格式化为 1h08:30 的形式,其中 1h 表示1小时,08:30 表示剩余的分钟和秒数。

例如,如果一个进程的 time+ 列显示为 1h08:30,表示该进程的累计 CPU 时间为1小时8分30秒。如果一个进程的 time+ 列显示为 00:01:30,表示该进程的累计 CPU 时间为1分30秒。

需要注意的是,htop 中的 time+ 列显示的是累计 CPU 时间,并不是实际的运行时间。如果一个进程在某个时间段内没有占用 CPU,那么它的 time+ 列也不会增加。因此,time+ 列并不能完全反映进程的运行时间,只能作为一个参考指标。

磁盘和内存占用情况

在 Linux 系统中,可以使用以下命令来查看文件的磁盘占用情况:

  1. ls 命令:该命令可以列出文件的详细信息,包括文件大小和修改时间等。例如,使用以下命令可以列出当前目录下所有文件的详细信息:

    ls -l

    在输出结果中,第5列为文件大小,以字节为单位。

  2. du 命令:该命令可以查看文件或目录的磁盘占用情况。例如,使用以下命令可以查看当前目录下所有文件的磁盘占用情况:

    du -h

    在输出结果中,第1列为文件或目录的磁盘占用大小,以人类可读的格式显示(例如,KB、MB、GB 等)。

  3. df 命令:该命令可以查看文件系统的磁盘占用情况。例如,使用以下命令可以查看当前系统中所有文件系统的磁盘占用情况:

    df -h

    在输出结果中,第3列为文件系统的总容量,第4列为已使用的容量,第5列为可用的容量,第6列为使用率。

需要注意的是,以上命令都可以通过参数来指定要查看的文件或目录。例如,使用 ls -l /path/to/file 命令可以查看指定文件的详细信息,使用 du -h /path/to/directory 命令可以查看指定目录的磁盘占用情况。

你可以使用 du 命令来按目录维度展示内存占用情况。du 命令可以查看文件或目录的磁盘占用情况,并支持按照目录层级展示结果。

以下是按目录维度展示内存占用的示例命令:

du -h --max-depth=1 /path/to/directory

在上面的命令中,-h 选项表示以人类可读的格式显示结果,--max-depth=1 选项表示只展示一级目录的结果,/path/to/directory 是要查看的目录路径。

如果要展示多级目录的结果,可以将 --max-depth 选项的值设置为相应的层级数。例如,要展示两级目录的结果,可以使用以下命令:

du -h --max-depth=2 /path/to/directory

需要注意的是,du 命令默认会递归地查看目录下所有子目录的磁盘占用情况,因此在查看大型目录时可能需要等待一段时间。如果只想查看目录本身的磁盘占用情况,可以使用 -s 选项。例如,使用以下命令可以查看 /path/to/directory 目录本身的磁盘占用情况:

du -h -s /path/to/directory

/sys/fs/cgroup 目录是 Linux 内核中的一个虚拟文件系统,用于管理 cgroup。cgroup 是一种 Linux 内核特性,用于限制和隔离进程的资源使用,包括 CPU、内存、磁盘等资源。

/sys/fs/cgroup 目录下,每个子目录都代表一个 cgroup,其中包含了该 cgroup 的资源限制和统计信息。例如,/sys/fs/cgroup/memory 目录下包含了内存限制和统计信息,/sys/fs/cgroup/cpu 目录下包含了 CPU 限制和统计信息,/sys/fs/cgroup/blkio 目录下包含了磁盘 I/O 限制和统计信息等。

因此,/sys/fs/cgroup 目录中的信息反映的是 cgroup 中的资源使用情况,包括内存、CPU、磁盘等资源。在上面的代码中,/sys/fs/cgroup/memory 目录用于获取容器的内存使用情况,包括内存限制、内存使用量和内存统计信息。

/sys/fs/cgroup 目录下的内存信息只能代表当前 cgroup 的内存使用情况,不能代表整个 pod 的内存使用量。一个 pod 可能包含多个容器,每个容器都有自己的 cgroup,因此需要分别获取每个容器的内存使用情况,才能得到整个 pod 的内存使用量。

在 Kubernetes 中,可以通过 kubectl top pod 命令来获取 pod 的 CPU 和内存使用情况。该命令会返回每个容器的 CPU 和内存使用量,以及整个 pod 的 CPU 和内存使用量。该命令的实现原理是通过调用 Kubernetes API 获取容器的 CPU 和内存使用情况,然后将其汇总计算得到整个 pod 的 CPU 和内存使用量。

因此,如果需要获取整个 pod 的内存使用量,建议使用 kubectl top pod 命令或者调用 Kubernetes API 来获取容器的内存使用情况,并将其汇总计算得到整个 pod 的内存使用量。

如果您从当前 pod 的控制台进入 /sys/fs/cgroup 目录,那么您所看到的 /sys/fs/cgroup/memory 目录下的内存信息将代表当前 pod 的内存使用量。这是因为在 pod 内部,每个容器都会有自己的 cgroup,并且 pod 的 cgroup 会包含所有容器的 cgroup。

因此,当您在当前 pod 的控制台中查看 /sys/fs/cgroup/memory 目录下的内存信息时,您将看到当前 pod 中所有容器的内存使用情况的汇总。这包括每个容器的内存限制、内存使用量和内存统计信息。

请注意,这仅适用于当前 pod 内部的视角。如果您想获取整个 pod 的内存使用量,您需要在 pod 所在的宿主机上查看相应的资源使用情况,或者使用 Kubernetes API 或相关工具来获取整个 pod 的内存使用量。

cgroup 是 Linux 内核中的一种特性,用于限制和隔离进程的资源使用,包括 CPU、内存、磁盘等资源。cgroup 可以将一组进程组织成一个 cgroup,然后对该 cgroup 中的进程进行资源限制和统计。

在 Linux 中,cgroup 通过虚拟文件系统来实现。cgroup 的虚拟文件系统通常被挂载在 /sys/fs/cgroup 目录下。该目录下的子目录代表不同的 cgroup 类型,例如 cpumemoryblkio 等。每个 cgroup 类型下又包含了多个 cgroup,每个 cgroup 代表一个 cgroup 实例。

/sys/fs/cgroup 目录下,每个 cgroup 实例都有自己的一组虚拟文件,用于限制和统计该 cgroup 中进程的资源使用情况。例如,在 memory cgroup 中,可以通过 memory.limit_in_bytes 文件来限制 cgroup 中进程的内存使用量,通过 memory.usage_in_bytes 文件来统计 cgroup 中进程的内存使用量。

/sys/fs 目录是 Linux 中的一个虚拟文件系统,用于提供文件系统相关的信息和接口。/sys/fs/cgroup 目录下的虚拟文件系统是 sysfs 文件系统的一个子目录,用于提供 cgroup 相关的信息和接口。因此,fs/sys/fs/cgroup 中代表 sysfs 文件系统。

在 Linux cgroup 中,当进程的内存使用率高时,所指的内存通常是虚拟内存,而不一定是系统实际的物理内存。

虚拟内存是一种抽象概念,它将进程的地址空间划分为多个虚拟页面,每个页面可以映射到物理内存、磁盘交换空间或其他设备。进程使用的内存可以超过系统实际可用的物理内存大小,因为虚拟内存允许将未使用的页面交换到磁盘上,以释放物理内存供其他进程使用。

在 cgroup 中,内存使用率通常是基于虚拟内存的统计。例如,memory.usage_in_bytes 文件中记录的是进程使用的虚拟内存大小,而不是实际占用的物理内存大小。因此,当 cgroup 中进程的内存使用率高时,表示进程正在使用较多的虚拟内存资源。

要了解系统实际的物理内存使用情况,需要综合考虑所有进程的虚拟内存使用情况、系统缓存、内核使用的内存等因素。可以使用工具如 freetophtop 等来查看系统的物理内存使用情况。

分析实例

htop工具分析出以下数据

op - 10:12:52 up 3 days,  5:29,  0 users,  load average: 12.97, 13.51, 13.64
Tasks:   8 total,   1 running,   7 sleeping,   0 stopped,   0 zombie
%Cpu0  :  39.9/60.1  100[|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||]     %Cpu1  :  37.7/62.3  100[|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||]
GiB Mem : 34.8/3.8      [                                                                 ]
GiB Swap:  0.0/0.0      [                                                                 ]

    PID USER      PR  NI    VIRT    RES  %CPU  %MEM     TIME+ S COMMAND                                                                                                                      
      1 root      20   0    0.8m   0.0m   0.0   0.0   0:03.46 S tini -- docker-entrypoint.sh                                                                                                 
      7 root      20   0    2.3m   1.7m   0.0   0.0   0:00.00 S  `- /bin/bash /bin/docker-entrypoint.sh                                                                                      
     83 root      20   0    1.6m   0.0m   0.0   0.0   0:03.60 S      `- tail -f /dev/null                                                                                                    
     82 root      20   0    1.6m   0.7m   0.0   0.0   0:01.28 S  `- crond                                                                                                                    
  99542 root      20   0  700.1m  16.3m   0.0   0.4   0:04.83 S  `- ./bin/zhiyan-log-docker-conf -config-file config/system/confd.toml                                                       
  99558 root      20   0  756.9m  72.8m   0.0   1.9   0:06.59 S  `- ./bin/zhiyan-log-docker-agent -c ./config/user/filebeat.yaml                                                             
 134993 root      20   0    2.5m   2.2m   0.0   0.1   0:00.00 S /bin/bash

​ 从top命令输出来看,CPU占用率非常高,,负载均衡也很高,也就是load average的值非常高,分别为12.97、13.51和13.64。load average是系统负载的一个指标,它表示在过去1分钟、5分钟和15分钟内,系统中平均有多少个进程处于就绪状态或者等待资源状态。一般来说,load average的值越高,表示系统的负载越大,CPU利用率也越高但是没有一个进程占用了大量的CPU资源。这种情况可能是由于多个进程共同占用了CPU资源,导致CPU占用率高但是没有一个进程占用大量CPU资源。

​ 正常的load average的值的区间取决于系统的硬件配置、负载情况和应用程序的特性等因素。一般来说,如果系统的load average值在CPU核心数的1-2倍之间,表示系统的负载比较正常。例如,如果系统有4个CPU核心,那么load average值在4-8之间是比较正常的。如果load average值超过了CPU核心数的2倍,表示系统的负载比较高,需要进一步分析和优化。

​ 另外,从%Cpu(s)的输出来看,CPU利用率也非常高,us和sy的值分别为38.1%和61.5%,表示CPU的大部分时间都在处理用户进程和系统进程。这也说明了为什么load average的值非常高。

详细分析进程

​ 在Linux系统中是如何区分区分系统进程和用户进程的呢?每个进程都有一个UID,用来标识进程所属的用户。系统进程通常是由root用户或其他系统用户启动的,它们的UID为0或其他系统用户的UID。这些进程通常是用来管理系统资源、执行系统任务、提供系统服务等,例如init进程、systemd进程、sshd进程等。用户进程通常是由普通用户启动的,它们的UID为普通用户的UID。这些进程通常是用来执行用户任务、运行应用程序等,例如浏览器进程、编辑器进程、编译器进程等。

可以使用ps命令或top命令来查看进程的UID和其他信息,例如:

ps aux | grep <进程名>

其中,<进程名>是要查找的进程名。执行该命令后,会输出进程的UID、PID、CPU占用率、内存占用率等信息。通过查看进程的UID,可以区分系统进程和用户进程。

我们看到python进程占有CPU相对高,那么进一步排查它,我们先在inux查看python进程占用端口的进程活跃情况,是否有请求进来

使用netstat命令来查看某个端口的进程活跃情况和是否有请求进来。下面是netstat命令的使用方法:

  1. 查看某个端口的进程活跃情况:

    netstat -tlnp | grep <port>

    其中,是要查看的端口号。执行该命令后,会输出所有监听该端口的进程信息,包括进程的PID、进程名、协议等。

  2. 查看某个端口的请求情况:

    netstat -anp | grep <port>

    其中,是要查看的端口号。执行该命令后,会输出所有与该端口建立的连接信息,包括本地IP地址、本地端口号、远程IP地址、远程端口号、连接状态等。

由于端口8000正在被Python进程占用,并且该进程正在监听该端口。这意味着该端口已经被占用,但是当前并没有任何连接建立。

如果你想查看该端口是否有请求进来,可以使用类似以下命令:

复制tcpdump -i any port 8000

该命令会在终端中输出所有与端口8000建立的连接信息,包括本地IP地址、本地端口号、远程IP地址、远程端口号、连接状态等。需要注意的是,tcpdump命令需要root权限才能执行。

[root@postpay-lifecyclemgr-intl-2255-0 /]# tcpdump -i any port 8000
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
19:58:01.665950 IP localhost.59584 > localhost.irdmi: Flags [S], seq 985772684, win 65535, options [mss 65495,sackOK,TS val 3005806978 ecr 0,nop,wscale 9], length 0
19:58:01.665969 IP localhost.irdmi > localhost.59584: Flags [S.], seq 2185908257, ack 985772685, win 65535, options [mss 65495,sackOK,TS val 3005806978 ecr 3005806978,nop,wscale 9], length 0
19:58:01.665985 IP localhost.59584 > localhost.irdmi: Flags [.], ack 1, win 128, options [nop,nop,TS val 3005806978 ecr 3005806978], length 0
19:58:01.666064 IP localhost.59584 > localhost.irdmi: Flags [P.], seq 1:83, ack 1, win 128, options [nop,nop,TS val 3005806978 ecr 3005806978], length 82
19:58:01.666072 IP localhost.irdmi > localhost.59584: Flags [.], ack 83, win 128, options [nop,nop,TS val 3005806978 ecr 3005806978], length 0
19:58:01.667408 IP localhost.irdmi > localhost.59584: Flags [P.], seq 1:175, ack 83, win 128, options [nop,nop,TS val 3005806980 ecr 3005806978], length 174
19:58:01.667418 IP localhost.59584 > localhost.irdmi: Flags [.], ack 175, win 131, options [nop,nop,TS val 3005806980 ecr 3005806980], length 0

据tcpdump命令输出来看,该端口正在接收来自本地IP地址为localhost、本地端口号为59584的TCP连接请求。该连接请求的目的地是本地IP地址为localhost、目标端口号为irdmi的TCP端口。同时,该连接请求的标志位为S,表示该连接请求是一个同步请求。

如果你想查看更多关于该连接请求的信息,可以使用类似以下命令:

tcpdump -i any port 8000 -nnvvS

该命令会在终端中输出更详细的关于连接请求的信息,包括源IP地址、源端口号、目的IP地址、目的端口号、标志位等。需要注意的是,该命令的输出比较详细,可能会比较难以理解。

tcpdump命令输出的结果可以看到端口的请求并不活跃,由此可得出CPU高的原因可能不是它

分析定时任务

在Linux系统中,可以使用crontab命令来管理定时任务。下面是一些常用的crontab命令:

  1. 查看当前用户的定时任务:

    crontab -l

    该命令会列出当前用户的所有定时任务。

  2. 编辑当前用户的定时任务:

    crontab -e

    该命令会打开当前用户的定时任务配置文件,你可以在该文件中添加、修改或删除定时任务。

  3. 删除当前用户的所有定时任务:

    crontab -r

    该命令会删除当前用户的所有定时任务。

需要注意的是,crontab命令只能管理当前用户的定时任务,如果你想查看其他用户的定时任务,需要使用root权限执行crontab命令。另外,定时任务的配置文件通常位于/var/spool/cron目录下,你可以使用类似以下命令来查看该目录下的所有定时任务文件:

ls -l /var/spool/cron

据定时任务配置,以下是每个任务的周期和频率:

  1. /1 * * * * 表示每分钟执行一次,即每分钟的第0秒执行 /usr/local/bin/cpu_usage_updater.py &>/dev/null 命令。
  2. /1 * * * * 表示每分钟执行一次,即每分钟的第0秒执行 chmod a+w /dev/shm/.pkgadmin.cron.lck 命令。
  3. */2 * * * * 表示每2分钟执行一次,即每2分钟的第0秒执行 bash /data/release/bill-assistant/console_health_check.sh 命令。
  4. * * * * * 表示每分钟执行一次,即每分钟的第0秒执行 chown -R user_00:users /data/release/postpay-lifecyclemgr-intl/chown -R user_00:users /data/log/postpay-lifecyclemgr-intl/ 命令。
  5. 0 0 * * * 表示每天的0点0分执行一次,即每天的0点0分执行 cat /dev/null > /data/log/php-7_2_1-swoole_4_5_1_qbilling-1.0/php_errors.log 命令。

需要注意的是,定时任务的周期和频率可以通过修改crontab配置文件来调整。如果你想修改定时任务的周期和频率,可以使用 crontab -e 命令编辑crontab配置文件,并按照crontab语法修改相应的定时任务配置。

要修改crontab并使其生效,你需要执行以下步骤:

  1. 使用 crontab -e 命令编辑当前用户的定时任务配置文件。
  2. 修改定时任务的周期和命令,并保存文件。
  3. 确认定时任务配置文件已经保存,并退出编辑器。
  4. 重启cron服务,使新的定时任务配置生效。你可以使用以下命令重启cron服务:

    sudo systemctl restart cron

    如果你的系统不是基于systemd的,可以使用以下命令重启cron服务:

    sudo service cron restart

    重启cron服务后,新的定时任务配置就会生效。

需要注意的是,修改定时任务配置文件后,新的定时任务不会立即生效,而是要等待cron服务下一次执行定时任务时才会生效。如果你想立即执行新的定时任务,可以手动执行一次定时任务,或者重启cron服务。另外,如果你修改了root用户的定时任务配置文件,需要使用root权限执行以上操作。

查看日志技巧

在Linux系统中,可以使用以下命令来查看日志文件:

  1. tail 命令:该命令可以实时查看日志文件的最新内容。例如,使用以下命令可以查看 /var/log/messages 文件的最后10行内容:

    复制tail /var/log/messages

    如果想实时查看日志文件的最新内容,可以使用 -f 选项。例如,使用以下命令可以实时查看 /var/log/messages 文件的最新内容:

    复制tail -f /var/log/messages
  2. less 命令:该命令可以分页查看日志文件的内容,并支持搜索。例如,使用以下命令可以查看 /var/log/messages 文件的内容:

    复制less /var/log/messages

    less 命令中,可以使用 / 命令来搜索关键字。例如,输入 /error 可以搜索包含 error 关键字的内容。

  3. grep 命令:该命令可以搜索指定文件中包含指定关键字的行。例如,使用以下命令可以搜索 /var/log/messages 文件中包含 error 关键字的行:

    复制grep "error" /var/log/messages

    如果想搜索多个文件,可以使用通配符 *。例如,使用以下命令可以搜索 /var/log 目录下所有以 messages 开头的文件中包含 error 关键字的行:

    复制grep "error" /var/log/messages*

需要注意的是,日志文件通常存储在 /var/log 目录下,不同的日志文件对应不同的应用程序或系统组件。在查看日志文件时,需要根据实际情况选择相应的日志文件,并使用合适的命令进行查看和搜索。