找回密码
 立即注册
首页 业界区 业界 经验贴!万字总结网卡丢包及ping延迟等网络问题排查思路 ...

经验贴!万字总结网卡丢包及ping延迟等网络问题排查思路

窖咎 2025-7-2 14:03:43
引言

在运维过程中,出现网络问题是非常棘手的,当访问某服务出现时通时不通的情况时,我们应该如何排查?是不是网卡配置有问题?是不是内核参数有问题?是多网卡吗?有没有做bond?复杂的网络环境经常搞得人晕头转向,本文就列举笔者运维中遇到过的典型的的网络问题现象,来记录一下其排查的思路和步骤。
问题现象

服务器丢包、网络时断时续、Ping时响应时间忽快忽慢、网卡接收流量正常返回流量失败等网络问题,都可以使用以下通用的排查步骤,再结合自身环境的情况,逐一排查怀疑点,最终定位问题所在。
排查工具及案例分析

我们首先搞清楚数据包从网卡接收,一直到应用程序收到,其中间都发生了什么?
基本流程如下:
数据包进入网卡 --> 网卡硬件缓存FIFO --> 驱动缓冲区(Ring Buffer) ---> 协议栈处理缓冲区(sk_buff) --> 传输层缓冲区(TCP/UDP接收窗口) -->  Socket接收队列 --> 用户态应用缓冲(应用程序)
大致流程图如下:
1.png

数据到达网卡后,会先存在网卡硬件缓存FIFO,然后通过DMA传输到Ring Buffer指向的skb数据缓冲区(用于存储网络数据包的结构体,包含了数据包的内容和元信息),也就是sk_buff。之后触发cpu硬中断。然后是协议栈(TCP/IP)的处理,内核从Ring Buffer中取出skb,调整指针,发送至传输层缓冲区。然后发送至socket接收缓冲区。最后用户态的应用程序调用recv()将数据包取出。
排查工具介绍

网卡层面排查工具


  • 通过ifconfig查看
    1. # ifconfig em1
    2. em1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
    3.         inet 172.30.210.199  netmask 255.255.255.0  broadcast 172.30.210.255
    4.         inet6 fe80::7693:5725:5504:e974  prefixlen 64  scopeid 0x20<link>
    5.         ether 1c:83:41:93:20:46  txqueuelen 1000  (Ethernet)
    6.         RX packets 271922  bytes 29995005 (28.6 MiB)
    7.         RX errors 0  dropped 3396  overruns 0  frame 0
    8.         TX packets 325387  bytes 132126535 (126.0 MiB)
    9.         TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    10.         device interrupt 94
    复制代码
    RX(receive)表示接收报文,TX(transmit)表示发送报文。
    对于接收数据,重点需要关注errors、dropped和overruns三个参数值的大小:
    RX errors:表示总的收包的错误数量,这包括too-long-frames错误,Ring Buffer溢出错误,crc校验错误,帧同步错误,FIFO overruns以及missed pkg等等。
    RX dropped:表示数据包已经进入了Ring Buffer,但是由于内存不够等系统原因,导致在拷贝到内存的过程中被丢弃。
    RX overruns:表示了fifo的overruns,这是由于Ring Buffer(aka Driver Queue)传输的IO大于kernel能够处理的IO导致的,overruns的增大意味着数据包没到Ring Buffer就被网卡物理层给丢弃了,而CPU无法及时处理中断是造成Ring Buffer满的原因之一
  • 查看/proc/net/dev文件
    1. # cat /proc/net/dev
    2. Inter-|   Receive                                                |  Transmit
    3. face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
    4.     lo:  176076     472    0    0    0     0          0         0   176076     472    0    0    0     0       0          0
    5.    em1: 30638495  276730    0 3620    0     0          0        49 132188337  326011    0    0    0     0       0          0
    6. docker0: 2367947     283    0    0    0     0          0         0    65366     393    0    0    0     0       0          0
    复制代码
    同样需要特别关注errs、drop和fifo的值,其对应了ifconfig的errors、dropped和overruns,其中drop和fifo的最大区别是:
    drop:表示这个数据包已经进入到网卡的接收缓存fifo队列,并且开始被系统中断处理准备进行数据包拷贝(从网卡缓存fifo队列拷贝到系统内存),但由于此时的系统原因(比如内存不够),导致这个数据包被丢掉。
    fifo:表示这个数据包还没被进入到网卡的接收缓存fifo队列就被丢掉,因此,此时的网卡fifo是满的。至于为什么是满的,可能是系统繁忙,来不及响应网卡中断,导致网卡里数据包没有及时拷贝到系统内存,所以在遇到此情况时,可以检测cpu负载与cpu中断情况。
  • ethtool工具查看网卡数据
    1. # ethtool -S em1 | grep -iE 'error|drop'
    2.      tx_underflow_errors: 0
    3.      tx_carrier_error_frames: 0
    4.      tx_excessive_deferral_error: 0
    5.      rx_crc_errors: 0
    6.      rx_align_error: 0
    7.      rx_crc_errors_small_packets: 0
    8.      rx_crc_errors_giant_packets: 0
    9.      rx_length_errors: 0
    10.      rx_out_of_range_errors: 0
    11.      rx_fifo_overflow_errors: 0
    12.      rx_watchdog_errors: 0
    13.      rx_receive_error_frames: 0
    14.      fatal_bus_error: 0
    复制代码
    主要观察rx_length_errors、rx_out_of_range_errors和rx_fifo_overflow_errors参数

  • netstat工具
    netstat查看网卡的收发包以及丢包情况:
    1. # netstat -i
    2. Kernel Interface table
    3. Iface             MTU    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
    4. docker0          1500   786339      0      0 0       1440038      0      0      0 BMRU
    5. em1              1500  3999545 18446744073709551615   4805 0       2278246      0      0      0 BMRU
    6. lo              65536      774      0      0 0           774      0      0      0 LRU
    复制代码
    netstat按照协议分类统计:
    1. # netstat -s
    复制代码
