引言
在运维过程中,出现网络问题是非常棘手的,当访问某服务出现时通时不通的情况时,我们应该如何排查?是不是网卡配置有问题?是不是内核参数有问题?是多网卡吗?有没有做bond?复杂的网络环境经常搞得人晕头转向,本文就列举笔者运维中遇到过的典型的的网络问题现象,来记录一下其排查的思路和步骤。
问题现象
服务器丢包、网络时断时续、Ping时响应时间忽快忽慢、网卡接收流量正常返回流量失败等网络问题,都可以使用以下通用的排查步骤,再结合自身环境的情况,逐一排查怀疑点,最终定位问题所在。
排查工具及案例分析
我们首先搞清楚数据包从网卡接收,一直到应用程序收到,其中间都发生了什么?
基本流程如下:
数据包进入网卡 --> 网卡硬件缓存FIFO --> 驱动缓冲区(Ring Buffer) ---> 协议栈处理缓冲区(sk_buff) --> 传输层缓冲区(TCP/UDP接收窗口) --> Socket接收队列 --> 用户态应用缓冲(应用程序)
大致流程图如下:
数据到达网卡后,会先存在网卡硬件缓存FIFO,然后通过DMA传输到Ring Buffer指向的skb数据缓冲区(用于存储网络数据包的结构体,包含了数据包的内容和元信息),也就是sk_buff。之后触发cpu硬中断。然后是协议栈(TCP/IP)的处理,内核从Ring Buffer中取出skb,调整指针,发送至传输层缓冲区。然后发送至socket接收缓冲区。最后用户态的应用程序调用recv()将数据包取出。
排查工具介绍
网卡层面排查工具
- 通过ifconfig查看
- # ifconfig em1
- em1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
- inet 172.30.210.199 netmask 255.255.255.0 broadcast 172.30.210.255
- inet6 fe80::7693:5725:5504:e974 prefixlen 64 scopeid 0x20<link>
- ether 1c:83:41:93:20:46 txqueuelen 1000 (Ethernet)
- RX packets 271922 bytes 29995005 (28.6 MiB)
- RX errors 0 dropped 3396 overruns 0 frame 0
- TX packets 325387 bytes 132126535 (126.0 MiB)
- TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
- 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文件
- # cat /proc/net/dev
- Inter-| Receive | Transmit
- face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
- lo: 176076 472 0 0 0 0 0 0 176076 472 0 0 0 0 0 0
- em1: 30638495 276730 0 3620 0 0 0 49 132188337 326011 0 0 0 0 0 0
- 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工具查看网卡数据
- # ethtool -S em1 | grep -iE 'error|drop'
- tx_underflow_errors: 0
- tx_carrier_error_frames: 0
- tx_excessive_deferral_error: 0
- rx_crc_errors: 0
- rx_align_error: 0
- rx_crc_errors_small_packets: 0
- rx_crc_errors_giant_packets: 0
- rx_length_errors: 0
- rx_out_of_range_errors: 0
- rx_fifo_overflow_errors: 0
- rx_watchdog_errors: 0
- rx_receive_error_frames: 0
- fatal_bus_error: 0
复制代码主要观察rx_length_errors、rx_out_of_range_errors和rx_fifo_overflow_errors参数
- netstat工具
netstat查看网卡的收发包以及丢包情况:- # netstat -i
- Kernel Interface table
- Iface MTU RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
- docker0 1500 786339 0 0 0 1440038 0 0 0 BMRU
- em1 1500 3999545 18446744073709551615 4805 0 2278246 0 0 0 BMRU
- lo 65536 774 0 0 0 774 0 0 0 LRU
复制代码 netstat按照协议分类统计: 内核层面排查工具
- ss 和 netstat
ss 和 netstat类似,都可以获取当前系统的网络状态,常用的参数有:
查看所有监听状态(tcp/udp)套接字:- # ss -tnlp
- # ss -unlp
- State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
复制代码 显示所有套接字:- # ss -an
- Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port
- # netstat -anp
- Active Internet connections (servers and established)
- Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
- Active UNIX domain sockets (servers and established)
- 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 Address
ort:远端地址和端口
- 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节点数等,例如:- # lscpu
- 架构: aarch64
- CPU 运行模式: 64-bit
- 字节序: Little Endian
- CPU: 128
- 在线 CPU 列表: 0-127
- 每个核的线程数: 1
- 每个座的核数: 64
- 座: 2
- NUMA 节点: 4
- 厂商 ID: HiSilicon
- BIOS Vendor ID: HiSilicon
- 型号: 0
- 型号名称: Kunpeng-920
- BIOS Model name: HUAWEI Kunpeng 920 7260
- 步进: 0x1
- Frequency boost: disabled
- CPU 最大 MHz: 2600.0000
- CPU 最小 MHz: 200.0000
- BogoMIPS: 200.00
- L1d 缓存: 8 MiB
- L1i 缓存: 8 MiB
- L2 缓存: 64 MiB
- L3 缓存: 256 MiB
- NUMA 节点0 CPU: 0-31
- NUMA 节点1 CPU: 32-63
- NUMA 节点2 CPU: 64-95
- NUMA 节点3 CPU: 96-127
- Vulnerability Gather data sampling: Not affected
- Vulnerability Itlb multihit: Not affected
- Vulnerability L1tf: Not affected
- Vulnerability Mds: Not affected
- Vulnerability Meltdown: Not affected
- Vulnerability Mmio stale data: Not affected
- Vulnerability Retbleed: Not affected
- Vulnerability Spec store bypass: Not affected
- Vulnerability Spectre v1: Mitigation; __user pointer sanitization
- Vulnerability Spectre v2: Not affected
- Vulnerability Srbds: Not affected
- Vulnerability Tsx async abort: Not affected
- 标记: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma
- dcpop asimddp asimdfhm
复制代码 可见cpu为128核心,NUMA节点有四个。
查看网卡绑定在那个NUMA上
- # cat /sys/class/net/enp125s0f0/device/numa_node
- 0
复制代码 所以网卡enp125s0f0在numa node 0上的cpu中运行,性能更好
查看网卡的亲和性CPU
- cat /sys/class/net/enp125s0f0/device/local_cpulist
- 0-31
复制代码 可见更倾向于使用CPU 0-31
查看CPU使用率、负载情况
使用top、ps等命令,确认负载情况
查看网卡队列
网卡多队列技术是指将各个队列通过中断绑定到不同的核上,从而解决网络I/0带宽升高时单核CPU的处理瓶颈,提升网络PPS和带宽性能。在相同的网络PPS和网络带宽的条件下,与1个队列相比,2个队列最多可提升性能达50%到100%,4个队列的性能提升更大。- # ethtool -l enp125s0f1
- Channel parameters for enp125s0f1:
- Pre-set maximums:
- RX: 0
- TX: 0
- Other: 1
- Combined: 1
- Current hardware settings:
- RX: 0
- TX: 0
- Other: 1
- Combined: 1
复制代码 这里可以看到,其网卡队列只有一个,这里有两种情况:
1是此网卡的确是单队列网卡;2是此网卡是多队列网卡,但由于机器启动时BIOS中NIC的配置里,只配置了1个队列。
如何判断网卡是否支持多队列呢?按照以下步骤排查:
(1)查看网卡的bus-info- # ethtool -i enp125s0f0
- driver: hns3
- version: 4.19.90-89.21.v2401.ky10.aarch6
- firmware-version: 1.9.40.6
- expansion-rom-version:
- bus-info: 0000:7d:00.0
- supports-statistics: yes
- supports-test: yes
- supports-eeprom-access: no
- supports-register-dump: yes
- supports-priv-flags: yes
复制代码 (2)查看网卡是否支持多队列- # lspci -vvv | grep -A 80 '7d:00.0' | grep -i msi-x
- Capabilities: [a0] MSI-X: Enable+ Count=131 Masked-
复制代码 此回显表示,此网卡支持多队列,且最高支持131队列。但为什么只显示为1个队列呢?很有可能是在BIOS中的配置引起的。
查看Ring Buffer大小
- # ethtool -g enp125s0f0
- Ring parameters for enp125s0f0:
- Pre-set maximums:
- RX: 32760 # 最大支持 32760
- RX Mini: 0
- RX Jumbo: 0
- TX: 32760 # 最大支持 32760
- Current hardware settings:
- RX: 1024 # 目前设置为 1024
- RX Mini: 0
- RX Jumbo: 0
- TX: 1024 # 目前设置为 1024
复制代码 以上基本的配置确认后,下一步查看丢包情况
此案例ping时仅有延迟,无丢包情况,所以把重点放在cpu资源、中断的情况上。
分析网卡中断情况
- # grep hns3-0000:7d:00.0 /proc/interrupts
- 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,与本次问题处理无关):- # cat /proc/interrupts
- CPU0 CPU1 CPU2 CPU3
- 0: 3710374484 0 0 0 IO-APIC-edge timer
- 1: 20 0 0 0 IO-APIC-edge i8042
- 6: 5 0 0 0 IO-APIC-edge floppy
- 7: 0 0 0 0 IO-APIC-edge parport0
- 8: 0 0 0 0 IO-APIC-edge rtc
- 9: 0 0 0 0 IO-APIC-level acpi
- 12: 240 0 0 0 IO-APIC-edge i8042
- 14: 11200026 0 0 0 IO-APIC-edge ide0
- 67: 19386473 0 0 0 IO-APIC-level eth0
- NMI: 0 0 0 0 Non-maskable interrupts
- LOC: 3737150067 3737142382 3737145101 3737144204 Local timer interrupts
- ERR: 0
- MIS: 0
复制代码
- 第一列为如 0、1、67 等,表示硬件设备的中断请求编号
- 与CPU对应的每列数字表示对应 CPU 核心处理该中断的总次数。例如, 3710374484`表示 CPU0 处理了 IRQ0 中断超过 37 亿次
- 倒数第二列为中断类型标志
- 最后一列代表关联设备/驱动名称
通过上述方式再结合实际信息可以确认,网卡enp125s0f0设备的硬中断处理发生在CPU19中。
扩展:
还可以通过cat /proc/irq/263/smp_affinity来确认263编号的设备,其进行中断的cpu是第几个,例如:- cat /proc/irq/263/smp_affinity
- 00000000,00000000,00000000,00080000
复制代码这里展示的是十六进制的写法,00080000等同于0x00080000
首先需要将十六进制转为二进制,十六进制中每一位数对应二进制的四位数(一个十六进制位代表四个比特位),前边的三段全为0,所以不用计数,只需计算第四段的00080000,换算为:- 十六进制位: 0 0 0 8 0 0 0 0
- 二进制位: 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 --> 保存退出
此时再查看网卡队列数:- # ethtool -l enp125s0f0
- Channel parameters for enp125s0f0:
- Pre-set maximums:
- RX: 0
- TX: 0
- Other: 1
- Combined: 60
- Current hardware settings:
- RX: 0
- TX: 0
- Other: 1
- Combined: 60
复制代码 再次查看中断分布情况:- # grep hns3-0000:7d:00.0 /proc/interrupts | wc -l
- 60
复制代码 已经是60个队列了,再结合前边网卡的亲和性CPU配置:- # cat /sys/class/net/enp125s0f0/device/local_cpulist
- 0-31
复制代码 此网卡更亲和于NUMA 0节点第0-31个CPU,所以我们可以先将队列改为32个,使其分布到0-31个cpu核中:- # ethtool -L enp125s0f0 combined 32
- # ethtool -l enp125s0f0
- Channel parameters for enp125s0f0:
- Pre-set maximums:
- RX: 0
- TX: 0
- Other: 1
- Combined: 60
- Current hardware settings:
- RX: 0
- TX: 0
- Other: 1
- Combined: 32
- # grep hns3-0000:7d:00.0 /proc/interrupts | wc -l
- 32
复制代码 随后还可以通过cat /proc/irq//smp_affinity查看其32个队列分别绑定哪个cpu核,也可以通过echo "5" > /proc/irq/268/smp_affinity来手动指定某个队列绑定哪个cpu核,用来实现网卡中断的效率最大化。
内核进程调度延迟排查
若经排查发现CPU19使用率正常,此时可怀疑是否为某些进程占有了CPU资源或者软锁不可抢占,导致内核调度被阻塞,最终导致的ping延迟,且收到包的时候统一往后推延。
此时我们需要使用perf等内核性能分析工具来进行排查诊断:- # 捕捉并记录与进程/线程调度相关的性能事件,-T(任务生命周期跟踪),sleep 10(持续记录10s)
- perf sched record -aT sleep 10
复制代码 再分析生成的perf.data:- # 分析任务的平均/最大调度延迟
- 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的容量。
查看当前容量设置及最大支持容量:- # ethtool -g enp125s0f0
- Ring parameters for enp125s0f0:
- Pre-set maximums:
- RX: 32760 # 最大支持 32760
- RX Mini: 0
- RX Jumbo: 0
- TX: 32760 # 最大支持 32760
- Current hardware settings:
- RX: 1024 # 目前设置为 1024
- RX Mini: 0
- RX Jumbo: 0
- TX: 1024 # 目前设置为 1024
复制代码 最大支持32760,而目前是1024,可适量调大其值:- # ethtool -G enp125s0f0 rx 4096 tx 4096
- # ethtool -g enp125s0f0
- Ring parameters for enp125s0f0:
- Pre-set maximums:
- RX: 32760
- RX Mini: 0
- RX Jumbo: 0
- TX: 32760
- Current hardware settings:
- RX: 4096
- RX Mini: 0
- RX Jumbo: 0
- TX: 4096
复制代码 netdev_max_backlog溢出
cat /proc/net/softnet_stat文件是记录每个CPU的软中断处理情况。- # cat /proc/net/softnet_stat
- 00120244 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
- 0013df4c 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
- 001458ef 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
- 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参数是否过小:- # sysctl -a | grep net.core.netdev_max_backlog
- 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参数数值:- # sysctl -a | grep net.core.netdev_budget
- net.core.netdev_budget = 1024
- 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都有一个读写缓冲区。
- 读缓冲区:缓存远端发来的数据,如果读缓冲区已满,就不能再接收新的数据。
- 写缓冲区:缓存要发出去的数据,如果写缓冲区已满,应用程序的写操作就会堵塞。
内核参数配置如下:- # sysctl -a | grep mem
- net.core.optmem_max = 524288
- net.core.rmem_default = 2097152
- net.core.rmem_max = 16777216
- net.core.wmem_default = 2097152
- net.core.wmem_max = 2097152
- net.ipv4.tcp_mem = 180486 240648 360972
- net.ipv4.tcp_rmem = 4096 131072 6291456
- net.ipv4.tcp_wmem = 4096 16384 4194304
- net.ipv4.udp_mem = 360972 481297 721944
- net.ipv4.udp_rmem_min = 4096
- 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的统计信息:- # cat /proc/net/udp
- sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode ref pointer drops
- 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相关内容:- # cat /proc/net/snmp
- Udp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti MemErrors
- Udp: 505 24 0 566 0 0 0 2695 0
复制代码重点关注InErrors和RcvbufErrors两个指标
当看到以上两个指标数值基本一样时,99.9%的问题出现在socket receive queue上了。
半连接队列和全连接队列溢出
相信大家对TCP三次握手四次挥手的流程已熟记心中(记不住也没关系,贴图了)
在建立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:- # ethtool -k enp125s0f0 | grep -E "tcp-segmentation-offload|generic-segmentation-offload"
- tcp-segmentation-offload: on
- generic-segmentation-offload: on
复制代码 下图为关闭TSO/GSO、开启TSO和开启GSO的数据发送过程对比:
LRO和GRO:
网卡接收时同样有LRO和GRO:
LRO是将网卡接收到的多个数据包合并成一个大的数据包,然后再传递给网络协议栈处理的技术,这样可以提高系统接收数据包的能力,减轻CPU负载。
GRO基本与LRO类似,客户了LRO的一些缺点,更通用,后续的驱动都使用GRO的接口。
查看网卡是否支持且开启LRO/GRO:- # ethtool -k enp125s0f0 | grep offload
- generic-receive-offload: on
- large-receive-offload: off [fixed]
复制代码 数据接收过程基本与上图一直,只不过方向变为了由网卡发向应用程序,合包的步骤没有改变。
可能出现问题的场景:
当网卡仅支持TSO(发包)或LRO(收包)其一的时候,会导致数据包收发频率不一致,最终导致丢包的情况。现象大概率为仅收包时丢包,或是仅发包时丢包,这时需要注意检查其分包合包的配置。
开启或关闭分包和发包的命令如下:- # 示例
- ethtool -K enp125s0f0 tso off
复制代码 MTU/MSS导致丢包
MTU
Maximum Transmit Unit,最大传输单元,即物理接口(数据链路层)提供给其上层(通常为ip层)单次最大传输数据大小;以普遍使用的以太网接口为例,缺省值MTU=1500Byte,这是以太网接口对IP层的约束。
如果ip层有1500Byte数据需要发送,需要分片才能完成发送,这些分片有一个共同点,即Header ID相同。
查看MTU:- # ip a
- 2: enp125s0f0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
- # netstat -i
- Kernel Interface table
- Iface MTU RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
- enp125s0f0 1500 13429030 0 444287 0 19834143 0 0 0 BMRU
- # cat /sys/class/net/enp125s0f0/mtu
- 1500
复制代码 修改MTU:- # 临时修改,重启后失效
- ifconfig ens01 mtu 1200
- # 永久修改
- 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值大小:
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:- # ping -c 3 -M do -s 1480 172.30.210.125
- PING 172.30.210.125 (172.30.210.125) 1480(1508) bytes of data.
- ping: local error: message too long, mtu=1500
- ping: local error: message too long, mtu=1500
- ping: local error: message too long, mtu=1500
- 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时,即可正常通信:- # ping -c 3 -M do -s 1472 172.30.210.125
- PING 172.30.210.125 (172.30.210.125) 1472(1500) bytes of data.
- 1480 bytes from 172.30.210.125: icmp_seq=1 ttl=63 time=0.783 ms
- 1480 bytes from 172.30.210.125: icmp_seq=2 ttl=63 time=0.668 ms
- 1480 bytes from 172.30.210.125: icmp_seq=3 ttl=63 time=0.658 ms
- 3 packets transmitted, 3 received, 0% packet loss, time 2034ms
复制代码 sock结构体中pmtudisc变量可通过setsockopt系统调用进行设置,用户也可以通过ip命令对MTU值进行锁定,不允许修改,如下命令将锁定到网关的mtu值为1300字节:- ip route add 0.0.0.0 via 192.168.18.252 mtu lock 1300
复制代码 结语
本文主要对遇到过的网络问题总结一下排查思路,方便日后处理网络问题,仅为个人观点,为大家提供参考。
排查方法不一定全面,抛砖引玉,有更多网络知识、精通网络的大拿们可以将更好的思路分享出来,文中有描述错误的地方,也欢迎指正。
来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除 |