背景

公司的kubernetes集群服务器经常出现CPU持续升高的问题,以下是针对Linux服务器排查进程CPU使用率高的问题,进一步定位是哪个容器的CPU占用比较高

一.CPU使用率的构成

Linux系统的CPU使用率包含多个维度。

# 使用top命令查看CPU详细信息
%Cpu(s):  1.2 us,  1.5 sy,  0.0 ni, 95.5 id,  0.0 wa,  0.4 hi,  1.4 si,  0.0 st

各字段含义:

  • us (user): 用户空间进程消耗的CPU时间

  • sy (system): 内核空间消耗的CPU时间

  • ni (nice): 改变过优先级的进程消耗的CPU时间

  •  id (idle): 空闲CPU时间

  • wa (iowait): 等待I/O完成的CPU时间

  • hi (hardware interrupt): 硬件中断消耗的CPU时间

  • si (software interrupt): 软件中断消耗的CPU时间

  • st (steal): 虚拟机被hypervisor偷走的CPU时间

其中需要重点关注的CPU使用率的值

  • 用户态CPU高(us高):通常是应用程序代码执行导致,如死循环、复杂计算、正则匹配等

  • 内核态CPU高(sy高):可能是系统调用过多、网络包处理、进程创建销毁频繁等

  •  I/O等待高(wa高):磁盘I/O性能瓶颈,CPU在等待磁盘响应

  • 软中断高(si高):通常与网络流量大有关,如DDoS攻击、数据传输密集等

CPU使用率100%并不代表CPU真正在"工作",wa高的情况下CPU实际是在等待,优化方向完全不同。

二.定位CPU使用率高的思路

1.定位进程

使用top命令快速定位
# 启动top并按CPU使用率排序
top -c

# 关键快捷键:
# P: 按CPU使用率排序
# M: 按内存使用率排序
# c: 显示完整命令行
# 1: 显示每个CPU核心的使用情况

截图如下

重点关注的信息:

进程PID和命令:记录占用CPU最高的进程ID和完整命令

CPU%:单个进程占用的CPU百分比(注意多核服务器可能超过100%)

TIME+:进程累计使用的CPU时间

S列(状态):R(运行中)或D(不可中断睡眠,通常在等I/O)

使用htop(如果已安装)
htop
# 更直观的界面,彩色显示,支持鼠标操作
# F5: 树状显示进程关系
# F6: 选择排序字段
# F9: 杀死进程

使用ps命令快速抓取
# 获取CPU占用TOP10的进程
ps aux | head -1; ps aux | sort -rn -k3 | head -10


# 查看某个用户的所有进程
ps -u test -o pid,ppid,%cpu,%mem,cmd --sort=-%cpu | head -20

检测的shell脚本参考

#!/bin/bash
# cpu_quick_check.sh - 快速CPU诊断脚本

echo"========== CPU整体状态 =========="
uptime
echo""

echo"========== CPU详细使用率 =========="
top -bn1 | head -20
echo""

echo"========== CPU占用TOP10进程 =========="
ps aux --sort=-%cpu | head -11
echo""

echo"========== 当前CPU核心数 =========="
grep -c processor /proc/cpuinfo
echo""

echo"========== 负载均衡情况 =========="
mpstat -P ALL 1 1

准定位到具体线程和代码

查看进程内的线程CPU占用
# 方法1: 使用top查看线程
top -H -p <PID>
# -H: 显示线程模式
# -p: 指定进程ID

# 方法2: 使用ps查看线程
ps -Lp <PID> -o pid,lwp,ppid,pcpu,comm --sort=-pcpu | head -20
# -L: 显示线程
# lwp: 线程ID


#记录CPU占用最高的线程ID(LWP),并转换为16进制(因为堆栈信息中线程ID是16进制)
# 假设线程ID为12345
printf"0x%x\n" 12345
# 输出: 0x3039
Java应用定位到具体代码
# 1. 找到高CPU线程ID
top -H -p <Java进程PID>
# 假设找到线程ID 12345

# 2. 转换为16进制
printf"0x%x\n" 12345  # 得到 0x3039

# 3. 导出Java线程堆栈
jstack <Java进程PID> > /tmp/jstack.log

# 4. 在堆栈文件中搜索该线程
grep -A 20 "0x3039" /tmp/jstack.log

# 一键脚本
show_java_high_cpu_thread() {
local pid=$1
local thread_id=$(top -bn1 -H -p $pid | grep -v PID | sort -rn -k9 | head -1 | awk '{print $1}')
local hex_tid=$(printf"0x%x"$thread_id)
echo"High CPU Thread ID: $thread_id ($hex_tid)"
    jstack $pid | grep -A 30 "$hex_tid"
}

# 使用: show_java_high_cpu_thread <PID>

Python应用定位