内核层面排查工具


  • ss 和 netstat
    ss 和 netstat类似,都可以获取当前系统的网络状态,常用的参数有:
    查看所有监听状态(tcp/udp)套接字:
    1. # ss -tnlp   
    2. # ss -unlp
    3. State  Recv-Q  Send-Q   Local Address:Port   Peer Address:Port  Process
    复制代码
    显示所有套接字:
    1. # ss -an
    2. Netid  State  Recv-Q  Send-Q  Local Address:Port  Peer Address:Port
    3. # netstat -anp
    4. Active Internet connections (servers and established)
    5. Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    6. Active UNIX domain sockets (servers and established)
    7. Proto RefCnt Flags       Type       State         I-Node   PID/Program name     Path
    复制代码
    Linux netstat命令结果分为两部分:

    • Active lnternet connections(有源Internet连接):用于网络连接传输
    • Active UNIX domain sockets(有源UNIX套接字):只能用于本地通信,性能比第一种好
    Active Internet connections 部分:

    • Proto:通过此字段可以看到连接使用的是什么协议,主要是TCP、UDP,还有TCP6、UDP6这就是使用了ipv6的协议
    • Recv-Q(非LISTEN状态下):表示收到的数据已经在本地接收缓冲,但是还有多少没有被进程取走,recv()。如果接收队列Recv-Q一直处于阻塞状态,也就是Recv-Q值不为零并且值挺大,可能是遭受了Dos 攻击。
    • Send-Q(非LISTEN状态下):对方没有接收的数据,仍然在本地缓冲区中,或者说没有收到对方的Ack。如果发送队列Send-Q不能很快的清零,可能是有应用向外发送数据包过快,或者是对方接收数据包不够快。这时候就要调整发送速度或者接收速度了。例如:如果看到是大量的send-Q,可以判定是发送数据给目的地址的时候出现了阻塞的问题,导致了包堆积在本地缓存中,不能成功发出去。那么问题就可能产生在了客户端,根据业务逻辑可以看看是不是客户端发送的TCP长连接数量过多。验证办法,尝试减少客户端和服务的长连接,查看效果。
    • Local Address:本地地址和端口
    • Foreign Address/Peer Addressort:远端地址和端口
    • State:标识tcp连接状态
    • PID/Program name:使用该连接的进程id和name
    为什么Recv-Q和Send-Q标注了为非LISTEN状态下?因为在LISTEN状态下:

    • Recv-Q表示的当前等待服务端调用 accept 完成三次握手的 listen backlog数值,也就是说,当客户端通过 connect()去连接在listen()的服务端时,这些连接会-直处于Recv-Q这个queue里面直到被服务端accept()
    • Send-Q表示的则是最大的 listen bäcklog 数值。
    Active UNIX domain sockets部分:

    • RefCnt:引用计数(即通过此套接字附加的进程),也就是连接到本套接口上的进程数量
    • Flags:SO_ACCECPTON进程正在等待连接请求还未连接的套接字
    • Type:套接字类型有SOCK_DGRAM数据报(无连接)模式、SOCK_STREAM流(连接)套接字、SOCK_RAW原始套接字、SOCK_RDM这个服务器提供可靠传递的消息、SOCK_SEOPACKET一个顺序数据包套接字和SOCK_PACKET原始接口访问套接字
    • State:套接字当前的状态。包括FREE分配套接字、LISTENING套接字正在侦听连接请求、CONNECTING套接字即将建立连接、CONNECTED已连接套接字、DISCONNECTING套接字正在断开连接和empty套接字未连接到另一个套接字
    • I-Node:文件的Inode节点
    • Path:附加到套接字的相应进程的路径名

  • sysctl:内核参数查看及修改命令
  • perf+ftrace:内核性能分析工具
    场景命令示例实时监控中断sudo perf top -e irq:irq_handler_entry统计中断事件sudo perf stat -e irq:* -a sleep 10记录调用栈sudo perf record -e irq:irq_handler_entry -g -a -- sleep 30生成火焰图perf script系统中断计数器watch -n 1 "cat /proc/interrupts"
