ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Linux系统故障排查实战:负载、内存、磁盘与日志命令组合技巧

Linux系统故障排查实战:负载、内存、磁盘与日志命令组合技巧 Linux系统常用命令写到第十五篇前面十四篇把文件操作、文本处理、权限管理这些基础知识都过了一遍。这篇不再讲单个命令的孤立用法而是把它们串起来讲一个每天都在发生的真实场景线上服务突然变慢、日志刷得飞快、磁盘悄悄被写满的时候你手里这些命令该怎么组合着用才能最快把问题揪出来。适合正在系统学习Linux、或者刚接手服务器维护的朋友内容全部来自我实际操作过的经验没有什么纸上谈兵的东西。1. 先给系统把脉负载与进程状态的速查命令遇到系统变慢的第一反应不是去翻代码而是先用最快的命令拿到全局状态。我自己的习惯是三条命令连续敲uptime、top、vmstat。这三条下来CPU、内存、进程、系统瓶颈基本能看出个大概再往下才是针对性的深挖。1.1 读懂uptime那一串数字uptime的输出里有一项load average0.30, 0.12, 0.08。很多新手盯着这三个数不知道什么意思。简单说它表示过去1分钟、5分钟、15分钟内处于可运行状态和不可中断睡眠状态的进程平均数。不是说负载多高就一定有问题关键在于对比CPU核数8核的机器负载到4完全正常2核的机器负载到4基本就在打摆子了。我判断负载有个笨办法看趋势而不是看单个值。如果1分钟负载明显高于15分钟说明系统刚刚开始变忙这是突发流量还是异常进程需要马上用top确认如果三个数都高说明已经持续忙了很久要考虑扩容或者代码层面的瓶颈了。1.2 top的交互操作和几个隐藏参数top是必会的命令但大多数人只知道进去看一眼就退出来太可惜了。按1可以展开每个CPU核的使用情况你会发现某个核100%而其他核空闲这种单线程瓶颈在排查Java应用时特别常见。按P按CPU排序、按M按内存排序这两个是最常用的。我建议再记住一个按键c可以显示进程的完整命令行很多进程光看名字根本不知道它是干嘛的看到完整路径才好判断是不是被入侵了。top还有个缺陷它是瞬时快照如果你不在现场盯着飙高几秒的CPU根本捕捉不到。所以我的习惯是叠加一个固定周期采样命令比如用top -b -n 5 -d 2把5次采样结果输出到文件里事后慢慢看。如果机器上装了pidstat我更推荐pidstat -p ALL 2 5它把每个进程的CPU和内存变化记录得清清楚楚拿来定位瞬时尖峰比top好用得多。1.3 用ps锁定可疑进程当top显示某个进程CPU占用离谱下一步就是用ps确认它的详细信息。常用的组合是ps -eo pid,ppid,user,%cpu,%mem,cmd --sort-%cpu按CPU占用降序排列。注意加--sort-%cpu不然默认是PID排序一眼扫过去根本看不出谁是大户。定位到具体进程后我不急着kill而是先看它的父进程ppid。一个可疑进程往往是另一个程序的子进程顺着ppid往上查能发现是不是某个守护进程自己拉起来的还是被外部注入的。曾经遇到一台机器CPU一直50%上下ps查到一个名字很正常的进程顺着ppid发现它是我前一天启动的一个临时脚本的子进程脚本忘了退出这从根上解决了问题。2. 内存与Swap别被free的输出骗了内存问题比CPU问题隐蔽因为Linux的内存策略和Windows不太一样你看到的used很多其实是缓存并不是真的被业务占用。很多刚接手服务器的人一上来看到used 30G吓得赶紧找地方加内存其实完全没必要。2.1 free -h 的关键是availablefree -h的输出里有两列特别容易被误读buff/cache和available。buff/cache是Linux把读过的文件、写过的数据留在内存里做缓存目的是下次访问更快。这部分内存在业务真正需要时是可以释放掉的所以真正需要关心的是available它表示在不触发swap的情况下还能给新程序分配多少内存。判断内存是否紧张我会看两个数available是不是低于系统总内存的10%以及swap是不是在持续增长。如果swap已经用了几GB还在涨基本说明内存是真的不够了这时候不要犹豫该优化程序内存占用就优化该加内存就加。2.2 排查OOMdmesg是关键内存耗尽时Linux的OOM Killer会杀掉最肥的进程有时候它专挑重要的业务进程下手服务莫名挂掉往往就是这个原因。排查OOM我喜欢用dmesg -T | grep -i -E out of memory|killed process如果看到内核把某个进程杀了后面会跟着一段进程的内存占用明细。这里有个容易被忽略的细节OOM Killer不会单独出现一次就结束它往往是内存持续紧张、反复触发。所以我在日志里搜到第一条OOM记录后会再往前翻几十行看看杀进程之前是不是有什么进程内存快速增长。以前某台机器跑着一个缓存服务每次业务高峰就报OOM查来查去发现是缓存过期时间设得太长堆内存直接顶到上限调整参数之后问题再不出现。2.3 内存泄漏的初步验证如果怀疑某个进程内存泄漏不要凭感觉判断。用ps -eo pid,rss,cmd然后隔几个小时再看一次对比同一个PID的RSS有没有持续上涨。也可以用pidstat -r 5 100它会每秒输出一次每个进程的内存变化坚持看到100次基本能画出内存走势。实际处理时你会发现内存泄漏最恶心的一点是它涨到一定程度才触发OOM而在这之前系统看起来一切正常。我的经验是给关键进程的启动脚本里加上内存监控的告警比如RSS超过阈值就发通知别等OOM杀进程了才被动处理。作为Linux排查手段这比加内存更治本。3. 磁盘空间与inode写满的N种姿势磁盘问题看似简单实际上最容易因为误判而浪费时间。我见过太多人看到服务器报no space left on device第一反应就是df -h然后发现磁盘明明还有几十GB一头雾水接着重启服务结果问题照旧。原因很简单磁盘满有两种满法一种是空间满另一种是inode满。3.1 空间满 vs inode满的判断方法df -h看到的是块设备的使用量df -i看到的是文件节点使用量。所有文件都要占用一个inode如果你存放了大量小文件代码仓库、日志切割、临时文件就算总占用只有几百MBinode也可能先耗尽。判断办法就一条df -h显示空间充足但写文件仍然报错立刻敲df -i。inode满的常见战场在高频写入日志的目录、队列软件的持久化目录、还有Docker的overlay2文件系统目录。清理思路是先用df -i确认哪个挂载点满了然后进到对应目录一层层用ls -lU | wc -l统计文件数量找到堆积最猛的那个子目录。曾经处理过一台机器的根分区inode满了排查下来是一个软件的崩溃日志每次启动生成上万个几十字节的小文件积累几个月直接顶爆inode清理完再把日志量上限调整好才算彻底解决。3.2 找出谁在占用空间du的合理用法du -sh *在目录里看单个文件夹占用是基本功但目录层级深的时候逐层跑很浪费时间。我推荐先跑du -h --max-depth2 2/dev/null | sort -rh | head -20一次性列出两层以内的大块头。注意加上2/dev/null要不然各种没有权限的目录会刷屏。还有一种更隐蔽的情况文件被删了但空间没有释放。这是因为有进程还握着这个文件的句柄你在磁盘上看不到它它却实实在在占着空间。判断方法是lsof L1它会把所有被删除但仍被打开的文件列出来看到哪个文件大就顺着查是哪个进程打开的处理掉进程或清空文件都会释放空间。3.3 磁盘性能要看iostat而不是盯着dfdf只能看容量看不了速度。系统变慢也可能是因为磁盘I/O成为瓶颈这时候用iostat -x 1它会每秒刷新一次磁盘的详细性能指标。%util接近100%并不一定代表磁盘出问题这点很多人搞反了如果请求队列很深、await很高而svctm很低那多半是磁盘达到饱和反过来%util高但await不高可能是磁盘在正常服务大量并发请求还没到物理极限。定位到磁盘忙下一步是找出谁在读写。iostat显示的是磁盘维度的统计想看进程维度的I/O可以用iotop它会像top一样实时列出每个进程的读写速度。需要root权限这也是排查物理机问题时特别高效的手段先iotop看到怪物进程再顺着进程号用lsof -p PID看它打开了哪些文件基本就能锁定是哪个业务在骚扰磁盘了。4. 日志分析实战journalctl和logrotate的组合拳日志是我们还原故障现场最重要的材料。systemd的日志系统和传统/text日志不太一样它有结构化字段时间、进程号、服务名都能直接过滤。很多人只会用tail -f看实时日志遇到要从10分钟前开始查就慌了其实journalctl就是为这个设计的。4.1 journalctl的常用过滤姿势先记最常用的几个参数组合journalctl -u nginx.service只看某个服务自己的日志。journalctl --since 10 minutes ago只看最近10分钟的。journalctl -p err -b只看本次开机以来的错误级别日志-p后面可选debug、info、notice、warning、err、crit、alert、emerg。journalctl -f等同于tail -f实时跟踪最新日志。真正排查问题时我会把它们叠加起来比如某个服务在凌晨3点挂过直接journalctl -u 服务名 --since 03:00 --until 03:30把这个半小时内该服务的所有日志捞出来比先切换到/var/log目录翻文件效率高一个量级。有个小细节journal日志是二进制的直接cat会得到乱码。但是对运维来说它是宝库因为它把服务名、PID、时间戳全部结构化了想查某个具体参数直接-o json-pretty输出JSON格式脚本解析非常方便。我刚用journalctl的时候也在终端里迷茫过后来习惯它以后甚至大多数场景都不再手动翻/var/log/messages了。4.2 logrotate配置解读与常见踩坑日志如果不轮转早晚把磁盘塞满所以logrotate是每个服务器都必须配置的。默认的配置路径在/etc/logrotate.d/里面每个应用一个文件。一个典型配置长这样/var/log/myapp/*.log { daily rotate 7 compress delaycompress missingok notifempty copytruncate }逐行解释一下daily表示每天轮转一次rotate 7表示保留7份旧日志超过就删除最早的compress表示把轮转下来的日志压缩成.gzdelaycompress比较微妙它表示下一次轮转时才压缩前一次的日志目的是给还在写日志的进程一个缓冲避免日志文件被压缩导致句柄混乱missingok是文件不存在时别报错notifempty是空日志不需要轮转copytruncate会先复制当前日志再清空原文件适合那些不支持重新打开日志文件的程序。我踩过一个logrotate的坑某应用自己也在做日志分割两套机制撞在一起结果日志文件被复制来复制去磁盘空间反而暴涨。后来把应用自身的分割关掉只保留logrotate的规则问题立刻消失。所以配置任何日志轮转前先搞清楚你的应用是不是已经自带了这个功能别让两套机制打架。4.3 用日志定位周期性故障的实际案例有一次线上的一个业务进程每天下午4点准时报错一看就是定时任务触发的但查了一圈排错的代码没头绪。后来我用journalctl -u 该业务服务 --since 16:00 --until 16:01把每天下午4点前后的日志全导出来对比发现报错前总有几十秒的延迟而延迟时间正好和这个时段的大批量数据归档任务重叠。磁盘I/O被归档任务吃满业务进程等文件写入等不到最后超时。后续把归档任务错峰之后报错再也没出现过。日志的价值就在这里它不是万能的但它能提供准确的时间和上下文帮你把何时发生和什么在发生直观地拼在一起。不要小看这个信息维度它经常能缩短排查时间的一半以上。5. 网络连接与端口ss命令的一手资料网络排查以前大家习惯用netstat现在大部分发行版上推荐用ss它比netstat快得多输出信息也更丰富。端口占用、连接状态统计、异常连接定位用ss基本一套搞定。5.1 端口占用排查ss -tulnp服务启动失败提示端口被占用优先跑ss -tulnp。参数解释-t表示只看TCP、-u表示UDP、-l表示监听状态、-n不解析服务名、-p显示占用进程的PID。输出结果里Local Address列对应监听地址和端口Process列能看到进程名字。只看某个特定端口谁占用了可以加一个过滤器ss -lptn sport :8080如果查出来不是你的预期进程进程名模糊时再补一句ps -fp PID就能看到这个进程的完整启动命令。我有一次帮朋友排查一个服务端口冲突ss显示8080被一个叫java的进程占了ps一看是一条完全不相干的旧版应用还挂在上面直接kill掉就清静了。5.2 连接状态统计与TIME_WAIT定位服务对外报错too many open files或者总感觉连接异常先看整体状态分布ss -s。它会直接汇总出TCP的各个状态数量一目了然。如果TIME_WAIT数量特别多说明短连接大量被创建和关闭这是正常的但如果数量持续不减就要思考是不是客户端设置了过短的连接超时、服务端又没有启用连接复用。想细看某个状态的连接是谁可以用ss -tan ( state time-wait ) | head -30它会列出所有TIME_WAIT连接的源地址和目标地址。看到哪个IP在刷连接基本就能定位是哪个客户端在频繁建连。配合lsof -i:端口可以查具体进程的网络句柄对排查连接数飙升非常有效。5.3 关键的网络性能快筛命令除了端口和连接我还会用ping看基础连通性和延迟用curl -w查看某个HTTP请求的详细耗时分布。curl -w这个参数能输出DNS解析时间、TCP连接时间、TLS握手时间、首字节时间等多个阶段比笼统看一个总时间精确太多。怀疑网络慢时先用它把瓶颈定位到连接阶段还是传输阶段再对症排查而不是一上来就抓包。6. 高频故障与排查技巧速查把前面这些命令整理成一张速查表每次遇到问题照着走能省很多时间。这不是万能的排查手册但覆盖了80%的日常故障场景。症状第一步命令关键判断系统整体响应慢uptime top负载趋势、CPU占用大户内存看不明白free -havailable是否见底、swap是否增长服务偶发抖动journalctl --since时间点附近日志有无异常磁盘报空间不足df -h vs df -i区分块空间满还是inode满删除文件后空间不释放lsof L1找持有已删文件句柄的进程端口起不来ss -tulnp ps -fp确认占用进程和父进程连接数异常高ss -s ss -tan看状态分布和来源IP日志文件越来越肥logrotate -d 配置先debug模拟轮转排查顺序我总结成一句话先看负载和整体状态再抓CPU和内存大户然后看磁盘和网络最后翻日志。不要一上来就钻进业务代码里大多数时候问题出在资源层而不是逻辑层。针对具体命令的参数记不全不要太焦虑配上man手册和-h帮助就够。我自己的做法是把常用排查命令写成一个小脚本登录机器先跑一遍输出当前负载、内存、磁盘、端口概况几十秒钟就能对机器的健康状况有个整体评估比每次临时敲命令快得多。附录几条我最常组合使用的命令把这些命令以alias形式固化到~/.bashrc里日常排查会顺手很多alias topcpups -eo pid,ppid,user,%cpu,%mem,cmd --sort-%cpu | head -20 alias memcheckfree -h echo --- ps -eo pid,rss,cmd --sort-rss | head -10 alias diskioiostat -x 1 5 alias lstconnss -s ss -tan | awk {print \$1} | sort | uniq -c | sort -rn alias oomlogdmesg -T | grep -i -E out of memory|killed process个人体会是排查问题的核心不是记住多少命令而是建立一套固定的检查节奏。每次故障都从整体到局部、从资源到日志按同一套顺序来熟练以后两三分钟就能缩小范围剩下的时间去验证假设就够了。这套思路比临时翻手册有效太多。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进