多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Linux网络排障指南:传输层与应用层诊断命令实战

Linux网络排障指南:传输层与应用层诊断命令实战 接手上一篇我们已经把链路层到 IP 层的基础打通了。这篇是Linux网络障碍诊断三步法的下半部分重点解决两件事传输层和应用层怎么查以及整个排障流程怎么串起来。传输层看的是端口能不能通、连接能不能建立应用层看的是进程有没有真的在工作、业务响应慢在哪里。很多同事排查网络问题习惯直接从应用日志下手结果绕了一圈又回到起点。其实只要把“连接状态”和“进程状态”这两组信息看明白大部分疑难问题都能快速收敛。上篇结尾提到网络层合格之后绝大多数人的下一步直觉是“去看应用日志”这就是最容易浪费时间的地方。你以为问题是应用没起来其实是握手根本没完成你以为是对端服务挂了其实是中间路径丢包。所以这篇给出一套可复现的判定顺序以及我最常用的一组命令组合。适合Linux运维、SRE、后端开发也包括那些自己搭过代理、网关、NAS的个人玩家——只要你的服务跨机器通信这篇就值得读完。1. 先验收网络层别让传输层背锅开始查传输层之前我强烈建议你先花两分钟做一个网络层“验收”动作。不是说上篇讲过的内容要再做一遍而是你心里必须清楚当前是“路径通但慢”还是“路径不通”还是“通但不稳定”。很多传输层疑难问题的表象其实是网络层隐患的发酵。1.1 一条命令确认基础连通最基础但常被忽略的是ip route get而不是ping。ping只能告诉你目标 IP 是否回应却无法告诉你系统实际走哪条路由出网。尤其在多网卡、多路由、策略路由的机器上ping都能通但业务流量走的链路可能是另一条。ip route get 192.168.10.5输出会直接告诉你源地址、下一跳、出网卡。如果业务主机绑定的 IP 和实际路由出口不一致后面查端口、抓包都会被带偏。我在排查过程中见过太多次“客户端到服务端不通”最后发现是服务端回包走错了网卡。这个问题的典型特征就是从服务端 ping 客户端能通但服务端主动连客户端端口却超时。其次是ping -c 10统计丢包和延迟抖动ping -c 10 -i 0.2 192.168.10.5关注三点丢包率是否为 0、延迟的 min/max 差异是否过大、有没有乱序。只要丢包率不是 0就不要急着去查传输层因为 TCP 对丢包极其敏感1% 的丢包就可能让一个大文件传输速率下降一半以上。这里不是夸张TCP 拥塞控制遇丢包会退避窗口重传占用的等待时间比正常传输还长。1.2 路径上的每一跳都要有数如果 ping 的通但延时不正常我会用mtr把路径上每一跳都跑一遍。mtr相当于持续版的traceroute它能告诉你每一跳的丢包率和延迟平均值mtr -rwz 192.168.10.5看mtr输出有个要点不要只看最后一跳要看中间跳的丢包率变化趋势。中间某跳丢包率上涨但下一跳又降回 0这种情况往往是那一跳设备限流了 ICMP不用恐慌但如果丢包率从某跳开始持续上涨到最后一跳说明链路确实不稳定。还有一个容易被忽略的点是 MTU。传输层报“连接超时”但网络层ping小包全通这种情况十有八九是路径 MTU 问题。测试方式ping -M do -s 1472 192.168.10.5带-M do表示禁止分片-s 1472是 1500 字节 MTU 减去 IP 头 20 字节和 ICMP 头 8 字节后能携带的最大数据长度。如果这个命令失败而-s 1400成功说明路径上有节点 MTU 小于 1500多半是隧道封装或者中间设备配置不当。TCP 有 PMTUD 机制但不少网络设备出于安全考虑丢弃了 ICMP 不可达报文导致 TCP 无从得知应该减小报文大小最后表现为“卡在握手之后、应用层迟迟不返回”。我在章节末尾先给你留一句结论如果ping小包正常、大包异常先解决 MTU再谈传输层和应用层。2. 传输层排障连接状态比 ping 结果诚实得多网络层验收通过接下来要看传输层。传输层的核心是 TCP/UDP 端口和连接状态。很多“应用连接不上”的工单真正问题不是应用挂了而是握手根本没完成。2.1 ss 输出到底该怎么读我首选ss而不是netstat因为ss直接读内核 socket 信息速度快且输出更完整在连接数上万的机器上netstat会卡到你怀疑人生。看监听端口的命令ss -tlnp常用的还有ss -tunp加u看 UDP加n不做域名解析加p显示进程信息。不加p可能权限不足看不出进程普通用户建议sudo ss -tunp。输出里的Recv-Q和Send-Q在不同状态含义不同这是新手最容易误解的地方在LISTEN状态Recv-Q表示当前 accept 队列积压的连接数如果长期不为 0说明应用 accept 速度跟不上新连接到达速度。在ESTABLISHED状态Recv-Q表示内核接收缓冲区中还有多少数据没被应用读取Send-Q表示发送缓冲区还有多少数据没被对端确认。Recv-Q持续积压说明应用逻辑卡住了典型是单线程处理慢或死锁Send-Q持续积压说明对端读取速度慢或者网络拥塞典型是消费者处理不过来了。这两个方向一个指向本地应用一个指向对端或网络别搞反。2.2 半握手、半关闭、TIME_WAIT三个高频状态的根因用ss -tan state established或者直接看完整状态分布ss -tan | awk {print $1} | sort | uniq -c | sort -rn重点关注的状态有三个它们对应的根因完全不同第一SYN_RECV大量堆积。说明服务端收到了客户端 SYN但三次握手始终无法完成连接卡在半路。最常见的原因是服务端 accept 队列满内核再收到新 SYN 也只能丢弃。这时要看ss -ltn里监听端口的Recv-Q是否已满以及系统参数net.core.somaxconn和程序自身 backlog 配置是否过小。Nginx、Tomcat 这类服务都有独立的 backlog 参数光调系统somaxconn不调应用参数等于白调。排查命令ss -s cat /proc/sys/net/core/somaxconn netstat -s | grep -i listen第二CLOSE_WAIT大量堆积。这个状态 99% 不是网络问题而是应用代码问题。CLOSE_WAIT表示对端已经关闭连接但本地应用没有调用 close 关闭 socket连接处于半关闭状态。Java 里常见的场景是 HttpClient 连接池没释放、资源未关闭。遇到CLOSE_WAIT堆积别再去调内核参数了去翻代码。第三大量TIME_WAIT。这个被很多文章渲染成性能杀手其实它是主动关闭方正常的等待状态持续 2MSL 用来让旧连接的延迟报文自然消失。系统层面会把 TIME_WAIT 连接数控制在合理范围如果数量确实大到影响了新连接分配优先考虑调整net.ipv4.ip_local_port_range扩大可用端口范围而不是盲目开tcp_tw_reusesysctl net.ipv4.ip_local_port_range默认通常是32768 60999如果连接数接近这个上限新连接就分配不到源端口表现为“连接一会儿成功一会儿失败”。这也是传输层排查里常被忽略的瓶颈。2.3 telnet/nc/tcpdump从症状到证据的进阶顺序端口通不通我的判定顺序是三层递进的。第一步用telnet或nc做基础连通性测试telnet 192.168.10.5 8080 nc -vz -w 3 192.168.10.5 8080如果连接立即被拒绝说明对端端口没有服务在监听内核会直接回 RST如果连接一直卡住不动直到超时说明 SYN 报文发出后没有任何响应——可能是防火墙 DROP也可能是路径丢包这两种情况处理方式完全不同。抓包能区分。第二步用tcpdump抓握手包这是从“猜测”到“证据”的关键动作。在服务端抓包tcpdump -i eth0 -nn -c 20 host 192.168.10.100 and port 8080正常情况你会看到三次握手SYN、SYN-ACK、ACK。如果只看到 [SYN] 反复重传没有 [SYN, ACK]说明服务端的内核收到了 SYN 但由于队列满或防火墙规则没有回包或者只是抓包顺序不对服务器收到了而系统没回。接下来看下一条。如果看到了 [SYN, ACK] 却没有后续 [ACK]问题基本在回包路径上客户端收到 SYN-ACK 的报文在途中丢了或者客户端防火墙把入方向 SYN-ACK 拦了。这种不对称故障用ping是测不出来的因为 ICMP 与 TCP 走了完全不同的处理路径。第三步如果想知道传输质量nc只能测通断测不了吞吐。这时候需要上iperf3iperf3 -s # 服务端 iperf3 -c 192.168.10.5 -t 10 -M 1400 # 客户端限制 MSS 为 1400iperf3的-M参数能直接指定 MSS如果-M 1400速度正常、-M 1450或默认值速度骤降几乎可以断定路径 MTU 小于 1500。这个测试结果比任何日志都有说服力直接定位到网络层。2.4 防火墙与网卡卸载两个容易误判传输层的地方传输层排障有两个“障眼法”经常误导人。第一个是防火墙。服务端监听端口明明存在客户端连接却超时先别急着怀疑内核看防火墙规则是否把入方向 DROP 了iptables -L -n -v nft list ruleset firewall-cmd --list-all注意 DROP 和 REJECT 的区别DROP 会导致客户端一直等到超时表现为连接卡住REJECT 会立即回 RST 或 ICMP 不可达表现为连接秒断。如果看到大量 DROP 且报文方向是 INPUT说明策略拦截确实存在。这种情况查应用日志是查不出什么的因为应用根本没收到任何连接。第二个是网卡卸载。TSO/GRO 这类 offload 特性会把小包合并成大包交给网卡处理抓包时会看到超过 MTU 的大包或者错误的 checksum但这不代表网络真的有问题。判断方法是在抓包时加一句说明如果抓包显示 checksum 错误但通信正常多半是 offload 的显示假象。有条件的可以临时关闭卸载对比ethtool -K eth0 tx off rx off经验之谈判断传输层问题要同时看连接状态、系统计数器和抓包三个维度单一维度都会产生盲区。3. 应用层从端口到日志的完整排查链传输层证明“连接能建立”问题并不一定结束。真正的业务故障往往发生在连接建立之后——应用进程活着但响应超时、返回错误、或者干脆无响应。这时候进入应用层排查。3.1 systemd 状态不等于业务健康最常见的误区是systemctl status nginx显示active (running)就觉得应用没问题。这个状态只说明主进程没退出绝不代表进程在正常干活。进程可能卡在死循环、阻塞在 IO、或者因为资源耗尽而在“假死”。判断应用层是否健康我一般按这个顺序第一确认进程存在且监听端口正确ss -tlnp | grep 8080 ps aux | grep java第二做本机回环测试绕过所有外部网络因素curl http://127.0.0.1:8080/healthzcurl返回 200 说明进程和端口都正常问题在外部curl卡住或 5xx 说明应用本身已经在异常状态。这一步把网络因素剥离出去定位范围立刻缩小一半。第三对 HTTP 服务关注响应耗时分布而不是只看状态码curl -o /dev/null -s -w DNS解析: %{time_namelookup}s\nTCP连接: %{time_connect}s\nTLS握手: %{time_appconnect}s\n首字节: %{time_starttransfer}s\n总耗时: %{time_total}s\n http://127.0.0.1:8080/几个时间指标的含义time_connect大说明 TCP 握手慢time_starttransfer始终很大说明服务端处理慢。在本地回环测试时time_connect应该接近 0ms如果本地回环都慢那问题就锁死在应用处理逻辑或它依赖的下游资源。3.2 日志里没有根因只有线索应用日志是排查应用层问题的起点但极少直接给你根因。日志里的“数据库连接超时”根因可能是数据库负载高、网络路径丢包、连接池耗尽甚至只是时钟偏移引发的超时判断错误。我的习惯是先确定时间窗口。故障发生时刻的前后 5 分钟所有日志集中看不要只看错误行。用journalctl统一捞取系统服务日志journalctl -u nginx --since 2024-11-01 10:00:00 --until 2024-11-01 10:10:00 -n 500 --no-pager看依赖方向。应用日志报“连不上 MySQL”先去ss -tn | grep 3306看连接数是否正常连接数异常再去查 MySQL 端负载连接数正常但报错才考虑应用逻辑问题。日志排障最容易犯的错是“症状即根因”。报错信息描述的是症状不是原因原因往往在它依赖的上游或者网络链路上。给应用打日志时我建议每个外部依赖调用的入口和出口都记耗时和状态不然线上遇到问题只能靠猜。3.3 用回环网络把问题切成两半应用层和网络层交织在一起时最高效的做法是用“回环测试”把问题切成两半。本地回环网络127.0.0.1不经过任何物理网卡不存在丢包、拥塞、防火墙本机防火墙规则有时也拦回环可以在 iptables 里确认。如果本地回环测试正常那么问题一定出在“服务端 → 客户端”的物理路径或对端配置上如果本地回环测试异常那么问题一定出在服务端自身。再进一步如果服务端本机测试正常、客户端跨网络访问慢用curl的耗时拆解就能定位慢在哪一段time_namelookup大DNS 解析慢。time_connect大TCP 握手往返时间长大概率是网络延迟或链路拥塞。time_appconnect大TLS 握手慢可能是证书链验证慢、加密协商算法太弱也可能是 TCP 往返本来就慢。time_starttransfer大服务端处理请求慢重点查应用逻辑和上游依赖。time_starttransfer正常但time_total大响应体传输慢指向带宽瓶颈或路径 MTU。这个“一刀切成两半”的思路比满屏日志地找问题快得多。3.4 动态端口与SELinux应用层障眼法应用层排障还有一个高频盲区——动态端口协议和 SELinux。以 FTP 为例控制连接用 21 端口数据连接会在一个动态范围内协商端口。如果防火墙只放行了 21实际数据传输时客户端无法连上动态端口表现为“连接成功但列表加载失败”或“下载超时”。不是应用坏了是防火墙策略和应用协议不匹配。解决思路是限制服务端被动端口范围然后在防火墙放行对应区间# vsftpd 配置 pasv_min_port30000 pasv_max_port31000然后是 SELinux。新装的 Nginx 或自定义端口服务经常出现监听端口存在、回环 curl 正常但外部访问异常journalctl里隐隐约约能看到 avc deny 记录。检查方式ausearch -m avc -ts recent getenforce如果确认是 SELinux 导致按需调整布尔值或自定义策略setsebool -P httpd_can_network_connect 1这类问题查应用日志基本无解因为应用本身没报错是系统安全策略把连接拒了。我在不少线上环境里靠这条命令救回过濒临误判的排查方向。4. 排障流程总结把三层拧成一条主线文章到了这里三层排查工具都过了一遍重点开始收束怎么把这些命令串成一个稳定可复用的排障流程。4.1 三层判据速查表我把每一步的“核心问题”“一句话判据”“常用命令”整理成一张速查表每次排障前先对照这张表确认自己当前卡在哪一层。排查层核心问题一句话判据常用命令网络层路径通不通延迟和丢包如何ping 小包无丢包且延迟符合预期大包测试正常mtr 路径无异常上涨ping、mtr、ip route get、ping -M do传输层握手能否完成连接状态是否健康ss 能看到 ESTABLISHEDtelnet 能通无 SYN_RECV 堆积抓包握手完整ss、telnet/nc、tcpdump、ss -s应用层进程是否真的在工作响应耗时分布在哪一段回环接口正常、健康检查 200、日志无持续错误curl 耗时分布合理systemctl、journalctl、curl -w、ausearch这张表的用法不是从上往下死板执行而是用来确认“我现在掌握的结论是否足以排除某一层”。比如你已经确认握手正常那传输层基本可以放行直接进应用层如果你还没确认过丢包情况就不要轻易下“应用慢”的结论。4.2 排障时的正确姿势与坏习惯排障方法对了一半操作姿势不对也会前功尽弃。我见过太多同事因为坏习惯把简单问题复杂化坏习惯一跳层排查。应用报错直接翻应用日志发现日志没有明确线索回头才想起看链路。结果因为反复试错已经改动了多处配置真正的问题现场被污染了。正确做法是按层级顺序排查每层要么确认正常要么定位异常不要凭感觉跳层。坏习惯二并发操作不留痕。在同一个会话里同时改了三条配置、重启了服务结果问题解决了却不知道是哪个改动起的作用下次问题复现依然抓瞎。我建议排查过程中记录“变更 — 验证”的小闭环每次只改一个变量改完立即验证验证结果记到时间线里。在 tmux 里开两个窗格一个做操作一个记命令和结论。坏习惯三不保留现场。tcpdump 抓包抓到证据却因为文件太大没有保存等事后复盘时已经拿不出原始数据。抓包脚本要养成-c限制包数量并输出到文件的好习惯tcpdump -i eth0 -nn -c 1000 -w /tmp/tcpdump_$(date %Y%m%d_%H%M%S).pcap host 192.168.10.5 and port 80804.3 沉淀每一条经验我的排障手顺排障流程能真正沉淀下来靠的不是脑子而是手顺文档。每次解决一个问题我会在文档里追加一条记录格式固定为四行现象业务视角的一句话描述。定位过程按层排查的关键命令和输出结论。根因最终确认的原因必须是可验证的客观描述。整改动作变更了什么参数、加了什么监控、补了什么告警。这个习惯的价值在于三个月后同一个问题再次出现你不需要重新推演一遍直接搜文档就能命中答案。我整理过的排障手顺里高频问题大概只占所有问题的 20%但这 20% 的问题消耗了排障团队 80% 的时间。有了手顺那 80% 的时间可以直接省掉。5. 真实案例复盘一次下载慢问题是怎么被定位的方法论讲再多不如一个真实案例有说服力。这个案例是在一个跨机房场景下碰到的过程完整覆盖了网络层、传输层、应用层三层排障思路。5.1 现象与第一次误判业务反馈从某台服务器下载大文件约 100MB速率始终在 3MB/s 上下浮动而正常预期是 30MB/s。同事的第一反应是应用限速翻了一遍 Nginx 配置没看到限速指令又怀疑磁盘 IO 瓶颈用iostat看了一圈磁盘读写一切正常。于是问题被移交给我时备注写着“应用层疑似异常但未定位”。这时候如果跟着应用层走大概率会陷入日志排查的死胡同。我的第一反应是先回到网络层把最基础的验收动作做一遍。5.2 网络层丢包测试先排除链路第一步ping -c 100目标机延迟稳定在 2ms 左右丢包率 0%小包链路看起来干干净净。但下载大文件表现差说明问题要么在吞吐能力要么在报文大小上单纯小包测试覆盖不到。于是做分片测试ping -M do -s 1472 192.168.10.5 ping -M do -s 1400 192.168.10.5结果显示-s 1472的包全部失败-s 1400的包全部成功。这是一个强烈的信号路径上某个节点的 MTU 小于 1500只是小包测试根本无法触发这个问题。5.3 传输层大包小包行为不一致为了进一步确认在两端跑iperf3用不同的 MSS 做对比# 客户端 iperf3 -c 192.168.10.5 -t 10 -M 1400 iperf3 -c 192.168.10.5 -t 10 -M 1450结果是-M 1400时速率直接跑满 30MB/s-M 1450时速率掉回 3MB/s。同时tcpdump抓包看到的场景是 MSS 较大的报文发出后长时间无 ACK最终触发重传而小报文完全正常。到这里传输层已经确认链路本身吞吐能力没问题问题出在路径对大包不友好。那为什么 TCP 没有自动协商到更小的 MSS因为路径上某个设备没有正确返回 ICMP 不可达消息导致 PTMU 探测失效。这是很多 MTU 问题的深层共性抓包时看到重传反而容易误判为网络丢包。5.4 应用层确认与整改应用层这边做了一次本地回环下载curl http://127.0.0.1:8080/大文件 -o /dev/null本地速度跑满磁盘上限说明服务进程没有问题。问题彻底锁定在“服务端到客户端路径的 MTU 不一致”上。整改有两种方案。一种是调整服务端网卡的 MTU但如果路径上节点很多或者业务复杂这种方案影响范围大、不好操作。我采用的是更精准的 TCP MSS Clamping 方案在服务端的 iptables 里把去往客户端的 TCP SYN 报文的 MSS 钳制到一个安全值iptables -t mangle -A POSTROUTING -p tcp --tcp-flags SYN,RST SYN -o eth0 -j TCPMSS --clamp-mss-to-pmtu这条规则强制 TCP 握手时通告的 MSS 不超过路径实际能承载的大小从根上避免了后续大包被静默丢弃。执行之后重新下载速率恢复到了预期值。5.5 这场排障后我加进手顺的三条备注这次排障之后我在自己的排障手顺末尾追加了三条备注现在的观点比当时更笃定用户报“下载慢”不等于应用慢。在确认路径 MTU 和实际吞吐之前不要应用层投入太多时间。iperf3 -M是最容易被低估的参数它能直接暴露路径 MTU 不匹配问题。tcpdump 看到大包重传而小包正常时优先怀疑路径 MTU 或网卡 offload而不是应用逻辑。这三条备注之后又帮我快速命中过至少四个同类问题这套流程也因此沉淀成了我们团队网络排障的默认起点。
返回列表