案例分析

ping延时问题分析

问题现象

压力测试场景下,无压力测试时,网络时延正常,有压力测试时,网络时延不稳定,体现在ping时延时较高。
排查思路

查看CPU情况

执行lscpu查看CPU基本情况,核心数、numa节点数等,例如:
  1. # lscpu
  2. 架构:                              aarch64
  3. CPU 运行模式:                      64-bit
  4. 字节序:                            Little Endian
  5. CPU:                                128
  6. 在线 CPU 列表:                     0-127
  7. 每个核的线程数:                    1
  8. 每个座的核数:                      64
  9. 座:                                2
  10. NUMA 节点:                         4
  11. 厂商 ID:                           HiSilicon
  12. BIOS Vendor ID:                     HiSilicon
  13. 型号:                              0
  14. 型号名称:                          Kunpeng-920
  15. BIOS Model name:                    HUAWEI Kunpeng 920 7260
  16. 步进:                              0x1
  17. Frequency boost:                    disabled
  18. CPU 最大 MHz:                      2600.0000
  19. CPU 最小 MHz:                      200.0000
  20. BogoMIPS:                          200.00
  21. L1d 缓存:                          8 MiB
  22. L1i 缓存:                          8 MiB
  23. L2 缓存:                           64 MiB
  24. L3 缓存:                           256 MiB
  25. NUMA 节点0 CPU:                    0-31
  26. NUMA 节点1 CPU:                    32-63
  27. NUMA 节点2 CPU:                    64-95
  28. NUMA 节点3 CPU:                    96-127
  29. Vulnerability Gather data sampling: Not affected
  30. Vulnerability Itlb multihit:        Not affected
  31. Vulnerability L1tf:                 Not affected
  32. Vulnerability Mds:                  Not affected
  33. Vulnerability Meltdown:             Not affected
  34. Vulnerability Mmio stale data:      Not affected
  35. Vulnerability Retbleed:             Not affected
  36. Vulnerability Spec store bypass:    Not affected
  37. Vulnerability Spectre v1:           Mitigation; __user pointer sanitization
  38. Vulnerability Spectre v2:           Not affected
  39. Vulnerability Srbds:                Not affected
  40. Vulnerability Tsx async abort:      Not affected
  41. 标记:                              fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma
  42.                                     dcpop asimddp asimdfhm
复制代码
可见cpu为128核心,NUMA节点有四个。
查看网卡绑定在那个NUMA上
  1. # cat /sys/class/net/enp125s0f0/device/numa_node
  2. 0
复制代码
所以网卡enp125s0f0在numa node 0上的cpu中运行,性能更好
查看网卡的亲和性CPU
  1. cat /sys/class/net/enp125s0f0/device/local_cpulist
  2. 0-31
复制代码
可见更倾向于使用CPU 0-31
查看CPU使用率、负载情况

使用top、ps等命令,确认负载情况
查看网卡队列

网卡多队列技术是指将各个队列通过中断绑定到不同的核上,从而解决网络I/0带宽升高时单核CPU的处理瓶颈,提升网络PPS和带宽性能。在相同的网络PPS和网络带宽的条件下,与1个队列相比,2个队列最多可提升性能达50%到100%,4个队列的性能提升更大。
  1. # ethtool -l enp125s0f1
  2. Channel parameters for enp125s0f1:
  3. Pre-set maximums:
  4. RX:             0
  5. TX:             0
  6. Other:          1
  7. Combined:       1
  8. Current hardware settings:
  9. RX:             0
  10. TX:             0
  11. Other:          1
  12. Combined:       1
复制代码
这里可以看到,其网卡队列只有一个,这里有两种情况:
1是此网卡的确是单队列网卡;2是此网卡是多队列网卡,但由于机器启动时BIOS中NIC的配置里,只配置了1个队列。
如何判断网卡是否支持多队列呢?按照以下步骤排查:
(1)查看网卡的bus-info
  1. # ethtool -i enp125s0f0
  2. driver: hns3
  3. version: 4.19.90-89.21.v2401.ky10.aarch6
  4. firmware-version: 1.9.40.6
  5. expansion-rom-version:
  6. bus-info: 0000:7d:00.0
  7. supports-statistics: yes
  8. supports-test: yes
  9. supports-eeprom-access: no
  10. supports-register-dump: yes
  11. supports-priv-flags: yes
复制代码
(2)查看网卡是否支持多队列
  1. # lspci -vvv | grep -A 80 '7d:00.0' | grep -i msi-x
  2.         Capabilities: [a0] MSI-X: Enable+ Count=131 Masked-
