十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

存储服务器大流量写入丢包断流?先查网卡Ring Buffer

存储服务器大流量写入丢包断流?先查网卡Ring Buffer 做过存储运维的朋友大概率都经历过这种场景业务端同时往存储服务器写数据刚开始跑得好好的突然某个客户端的写入速度掉到几十KB甚至直接断开重连。你赶紧ping一下服务器发现网络本身是通的带宽也看似正常折腾半天没找到根源。如果你遇到这种怪异现象我建议你先别急着怀疑网线、交换机或者磁盘阵列先看一眼网卡的 Ring Buffer。这个问题的典型表现就是存储服务器大流量写入时出现丢包、断流而排查的重点往往被放在TCP参数、磁盘IO、甚至光纤模块上很少有人第一时间想到网卡环形队列。但恰恰是这个看似底层的硬件缓冲设置在iSCSI、NFS、SMB这类存储协议场景下特别容易成为瓶颈。这篇文章我就把整个排查和处理过程拆开讲包括Ring Buffer的工作原理、怎么用ethtool定位硬丢包、如何调整和持久化配置以及调完之后仍然掉链子的后续排查方向。适合所有用CentOS/RHEL系系统、把服务器当存储用或者自己搭过iSCSI/NFS共享的朋友参考。1. 现象与初步判断存储服务器写入“卡住”的典型表现1.1 现场症状从“速度骤降”到“连接断开”多数存储故障不是突然完全瘫痪而是先出现一系列恼人的“慢性病”。我遇到过的典型案例是一台32核、64GB内存的存储服务器通过四口千兆网卡bonding对外提供iSCSI服务后端是硬件RAID阵列。白天业务正常时多台客户端同时往里写视频素材速度能稳定在200MB/s左右但一到高峰时段几个客户端几乎同时开始大批量写文件写入速度就像坐了过山车——从200MB/s瞬间跌到10MB/s随后某个客户端的iSCSI会话直接报错断开应用层提示“连接超时重试中”。在服务器上观察系统负载并不高CPU使用率不到30%磁盘IO也没到极限iostat显示的utilization还远未打满。这时候很多人会怀疑是网络带宽问题可千兆网卡理论带宽也就120MB/s左右四口bonding按说足够。但问题恰恰出在网卡收包环节而不在带宽总量。1.2 为什么存储写入场景最容易踩Ring Buffer的坑存储服务器的流量特征和普通Web服务器不一样它是持续的、大量的、数据包密集的单向或准单向流量。客户端写入时服务端网卡每秒钟要接收成千上万个数据帧尤其当MTU为1500字节时一个10MB的文件也会被拆成七千多个包。如果MTU是9000巨帧包数量会少一些但很多内网环境并没有全链路支持巨帧。TCP协议栈本身有接收缓冲区和拥塞控制正常情况下即使偶发丢包也能通过重传恢复不会影响最终数据完整性。但丢包的代价是巨大的一旦某个TCP段丢失发送端必须等待超时或收到重复ACK才触发重传这个等待时间会显著增加往返延迟。存储协议如iSCSI、SMB、NFS对延迟极其敏感延迟一升高客户端就可能判定会话超时从而断流重连。而Ring Buffer就是收包路径上最容易被忽略的一环如果网卡DMA写入数据包的环形队列容量太小新到的包没有位置存放网卡只能直接丢弃这就会造成我们看到的丢包和断流。1.3 第一步丢包定位先查ethtool的“硬”计数遇到这种现象别急着改TCP栈参数更不要一上来就换网线。先登录存储服务器用ethtool看一眼网卡的真实统计计数。CentOS 7/CentOS 8/RHEL系列都自带ethtool直接执行ethtool -S eth0 | grep -E rx|drop|discard|miss|error注意把eth0换成你实际的网卡名bonding的情况下要看bond成员的物理口。我通常重点看这几个字段rx_dropped表示驱动/网卡因为缓冲不足或资源问题主动丢掉的包rx_missed_errors表示硬件因为DMA描述符不足而错过接收的包这是Ring Buffer过小的典型信号rx_fifo_errors表示FIFO溢出和Ring Buffer过小也有直接关系rx_errors综合错误计数需要进一步分解如果这些计数从服务器开机到现在一直为0只能说明“暂时没发生硬丢包”。更好的做法是在故障发生时连续观察比如每两秒刷新一次看这些计数是否在肉眼可见地增长。如果rx_missed_errors在高峰时段不断上涨那基本可以确认就是Ring Buffer或者是中断处理能力不足导致的硬件丢包。2. Ring Buffer到底是什么为什么影响如此之大2.1 网卡环形队列的工作机制仓库门口的“卸货缓冲区”Ring Buffer中文叫环形缓冲队列是网卡驱动在内存中维护的一段环形数组专门用来存放网卡收发的数据帧描述符。你可以把它想象成一个港口仓库的卸货区网线相当于港口外的航道数据包是一艘艘到港的货船而CPU的协议栈则是仓库内部的搬运工。货船到达后港口必须先把它们停在卸货区搬运工再挨个把货物运进仓库。Ring Buffer就是这个“卸货区”的大小。它的工作流程是网卡收到数据包后通过DMA直接把数据写到Ring Buffer对应的内存区域然后通知CPU“有新包到了快来处理”。CPU确切地说是中断处理程序从Ring Buffer里拿到数据包交给协议栈处理然后释放这个位置Ring Buffer就空出来了继续接收新的包。如果Ring Buffer已经满了而CPU还没处理完前面的包新到的包就无处停放。港口只能拒绝后面的船靠岸网卡也只能将这包直接丢弃。这就是我们所说的“硬丢包”。2.2 默认值和大流量写入的冲突256/512真的够吗很多服务器的网卡默认Ring Buffer大小只有256或512个描述符如果是千兆网卡面对百兆级别的普通办公流量这个大小勉强够用但对于存储服务器这种每秒要处理上万个数据包的大流量写入场景256的深度实在太浅了。为什么这么说因为CPU处理数据包需要时间特别是当软中断负载较高、或CPU被其他任务抢占时包在Ring Buffer中的驻留时间会显著增加。假设网卡每毫秒到达100个包而CPU处理100个包需要2毫秒那缓冲区至少需要200个位置才能保证不丢包。如果这时又出现一个调度抖动CPU某1毫秒没有及时处理缓冲区就瞬间溢出。存储流量往往还有“突发性”多个客户端同时写入时包速可能瞬间翻倍默认的Ring Buffer深度根本兜不住。我做过一个简单的量化实验单客户端从存储服务器读取10GB文件MTU1500包速率平均值在每秒8000个左右当三个客户端同时读写时包速率峰值能到每秒25000个以上。这种包速率下512的Ring Buffer如果遇到CPU中断合并延迟很容易被打满。所以存储服务器上把Ring Buffer从512调到2048或4096很多时候能直接解决问题。2.3 为什么iSCSI、NFS这类存储协议对丢包尤其敏感有人可能会说“TCP本来就允许丢包重传丢几个包有什么大惊小怪”但存储协议对延迟的容忍度远低于普通HTTP浏览。以iSCSI为例客户端发出一个SCSI写命令封装成TCP包发到服务端服务端完成写入后回复一个iSCSI响应。整个交互模式下任何一个包丢失客户端都需要等待重传和响应响应时间可能从正常的0.2毫秒飙升到几毫秒甚至几十毫秒。如果丢包事件频繁发生累积的超时会导致TCP拥塞窗口不断缩小吞吐量急剧下降。存储服务商对iSCSI的延迟要求通常是在毫秒级别一旦超过上限客户端会认为对端不可用直接断开连接这就是我们看到的“断流”。SMB/NFS也类似它们都有会话超时机制网络抖动一大应用层就报错。更重要的是Ring Buffer丢包不只是“丢一个包”这么简单。当你看到Rx missed errors出现时往往是一瞬间连续丢了大量包因为队列是满的新来的全都被拒收TCP重传需要重发一堆数据网络状态雪上加霜。3. 定位与实测一步步找到“Ring Buffer设置不合理”3.1 查看当前Ring Buffer状态ethtool -g 一眼看清上限和当前值在调整之前先看当前硬件和驱动支持的最大值。执行ethtool -g eth0输出类似Ring parameters for eth0: Pre-set maximums: RX: 4096 RX Mini: 0 RX Jumbo: 0 TX: 4096 Current hardware settings: RX: 256 TX: 256这里的Pre-set maximums表示驱动/网卡允许配置的最大深度Current hardware settings表示当前生效值。如果当前RX数值明显小于上限比如这里RX只有256而最大值是4096那就有很大的调优空间。我见过有些服务器默认RX512、TX512但其实驱动支持到4096只是没配置而已。如果Pre-set maximums本身就是1024或更小说明这块网卡的驱动能力有限调优空间不大。这时候就要考虑从其他方向入手比如多队列、中断合并优化。3.2 用ethtool -S对比丢包计数在故障窗口下抓增长静态看一次统计意义不大关键要看故障时刻计数是否在增长。我的习惯是开两个终端窗口一个持续打流量另一个每隔一秒执行watch -n 1 ethtool -S eth0 | grep -E rx_missed|rx_dropped|rx_fifo 或者直接在压测过程中手动连续刷几次ethtool -S eth0 | egrep rx_missed|rx_dropped|rx_fifo ; sleep 1; ethtool -S eth0 | egrep rx_missed|rx_dropped|rx_fifo对比两次计数如果rx_missed_errors从100变成3000说明在这一秒内丢了几千个包那基本可以断定Ring Buffer深度不够或者中断处理有瓶颈。如果这两个计数完全不涨那丢包原因可能不在网卡接收队列而在内核协议栈或应用层需要继续排查。同时/proc/net/dev也能看到接口层的统计比如dropped、fifo可以作为交叉验证cat /proc/net/dev3.3 复现压测用fio和dd模拟大流量写入为了稳定复现问题我会在存储服务器上先用开源工具fio打一下块设备或文件同时从另一个客户端往挂载点写数据模拟真实存储写入。fio是存储性能测试的标配简单执行fio --filename/mnt/storage/testfile --size20G --rwwrite --bs128k --iodepth32 --numjobs8 --runtime60 --time_based --group_reporting --namewrite_test这个命令会生成8个线程并发写20GB文件块大小128k队列深度32。如果你通过NFS或iSCSI挂载了这个存储也可以直接在客户端跑fio写挂载点效果更接近真实业务。如果不想装fio用dd凑合也能测dd if/dev/zero of/mnt/storage/test.bin bs1M count20000 convfdatasync压测的同时观察之前说的丢包计数器。很多情况下你会看到rx_missed_errors随着测试启动快速上涨这就是最有力的证据。3.4 检查中断合并和多队列只调Ring Buffer可能不够如果Ring Buffer调到最大值后丢包依然存在就要检查另外两个配套因素合并中断Coalesce和多队列Multi-queue。中断合并机制允许网卡攒一批包再通知CPU从而降低中断次数提升吞吐。但如果合并间隔设置得太长比如rx-usecs200网卡会等待200微秒才通知CPU这期间Ring Buffer满了就只能丢包。可以查看当前合并参数ethtool -c eth0重点看rx-usecs和rx-frames。如果rx-usecs偏大可以适当调小比如改为15~30微秒提高CPU响应的及时性。但同时会带来更多中断CPU占用上升需要平衡。多队列方面网卡如果支持RSS接收侧扩展应该开启多个队列让不同队列的中断均匀分布在多个CPU核上。查看队列数ethtool -l eth0如果Current hardware settings的Combined值只有1说明所有流量都挤在一个队列上。驱动支持的情况下可以这样开ethtool -L eth0 combined 4开启后检查中断分布cat /proc/interrupts | grep eth0正常情况下可以看到各个队列的中断计数在不同CPU核心上交替增长。4. 调整方法与持久化配置从临时生效到重启不掉4.1 临时调整一行命令立竿见影确认问题后调整Ring Buffer非常简单ethtool -G eth0 rx 4096 tx 4096把RX和TX的深度都设置成4096。如果网卡型号比较老可能只支持RX不支持TX甚至不支持手动修改。没关系能调多少调多少。执行后立刻用ethtool -g eth0确认当前值已经变成4096再跑一次压测观察丢失计数是否停止增长。这里有个小经验不要一上来就调满尤其是老驱动调到最大值可能带来新问题。建议先调成1024或2048观察一段时间不够再加。存储场景下一般2048已经足够盲目调到4096反而可能因为DMA内存分配过大导致驱动不稳定。4.2 CentOS下持久化配置ifcfg-ETHTOOL_OPTS、rc.local、systemd三种方案临时调整在重启后会失效必须做持久化。不同系统版本方法不太一样我按实际效果排个序。如果你的系统还在用network service管理比如CentOS 7的非NetworkManager环境可以在网卡配置文件/etc/sysconfig/network-scripts/ifcfg-eth0中加入ETHTOOL_OPTS-G eth0 rx 4096 tx 4096保存后重启网络服务或重启网卡配置会生效。这种方式对CentOS 7/8都有效前提是网卡由ifup脚本管理而不是NetworkManager。第二种方法是写成rc.local。CentOS 7的/etc/rc.local需要执行权限先chmod x /etc/rc.local然后在里面加一行/usr/sbin/ethtool -G eth0 rx 4096 tx 4096缺点是rc.local的执行时机较晚可能在网卡启动后但服务启动前对于某些服务已经足够。CentOS 8/RHEL8之后rc.local默认可用性有所变化我不太推荐。第三种更稳健的方法是创建一个systemd oneshot服务。我在生产环境就用这种方式因为可控性强依赖关系明确。创建/etc/systemd/system/ethtool-ringbuffer.service[Unit] DescriptionSet network ring buffer to optimized size Afternetwork.target [Service] Typeoneshot ExecStart/usr/sbin/ethtool -G eth0 rx 4096 tx 4096 RemainAfterExityes [Install] WantedBymulti-user.target然后执行systemctl daemon-reload systemctl enable ethtool-ringbuffer.service systemctl start ethtool-ringbuffer.service这样每次开机都会在network.target启动之后自动设置Ring Buffer。如果你有多个网卡要设置可以在ExecStart里写多行命令每行一个网卡。4.3 配套优化中断合并、多队列、RSS与CPU亲和性前面说了光调Ring Buffer有时候治标不治本。如果网卡支持多队列强烈建议开启。以下是我在存储服务器上常用的一组组合命令假设网卡驱动支持# 调整Ring Buffer大小 ethtool -G eth0 rx 4096 tx 4096 # 调整中断合并适当降低合并延迟提高响应及时性 ethtool -C eth0 rx-usecs 15 rx-frames 8 # 开启多队列以4个队列为例按实际CPU核心数调整 ethtool -L eth0 combined 4 # 设置RSS哈希让流量均匀分布在队列上 ethtool -X eth0 equal 4如果系统里有irqbalance服务建议让它自动平衡中断如果没有可以手动绑定中断到指定CPU核心。手动绑定示例# 先查看eth0队列中断号 grep eth0 /proc/interrupts # 然后把第一个队列中断绑定到CPU0 echo 1 /proc/irq/中断号/smp_affinityCPU亲和性这块要结合自己的部署环境来定不是越复杂越好。存储服务器上如果业务相对单一让irqbalance自动处理通常就够了。4.4 参数不是越大越好内存占用、延迟与稳定性Ring Buffer调大带来的主要好处是“缓冲余量”变大但这并不代表数值越大越优。每个ring描述符都会关联一个数据缓冲区RX/TX都设成4096时仅一个网卡就可能占用几十MB内存虽然对现代服务器来说不算什么但如果你的机器有多个网卡每个网卡都设成最大值累计内存开销也可观。更重要的是队列深度过大会增加数据包在缓冲区的排队延迟。对于存储这类延迟敏感场景延迟升高可能带来副作用。所以我的经验是先调整到2~4倍的充足度比如从256调到1024或2048绝大多数场景已经能解决问题只有确认峰值流量极大时才考虑4096。如果一个网卡默认最大值连1024都不到那说明驱动或硬件本身能力有限与其硬调不如换网卡或降低单网卡负载。5. 常见问题排查与经验总结5.1 调整后依然丢包检查网卡之外的内核队列如果调整了Ring Buffer、多队列、中断合并之后ethtool -S里的rx_dropped、rx_missed_errors不再增长但客户端仍反馈延迟高、断流那就需要把排查重心转移到内核协议栈的另一个缓冲netdev_max_backlog。这是内核中处理网络设备接收队列的最大包数量当CPU来不及处理时数据包会积压在这个队列里。查看它的溢出情况可以执行cat /proc/net/softnet_stat输出第一个数字是CPU0的收包总数第二个数字是软中断丢包总数。如果第二列不为0且在增长说明软中断处理速度跟不上需要调大backlogsysctl -w net.core.netdev_max_backlog10000再配合调整somaxconn和TCP缓冲区sysctl -w net.core.somaxconn1024 sysctl -w net.ipv4.tcp_rmem4096 87380 16777216 sysctl -w net.ipv4.tcp_wmem4096 65536 16777216当然这些参数要结合实际情况不要照搬。存储服务器上重点还是先解决网卡层的硬丢包。5.2 网卡型号、驱动版本差异对调参的限制不同的网卡芯片/驱动对ethtool参数的支持差异很大。Intel的ixgbe、i40e、ice系列驱动都支持Ring Buffer调整和多队列Broadcom的bnx2x、tg3系列也基本支持但一些入门级Realtek网卡比如RTL8111/8168的驱动功能就非常有限可能连ethtool -g都看不到多少调节空间甚至不支持多队列。如果服务器要用作存储我建议不要在主板上省网卡的钱。Intel/博通/迈络思等服务器网卡在驱动稳定性和调优参数上会好很多。如果已经遇到Ring Buffer受限的问题另一个替代思路是使用网卡bonding分流用多个物理网口做起bond流量分散后每个口的包速率下降缓冲区压力自然缓解。但要注意bond模式的选择存储场景一般选mode 4802.3ad配合交换机动态聚合或者mode 6balance-alb在某些环境下也能用。5.3 一次实际调优的记录调前调后对比这里记录一次典型的调优过程供参考。某存储服务器两块Intel I350千兆网卡bond成mode 4对外提供iSCSI存储。高峰时三台客户端同时写入现象是每秒都有iSCSI会话断开重连服务器上ethtool -S bond0的成员口eth0、eth1看到rx_missed_errors以每秒几千的速度增长。当时的默认配置是RX512、TX512Combined队列数1。调整步骤临时把两个物理口的RX/TX都改成2048ethtool -G eth0 rx 2048 tx 2048 ethtool -G eth1 rx 2048 tx 2048开启每个口的4队列RSSethtool -L eth0 combined 4 ethtool -L eth1 combined 4适当调整中断合并ethtool -C eth0 rx-usecs 15 tx-usecs 15 ethtool -C eth1 rx-usecs 15 tx-usecs 15写入systemd服务使配置持久化。调整后rx_missed_errors不再增长iSCSI会话稳定客户端写入速度从原来断断续续的几十MB/s恢复到稳定的230MB/s千兆bond下的合理水平。CPU使用率比之前高了5%左右但远未成为瓶颈。这算是一次非常典型的“Ring Buffer设置不合理导致丢包断流”的解决案例。5.4 建立监控意识用开源工具提前发现丢包趋势与其等到业务报障再去排查不如提前做监控。CentOS系统里的sysstat自带sar -n EDEV可以看网卡设备错误sar -n EDEV 1其中rxdrop/s列就是每秒接收丢包数我建议运维人员每天登录后都看一眼同时用脚本设置阈值报警。如果你们已经有Prometheus监控体系node_exporter自带的node_network_receive_drop_total指标就是这个数据直接定义一个告警规则比如5分钟内的增量超过100就触发警告可以提前把你从睡梦中叫醒。这也是我喜欢开源软件的原因——监控、压测、告警全部有成熟的免费工具链可以搭起来。存储服务器看似只是“把服务器当存储用”但背后涉及的网络参数调优一点不比应用服务器少提前架好监控能省下大量救火时间。最后分享一个我个人的习惯每次给存储服务器更换或升级网卡驱动、系统内核之后我都会重新检查一遍ethtool -g的当前设置。因为驱动升级有时会重置硬件参数如果忘了持久化配置Ring Buffer会在不知不觉间回到默认值问题很可能在几周后再次出现。还有一个小技巧把服务器所有网卡的Ring Buffer、队列数、中断合并参数整理成一个表格放在文档里每次变更后对照检查能少踩很多坑。这就是我在多次处理“大流量写入丢包断流”之后最想对大家说的话。
返回列表