服务器负载突然飙升,SSH连接卡顿,业务响应迟缓,这是运维人员的噩梦。面对这种情况,手忙脚乱地重启服务是最低级的手段。真正的高手会有一套固定的排查命令组合,像外科手术般精准定位病灶。下面直接给出这套组合拳,从全局到细节,一步步揪出真凶。
第一板斧:top 看全局登录服务器的第一件事,就是敲下 top 命令。这个命令会给你一个系统资源的全景视图。重点看第一行的 load average:0.15, 0.20, 0.18。这三个数值分别代表过去1分钟、5分钟、15分钟的系统平均负载。如果你的CPU是4核,那么负载数值超过4.0就意味着有进程在排队等待CPU资源。如果1分钟的值远大于15分钟的值,说明负载正在急剧上升。接着看第三行,CPU状态。us(用户态)高,说明是应用程序在消耗CPU;sy(内核态)高,通常是大量的系统调用或者I/O操作;wa(I/O等待)高,这是最危险的信号,意味着CPU在干等磁盘读写,通常是内存不足导致大量使用swap,或者磁盘性能达到瓶颈。按下数字键“1”,可以展开查看每个CPU核心的负载情况,看看是不是某个核心被打满了。
top 只能看到动态排名,要精准定位,需要配合 ps 命令。直接执行以下组合命令,按CPU使用率降序排列,找出最消耗CPU的进程:
ps aux --sort=-%cpu | head -n 10
这条命令会列出CPU占用前十的进程。拿到PID后,深入查看该进程的详细信息:
ps -o pid,ppid,user,%cpu,%mem,stat,start,time,cmd -p [PID]
这里要特别关注 STAT 字段。常见的状态有:R(正在运行)、S(睡眠)、D(不可中断睡眠,通常是等待I/O)。如果你发现大量进程处于 D 状态,那么问题几乎可以断定出在磁盘I/O或者网络文件系统上。如果进程状态是 Z(僵尸进程),虽然不直接消耗资源,但过多会耗尽进程表。找到可疑进程后,可以用 lsof -p [PID] 查看它打开了哪些文件,或者用 strace -p [PID] 跟踪它的系统调用,看它卡在哪里。但注意,strace 在线上环境要慎用,它会对进程性能产生较大影响。
如果 top 中 wa 值很高,或者 ps 看到很多 D 状态进程,就要动用 vmstat 了。执行 vmstat 1 10,每秒输出一次,共10次。核心关注这几列:si 和 so(swap in/out),如果这两个数值持续不为0,说明物理内存严重不足,系统在频繁地进行内存和swap分区的交换,这是性能杀手。bi 和 bo(block in/out),代表每秒从块设备读入和写出的块数量,数值突增说明磁盘I/O压力大。再结合 iostat -x 1 查看具体是哪块磁盘的利用率接近100%,以及 await(平均等待时间)是否过长。很多时候,负载飙升的根源不是CPU,而是磁盘。比如数据库的慢查询导致大量随机读,或者日志文件疯狂写入撑爆了磁盘带宽。
CentOS服务器多半是提供网络服务的,网络连接数爆炸同样会导致负载飙升。执行 netstat -antp | awk '{print $6}' | sort | uniq -c | sort -rn 可以统计各种连接状态的数量。如果你发现大量的 TIME_WAIT 状态连接,说明服务器在处理大量短连接,需要优化内核参数 tcp_tw_reuse 和 tcp_tw_recycle(注意4.x内核后recycle已移除)。如果看到大量的 SYN_RECV 状态,很可能遭遇了SYN Flood攻击,需要检查防火墙规则。更严重的是 ESTABLISHED 连接数过多,把文件句柄占满了。通过 ulimit -n 查看当前用户的文件句柄限制,再用 lsof | wc -l 统计当前打开的文件总数。如果接近上限,就会报“Too many open files”错误,导致服务无法接受新连接。临时放宽限制用 ulimit -n 65535,永久修改需调整 /etc/security/limits.conf。
有时候,应用层面看不出任何问题,负载却居高不下,这时候要怀疑硬件或者内核驱动了。执行 dmesg -T | tail -n 50,查看最近的系统日志。如果看到类似“Out of memory: Kill process”的字样,说明触发了OOM Killer,某个进程因为内存泄漏被内核杀掉了。如果看到大量的“I/O error”或者磁盘重试信息,说明硬件可能即将损坏,RAID卡在尝试修复,这会严重拖慢I/O。还有一种情况是软件导致的软死锁,dmesg会输出“BUG: soft lockup - CPU#n stuck for xx s”,这通常是由于内核bug或者驱动程序问题导致某个CPU核心长时间被占用且无法调度。
负载高不一定是内部原因,也可能是外部流量异常。用 iftop -i eth0 -P 可以实时看到网络带宽的使用情况,以及是哪些IP和端口之间的流量最大。如果发现某个IP占用了巨大带宽,可以用 iptables 临时封禁,或者用 tcpdump -i eth0 host [IP] -w dump.pcap 抓包分析。有时候,CDN回源策略错误、爬虫疯狂抓取、或者某个文件被大量下载,都会瞬间打满带宽,导致服务响应变慢,进而拖高负载。
排查过程中,别忘了两个容易被忽略的角落。用 history 命令看看最近执行了什么操作,是不是有人误跑了资源密集型的脚本。再检查 crontab,执行 for user in $(cut -f1 -d: /etc/passwd); do crontab -u $user -l 2>/dev/null; done 列出所有用户的定时任务。负载飙升如果具有规律性,比如每隔整点或每天的某个固定时间,几乎可以肯定是定时任务触发的。比如日志切割脚本写得有问题,切割后没有关闭文件句柄;或者备份脚本在业务高峰期执行,占用了大量I/O资源。
排查出原因后,不要急着杀进程。如果确认是某个进程异常,但它是关键服务,不能直接 kill -9。优先用 renice -n 19 -p [PID] 降低它的优先级,把CPU资源让给其他进程。如果是内存泄漏,可以尝试 systemctl restart [service] 重启服务。如果系统已经卡到无法执行命令,可以尝试使用 SysRq 组合键进行底层控制。执行 echo 1 > /proc/sys/kernel/sysrq 开启SysRq功能,然后按顺序使用 Alt+SysRq+f 调用OOM Killer,或者 Alt+SysRq+b 强制重启(这是最后的手段)。在重启之前,务必用 sync 命令把内存数据刷到磁盘,尽量减少数据丢失。
一次成功的排查,抵不过日常的监控。建议安装并配置 sar 工具,它会自动收集系统的CPU、内存、I/O、网络等历史数据。当负载飙升时,通过 sar -q -f /var/log/sa/sa[日期] 查看过去的负载队列,sar -r 看内存使用,sar -b 看I/O情况。有了历史基线,你才能知道当前的负载是否异常,以及异常是从哪个时间点开始的。配合 pidstat 1 可以实时查看每个进程的CPU使用详情,比 top 更适合记录和回溯。把这些命令组合成脚本,接入告警系统,当负载超过阈值时自动执行并保存现场,这才是专业运维的闭环操作。