复制代码
此回显表示,此网卡支持多队列,且最高支持131队列。但为什么只显示为1个队列呢?很有可能是在BIOS中的配置引起的。
查看Ring Buffer大小
  1. # ethtool -g enp125s0f0
  2. Ring parameters for enp125s0f0:
  3. Pre-set maximums:
  4. RX:             32760        # 最大支持 32760
  5. RX Mini:        0
  6. RX Jumbo:       0
  7. TX:             32760        # 最大支持 32760
  8. Current hardware settings:
  9. RX:             1024        # 目前设置为 1024
  10. RX Mini:        0
  11. RX Jumbo:       0
  12. TX:             1024        # 目前设置为 1024
复制代码
以上基本的配置确认后,下一步查看丢包情况
  1. # ifconfig enp125s0f0
复制代码
此案例ping时仅有延迟,无丢包情况,所以把重点放在cpu资源、中断的情况上。
分析网卡中断情况
  1. # grep hns3-0000:7d:00.0 /proc/interrupts
  2. 263:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0     781452          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0   ITS-MSI 65536001 Edge      hns3-0000:7d:00.0-TxRx-0
复制代码
其中hns3为网卡的driver名称,0000:7d:00.0为网卡的bus-info。可以使用ethtool -i enp125s0f0查看
使用以上命令确认处理中断的cpu是第几个核心,例如(以下只是讲述如何判断处理中断发生在哪个核心cpu,与本次问题处理无关):
  1. # cat /proc/interrupts
  2.            CPU0       CPU1       CPU2       CPU3      
  3.   0:  3710374484          0          0          0   IO-APIC-edge    timer
  4.   1:         20          0          0          0   IO-APIC-edge    i8042
  5.   6:          5          0          0          0   IO-APIC-edge    floppy
  6.   7:          0          0          0          0   IO-APIC-edge    parport0
  7.   8:          0          0          0          0   IO-APIC-edge    rtc
  8.   9:          0          0          0          0   IO-APIC-level   acpi
  9. 12:        240          0          0          0   IO-APIC-edge    i8042
  10. 14:   11200026          0          0          0   IO-APIC-edge    ide0
  11. 67:   19386473          0          0          0   IO-APIC-level   eth0
  12. NMI:          0          0          0          0   Non-maskable interrupts
  13. LOC: 3737150067   3737142382   3737145101   3737144204   Local timer interrupts
  14. ERR:          0
  15. MIS:          0