# 使用py-spy(需提前安装: pip install py-spy)
py-spy top --pid <PID>

# 查看火焰图
py-spy record -o profile.svg --pid <PID> --duration 30

# 使用gdb(如果py-spy不可用)
gdb -p <PID>
(gdb) py-bt
(gdb) thread apply all py-list

Node.js应用

# 1. 发送SIGUSR1信号生成CPU profile
kill -USR1 <PID>

# 2. 使用内置profiler
node --prof app.js
# 停止后会生成isolate-xxx-v8.log文件
node --prof-process isolate-xxx-v8.log > processed.txt

# 3. 使用clinic.js(更友好的工具)
npm install -g clinic
clinic doctor -- node app.js
Go应用定位
# 如果应用开启了pprof(通常在代码中引入了net/http/pprof)
# 查看CPU profile
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30

# 进入交互式界面后:
(pprof) top 10  # 查看CPU占用前10的函数
(pprof) list <function_name>  # 查看具体函数代码
(pprof) web  # 生成可视化图表(需安装graphviz)
C/C++应用定位
# 使用perf工具(最强大的Linux性能分析工具)
# 1. 记录30秒的CPU采样
perf record -p <PID> -g -- sleep 30

# 2. 查看报告
perf report

# 3. 生成火焰图(需要FlameGraph工具)
perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg

有些情况下,CPU高占用并非应用代码问题,而是系统级资源瓶颈导致

检查I/O等待,
  • 如果%wa (iowait)高(>20%),且%util接近100%,说明是磁盘I/O瓶颈

  • • 优化方向:检查是否有大量日志写入、数据库查询未命中索引、大文件操作等

# 查看I/O等待时间
iostat -x 1 5

# 关键指标:
# %util: 接近100%说明磁盘已饱和
# await: 平均等待时间,SSD应<5ms,机械盘应<20ms
# r/s, w/s: 每秒读写次数

# 查看哪些进程在等待I/O
pidstat -d 1 5

# 查看具体文件操作
iotop -o  # 只显示有I/O操作的进程
检查网络软中断
  • 如果%si (software interrupt)高(>10%),通常是网络流量大

  • • 常见原因:DDoS攻击、大量短连接、网络包处理不合理

# 查看软中断统计
cat /proc/softirqs

# 持续观察软中断变化
watch -d "cat /proc/softirqs | head -20"

# 查看网络流量
iftop -i eth0
# 或者
nload eth0

# 查看网络连接数
netstat -antp | wc -l
ss -s  # 显示socket统计
检查进程创建销毁频率
  • 如果%sy (system CPU)高(>30%),可能是系统调用过多

  • • 常见原因:CGI程序每次都fork新进程、shell脚本大量调用外部命令

# 查看进程创建速率
pidstat 1 5

# 查看系统调用统计
strace -c -p <PID>

# 查看fork/exec调用频率
perf stat -e 'syscalls:sys_enter_*' -a sleep 10 | grep -E "(fork|exec|clone)"
检查上下文切换
  • 上下文切换>100万次/秒,可能存在性能问题

  • • 非自愿切换过多,说明进程/线程数过多,CPU调度压力大

# 查看上下文切换频率
vmstat 1 5
# 关注 cs (context switch) 列

# 查看每个进程的上下文切换
pidstat -w 1 5
# cswch/s: 自愿上下文切换(主动让出CPU)
# nvcswch/s: 非自愿上下文切换(被强制切换)
检查内存压力
  • 如果内存不足导致频繁swap,CPU会消耗在内存页换入换出上

  • • MemAvailable < 10%总内存,且swap使用率高,需要关注

# 查看内存使用情况
free -h

# 查看内存详细信息
cat /proc/meminfo | grep -E "(MemTotal|MemFree|MemAvailable|SwapTotal|SwapFree)"

# 查看是否有OOM killer
dmesg | grep -i "out of memory"
grep -i "out of memory" /var/log/messages

# 查看内存分配
slabtop
综合诊断命令
# 使用dstat综合查看(需安装)
dstat -tcmndylsp --top-cpu --top-mem 5

# 使用sar(系统活动报告)
sar -u 1 10  # CPU使用率
sar -q 1 10  # 负载和运行队列
sar -w 1 10  # 上下文切换

# 一键生成诊断报告
(
echo"=== System Load ==="
uptime
echo""
echo"=== CPU Stats ==="
mpstat -P ALL 1 1
echo""
echo"=== Memory Stats ==="
free -h
echo""
echo"=== I/O Stats ==="
iostat -x 1 1
echo""
echo"=== Network Stats ==="
ss -s
echo""
echo"=== Top Processes ==="
ps aux --sort=-%cpu | head -20
) | tee cpu_diagnosis_$(date +%Y%m%d_%H%M%S).txt

更多推荐