如果你问我 Linux 运维和后台开发里最值得花时间啃的模块,我的答案不是某条命令,而是网络层。早几年我也觉得网络层就是 IP 地址和路由表,能 ping 通就能上班。直到被线上故障教育了几轮,才明白数据从网卡进来,经过驱动、协议栈、Socket,最后到应用进程,每一站都有计数器、队列和超时时间,任何一环出问题,业务表现都会变得很奇怪。这篇文章不打算背 OSI 七层模型,而是结合我真实踩过的坑,把 Linux 网络层从收包到排查、从 TCP 参数到容器网络的核心逻辑完整走一遍,适合刚开始学 Linux 网络的人,也适合想补齐网络排查能力的运维和 SRE。
1. 收包链路:数据包从网卡到应用进程的每一站
1.1 从硬中断到软中断:网卡收包不是“内核顺便处理”的
很多初学者以为数据到了网卡,内核就会自然而然地处理它。实际上,一条数据包从物理网线进入应用进程,首先要过的就是驱动和中断这道关。网卡通过 DMA 把数据直接写入内存里的环形缓冲区(Ring Buffer),此时 CPU 还没有参与拷贝,这个设计是为了避免大量数据在网卡和内核之间来回复制。写入完成后,网卡发起硬件中断,通知 CPU 有数据到达。硬件中断处理程序必须非常短,因为它会打断正在运行的进程,如果长时间占用 CPU,系统整体延迟会明显升高。
真正处理数据包的逻辑被放到了软中断里,也就是 Linux 的 NET_RX_SOFTIRQ。这里有一个关键机制叫 NAPI。在高流量场景下,网卡会先关闭硬中断,驱动反复从 Ring Buffer 里取包交给协议栈,直到取完或者达到预算,再重新开启硬中断。这种“批量收包”的思路,避免了每个数据包都触发一次硬中断,极大降低了 CPU 开销。另一个容易忽略的点是中断亲和性。默认情况下,网卡中断可能全部落在 CPU0 上,导致 CPU0 的软中断使用率飙升,其他核心却很空闲。可以用 /proc/interrupts 查看各 CPU 上的中断分布,也可以配置 IRQ affinity 或开启 RPS(Receive Packet Steering)来让软中断分摊到多个 CPU。
判断软中断是否在丢包,最直接的文件是 /proc/net/softnet_stat。这个文件每行对应一个 CPU,列的含义在不同内核版本上略有差异,但第二列和第三列分别和 CPU 收到的包数量、由于队列溢出导致丢包的数据包数量有关。如果第三列持续增长,说明你的网络包在进入协议栈之前就已经被丢弃了。很多人排查丢包只盯着应用日志,却不知道问题早在这一层就发生了。
1.2 sk_buff:Linux 网络层最重要的数据结构
在 Linux 内核里,网络数据包不会赤裸裸地在各层之间传递,而是被包装在一个叫 sk_buff(Socket Buffer)的结构体里。这个结构体里保存着数据包在各个协议层的头部指针,比如 mac_header、network_header、transport_header,还有数据包长度、mark 标记、时间戳等信息。为什么需要这样一个结构?因为数据包在链路层要加以太网头,在网络层要处理 IP 头,在传输层要解析 TCP/UDP 头。如果只用一块原始内存从头到尾处理,每层都要移动指针,还要频繁拷贝,效率太低了。
sk_buff 的设计允许每个协议层只操作自己关注的那一段,通过调整指针就能完成包头封装和解析。同时它支持 headroom 预留,也就是在数据区前面预留出一块空间,当数据包向上一层传递时,不需要把整包数据往后挪,只需要在头部插入新的协议头。这个机制也成了后来零拷贝技术的基础之一。我们平时写应用不会直接跟 sk_buff 打交道,但理解它有助于看懂协议栈行为。比如抓包时发现 TCP 分段异常,很多时候不是 sk_buff 的问题,而是 MTU 和 IP 分片的问题。
内核在分配 sk_buff 时使用的是专门的内存缓存,如果系统内存碎片化严重,分配失败,网络吞吐就会突然下降。这类问题在长时间运行的服务器上并不少见,尤其是在频繁创建和销毁连接的场景下。如果你看到 dmesg 里有类似 kvmalloc 失败或者网络吞吐抖动,却又找不到应用层原因,往内存碎片和 sk_buff 缓存分配的方向查,往往会有收获。
1.3 数据包可能在哪几个点位丢失
一条数据包从网卡到应用进程,有四个最容易丢包的卡口。
第一个是网卡 Ring Buffer。当流量瞬间暴涨,数据包到达速率超过内核处理速度,Ring Buffer 被占满,新到的数据包无处存放,网卡会直接丢弃。查看 ethtool -S eth0 时,rx_missed、rx_dropped 这些计数器增长就能看到。第二个是软中断处理不过来。数据包进入软中断队列后,如果 CPU 处理速度跟不上,队列溢出,/proc/net/softnet_stat 会记录丢包。第三个是 IP 层和 netfilter 层。路由查找失败、iptables 规则做了 DROP、IP 分片重组超限,都可能导致丢包。第四个是 TCP 层和 Socket 层。Socket 接收缓冲区满,或者 listen 的 accept 队列满,应用没有及时取走数据,内核只能丢弃或触发重传。
把这四个卡口记在脑子里,排查问题时就不是笼统地看“是否丢包”,而是会问自己“丢在哪个环节”。不同的丢包位置,对应的计数器和命令完全不同。这也是网络层排错和纯应用层排错最大的区别:应用层只需要看日志,网络层需要把整条流水线串起来看。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 排查网络问题时我优先使用的命令组合
2.1 ip 命令:地址、路由、邻居关系一次看全
老教程里大量使用 ifconfig、route、arp,这些命令在新环境里未必默认安装,而且信息展示也不够系统。我更推荐 iproute2 套件,也就是以 ip 开头的这一组命令。ip addr 查看接口和 IP 地址,ip link 查看接口状态,ip route 查看路由表,ip neigh 查看邻居表。排查“能 ping 通同网段,但访问对端服务不通”时,ip route get 10.0.0.1 非常实用,它能直接告诉你内核会选择哪条路由、从哪个接口发出数据包。
code复制$ ip addr show eth0
$ ip link show eth0
$ ip route get 192.168.1.100
$ ip neigh show
ip link 输出里的 state UP 并不完全代表物理链路健康,有些接口状态显示 UNKNOWN 但实际网络是通的。真正想确认物理链路,还是得依靠 ethtool。另外,改完网络配置后不要忘了 ip addr flush 和重新加载,不同网络管理工具之间容易产生缓存,导致配置看起来改了,实际生效的还是旧的。
2.2 ethtool 不只是看速率,还要看队列和丢包
ethtool 是排查物理网卡问题时不可绕过的工具。ethtool eth0 看协商速率和双工模式,ethtool -S eth0 看网卡统计信息,ethtool -g eth0 看 Ring Buffer 当前值和最大值,ethtool -l eth0 看网卡队列数量。有一回用户反馈千兆网卡传输速度只有 200Mbps,我过去一查,ethtool eth0 显示速度协商成了 100Mbps。换了一根网线后恢复千兆。这种情况从应用层看完全无解,只有 ethtool 能告诉你真相。
另一个场景是网卡驱动统计里的丢包。ethtool -S 输出很长,通常要 grep 关键字。重点关注 rx_missed、rx_dropped、tx_dropped、tx_errors。如果 rx_missed 持续增长,往往是 Ring Buffer 太小,可以尝试 ethtool -G eth0 rx 4096 tx 4096 调整。但注意,Ring Buffer 调大可能增加内存占用和延迟,不是越大越好。调整后必须压测观察,确认丢包消失且延迟没有明显劣化,才算真正解决问题。
2.3 ss 比 netstat 高效:连接状态和缓冲区一眼定位
很多系统里 netstat 已经不再默认安装,而且在高连接数下性能很差,因为它会遍历 /proc/net/tcp。更推荐用 ss,它直接通过内核 netlink 接口获取信息,速度快,输出也更有结构。ss -tnp 能列出当前 TCP 连接,包含状态、接收队列、发送队列和对应进程。排查连接堆积时,我最先看的就是 Recv-Q 和 Send-Q 两列。
code复制$ ss -tnp | grep :8080
$ ss -lnt
$ ss -s
如果 LISTEN 状态下的 Recv-Q 长期不为 0,说明有连接在 accept 队列里等着应用去取,但应用没来得及处理,这通常是应用线程池不够或者 backlog 太小。如果 ESTABLISHED 连接的 Send-Q 一直增长,说明对端接收能力不足,或者网络拥塞导致数据发不出去。ss -s 可以总览整个系统的 TCP 连接统计,比如当前有多少连接处于 TIME_WAIT、ESTABLISHED、SYN-SENT,适合快速感知系统连接健康度。
2.4 tcpdump 抓包,过滤条件决定你能不能从数据里走出来
抓包是所有网络层排错的终点。很多新手一上来就 tcpdump -i any -w /tmp/a.pcap,抓了一堆无关数据包,文件巨大,分析半天也找不到重点。我的习惯是先加 -nn 不做域名和端口反解,-c 100 抓少量包看大概。确认目标后,再用更精确的过滤条件抓取并落盘分析。
code复制$ tcpdump -nn -i eth0 tcp and host 192.168.1.10 and port 8080 -c 100
$ tcpdump -nn -i eth0 tcp and src host 10.0.0.2 and dst port 8080 -w /tmp/app.pcap
抓包时容易踩的坑有三个。第一,-i any 虽然在所有接口上抓包,但某些驱动在 any 模式下不会暴露完整的 VLAN 信息,环境里有 VLAN 时最好指定具体接口。第二,回环接口 lo 上的流量默认不会出现在 eth0 抓包里,需要单独用 -i lo 抓。第三,抓 HTTPS 流量时只能看到加密后的数据,无法看到明文内容,但仍然可以从 TCP 序列号、重传、RST 标志判断链路质量。抓包是用来验证网络行为的,不是用来替代应用日志的。
2.5 ping 与 mtr:先判断是端到端问题还是单点问题
网络不通或延迟高的第一反应是 ping。但 ping 只能判断当前主机到目标是否可达,不能告诉我们瓶颈在哪一跳。mtr 能直观显示从本机到目标之间每一跳的丢包率和延迟,对路由环路、中间链路拥塞非常敏感。我通常这样判断:如果 mtr 里目标节点之前丢包高,但最后一跳正常,那大概率是中间节点做了限速或 ICMP 限制,不一定是真实丢包;如果最后一跳的丢包率也很高,那问题就在目标主机或接近目标主机的链路上。
code复制$ mtr -rw 192.168.1.1
$ ping -c 100 -i 0.2 10.0.0.1
需要注意的是,很多云厂商和运营商会故意丢弃一部分 ICMP 包,导致 mtr 显示中间节点丢包严重,但 TCP 业务却完全正常。所以 mtr 只能作为一个参考,不能单凭它下结论。真正的业务链路质量,还是要靠 TCP 层统计和抓包来验证。
3. TCP 状态机和队列参数才是性能问题的真正源头
3.1 三次握手背后的两条队列
TCP 握手不是简单地在客户端和服务端之间走一圈就够了。服务端 listen 时,内核会维护两条队列:半连接队列,也就是 SYN Queue;全连接队列,也就是 Accept Queue。客户端发来 SYN,服务端在 SYN Queue 里记录连接状态并回复 SYN+ACK;客户端回应 ACK 后,内核把这个连接从 SYN Queue 移到 Accept Queue,等待应用调用 accept()。
这两个队列一旦满了,后果很不一样。SYN Queue 满时,服务端可能直接丢弃 SYN,客户端表现为连接超时;Accept Queue 满时,新连接可能被 RST 或者只能在队列里等待,客户端表现为偶尔能连上、偶尔连不上。相关内核参数包括 net.ipv4.tcp_max_syn_backlog、net.core.somaxconn,以及应用层 listen 时传入的 backlog。实际生效的 Accept Queue 上限是 min(backlog, somaxconn)。你以为 nginx 写了 listen 80 backlog=1024 就足够,但系统 somaxconn 默认只有 128,最终队列长度还是 128。
最直观的检查方式是 ss -lnt,看 LISTEN 状态的 Recv-Q 是否长期不为 0。如果 Recv-Q 稳定在 128 或 256 这种整数,基本可以确定队列满导致连接堆积。解决方法不是只调一个参数,而是同时调大 somaxconn、应用层 backlog,同时确认应用线程池能及时处理连接。否则队列再大,也只是把问题往后推迟。
3.2 TIME_WAIT 不能只靠“关闭它”解决
TIME_WAIT 是主动关闭连接的一方在收到 FIN 后进入的状态,作用是让旧连接中可能残留的报文在网络中自然消亡,避免污染新连接。它本身是 TCP 可靠性的重要保证,不是故障。但当短连接数量巨大时,TIME_WAIT 会占用本地端口,导致新连接无法建立,很多人就开始想办法关掉它。
网上流传最广的两个参数是 tcp_tw_reuse 和 tcp_tw_recycle。先说结论:tcp_tw_recycle 在内核 4.12 之后已经被移除,而且它依赖时间戳选项,在 NAT 环境下会引发非常隐蔽的连接问题,严重时会导致一部分客户端连接被 RST,这类故障很难从应用日志里发现。tcp_tw_reuse 只能用于发起连接的一方,不能解决服务端主动关闭大量连接导致的 TIME_WAIT 堆积,因为服务端不可能复用同一个四元组去连接已经断开的客户端。
更稳妥的办法有三种:扩大临时端口范围,让系统有更多端口可用;在应用层引入连接复用,用长连接替代短连接;或者调大 keepalive,让连接不要频繁断开重建。如果 TIME_WAIT 数量虽然很多,但端口没有耗尽,业务指标正常,完全可以不处理。TCP 设计者让连接等两分钟,是为了更可靠,而不是为了给你添堵。
3.3 拥塞控制算法和缓冲区调参,别盲抄配置
TCP 的单连接吞吐上限,很大程度上由接收窗口和拥塞窗口决定。Linux 里对应的参数是 net.ipv4.tcp_rmem 和 net.ipv4.tcp_wmem,每个参数都有三个数值:最小值、默认值、最大值。内核会自动在范围内调整缓冲区大小,但应用也可以通过 setsockopt 设置 SO_RCVBUF 和 SO_SNDBUF 覆盖默认值。缓冲区太小,大带宽时吞吐上不去;缓冲区太大,延迟和内存占用都会上升,要按业务特点权衡。
拥塞控制算法方面,默认 cubic 在大部分场景表现不错,但在高带宽长链路网络上,BBR 往往能榨出更多带宽。启用 BBR 只需要把 net.ipv4.tcp_congestion_control 改成 bbr,并且确认内核加载了 tcp_bbr 模块。但 BBR 不是银弹,有些老旧网络设备对 pacing 的支持不好,可能导致重传率升高。调优后一定要对比业务延迟、吞吐和重传率三个指标,不能只看带宽跑满了就欢呼。
还有容易被忽略的是 qdisc 队列。默认 pfifo_fast 在突发流量下容易出现 bufferbloat,表现为延迟忽高忽低。配合 BBR 时,通常建议使用 fq 队列,可以用 tc qdisc show 查看当前队列,再用 sysctl net.core.default_qdisc=fq 修改默认值。调完这些参数后,建议用长时间压测验证,不要只跑几十秒就下结论。
3.4 iptables 规则匹配顺序也会制造“网络层假故障”
iptables 是 Linux 网络层一个非常关键的组件,但它也是很多诡异网络问题的来源。iptables 规则是链式匹配、从上到下执行的,一旦命中某条规则,就按照目标动作处理,不再继续往下匹配。这个设计意味着规则的顺序直接影响网络行为。常见的问题是把宽松的 Drop 规则放在允许规则前面,结果原本正常的流量被丢弃,而 iptables 计数里能看到 DROP 数量在涨。
用 iptables -L -n -v 查看每条规则的匹配计数,能帮你快速找到被 DROP 的流量来源。除了 raw、mangle、nat、filter 这几个表,还有 conntrack 状态匹配。很多性能问题出现在 conntrack 表满的情况下,尤其是容器和 NAT 环境,每条新连接都要在 conntrack 表里建立记录,表满后新连接会被直接丢弃,系统日志会出现 nf_conntrack: table full, dropping packet。这种时候光调大 nf_conntrack_max 是治标不治本,还要看业务侧是不是短连接太多、NAT 规则有没有冗余。
排查 iptables 相关问题,我建议先看目标链的计数器,再结合 tcpdump 验证。因为 iptables 规则处理位置比 tcpdump 更靠前,有时 tcpdump 抓不到被 drop 的包,但 iptables 计数已经体现了。反过来也一样,不能因为 iptables 没有 drop 计数,就认为规则没问题,还要确认 NAT 改写是否生效。
4. 一次业务超时从应用到网卡的全链路排查复盘
4.1 现象与第一波误判:慢查询背了锅
之前我们一个后端服务在晚高峰时接口 P99 延迟从 10ms 涨到 200ms,页面大量超时。第一反应是数据库慢查询,因为所有开发都觉得数据库一慢,接口就慢。但检查了数据库监控和慢日志,SQL 执行时间都正常。然后看应用线程栈,发现大量线程阻塞在对下游服务发起的 HTTP 调用上。下游服务的 CPU 也不高,这时我开始怀疑网络层。
传统的应用排查思路到这里就会卡住,因为日志里只能看到调用下游超时,却看不到超时的根因。如果直接去调下游服务的超时时间,问题不会消失。我们后来把目光转向网络层,整个过程让我意识到,很多“应用超时”的本质是网络层丢包,只是丢包率不高,没有达到触发熔断的程度,却足以让长尾延迟非常难看。
4.2 用计数器定位到网卡丢包
我先是跑了 sar -n DEV 1 观察网卡吞吐,发现 eth0 的 rxkB/s 并不算高,但 rxdrop 在增长。接着用 ethtool -S eth0 | grep -i drop,看到 rx_missed 和 rx_dropped 都在涨。然后查看 /proc/net/softnet_stat,发现 CPU0 那一列的 dropped 持续上涨。再看 /proc/interrupts,网卡中断几乎全部落在 CPU0。这时候真相基本清楚了:流量集中在单核,网卡 Ring Buffer 默认值又偏小,晚高峰突发流量把队列塞满,导致丢包。
TCP 层一旦发生丢包,发送端会重传,但重传不是马上发生的,要等超时或快速重传触发。重传带来的额外时延让接口 P99 直接飙升,而平均延迟看起来可能还能接受。这就是为什么很多性能问题只看平均值会完全看不出来,反而要关注长尾延迟。
4.3 调大 Ring Buffer、打开 RPS 以后
我分两步处理。第一步,把网卡 Ring Buffer 调大:
code复制$ ethtool -G eth0 rx 4096 tx 4096
第二步,开启 RPS,把软中断分散到多核。RPS 不需要网卡硬件支持多队列,完全靠内核把收到的包分发给不同 CPU。将 CPU 掩码写入对应队列的 rps_cpus 文件即可:
code复制$ echo f > /sys/class/net/eth0/queues/rx-0/rps_cpus
其中 f 表示使用 CPU0 到 CPU3。调完后再看 ethtool -S 和 /proc/net/softnet_stat,丢包指标都不再增长,P99 恢复到 12ms。这个案例给我的启发是:网络层排查的难点不是命令不会敲,而是不知道每个指标对应哪个环节。如果一开始只抓包,可能都看不到问题,因为丢包发生在驱动和协议栈入口,应用层面的 tcpdump 不一定能看到。
4.4 用 tcpdump 抓包验证重传和乱序
调整完参数后,不能光看丢包计数器降了就算结束,还要验证业务链路上的 TCP 行为。我抓了调整前后各一分钟的包,用 Wireshark 分析重传率。正常情况下,抓到的包应该呈现平滑的序列号推进;如果出现大量 TCP Retransmission 和 Dup ACK,说明网络层仍然存在不稳定因素。
抓包时要注意抓包点位置。如果是跨机器访问,最好在服务端和客户端同时抓包,才能区分丢包是发生在出方向还是入方向。只看单边抓包,很容易把对端没有回复的问题误判成自己这边的问题。通过对比两边的 tcpdump 时间戳和序列号,能更精确定位丢包发生在哪一段链路。
4.5 这类问题容易踩的坑:单一指标会骗人
netstat -i 显示的 RX-DRP 和 ethtool 的 rx_dropped,含义并不完全相同;sar -n EDEV 和 ifconfig 的计数器也是不同采样方式。只看其中一个指标,很容易得出错误结论。我的习惯是每次排查都把四组证据放在一起对比:网卡计数器、软中断统计、TCP 层的重传统计、tcpdump 抓包结果。如果四组数据能互相印证,基本可以定位;如果对不上,就继续往下挖,而不是急着改参数。
这种多指标交叉验证的习惯,尤其适合处理那些“时好时坏”的网络问题。很多网络故障是间歇性的,单次采样可能完全正常,连续轮询才能看到趋势。我一般会同时开几个终端,一个跑 tcpdump,一个轮询 ethtool 和 softnet_stat,另一个记录业务监控,调整参数后至少观察 15 分钟,确认指标没有回弹才算完成。
5. 容器网络层的隐藏变量:namespace、veth 与 iptables
5.1 network namespace 是容器网络的起点
容器和传统虚拟机在网络层面最大的区别在于 network namespace。每个 network namespace 都有自己独立的网卡、IP 地址、路由表和 iptables 规则,所以容器里看到的网络环境和宿主机完全不同。Docker 创建容器时,会创建一对 veth 虚拟网卡,一端放进容器的 network namespace 里,另一端挂在 docker0 网桥上。容器之间通过 docker0 桥接通信,类似在宿主机内部组了一个局域网。
这个设计带来一个常见的问题:在宿主机上执行 ip addr 看不到容器内部的网卡,只能看到 veth 对端。如果你需要进入容器的网络命名空间排查,可以用 ip netns exec 命令,但 Docker 容器的 namespace 不会默认显示在 ip netns list 里。一种办法是通过容器 PID 进入,例如 nsenter -t <pid> -n ip addr,这样才能看到容器内部的真实网络状态。
5.2 Docker 端口映射的 NAT 流程与 iptables 审查
docker run -p 8080:80 之所以能让外部访问容器内的服务,依赖的是 iptables NAT 规则。流量到达宿主机 8080 端口后,内核通过 DNAT 把目标地址改写为容器 IP 的 80 端口,然后通过 docker0 转发给容器。容器的回复包需要做 SNAT,否则源地址是容器内网地址,客户端无法回包。理解这条链路后,排查容器端口不通时就知道先看 iptables 规则,而不是反复检查容器内进程是否启动。
常用排查命令是:
code复制$ iptables -t nat -L -n -v
$ iptables -L FORWARD -n -v
如果发现 DNAT 规则存在但流量依然不通,下一步要看 FORWARD 链的策略。很多新环境里默认 FORWARD 策略是 DROP,或者没有放行 docker0 与 eth0 之间的转发流量,导致容器外网不通。不要只盯着 nat 表,filter 表同样关键。另外,docker 自定义网络与默认 bridge 网络的规则不同,使用自定义网络时,部分流量不走 docker0,排查方式会有差异。
5.3 容器网络排错中容易忽略的三个细节
第一个细节是 net.ipv4.ip_forward 必须为 1,否则跨容器通信和容器访问外网都会失败。安了 docker 之后它通常会自动打开,但如果你手动修改过内核参数,可能把它覆盖成 0。第二个细节是 docker 默认网段可能与公司内网网段冲突。如果容器网段和外部某台机器地址冲突,路由表会变得混乱,出现一些看起来像“网络黑洞”的行为。修改 daemon 配置中的 bip 参数,或者创建自定义网络时选一个不冲突的网段,能避免很多莫名问题。
第三个细节是 conntrack 表项耗尽。NAT 依赖 conntrack 记录连接状态,大量短连接会导致表项快速占满,新连接建立失败。dmesg 里如果出现 nf_conntrack: table full, dropping packet,说明问题已经很严重了。这时候要同时处理:调大 nf_conntrack_max,调小 nf_conntrack_buckets 相关的超时时间,更重要的是减少无效连接,比如让应用使用长连接。只调大表容量,内存开销会很可观,而且流量稍大又会再次占满。
5.4 跨主机和 Overlay 网络,tcpdump 要记得剥壳
现代容器环境里还有一类是跨主机网络,比如 Kubernetes 里的 Overlay 网络,通常基于 VXLAN 封装。宿主机上抓包时看到的以太网头之外还有一层 VXLAN 头,里面才封装着原始的 IP 包。如果你直接在宿主机上抓包并打开,看到的是封装的包,不剥掉外层就看不懂内层流量的源 IP 和目的 IP。
排查这类问题时,建议在容器里或者在虚拟网络设备上抓包。实在不行,就用 tcpdump 的过滤表达式针对 VXLAN 端口和 VNI 做过滤,再用 Wireshark 自动解封装。容器网络比传统物理网络多了好几层封装和转发,每多一层,就多一个可能丢包或引入延迟的点。不要一遇到容器网络问题就只查应用日志,先把网络链路从容器内网卡、veth、docker0、宿主机 eth0 到对端一层层走一遍,很多问题会清晰很多。
最后分享一个我自己的小习惯:在排查网络层问题时,我会开三个终端,一个跑 tcpdump,一个循环刷新 ethtool 和 softnet_stat,另一个记录业务监控曲线。每次调整完参数,不是看一眼数字就完事,而是持续观察至少 15 分钟,看指标是否回弹。网络层的问题最怕“一会儿好一会儿坏”,如果只看一分钟数据,很容易被瞬时抖动骗到。把这套方法用熟了,你也会发现网络层并没有想象中那么玄学。