复制代码


  • 第一列为如 0、1、67 等,表示硬件设备的中断请求编号
  • 与CPU对应的每列数字表示对应 CPU 核心处理该中断的总次数。例如, 3710374484`表示 CPU0 处理了 IRQ0 中断超过 37 亿次
  • 倒数第二列为中断类型标志
  • 最后一列代表关联设备/驱动名称
通过上述方式再结合实际信息可以确认,网卡enp125s0f0设备的硬中断处理发生在CPU19中。
扩展:
还可以通过cat /proc/irq/263/smp_affinity来确认263编号的设备,其进行中断的cpu是第几个,例如:
  1. cat /proc/irq/263/smp_affinity
  2. 00000000,00000000,00000000,00080000
复制代码
这里展示的是十六进制的写法,00080000等同于0x00080000
首先需要将十六进制转为二进制,十六进制中每一位数对应二进制的四位数(一个十六进制位代表四个比特位),前边的三段全为0,所以不用计数,只需计算第四段的00080000,换算为:
  1. 十六进制位:  0    0    0    8    0    0    0    0
  2. 二进制位:  0000 0000 0000 1000 0000 0000 0000 0000
复制代码
二进制1000表示:1 * 2^3 + 0 * 2^2 + 0 * 2^1 + 0 * 2^0 = 8
转换为二进制位后,从右往左数表示从CPU0开始,一直到CPUx。此时1在从右往左数的第20位,故其中断的CPU为CPU19。
知道这个规律后,就可以直接使用echo xx > /proc/irq/67/smp_affinity来修改中断的CPU核心。
解决思路

此问题现象是ping时有延时,未涉及TCP及应用,则在此不考虑tcp缓冲区以及socket缓冲区满的情况,也未发现丢包情况,故很大可能性为中断CPU资源不足,或内核进程调度延迟导致网卡驱动收包延迟,再针对性的进行排查。
CPU资源不足排查

通过上述排查步骤,可以确认网卡队列最大可支持131个,但仅开启了一个,且绑定的中断CPU为CPU19。
此时可通过sosreport或其他监控服务来确认CPU19是否存在长时间使用率较高的情况,若存在,则表示频繁的中断导致CPU19较为繁忙,中断处理不及时,最终导致ping时偶发延时高的现象。
这时就要开启网卡的多队列,使中断分布到多个CPU下,分散单个CPU的处理压力,本示例为鲲鹏920服务器,具体调整多队列操作如下:
进入BIOS --> Advanced --> Lom Configuration --> NIC Configuration --> Port1 Configuration --> 修改Tqp Number为60 --> 修改Function Number为1 --> 保存退出
此时再查看网卡队列数:
  1. # ethtool -l enp125s0f0
  2. Channel parameters for enp125s0f0:
  3. Pre-set maximums:
  4. RX:             0
  5. TX:             0
  6. Other:          1
  7. Combined:       60
  8. Current hardware settings:
  9. RX:             0
  10. TX:             0
  11. Other:          1
  12. Combined:       60
复制代码
再次查看中断分布情况:
  1. # grep hns3-0000:7d:00.0 /proc/interrupts | wc -l
  2. 60
复制代码
已经是60个队列了,再结合前边网卡的亲和性CPU配置:
  1. # cat /sys/class/net/enp125s0f0/device/local_cpulist
  2. 0-31
复制代码
此网卡更亲和于NUMA 0节点第0-31个CPU,所以我们可以先将队列改为32个,使其分布到0-31个cpu核中:
  1. # ethtool -L enp125s0f0 combined 32
  2. # ethtool -l enp125s0f0
  3. Channel parameters for enp125s0f0:
  4. Pre-set maximums:
  5. RX:             0
  6. TX:             0
  7. Other:          1
  8. Combined:       60
  9. Current hardware settings:
  10. RX:             0
  11. TX:             0
  12. Other:          1
  13. Combined:       32
  14. # grep hns3-0000:7d:00.0 /proc/interrupts | wc -l
  15. 32
复制代码
随后还可以通过cat /proc/irq//smp_affinity查看其32个队列分别绑定哪个cpu核,也可以通过echo "5" > /proc/irq/268/smp_affinity来手动指定某个队列绑定哪个cpu核,用来实现网卡中断的效率最大化。
内核进程调度延迟排查

若经排查发现CPU19使用率正常,此时可怀疑是否为某些进程占有了CPU资源或者软锁不可抢占,导致内核调度被阻塞,最终导致的ping延迟,且收到包的时候统一往后推延。
此时我们需要使用perf等内核性能分析工具来进行排查诊断:
  1. # 捕捉并记录与进程/线程调度相关的性能事件,-T(任务生命周期跟踪),sleep 10(持续记录10s)
  2. perf sched record -aT sleep 10
复制代码
再分析生成的perf.data:
  1. # 分析任务的平均/最大调度延迟
  2. perf sched latency -s max
复制代码
若内核线程包括软中断(ksoftirqd)都存在较大的调度延迟,例如2s,这说明从挂起软中断,要经历2s多的等待,软中断才得以执行。网卡的收包流程中,当网卡硬件收到报文时,先触发硬中断,在硬中断中触发软中断(NET_RX_SOFTIRQ),实际的收包程序是在软中断中完成的。而此场景中软中断延迟2s,最终导致了ping延迟的现象。
内核进程调度延迟的根本原因,大多是某个进程占有了CPU资源或者软锁且不可抢占导致内核调度被阻塞住了。需要结合实际的业务场景,抓取火焰图,甚至再结合内核源码,进一步定位是什么进程导致。
丢包问题分析

问题现象

服务器网卡存在发收包有丢包情况,或用户应用存在发收包丢包情况。
排查思路

文章开篇已经对ifconfig、ethtool、cat /proc/net/dev、ss等命令进行解释,并说明了如何判断丢包产生在哪个环节,再针对具体的环节进行排查和优化。
解决思路

以下列举并分析几种常见的丢包场景,以及排查的思路。
Ring Buffer溢出

Ring Buffer的空间是有限的,当收到的数据包速率大于单个CPU处理速度的时候,Ring Buffer就可能被占满,占满后再进来的数据包,将会被丢弃。

  • 中断能力不足导致的Ring Buffer占满
    如果网卡为单队列,网络数据包接收的速率又非常高,就有可能导致CPU中断不及时,数据包处理缓慢,最终导致Ring Buffer占满,新数据包被丢弃。
    这个就要根据上文中“CPU资源不足排查”步骤,来将网卡修改为多队列,增快CPU处理中断及数据包的速度,来避免此问题。
  • RingBuffer配置过小,或内核处理网络数据较慢
    当RingBuffer配置过小,或内核处理网络数据较慢时,会导致RingBuffer拥堵,可以调大RingBuffer的容量。
    查看当前容量设置及最大支持容量:
    1. # ethtool -g enp125s0f0
    2. Ring parameters for enp125s0f0:
    3. Pre-set maximums:
    4. RX:             32760        # 最大支持 32760
    5. RX Mini:        0
    6. RX Jumbo:       0
    7. TX:             32760        # 最大支持 32760
    8. Current hardware settings:
    9. RX:             1024        # 目前设置为 1024
    10. RX Mini:        0
    11. RX Jumbo:       0
    12. TX:             1024        # 目前设置为 1024
    复制代码
    最大支持32760,而目前是1024,可适量调大其值:
    1. # ethtool -G enp125s0f0 rx 4096 tx 4096
    2. # ethtool -g enp125s0f0
    3. Ring parameters for enp125s0f0:
    4. Pre-set maximums:
    5. RX:             32760
    6. RX Mini:        0
    7. RX Jumbo:       0
    8. TX:             32760
    9. Current hardware settings:
    10. RX:             4096
    11. RX Mini:        0
    12. RX Jumbo:       0
    13. TX:             4096
    复制代码
netdev_max_backlog溢出

cat /proc/net/softnet_stat文件是记录每个CPU的软中断处理情况。
  1. # cat /proc/net/softnet_stat
  2. 00120244 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
  3. 0013df4c 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
  4. 001458ef 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
  5. 00cda99f 00000000 0000000a 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
复制代码
它共有10列,每列都是16进制的数字,分别表示:
第一列:processed表示处理了多少个数据包
第二列:dropped表示丢弃了多少个数据包,因为队列满了或者内存不足
第三列:time_squeeze表示发生了多少次时间压缩,即软中断处理时间超过了预设的限制
第四列:cpu_collision表示发生了多少次CPU碰撞,即同一个数据包被多个CPU同时处理
第五列:received_rps表示接收到了多少个RPS(接收包转发)的数据包,即数据包被转发到了另一个CPU处理
第六列:flow_limit_count表示发生了多少次流量限制,即数据包被丢弃,因为超过了RPS的限制。
第七列:backlog_limit_count表示发生了多少次积压限制,即数据包被丢弃,因为超过了软中断队列的限制。
第八列:droppe_no_skbuf表示丢弃了多少个数据包,因为没有足够的sk_buff结构体来存储它们
第九列:no_cpu_resources表示丢弃了多少个数据包,因为没有足够的CPU资源来处理它们
第十列:reserved保留未用。
其中,第二列即代表由于netdev_max_backlog队列溢出而丢弃的包总数,若其不为0,可以通过修改增大net.core.netdev_max_backlog来尝试解决。
查看netdev_max_backlog参数是否过小:
  1. # sysctl -a | grep net.core.netdev_max_backlog
  2. net.core.netdev_max_backlog = 8000
复制代码
net.core.netdev_max_backlog用于控制网络接口接收数据包时的队列容量,如超过该阈值,新包将会被丢弃,可以适当调大。
net.core.netdev_budget导致丢包

/proc/net/softnet_stat文件第三列如果一直增加的话,表示SoftIRQ获取的CPU时间太短,来不及处理足够多的网络包,那么就需要减小netdev_budget的值,一直到不再增加。
查看net.core.netdev_budget参数数值:
  1. # sysctl -a | grep net.core.netdev_budget
  2. net.core.netdev_budget = 1024
  3. net.core.netdev_budget_usecs = 2000
复制代码
net.core.netdev_budget用于控制单次软中断(SoftIRQ)处理的数据包数量,低延迟敏感场景通常会降低其值,缩短软中断执行时间。高吞吐场景或dropped包持续增大时,可以适当增大其值。
netdev_budget_usecs表示单次软中断的最大处理时间(单位:微秒),避免软中断过度使用CPU
net.core.netdev_budget如果设置太小,软中断就会变多,软中断变多,就会导致sys上升,产生额外的cpu开销,导致load上升,load上升会影响处理包的个数会越来越少。
TCP/UDP/Socket Buffer溢出

Socket可以屏蔽Linux内核不同协议的差异,为应用程序提供统一的访问接口。每个Socket都有一个读写缓冲区。

  • 读缓冲区:缓存远端发来的数据,如果读缓冲区已满,就不能再接收新的数据。
  • 写缓冲区:缓存要发出去的数据,如果写缓冲区已满,应用程序的写操作就会堵塞。
内核参数配置如下:
  1. # sysctl -a | grep mem
  2. net.core.optmem_max = 524288
  3. net.core.rmem_default = 2097152
  4. net.core.rmem_max = 16777216
  5. net.core.wmem_default = 2097152
  6. net.core.wmem_max = 2097152
  7. net.ipv4.tcp_mem = 180486       240648  360972
  8. net.ipv4.tcp_rmem = 4096        131072  6291456
  9. net.ipv4.tcp_wmem = 4096        16384   4194304
  10. net.ipv4.udp_mem = 360972       481297  721944
  11. net.ipv4.udp_rmem_min = 4096
  12. net.ipv4.udp_wmem_min = 4096
复制代码
参数含义net.core.optmem_max单个套接字辅助缓冲区的最大值,控制元数据存储net.core.rmem_default全局的套接字接收缓冲区默认初始大小net.core.rmem_max全局的套接字接收缓冲区最大允许值net.core.wmem_default全局的套接字写入缓冲区默认初始大小net.core.wmem_max全局的套接字写入缓冲区最大允许值net.ipv4.tcp_mem整个TCP协议栈的内存使用,三个值 min pressure maxnet.ipv4.tcp_rmem单个 TCP 连接的接收缓冲区大小,三个值 min default maxnet.ipv4.tcp_wmem单个 TCP 连接的写入缓冲区大小,三个值 min default maxnet.ipv4.udp_mem所有UDP套接字的内存总量,三个值 min pressure maxnet.ipv4.udp_rmem_min每个 UDP 套接字接收缓冲区的最小值net.ipv4.udp_wmem_min每个 UDP 套接字写入缓冲区的最小值tcp buffer溢出:
tcp缓冲区不存在溢出丢包的情况,tcp有流量控制策略,根据tcp的流量控制中的滑动窗口机制,接收方会将窗口大小给到发送方,发送方再根据窗口大小来发送数据。
如果tcp socket buffer满的话,抓包时可以体现,会出现window zero的提示,这时就表示tcp buffer满了,通知发送方数据发慢一点,所以有时tcp传输非常慢时,也有可能是tcp buffer满了。
udp buffer溢出:
通过/proc/net/udp文件可以监控udp socket的统计信息:
  1. # cat /proc/net/udp
  2.    sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode ref pointer drops            
  3. 19338: 0100007F:0143 00000000:0000 07 00000000:00000000 00:00000000 00000000     0        0 93274 2 000000001c69a696 0
复制代码
重点关注tx_queue、rx_queue和drops
drops的数值一直增长的话,就是丢包了(仅代表接收路径上丢包),而tx_queue和rx_queue则是内核层面为socket收发数据分配的buffer。
还可以通过/proc/net/snmp文件查看udp相关内容:
  1. # cat /proc/net/snmp
  2. Udp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti MemErrors
  3. Udp: 505 24 0 566 0 0 0 2695 0
复制代码
重点关注InErrors和RcvbufErrors两个指标
当看到以上两个指标数值基本一样时,99.9%的问题出现在socket receive queue上了。
半连接队列和全连接队列溢出

相信大家对TCP三次握手四次挥手的流程已熟记心中(记不住也没关系,贴图了)
2.png

在建立TCP连接时,有两个非常重要的队列:半连接队列和全连接队列

  • 半连接队列:也称Syn队列,由/proc/sys/net/ipv4/tcp_max_syn_backlog指定,内核参数为net.ipv4.tcp_max_syn_backlog,存放收到SYN但未完成三次握手的连接,如果此队列满了,将不会保存新的tcp连接请求。
  • 全连接队列:也称Accept队列,由/proc/sys/net/core/somaxconn指定,内核参数为net.core.somaxconn,存放已完成三次握手、等待应用accept()的连接,如果此队列满了,新的连接请求到达时,将会被服务器拒绝或者丢弃,发送一个Connection refused错误信息到client。
网卡分片和组包时丢包

TSO和GSO:
TSO是利用网卡对TCP数据包分片,减轻CPU负荷的一种技术,也成为LSO(Large segment offload),其针对的是TCP,还有一种针对UDP的分片技术叫UFO。
GSO是TSO的增强,GSO不只针对TCP,针对任何协议,比TSO更通用,其会推迟数据分片直至发送到网卡驱动之前,此时会检查网卡是否支持分片功能(TSO、UFO),如果支持直接发送到网卡,如果不支持就在此时进行分片后再发往网卡。
如果网卡支持TSO/GSO,可以把最多64K大小的TCP payload(有效荷载)直接往下传给协议栈,此时ip层也不会进行分片,网卡会生成TCP/IP包头和帧头,这样可以offload很多协议栈上的内存操作,节省CPU资源。
查看网卡是否支持且开启TSO/GSO:
  1. # ethtool -k enp125s0f0 | grep -E "tcp-segmentation-offload|generic-segmentation-offload"
  2. tcp-segmentation-offload: on
  3. generic-segmentation-offload: on
复制代码
下图为关闭TSO/GSO、开启TSO和开启GSO的数据发送过程对比:
3.png

LRO和GRO:
网卡接收时同样有LROGRO
LRO是将网卡接收到的多个数据包合并成一个大的数据包,然后再传递给网络协议栈处理的技术,这样可以提高系统接收数据包的能力,减轻CPU负载。
GRO基本与LRO类似,客户了LRO的一些缺点,更通用,后续的驱动都使用GRO的接口。
查看网卡是否支持且开启LRO/GRO:
  1. # ethtool -k enp125s0f0 | grep offload
  2. generic-receive-offload: on
  3. large-receive-offload: off [fixed]
复制代码
数据接收过程基本与上图一直,只不过方向变为了由网卡发向应用程序,合包的步骤没有改变。
可能出现问题的场景:
当网卡仅支持TSO(发包)或LRO(收包)其一的时候,会导致数据包收发频率不一致,最终导致丢包的情况。现象大概率为仅收包时丢包,或是仅发包时丢包,这时需要注意检查其分包合包的配置。
开启或关闭分包和发包的命令如下:
  1. # 示例
  2. ethtool -K enp125s0f0 tso off
复制代码
MTU/MSS导致丢包

MTU
Maximum Transmit Unit,最大传输单元,即物理接口(数据链路层)提供给其上层(通常为ip层)单次最大传输数据大小;以普遍使用的以太网接口为例,缺省值MTU=1500Byte,这是以太网接口对IP层的约束。
如果ip层有1500Byte数据需要发送,需要分片才能完成发送,这些分片有一个共同点,即Header ID相同。
查看MTU:
  1. # ip a
  2. 2: enp125s0f0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
  3. # netstat -i
  4. Kernel Interface table
  5. Iface             MTU    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
  6. enp125s0f0       1500 13429030      0 444287 0      19834143      0      0      0 BMRU
  7. # cat /sys/class/net/enp125s0f0/mtu
  8. 1500
复制代码
修改MTU:
  1. # 临时修改,重启后失效
  2. ifconfig ens01 mtu 1200
  3. # 永久修改
  4. nmcli connection modify ens01 +ethernet.mtu 1200
复制代码
MSS
Maximum Segment Size(最大报文段大小),TCP提交给ip层最大分段大小,不包含TCP Header和TCP Option,只包含TCP Payload。
MSS是TCP用来限制application层最大的发送字节数,如果底层物理接口MTU=1500Byte,则MSS=1500-20(IP Header)-20(TCP Header)=1460Byte,如果application有2000Byte的数据发送,需要两个segment才可以完成,一个1460,一个540。
UDP的MSS=1500-20(IP Header)=1480Byte,并且MSS值不能小于最小的advmss值ip_rt_min_advmss,可通过PROC文件设置此值,默认为256。
在TCP三次握手期间抓包,可以看到协商后设置的MSS值大小:
4.png

PMTU
PMTU是Path MTU的缩写,指的是在网络通信中,从源主机到目的主机所经过的所有网络设备中,能够通过的最大数据包大小。PMTU的原理是通过不断发送大小不同的数据包,直到发现一个最大的数据包能够成功传输,从而确定PMTU的大小。
其主要是应用在TCP/IP协议中,用于避免IP分片。当一个数据包的大小超过了某个网络设备的MTU时,该设备会将数据包分片,然后再将分片后的数据包发送出去。这样会增加网络传输的负担,降低网络传输的效率。因此,TCP/IP协议中的PMTU发现机制可以帮助避免IP分片,提高网络传输的效率。
系统中通过内核参数/proc/sys/net/ipv4/ip_no_pmtu_disc来控制全局默认策略,当为0时,表示进行PMTU的发现,为1时表示不尽兴PMTU的发现。
可以使用ping命令来检测当前PMTU:
  1. # ping -c 3 -M do -s 1480 172.30.210.125
  2. PING 172.30.210.125 (172.30.210.125) 1480(1508) bytes of data.
  3. ping: local error: message too long, mtu=1500
  4. ping: local error: message too long, mtu=1500
  5. ping: local error: message too long, mtu=1500
  6. 3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2053ms
复制代码
-s:指定发送ICMP包的大小
-M:传输过程中,是否允许分包。Do为不允许,当收到长度比自身设置的MTU大的IP数据包,则不能拆分,只能丢弃;want表示在本地分段;dont表示不涉及DF(Don't Fragment)标志。
ICMP的IP数据包 = IP首部 + ICMP首部 + ICMP的PDU
IP首部占用20字节,ICMP首部占用8字节,所以当-s 1480,以及-M do时,MTU值1480+20+8=1508,超过1500,导致发送失败,并返回了PMTU值大小为1500,当将-s指定为1472时,即可正常通信:
  1. # ping -c 3 -M do -s 1472 172.30.210.125
  2. PING 172.30.210.125 (172.30.210.125) 1472(1500) bytes of data.
  3. 1480 bytes from 172.30.210.125: icmp_seq=1 ttl=63 time=0.783 ms
  4. 1480 bytes from 172.30.210.125: icmp_seq=2 ttl=63 time=0.668 ms
  5. 1480 bytes from 172.30.210.125: icmp_seq=3 ttl=63 time=0.658 ms
  6. 3 packets transmitted, 3 received, 0% packet loss, time 2034ms
复制代码
sock结构体中pmtudisc变量可通过setsockopt系统调用进行设置,用户也可以通过ip命令对MTU值进行锁定,不允许修改,如下命令将锁定到网关的mtu值为1300字节:
  1. ip route add 0.0.0.0 via 192.168.18.252 mtu lock 1300
复制代码
结语

本文主要对遇到过的网络问题总结一下排查思路,方便日后处理网络问题,仅为个人观点,为大家提供参考。
排查方法不一定全面,抛砖引玉,有更多网络知识、精通网络的大拿们可以将更好的思路分享出来,文中有描述错误的地方,也欢迎指正。

来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除

相关推荐

您需要登录后才可以回帖 登录 | 立即注册