ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linux系统运维故障排查:从现象到根因的实战方法

Linux系统运维故障排查:从现象到根因的实战方法 做了这么多年Linux系统运维我最常被同行问的一句话就是“有没有那种比较全的故障排查资料最好一册在手、天下我有那种。”每次看到有人晒出“191页Linux系统运维故障排查手册”我都会认真点开翻一翻。不是因为这些手册本身有多神而是这类资料背后代表的诉求特别真实系统运维、故障排查这件事光靠零散经验撑不起安全感你需要一套能在半夜两点把人从床上拉起来之后、依旧能稳住手脚的方法论。今天我不打算替任何手册做宣传而是结合这些年实打实踩过的坑、救过的火把Linux系统运维故障排查里真正通用的那部分逻辑拆开讲清楚。如果你正准备系统整理自己的排查能力或者遇到问题只知道重启但不知道下一步看什么这篇文章值得你花十分钟读完。先聊聊我对手册类资料的态度。191页的东西知识点一定很全但全不等于能用。故障排查的本质不是背命令而是建立一套“现象到根因”的映射思维看到这个症状第一时间想到哪几个可能性按什么顺序验证哪些数据必须在现场采集哪些操作做了反而会破坏现场。这一整套动作才是运维的核心手艺。下面我按自己的实战习惯把整个排查体系拆成几个部分每一部分都配上实际案例和可以直接拿走的命令组合。1. 排查前的准备比命令更重要的四件事很多人拿到的第一本故障排查书翻开就是top、free、df、netstat好像会敲这些命令就是排查高手。真到了生产环境你就会发现命令只是最后执行的那一下“手”真正决定排查效率的是动手之前脑子里有没有一张地图。1.1 先有“回滚”和“退出”方案再动手这是我在生产环境里吃过大亏之后总结出的第一条铁律。有一次线上MySQL实例hang住连接数被打满我当时的第一反应是立刻重启数据库。结果重启之后InnoDB做崩溃恢复花了四十多分钟才把redo日志应用完业务中断时间远远超过预期。后来复盘时发现当时完全有更温和的手段先抓取show engine innodb status的输出再做一次快照备份最后才考虑重建或切换。所以我现在的习惯是任何一台服务器出故障先问自己三个问题——这个操作会不会造成数据丢失操作能不能回退如果操作失败我还有没有备用路径这三个问题答不上来哪怕我看着像很着急手上也会先停一停。尤其在多节点架构里“先切流再排查”往往比“原地修复”更快恢复业务这需要平时就把切换预案准备好不能等事故发生了再临时想。1.2 用基线数据建立“正常值”记忆故障排查时最棘手的问题不是“哪里不对”而是“和什么比不对”。比如一台服务器的load average常年0.3某天突然变成2.5这并不一定代表故障可能只是业务高峰。反过来如果一台服务器load average一直很高但某个时刻突然降到了接近0这可能意味着业务进程已经不在处理请求了这种“假正常”更危险。我在每台重要服务器上都会保留一套监控历史至少覆盖CPU、内存、磁盘、网络、进程数、TCP连接数这几项。平时业务平稳的时候随手记一下“这台机器日常水位是多少”把这些数字和业务时段对应起来。真到排查的时候这些数据就是判断当前指标是“异常”还是“常规波动”的坐标轴。没有基线的排查只能靠猜有基线问题范围就能砍掉一大半。1.3 标准化处理流程先止损再定位后根治要说运维事故中最高频的失误就是对故障级别的误判。一个磁盘只读的故障如果只是一块数据盘出了问题可以先通过缩容或停掉非核心进程止损但如果根分区满了业务进程可能直接崩掉这时就要先清理pid文件、旧日志把空间腾出一点才能让服务先拉起来。我自己的习惯是把处理流程分成三层先是“止损层”确认有没有正在写入的数据、需不需要立刻切流或拉闸核心目标是让损失止住然后是“定位层”等现场情况稳住之后再来分析日志、指标、系统状态找到真正的原因最后是“根治层”补监控、修配置、完善预案确保同类问题不会再次发生。这三层绝对不能乱尤其是前两层。很多新人在系统还在高负载、业务还在报错的时候就拿着抓包工具去分析应用日志结果等日志分析完了业务已经挂了半天。1.4 建自己的故障笔记而不是收藏别人的手册191页手册也好网上几千条的故障案例也罢它们只能帮你补盲区真正能让你在事故现场做出快速判断的是你自己大脑里沉淀的那些“同款坑”。我在自己的笔记里记录每个案例时永远包含四个字段故障现象、根因分析、修复动作、如何避免。特别是“如何避免”这一步我会逼自己写到配置项、脚本或者监控阈值这个级别而不是写一句“以后注意”。有一次一个新来的同事处理Nginx 502他在手册上找到了“看error.log”这一步看完日志之后没能定位到上游超时反而去反复调worker_processes参数。其实他缺的不是命令而是没有在笔记里建立起“502是上游不响应不是Nginx本身配置问题”的判断线。后来我把这类关联思考都整理进了团队的故障手册再遇到502大家的第一反应就变成了检查PHP-FPM或后端应用的负载和超时配置。2. 高频故障场景标志性症状与第一反应故障场景千千万但落到Linux服务器上绝大多数问题都能归进几个常见大类硬件与虚空化层、系统层、网络层、应用与持久化层。每一类都有自己标志性的症状组合也能映射到固定的第一反应命令。掌握这种对应关系排查效率会明显提升。2.1 硬件与虚拟化层不是所有故障都会先在业务侧暴露物理服务器或者虚拟机最常见的隐藏问题是磁盘坏道和内存ECC报错。磁盘坏道的初期症状特别迷惑应用可能只是偶尔I/O等待变高数据库偶尔报一个检查点慢如果不看dmesg很容易当成普通磁盘性能问题去调I/O调度器。实际上只要在故障机器上执行dmesg -T | grep -i error|blocked|task hung就能发现磁盘控制器在反复重试坏块。内存故障更容易被忽略因为Linux内核通常会把坏页标记掉系统继续运行偶尔出现一次无法解释的进程崩溃。如果你发现同一个进程莫名其妙隔几天就core dump一次而且时间点没有任何规律建议去看看BMC/SEL事件日志或者执行mcelog --client检查硬件错误记录。曾有朋友的一台数据库服务器每隔一两周就发生一次实例崩溃监控图上内存使用率也不高排查了大半个月最后发现是内存条有偶发要修复的单元换掉之后问题立即消失。2.2 系统层从“能开机”到“能干活”中间隔着一堆检查“系统起不来”是每个运维早晚会遇到的事。我最常用的开机故障排查路径是先看grub菜单里的内核版本和启动参数确认默认启动项是否正确如果卡在某个服务上进入initramfs shell检查根分区文件系统是否完整xfs_repair或fsck能不能执行如果文件系统没有问题再看看驱动模块是否加载成功尤其是磁盘控制卡和网卡的驱动。系统层另一个高频故障是根分区空间用满。df -h一看100%du一找是/var/log或/tmp下面的大文件。处理逻辑大家都懂但我想提醒一个容易忽略的点删除文件后空间不一定马上释放如果有进程还持有被删文件的句柄df看到的还是满的。这时候用lsof | grep deleted把对应进程找出来确认是否可以重启该进程或者在确认安全的情况下直接清空文件: /var/log/xxx.log而不是rm掉一个正在被写入的日志文件。2.3 网络层延迟高不等于带宽小入口排队才是元凶我排查过的网络性能问题里至少有三分之一跟带宽无关。比如应用觉得“网络慢”实际表现是请求RT抖动厉害但网卡流量并不高。这种情况下第一反应应该看网卡软中断分布和各CPU的使用情况。用mpstat -P ALL 1观察是否有单个CPU被打满再用top里的si字段确认软中断占比如果是单队列网卡数据流量集中到一个CPU核就会产生“明明每个网卡只有一个中断号数据却全部卡在一个核”的瓶颈。出现这种问题时可以考虑启用RSSReceive Side Scaling或者用smp_affinity把中断绑到不同CPU核心上。之前的实践里在某台8核机器上做了一次均匀分散绑定Nginx的SSL握手性能提高了近一倍RT从几十毫秒降到个位数毫秒。这个案例想说明的是很多时候“网络慢”的根源不在链路而在系统侧的接收处理能力。2.4 应用层与数据库日志先行慢查询指标是锚点应用故障的排查我最反对一上来就重启。重启会把进程内存里的现场全部清掉很多中间状态再也无法获取。正确顺序应该是先看应用自己的日志再看进程的状态然后才是系统层的关联证据。比如Java应用内存问题先看一下GC日志和堆转储是否已经保留再执行jstat -gcutil和jmap -dump把现场留足再采取重启动作。数据库方面慢查询日志永远是最重要的第一依据。MySQL可以用slow_query_log记录执行时间超过阈值的SQL再配合explain查看执行计划大多数性能问题都能定位到全表扫描或者索引失效。很多团队在业务高峰期才打开慢查询日志这是很可惜的因为高峰期的压力问题恰恰需要在平时积累基线。把这个开关常开阈值设置在100毫秒不仅能定位问题还能反向推动研发优化SQL。3. 排查常用命令的“实操经验修正版”很多人列Linux命令大全能列几百个但真到了排查现场常用的也就是二三十个。关键是这些常用命令都有一些使用细节光看手册看不出价值我在下面列几个经历过实战考验的用法。3.1 top、free、df指标要看更要会看趋势top有两个参数我几乎每次都用top -c显示完整命令行能看出具体是哪个服务在占资源而不是只看到java或nginxtop -Hp PID查看某个进程内部每个线程的CPU占用Java踩满CPU的时候这一步能快速定位到具体线程再用jstack把线程栈拿到基本就能锁定问题代码。free这个命令很多人关注“可用内存”那一行其实在Linux里还有cache和buffer占了大量内存真正的内存短缺判断要看available这一列的数值趋势。如果available持续走低而buff/cache又居高不下往往是某个进程在大量读写文件但没有及时释放缓存这时不要急着清cache先找出来是谁在写否则清了也没用。df时一定要带-h是一定的但生产环境我习惯加df -hT把文件系统类型也显示出来。不同文件系统在故障时的处理方式差异很大xfs和ext4的修复命令完全不同先确认文件系统类型再动手能避免很多误操作。3.2 systemctl、journalctl、dmesg把启动日志养成一种习惯排查服务无法启动时不要只停留在“哦服务起不来”这个层面。systemctl status xxx会给你第一层信息但这往往是结果而不是原因。真正的原因在journalctl -u xxx -n 50里或者systemctl status显示的Process: exec那一行。举个例子Nginx启动失败如果你只看到“failed”而没有去看日志很可能错过端口被占用的真正报错因为Nginx的error.log里才会明确写出bind() to 0.0.0.0:80 failed。dmesg更适合用来排查内核层面的问题比如模块加载失败、硬件报错、OOM killer的行为。我见过最经典的OOM场景是内存明明还有很多剩余但进程被杀了看dmesg才发现是cgroup的内存限制被触发业务进程超出了容器设定的上限而不是整机内存不足。这种问题不看内核日志光在应用层调JVM参数完全是白费力气。3.3 ss、tcpdump、iostat揪出连接和块设备问题ss命令替代netstat已经很多年了我特别推荐两个组合ss -s看系统整体连接状态的汇总能快速判断TIME_WAIT或CLOSE_WAIT是否大量堆积ss -tnp可以看到占用某个端口的进程PID。遇到CLOSE_WAIT堆积99%是应用代码没有正确关闭连接需要去查应用侧而不是在系统上乱调参数。tcpdump是最后手段不要在还没有确认网络拓扑和数据流向的时候就开始抓包。先明确源IP、目标IP、端口、协议sudu tcpdump -i eth0 host 10.0.0.5 and port 3306 -w /tmp/mysql.cap把抓包结果保存成文件再用wireshark分析。有一次排查两个机房之间的数据同步延迟问题前后看了应用日志、数据库状态都没有发现异常最后用tcpdump对比了两个机房间TCP重传比例发现公网链路上偶发丢包才是元凶。iotop和iostat是块设备方向的利器。iotop能看到具体是哪个进程在做大量I/Oiostat -x 1能显示每条磁盘的等待队列长度和利用率。注意util达到100%并不能“直接”意味着磁盘满了还需要看await和svctm的比值。如果util很高但await很低说明请求在设备端没有被阻塞可能只是某一块盘请求过于密集如果await明显高于svctm的几倍说明I/O在排队这才是真正的性能瓶颈。4. 典型故障复盘从现象到根因的三次回溯知识归知识真正有血有肉的经验一定长在那些深夜处理过的故障里。我挑三个非常典型的案例把排查过程和思维转折完整写出来这三个案例对应三种非常常见的故障形态资源写满、网络偶发超时、误操作删除。4.1 案例一磁盘写满导致数据库只读现象业务突然大面积报错提示数据库无法写入。登录服务器后df -h发现根分区已经100%数据库的报错日志里明确写着“Read-only file system”。第一反应是什么很多人会直接开始删文件。但我现在的第一反应是先用mount -o remount,rw /把文件系统重新挂载成可写然后把数据库进程的数据目录临时切到另一块有空间的盘上保证能先把业务恢复起来。现场操作时我找到/var/log里几个超过5GB的旧日志先压缩再用rsync转移到备份盘腾出一部分空间接着把业务分区里的临时文件目录清理掉数据库恢复可写。但这只是止损不是根治。复盘时我才发现这台服务器的日志轮转脚本已经连续三天没有执行了。原因是脚本里用了find /var/log -mtime 7 -delete但当时系统时钟因为NTP不同步快了20分钟脚本执行时判断“今天还是未来”于是把所有普通日志都当成当天文件跳过导致日志越积越多。最终我调整了日志脚本的触发逻辑在find命令里显式排除当日文件并给日志目录加了inotify监控超过阈值就告警。这个案例的关键启示是磁盘满只是结果日志管理策略的缺陷才是根因。4.2 案例二DNS解析慢导致接口偶发超时现象某个内部微服务的调用经常在高峰期偶发超时每次持续几十秒到几分钟不等。查应用日志服务端没有明显报错数据库也没有慢查询监控面板上CPU、内存都正常。查网络侧用ping测试对端IP延迟一直很稳定。但进一步测试后发现业务代码里访问某个内部域名时偶发的延迟特别高。用dig对域名做解析测试发现解析时间在几毫秒到几百毫秒之间剧烈抖动。原因定位到/etc/resolv.conf里配置了两个上游DNS服务器其中一个因为跨机房网络质量差经常丢包glibc的解析器遇到超时会先等第一个服务器超时再发起第二次请求这个串行等待就变成了接口的偶发超时。解决办法是把质量差的DNS服务器从解析配置里先移除并调整了resolv.conf里的options timeout:1 attempts:1避免解析器反复重试。另外考虑到关键服务依赖内部域名解析我在services侧加了DNS解析结果的本地缓存业务代码不再每次请求前做一次完整解析。这类问题用系统命令查不出来但排查思路是通的任何偶发延迟都要把依赖链路上每个环节的时间消耗拆开测量而不是只盯着一台机器看。4.3 案例三日志清理脚本误删新日志现象早上一到办公室同事反馈某台服务的数据目录被清空了。排查发现前一天晚上数据目录里的文件被全部删除只剩一个空的目录结构。这个故障的原因非常反直觉原本写好的日志清理脚本只在目录超过80%时才会执行但在某次版本更新后定时任务的执行用户和目录权限发生了变化脚本里的变量值没有拿到绝对路径变成了一条类似rm -rf ${LOG_DIR}/*的命令而LOG_DIR在脚本错误条件下变成空字符串结果就是rm -rf /*的灾难。这个案例对我的教训非常大。虽然最终的备份能恢复大部分数据但让团队反复讨论了很久为什么允许生产环境存在一个可以直接rm -rf空变量路径的脚本现在我们的规范是所有涉及删除操作的脚本执行前必须判断变量是否非空在路径变量前加入哨兵前缀如/data/logs并统一在命令中用--preserve-root参数做保护。修改后我会拿一台临时机器先跑一遍脚本用set -x观察实际命令展开的路径而不是盲目自信。事后我还给定时任务增加了执行结果回传机制任何脚本执行异常都会通过监控发通知而不是让错误悄悄发生。5. 常见问题速查表一眼定位问题方向下面这张表是我在实际工作中反复用到的“症状到方向”对照表放在手边随时可以查适合打印出来贴在工位旁。症状第一优先检查项常用命令解决方向CPU居高不下用户态还是内核态、单核还是整体top -cmpstat -P ALL 1pidstat用户态看应用线程内核态看软中断和驱动内存持续下降available值、OOM记录free -hdmesg | grep -i oomcat /proc/meminfo定位占用进程检查漏释放或泄漏磁盘空间满大文件、被删未释放文件df -hTdu -sh *lsof | grep deleted轮转日志清理旧数据定位持有句柄的进程磁盘I/O排队util与await的关系iostat -x 1iotop优化读写模式更换硬件调整队列深度端口无法监听端口占用、SELinux、防火墙策略ss -lntpgetenforcefirewall-cmd规避冲突调整SELinux context或放行规则服务启动失败服务日志和systemd错误信息journalctl -u xxx -n 50systemctl status定位配置错误、依赖缺失、权限问题偶发连接超时DNS解析、网络重传、连接队列溢出dig tracess -stcpdump调整域名解析策略、加大backlog、检查链路质量文件系统只读内核日志中的I/O错误dmesg | tailmount修复磁盘或文件系统必要时联系硬件侧TIME_WAIT堆积连接关闭频率和keepalive配置ss -ssysctl net.ipv4.tcp_fin_timeout调短TIME_WAIT或调整应用连接复用策略CLOSE_WAIT堆积应用未正确关闭socketss -tnpjstack/pstack检查应用代码核心是补上close或资源回收逻辑进程OOM被杀cgroup限制、内存碎片、单进程内存暴涨dmesg | grep -i oomcat /sys/fs/cgroup/.../memory.events调大限制、优化内存占用、拆分进程粒度DNS解析慢上游DNS响应时间、本地缓存策略dig 8.8.8.8域名cat /etc/resolv.conf调整resolv.conf重试策略增加本地DNS缓存这张表不可能覆盖所有故障但可以帮你在面对未知问题时先锚定到最可能的领域然后再深入排查。6. 把手册变成自己的“故障决策树”每次看到XXX页的故障手册我都会建议大家不要只当收藏家。手册提供了足够多的知识点但运维最重要的能力是“决策”而决策依赖的是树状结构根据一个现象列出所有候选根因按发生概率和验证成本排序逐个排除。我见过很多优秀运维他们脑子里都有一套自己的决策树遇到问题不会慌乱因为每个分支都已经提前走过了。构建自己的故障决策树可以从三张清单开始。第一张是“症状清单”记录你日常遇到过和从手册中看到的各类故障现象每个症状写清楚出现时的上下文是什么哪些指标会是敏感指标。第二张是“命令清单”为每个症状写下对应的验证命令把命令的执行结果和“正常情况”写在一起这样你才知道什么结果意味着什么。第三张是“动作清单”每条动作都标清楚执行后的预期效果和回滚方式比如“重启Nginx之前先保存error.log验证当前配置nginx -t”。举个例子如果有人问你“一个服务突然变慢怎么排”你可以按这样的顺序回答先看系统负载和CPU状态确认是CPU被占满还是I/O阻塞再看应用日志确认是慢请求还是报错重试最后看依赖系统比如数据库、Redis、外部API的延迟是否有变化。这个顺序就是把最大的可能性放在最前面每一步都用数据验证而不是一上来就凭感觉猜。这一点也是我想特别强调的手册可以教你每一个命令怎么用但教不了你怎么把几十个命令串成一条高效的排查链路。链路这种东西只能靠自己在实战中反复打磨。每处理完一起故障我都会回头问自己一个问题如果下周一同样的问题再次发生能不能在五步之内定位到根因如果回答“不能”说明还需要把这个流程继续拆细、补充自动化脚本或监控指标。7. 附录我建议长期保留的运维习惯顺着上面的思路我再分享几个自己一直在坚持的运维习惯这些习惯帮我省下了大量救火时间也算是“手册之外”真正值钱的部分。习惯一给所有重要目录写一个“空间占用基线”。不是简单的df -h看一眼就完了而是把每个分区的日常工作负载、增长速率、预计哪天会达到80%都提前算出来。规划好日志轮转周期和清理策略比等到满了再拆东墙补西墙靠谱得多。习惯二每周抽查一次系统日志中的error关键字。不用等到故障发生才去看每周固定花十分钟在每台核心服务器上执行journalctl -p err -since -7 days把里面的error逐条过一遍。很多故障在爆发之前日志里已经积累了大量低级告警早处理就是省掉未来的夜晚。习惯三建立统一的“现场采集脚本”。平常就把top、free、df、ss、dmesg、uptime、last、history这些信息的采集命令汇总到一个脚本里发生故障时先跑一遍把输出保存到/tmp/sos目录作为排查和复盘的第一手现场数据。万一问题需要反复分析这些数据比回忆靠谱得多。习惯四不要在生产环境直接查大表或频繁执行全量扫描命令。这句话看起来多余但我见过太多人为了找一个大文件在根目录跑了du -sh *结果I/O飙高影响了正常业务。先分析一下大概位置用ls -lhS按大小排序定向排查或者先看监控图再决定是否全盘扫描。习惯五每个故障处理完顺手把处理过程中的关键信息沉淀成一条笔记。不要等到项目小结或者年底复盘时再补现场的记忆是最鲜活的。哪怕是半夜三点处理完的一次小故障花五分钟记下来三个月后回头看这就是你最宝贵的经验库。我个人在实际操作中的体会是所谓“191页的Linux系统运维故障排查手册”真正把它读透的方法不是从头到尾背而是先把自己已经遇到的故障全部对上号再去看那些工作中还没踩过的类型把每条经验转化成自己的操作条件反射。手册能给你的是知识边界而边界之内的判断力、决策速度和复盘能力都需要在一次次的真实故障中打磨出来。希望这篇内容能帮你少走一些弯路也欢迎你在自己的环境里把这些方法跑一遍再按照自己的场景改造成适合自己的排查路径。
返回列表