Linux网络故障定位汇总 一、常用工具1.1 nettrace对于Linux系统内核中的复杂网络异常问题可使用nettrace进行故障分析和诊断详细介绍和使用方法可参见这里nettrace工具1.2 ssss命令可以打印出来套接口的各种元数据信息在套接口调试方面比netstat要强大的多可以打印出来套接口的内存、网络拥塞等各种信息。对于TCP类型的套接口常用命令为ss -itme在下面的套接口内存信息中要关注一下t后面的数值这个代表了已经从buffer中发送但是还没离开本机的报文的数据量也就是当前套接口积压在qdisc或者网卡发送队列中的报文。skmem:(r0,rb87380,t0,tb2627840,f0,w0,o0,bl0,d0)上面内存相关的各个参数的意义分别是r收包缓冲区中的数据量rb收包缓冲区大小t已经被发送到L3但是还没从网卡发送出去的数据量tb发包缓冲区大小f预分配内存大小w发包缓冲区中的数据量还没发到L3blbacklog队列中的数据大小ts sack cubic wscale:6,10 rto:243 rtt:42.26/10.76 ato:40 mss:1228 pmtu:1500 rcvmss:1228 advmss:1448 cwnd:5 ssthresh:2 bytes_sent:14097941 bytes_retrans:11352 bytes_acked:14086553 bytes_received:1136677 segs_out:22647 segs_in:16449 data_segs_out:19616 data_segs_in:5418 send 1.2Mbps lastsnd:2 lastrcv:2 lastack:2 pacing_rate 1.4Mbps delivery_rate 6.6Mbps delivered:19524 busy:3316193ms unacked:1 retrans:0/132 dsack_dups:40 rcv_rtt:73.636 rcv_space:40636 rcv_ssthresh:186984 minrtt:34.34上面的一些信息是TCP套接口的一些扩展和内部信息重点说明一下其中的一些字段的含义rto重传定时器超时时间ato延迟ack定时器超时时间cwnd拥塞窗口大小ssthresh慢启动阈值retrans第一个代表正在重传的报文量第二个是总共重传过的报文个数dsack_dups通过DSACK确认的伪重传报文数app_limited当前套接口处于app_limited状态即由于上层没有足够的数据包而限制了带宽busy处于发包状态的时间rwnd_limited处于零窗口状态的时间对端收包慢sndbuf_limited由于内存不足而导致的发包受限的时间pacing_rate根据拥塞窗口和mss估算出来的发包带宽delivery_rate根据发包数据评估出来的带宽delivered已经发送且被确认的报文数量minrtt不考虑延迟ACK和压缩ACK计算出来的rtt可以理解为是环境延迟rtt第一个代表srtt即经过平滑处理的rtt第二个是rtt的平均方差代表着rtt的稳定性backoff当前正在超时重传的报文的回退次数可以理解为重传的次数其他用法//查看套接口使用情况 ss -s1.3 netstat使用netstat可以查看丢包统计信息。这个统计信息是全局的无法针对特定的连接。通过统计信息可以知道一些常见的问题。基本命令为netstat -s常见的统计对应的原因见表Linux网络统计参数详解除此之外还可以使用该工具查看当前网络连接的一些基本信息比如使用以下命令即可查看当前的TCP连接情况netstat -tpn在出现网络性能问题的时候可以使用这个命令简单地看一下套接口收、发包缓冲区中的数据大小。1.4 conntrackconntrack可以用来跟踪当前节点上的conntrack记录信息这对于定位nat场景的问题比较有帮助。常用的命令包括//列出当前跟踪到的所有的连接信息可以使用-s/-d/-r/-q等参数进行过滤 conntrack -L //查看conntrack的一些统计信息 conntrack -S //监控conntrack事件包括conntrack状态的变更等 conntrack -E当出现conntrack异常的时候还可以打开系统上的conntrack日志来进行监控打开方式//6代表着TCP协议 echo 6 /proc/sys/net/netfilter/nf_conntrack_log_invalid然后通过dmegs即可查看到异常日志。1.5 tclinux系统中一般使用tc来进行qdisc常用的命令如下//列出所有的网卡的qdisc队列信息 tc qdisc show //查看所有的qdisc队列上的统计信息主要看丢包和backlog队列信息 tc -s qdisc show //将eth0上的队列设置为pfifo类型 tc qdisc replace dev eth0 root pfifo1.6 ipfmLinux系统中进行流量统计的工具能够针对IP进行流量不是带宽统计。这个工具以daemon的方式运行在后台可以按照ipfm范例中的例子来进行配置文件的配置。配置文件中可以配置采样周期、日志位置、日志清理周期等信息。1.7 atop可以对系统的CPU、内存等整体情况进行监控可用于排查系统环境对网络的影响。常用命令atop -w atop.log 1这个命令会持续进行数据采集并将采集到的数据保存到atop.log文件中。使用命令atop -r atop.log可读取监控数据。t和T可进行采样时间的前进和后退。1.8 火焰图从下面的链接下载FlameGraphFlameGraph使用perf进行采样随后使用script命令报错到文件# perf record -F 1000 -a -g -- sleep 60 # perf script out.perf根据perf采样数据生成对应的火焰图# stackcollapse-perf.pl out.perf out.folded # flamegraph.pl out.folded kernel.svg1.9 tshark这个是命令行版本的wireshark对于centos系统可以通过安装wireshark软件包来获取该工具。常规的使用方法包括# 过滤TCP重传报文tshark-rxxx.pcap-Ytcp.analysis.retransmission or tcp.analysis.fast_retransmissiontshark-rxxx.pcap-Ytcp.seq 2880750022 tcp.seq 28806844861.10 iptables使用下面的命令可以对iptables中的规则进行traceiptables -A PREROUTING -t raw -p tcp -j TRACE modprobe ipt_LOG modprobe nf_log_ipv4 sysctl net.netfilter.nf_log.2nf_log_ipv4然后从/var/log/kern.log文件中可以看到对应的日志。二、开箱脚本下面的脚本可以在不额外安装软件的情况下获取到一些网络问题定位过程中的信息开箱即用方便快捷。2.1 丢包重传系统总体丢包重传(while true; do (cat /proc/net/snmp cat /proc/net/netstat) | awk (NR % 2)!0{proto$1; for (i2; iNF; i) data[i]$i; }(NR % 2)0{print proto; for (i2; iNF; i)printf %-32s:%d\n, data[i], $i}; echo ----; sleep 1; done) | awk -F : BEGIN{ir0; ip0; pr0; pp0; r0; p0; psrisrpfrifr0;}/RetransSegs/{r$2; if (ir0) prirr;}/TCPDSACKRecv /{p$2; if (ip0) ppipp;}/TCPFastRetrans/{fr$2; if (ifr0) ifrpfrfr; }/TCPSlowStartRetrans/{sr$2; if (isr0) psrisr$2;}/----/{ print strftime(%Y-%m-%d %H:%M:%S); printf retransmission: %12d %8d(pps) %8d(inc)\n, r, r-pr, r-ir; printf pseudo retrans: %12d %8d(pps) %8d(inc)\n, p, p-pp, p-ip; printf fast retrans: %14d %8d(pps) %8d(inc)\n, fr, fr-pfr, fr-ifr; printf timeout retrans: %11d %8d(pps) %8d(inc)\n, sr, sr-psr, sr-isr; printf drop retrans: %23d(pps) %8d(inc)\n, r-pr-(p-pp), r-ir-(p-ip); ppp; prr; psrsr; pfrfr;}单个套接口丢包重传while true; do ss -itn; echo ----; sleep 1; done | awk BEGIN{print strftime(%Y-%m-%d %H:%M:%S);}/ESTAB/{src$4; dst$5;}/^\t/{if (match($0, / retrans:[^ ]/)) { retranssubstr($0, RSTART 1, RLENGTH); retransgensub(/retrans:[0-9]*\//, , 1, retrans); if (match($0, /dsack_dups:[^ ]*/)) {dropgensub(/dsack_dups:/, , 1, substr($0, RSTART, RLENGTH)); dropretrans-drop; } else {dropretrans;} printf %s-%s total:%d drop:%d\n, src, dst, retrans, drop;} }/----/{print strftime(%Y-%m-%d %H:%M:%S);}2.2 SS监控watch -n 1 -d ss -itme -n watch -n 1 -d ss -itme -n src ip:port watch -n 1 -d ss -itme -n dst ip:port2.3 格式化统计信息用于将snmp/netstat接口中的信息从行显示转为列显示可用于类似的其他场景中cat /proc/net/snmp | awk (NR % 2)!0{proto$1; for (i2; iNF; i) data[i]$i; }(NR % 2)0{print proto; for (i2; iNF; i)printf %-32s:%d\n, data[i], $i}2.4 收发包CPU查找查看哪个队列在收包/发包watch -n 1 -d ethtool -S eth0 | grep -E rx[0-9]_bytes watch -n 1 -d ethtool -S eth0 | grep -E tx[0-9]_bytes找到目标队列所使用的中断号cat /proc/interrupts | grep key_wordkey_wordvirtio网卡virtio.*input. 或者 virtio.*output.mlx网卡mlx5_comp.*获取该中断绑定的CPUcat /proc/irq/irq_number/smp_affinity_list如果绑定了多个CPU核那么可以观察哪个CPU上中断增加量比较多来判断是哪个核在收包watch -n 1 -d cat /proc/interrupts | grep irq_number:也可以使用下面的中断的命令来直接观察中断量高的来判断是否是发包/收包中断。2.5 高频中断使用下面的命令可以查看哪个中断在哪个核上的频率比较高while true do cat /proc/interrupts sleep 1 done | awk /CPU0/{ if (NR ! 1) print strftime(%Y-%m-%d %H:%M:%S) } NR1{ cpu_count NF } $1 ~ /^[0-9]:/{ irq $1; desc for (i 2; i cpu_count 2; i) { key irq SUBSEP i; prev_val data[key]; data[key] $i; if (prev_val ) continue; delta $i - prev_val; if (delta 0) continue; desc sprintf(%s cpu%-3d:%8-d, desc, i - 2, delta); } if (desc ! ) { for (i cpu_count 2; i NF; i) desc desc $i printf %-6s%s\n, irq, desc; } fflush(stdout); }下面的命令是查看每秒新增软中断的whiletruedocat/proc/softirqssleep1done|awk /CPU0/{ if (NR ! 1) print strftime(%Y-%m-%d %H:%M:%S) } NR1{ cpu_count NF } $1 ~ /:/{ irq $1; desc for (i 2; i cpu_count 2; i) { key irq SUBSEP i; prev_val data[key]; data[key] $i; if (prev_val ) continue; delta $i - prev_val; if (delta 0) continue; desc sprintf(%s cpu%-3d:%-4d, desc, i - 2, delta); } if (desc ! ) printf %8s %s\n, irq, desc; fflush(stdout); }2.6 CPU处理报文统计下面的脚本可以统计每秒钟每个核上处理的接收到的报文数量、丢弃的报文数量和每个核上backlog队列中的报文数量whiletruedoecho----cat/proc/net/softnet_statsleep1done|awk--non-decimal-data BEGIN{ field[1] PROCESSED; field[2] DROP; field[12] BACKLOG; printf(%8s, ); for (k in field) printf(%12s, field[k]); printf(\n); } /----/{ cpu_startNR; if (NR ! 1) print strftime(%Y-%m-%d %H:%M:%S) } $1 ~ /[0-9a-z]/{ cpu NR - cpu_start - 1; changed 0 for (k in field) { processed 0x $k; key cpu SUBSEP k; if (!(key in data)) break; field[k] processed - data[key]; if (field[k] 0) changed 1; } if (changed) { printf(cpu%3-d: , cpu); for (k in field) printf(%12d, field[k]); printf(\n); } for (k in field) data[cpu SUBSEP k] 0x $k; }2.7 RPS/XPS获取当前网卡的RPS/XPS配置信息find/sys/class/net/eth0/queues/-typed-namerx-*|xargs-I%cat%/rps_cpusfind/sys/class/net/eth0/queues/-typed-nametx-*|xargs-I%cat%/xps_cpus设置网卡的RPS/XPSfind/sys/class/net/eth0/queues/-typed-namerx-*|xargs-I%sh-cecho ffff %/rps_cpusfind/sys/class/net/eth0/queues/-typed-nametx-*|xargs-I%sh-cecho ffff %/xps_cpus2.8 抓包间隔tcpdump-r./test.pcap|grepICMP|awkBEGIN{delta0; prev0; ts0;}{cursubstr($1, 7); if (prev) {tmp cur - prev; if (tmp delta) {delta tmp; ts $1;}} prev cur;}END{print delta; print ts;}2.9 GRO效果可以使用perf来统计一些计数。比如统计收包的数量这里的数量是经过GRO的报文的数量perfstat-enet:netif_receive_skb --sleep10这个是统计所有的收包的数量perf stat -e net:napi_gro_receive_entry -- sleep 10通过对比上下两个参数的比例就可以知道GRO的效果。可以两个一起采集perfstat-enet:netif_receive_skb,net:napi_gro_receive_entry --sleep10三、常见问题3.1 TW套接口在短连接的场景下比如HTTP打流测试、NGINX代理等环境上容易在短时间内积累大量的timewait类型的套接口。相比于常规的套接口tw套接口在内核中占用的资源要小得多但是过多的tw依然会导致资源消耗、性能下降、端口耗尽的问题。端口耗尽在没有开启tw_reuse的情况下tw类型的套接口也会占据当前的端口直到超时后被释放。过多的端口占用会引发严重的性能问题因为内核在查找可用端口时采用的是线性遍历查找的方式。如果可用端口数量比较少那么查找的效率就会非常低消耗大量的CPU。通过下面的命令打开tw_reuse可以避免这一问题echo 1 /proc/sys/net/ipv4/tcp_tw_reuse在当前的系统中这个参数默认是开启状态的。性能下降除了端口消耗引入的问题tw套接口过多也会导致套接口查找的性能下降这是因为tw套接口和普通的TCP套接口一样都处于ehash哈希表中。tw套接口过多会导致收包阶段在进行套接口查找的时候性能下降。当前系统上的tw套接口的总数量可以通过下面的接口来进行调整当数量达到这个阈值后会直接被释放$ cat /proc/sys/net/ipv4/tcp_max_tw_buckets 1048576超时时间在原生的Linux系统中tw套接口的存活时间是60s这个是固定的。但是在咱们内核剑走偏锋提供了接口可以调整这个时间。所以减少tw套接口的另一个方式就是减小这个值$ cat /proc/sys/net/ipv4/tcp_tw_timeout 603.2 网络收发包受干扰如果用户的业务进程CPU占用比较高那么是可能会影响到网络收发包的。这是因为现在的网络收发包基本上都是在软中断线程中被处理的而软中断线程的优先级默认是普通优先级调度算法是CFS完全公平调度。为了提升收发包的效率可以调整其优先级比如将所有的软中断线程的优先级改成实时线程RR调度策略pgrep ksoftirqd | xargs -I % chrt -r -p 20 %3.3 带宽上不去带宽打不上去原因无非有三种环境因素网络链路带宽受限出现拥塞丢包环境因素网络链路存在硬件故障出现概率丢包RTT较长1s内能够传递的数据的次数变少发端/收端性能问题接收端收包不及时或者发端发送数据不及时丢包限速针对上面的四个原因排查和解决的方法也不同需要分别对待。首先需要排查是哪种原因导致的带宽受限即先观察是否存在丢包重传。有两种方式根据2.1中提供的脚本来观察系统层面是否有丢包重传重点关注drop retrans这个指标。这个方式观察的是整个系统层面的丢包重传。根据2.1中提供的脚步来观察目标的连接是否有丢包重传重点关注drop这个指标。确定有丢包后可以将拥塞算法改成bbr算法这个算法允许一定的硬件丢包sysctl -w net.ipv4.tcp_congestion_controlbbr如果带宽有一定的提升说明是硬件错误丢包否则说明是链路上出现了网络拥塞导致的丢包。RTT限速使用命令ss -itn可以确定目标流的rtt情况。在网络没有出现丢包的情况下所能达到的理论带宽为带宽 发送窗口 * 1000 / rtt而TCP连接的发送窗口是由发送端的发包缓冲区和接收端的收包缓冲区共同决定的。从上面的公式可以看出来在rtt一定的情况下增加发送窗口可以提升带宽。如果当前连接的rtt比较高可以使用下面的命令提升窗口来提升带宽sysctl -w net.ipv4.tcp_rmem4096 131072 33554432 sysctl -w net.ipv4.tcp_wmem4096 65536 33554432注意如果这里的rtt远高于环境本身的延迟情况那么就考虑延迟发生在ECS内部需要使用下面的性能限速进行排查。如果rtt远大于min_rtt那么网络延迟可能不是由环境导致的而是由于ECS内部。性能限速上面的两个原因主要分析了环境导致的带宽受限这里主要考虑的是ECS内部因素导致的限速。ECS内部的问题主要考虑性能方面导致的带宽受限最基本的包括发端发送不及时、收端接收不及时等可以按照下面的思路来排查使用ss -tn命令来查看发送和接收端的buffer队列情况。对于接收端观察接收队列确保里面没有大量堆积的数据对于发送端观察发送队列确保里面有等待发送的数据。使用ss -tmn命令来观察发端堆积在网卡和qdisc中的数据重点关注指标tt指标过高的话会导致TSQ限速TCP Small Queue。默认情况下这个值不能高于当前带宽 / 1024。使用ss -tin命令来观察发送端是否存在rwnd_limited、sndbuf_limited的字样这两个代表着当前的TCP数据传输由于0窗口、发送缓冲区不足而停止发送的时长。