尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

存储服务器写入超时?Ring Buffer配置不当引发的网络丢包排查实录

存储服务器写入超时?Ring Buffer配置不当引发的网络丢包排查实录 半夜两点被电话叫醒存储服务器在做大规模数据迁移的时候断流了。业务方反馈 NFS 客户端写入直接超时重试也失败整个迁移任务卡住。我看了一眼监控物理链路 UP带宽占用很高但网卡错误计数里有一个数字在疯涨rx_missed。后来定位到根因就是 Ring Buffer 设置不合理默认值 256 在大流量写入面前根本不够用。这篇文章就完整还原这次故障的定位和处理过程内容包括 Ring Buffer 的原理、丢包类型的区分、调整方法和配套调优以及 CentOS 环境下的操作命令。适合正在维护存储服务器、NAS、备份服务器的运维和 SRE 参考尤其是那些机器流量长期偏高、偶尔出现写入超时的场景。哪怕你只是把服务器当存储用跑着 iSCSI 或者对象存储服务这篇文章里的思路同样适用。1. 故障现象与排查起点1.1 故障现场存储写入断流是怎么表现的先说现象。那台存储服务器是双万兆网卡绑定承载着多个客户端的 NFS 写入同时跑着一个大数据迁移任务持续往里头灌数据。故障时间段大概是凌晨一点多流量上去之后业务方开始陆续报写入超时rsync 直接报Connection reset by peerNFS 日志里全是nfs: server ... not responding。从服务器端看几个核心指标是这样的网卡链路正常ethtool eth0显示 Link detected: yes速率 10000Mb/s。系统负载不高CPU 使用率还有富余不是计算瓶颈。TCP 层重传率飙高netstat -s里retransmitted数值增长得非常快。最关键的ethtool -S eth0里rx_missed这个计数在以肉眼可见的速度往上跳。这种表现就是典型的收包路径出了问题。网卡收到了数据但内核来不及处理数据最终被丢弃了。TCP 层发现丢了包就会重传重传又加剧了流量很快就形成了恶性循环。1.2 从物理层到驱动层的排查路径遇到这种问题我的习惯是从物理层往上层一层一层排。第一步先排除链路问题检查光纤模块、网线、交换机端口有没有 CRC 错误或协商异常ethtool eth0看下速率和双工模式是否正常。这一步通常很快链路问题会有明显的rx_crc_errors或者rx_errors增长。第二步看网卡驱动层的统计信息ethtool -S eth0是核心命令。这里面的计数器非常多不要慌重点关注几个关键项rx_missed、rx_dropped、rx_no_buffer、rx_fifo_errors。这几个计数如果出现非零增长基本可以确定问题出在网卡到内核协议栈之间。第三步就是确认 Ring Buffer 配置了ethtool -g eth0看一下当前值和最大值。我那台机器就是典型的 Ring Buffer 太小而且我注意到一个细节这台存储服务器的驱动是ixgbe它的默认 rx 队列 Ring Buffer 是 512不是常见的 256即便如此在大流量下依然被打满了。2. Ring Buffer 到底是什么为什么默认值不够用2.1 用“排队叫号”理解 Ring BufferRing Buffer 翻译过来是环形缓冲区是网卡驱动和内核网络协议栈之间的一个数据暂存区。网卡收到数据包之后通过 DMA 直接写到一块预先分配好的内存区域这块内存就是 Ring Buffer。内核协议栈的软中断后续来取走这些数据包做处理处理完再把位置空出来。用银行柜台来类比就很直观。客户数据包进银行先在大厅排队区Ring Buffer等着柜员CPU 软中断叫号处理。如果排队区太小一下子涌进来太多客户后来的客户就只能被挡在门外这就是丢包。这个排队区的大小是固定的不支持动态扩容所以必须要提前按照流量峰值来规划。默认值通常是驱动定的有的 256有的 512这些默认值在设计时考虑的是通用场景不会为持续大流量高吞吐做特殊优化。2.2 大流量写入场景下为什么容易出问题存储服务器的流量模型和普通 Web 服务器有很大区别。Web 服务通常是请求响应模式流量有波峰波谷瞬时并发大但单包小存储写入则是持续的、大块的、多路并发的数据流。比如多个客户端同时往 NFS 共享里写文件或者正在跑备份任务、数据迁移任务网卡瞬间的包到达速率会非常高。这时候 Ring Buffer 的小队列很容易被打满。数据包到达速率超过内核软中断的处理能力Ring Buffer 填满之后再有新包进来就只能丢掉。存储服务对丢包极其敏感因为 TCP 重传带来的延迟波动会直接反映到写入时延上上层应用可能直接判定 IO 超时。还有一个容易忽略的点存储场景经常是“延迟低”和“吞吐高”两个目标同时要。Ring Buffer 太小延迟会升高但 Ring Buffer 也不是越大越好太大反而会导致数据包在缓冲区里积压同样抬高处理延迟。所以调整的时候要考虑业务模型的实际情况不是无脑调最大。2.3 查看当前参数的几个命令在动手调整之前先把当前状态摸清楚。我常用的三组命令# 查看 Ring Buffer 当前值和最大值 ethtool -g eth0 # 查看网卡统计信息里的丢包相关计数 ethtool -S eth0 | grep -i -E miss|drop|buffer|fifo # 查看系统层面的网卡收包统计 cat /proc/net/devethtool -g的输出很直观RX行是收包队列TX行是发包队列。Current列是当前配置值Maximum列是驱动和硬件支持的上限。我做故障诊断时特别看重Maximum值和Current值之间的差距如果当前值已经很小而 Maximum 很大那就说明还有调整空间。/proc/net/dev里的RX列有一个drop计数这是内核协议栈层面的丢弃统计和ethtool -S里的rx_dropped含义不完全一样后面细说。先记住一点/proc/net/dev看的是结果ethtool -S看的是过程两者要结合起来分析。3. 丢包类型区分rx_missed 和 rx_dropped 不是一回事3.1 ethtool -S 输出里的关键计数很多新手第一次看ethtool -S会懵计数器上百个项目到底看哪个我的经验是围绕“丢包”这个现象划出三个层次来观察计数器含义指示的问题rx_missed网卡硬件/FIFO 层因为 buffer 不足而丢弃的包Ring Buffer 太小或 DMA 映射不足rx_dropped驱动层接收路径上主动丢弃的包驱动处理不过来或内存不足rx_no_buffer系统分配 skb 缓冲区失败导致的丢包内存不足或 kmem 缓存耗尽rx_fifo_errors网卡 FIFO 溢出错误Ring Buffer 或 DMA 环配置不合理rx_missed是我这次故障里增长最明显的计数。它代表的含义是数据包已经被网卡 DMA 到内存但在进入协议栈之前因为没有可用的 Ring Buffer 描述符而被丢弃。这个数值如果持续增长几乎可以断定就是 Ring Buffer 大小不够。3.2 两种丢包的本质区别与排查方向rx_missed和rx_dropped虽然都带个 “drop”但问题的层次完全不一样。我打个比方rx_missed是银行大堂满了客人进不来属于硬件层面的排队区问题rx_dropped是客人进来了但柜员处理太慢、柜员累到直接按铃让后面的人别排了属于驱动和内核处理能力问题。所以排查方向上两者截然不同rx_missed增长优先检查 Ring Buffer 配置考虑调大rx队列的描述符数量。rx_dropped增长优先检查驱动是否有 bug、系统内存是否不足、内核软中断是否被其他逻辑阻塞。如果两个都在涨那就是 Ring Buffer 不够和 CPU 处理能力不够两个问题叠加需要组合拳来解。这次故障里rx_dropped也有小幅增长但主要矛盾是rx_missed所以我把重心放在了 Ring Buffer 上。调整完之后rx_missed归零rx_dropped也随之停止增长验证了这个判断。3.3 用多个指标交叉验证只看rx_missed一个指标容易误判。我当时还同时观察了这几个数据# 查看 CPU 软中断分布 cat /proc/softirqs | grep NET_RX # 查看网卡队列数 ethtool -l eth0 # 查看中断亲和性 cat /proc/interrupts | grep eth0如果软中断都集中在单个 CPU 上即便调大了 Ring Buffer也还是存在单核处理瓶颈。那个备份任务的流量非常大但我的机器是 8 队列的万兆网卡软中断分散得还算平均所以瓶颈确实是在 Ring Buffer 上。有一个细节值得留意ethtool -S里的计数器是累计值不是实时速率。排查时要先记录一个基准值过几秒再记录一次看差值的变化趋势。如果差值在快速增大说明丢包正在持续发生如果数值稳定不动可能只是历史遗留实际流量没有问题。4. 调整 Ring Buffer 的完整操作流程4.1 临时调整ethtool -G先做临时调整观察效果。命令格式很简单# 查看当前值 ethtool -g eth0 # 调整 rx 队列的描述符数量为 4096 ethtool -G eth0 rx 4096 # 调整 tx 队列的描述符数量为 4096如果发送方向也有压力 ethtool -G eth0 tx 4096这里有个常见坑不是所有网卡驱动都支持tx单独调整而且rx和tx的参数名在部分驱动里是rx、tx在另一些驱动里是rx-descs、tx-descs。我在旧版 CentOS 6 上就遇到过参数不识别的问题用ethtool -G eth0 --help看一下当前驱动支持的方式最稳妥。调整之后立刻用ethtool -g eth0确认是否生效。有些驱动会四舍五入到特定值比如你想设置 3000它可能实际设置成 3072这属于正常现象因为描述符的数量通常要对齐到 2 的幂次或者特定的倍数。临时调整的效果立竿见影。我当时把rx从 512 调到 4096 后rx_missed的增速明显放缓几分钟后完全停止增长。业务方反馈写入超时也逐渐恢复。4.2 永久生效的三种方式临时调整的问题是一重启就丢。永久生效有几种方式按推荐程度排序第一种在网卡配置文件里指定。CentOS 系的/etc/sysconfig/network-scripts/ifcfg-eth0可以加ETHTOOL_OPTS-G eth0 rx 4096 tx 4096这种方式最干净网络服务重启时会自动执行。注意ETHTOOL_OPTS里的设备名要和 ifcfg 文件的设备名一致用NAME或者DEVICE字段确认。第二种写进/etc/rc.local。简单粗暴但要注意 CentOS 7 之后的系统默认/etc/rc.local需要chmod x才有执行权限而且它是最后启动的可能会晚于网卡初始化。ethtool -G eth0 rx 4096 tx 4096第三种用 systemd 服务管理。适合有配置管理工具如 Ansible的环境可控性最强[Unit] DescriptionSet eth0 ring buffer size Afternetwork-online.target [Service] Typeoneshot ExecStart/usr/sbin/ethtool -G eth0 rx 4096 tx 4096 RemainAfterExityes [Install] WantedBymulti-user.target我个人的建议是生产环境优先用第一种因为它跟着网络服务走语义最清晰。如果公司有统一的 systemd 管理规范那就用第三种。/etc/rc.local只适合临时兜底不建议长期使用。4.3 配套调优让调整效果最大化调完 Ring Buffer 不代表一劳永逸。如果内核处理能力跟不上Ring Buffer 再大也只是把丢包延后没有真正解决问题。我当时做了三件配套优化第一检查并调整网卡队列数量。用ethtool -l eth0查看当前队列数和最大队列数再用ethtool -L eth0 combined 8把队列配满。多队列可以让多个 CPU 核心分担收包中断避免单核打满。第二开启 RPSReceive Packet Steering。对于不支持多队列的网卡或者队列数少于 CPU 核心数的场景RPS 可以在软件层面把收包软中断分散到多个 CPU。配置方式echo ff /sys/class/net/eth0/queues/rx-0/rps_cpus这里的ff是 CPU 亲和性的位图表示允许使用前 8 个 CPU 核心。如果 CPU 只有 4 核就用0f。具体核数要按机器实际情况来不要照抄。第三检查中断合并参数。ethtool -c eth0可以查看当前的中断合并策略rx-usecs表示延迟多少微秒再触发中断。适当增大这个值可以减少中断次数降低 CPU 开销但代价是增加延迟。存储写入场景一般保持默认即可除非 CPU 软中断已经成了明显瓶颈。4.4 调整后的验证方法调整完必须压测验证不然不敢说问题解决了。我的验证分三步第一步看计数是否归零。连续执行两次ethtool -S eth0 | grep -i miss间隔 30 秒到 1 分钟确认rx_missed不再增长。第二步业务层观察。让业务方重新跑一遍写入任务观察 NFS 或 iSCSI 客户端是否还有超时现象。有条件的话在客户端用dd写一个大文件测一下吞吐dd if/dev/zero of/mnt/nfs_share/test.img bs1M count2048 convfdatasync第三步检查端到端延迟。腾讯云上可以看自建环境就用ping加上-f参数看有没有分片丢包更精确的是在客户端和服务端同时抓包确认 TCP 重传明显减少。我当时调整完之后备份任务重新跑起来rx_missed零增长客户端延迟从之前的动不动几百毫秒降到了个位数毫秒。最直观的变化是业务的超时告警彻底消失了。5. 常见问题与排查速查手册5.1 ethtool -G 报错怎么办调整 Ring Buffer 时最容易遇到的报错是Operation not supported或者Invalid argument。我之前在一台 CentOS 服务器上执行ethtool -G eth0 rx 4096直接报错查下来发现是驱动版本太老不支持调整到 4096。解决办法是先看驱动支持的最大值ethtool -g eth0输出里的Maximum字段就是上限。如果 Maximum 只有 2048那设置 4096 肯定失败。另外还要确认驱动模块是否加载正确ethtool -i eth0这里的driver字段会显示当前用的驱动。ixgbe、i40e、e1000e这些常见驱动对 Ring Buffer 的支持程度不一样。如果确实是驱动太老可以尝试更新驱动或者升级内核。还有一种诡异的情况某些网卡的中断合并和 Ring Buffer 共用同一块寄存器空间修改 Ring Buffer 时如果同时有流量在跑可能触发Device or resource busy。这种时候可以短暂停掉业务或者换个维护窗口再操作。5.2 调大 Ring Buffer 还是丢包怎么办如果已经把 Ring Buffer 调到最大值了丢包依然存在那就说明问题不在 Ring Buffer 上这时候要重新审视整个收包链路。常见的原因有几个第一个是 CPU 软中断处理不过来。检查top里的si字段如果长时间超过 50%说明内核处理收包已经占了大量 CPU 资源。解决方案是加网卡队列、开 RPS、或者把中断绑到多个核心上。第二个是内存分配失败。rx_no_buffer和rx_dropped同时增长时优先检查free -m看是否有足够的内存可供内核分配 skb 缓冲区。如果内存长期不足可以考虑调低net.core.rmem_max和net.core.rmem_default之外的一些参数但这套组合拳比较复杂日常场景不多见。第三个是驱动 bug。有些老版本驱动在高版本内核上会出现 DMA 映射异常导致 buffer 无法释放。这种情况单纯调参数没用需要升级驱动或者内核。我遇到过一次igb驱动在特定内核版本上包转发性能异常升级驱动后问题消失。判断原则rx_missed为主继续往 Ring Buffer 和驱动方向查rx_dropped为主转向 CPU 软中断、内存、协议栈参数方向查。5.3 CentOS 下查看丢包的实用命令清单这里整理一份我在 CentOS 上排查丢包问题时必用的命令按优先级排列# 1. 网卡统计信息看关键计数 ethtool -S eth0 | grep -i -E miss|drop|buffer|fifo # 2. 网卡 Ring Buffer 当前值 ethtool -g eth0 # 3. 系统级收发包统计 cat /proc/net/dev # 4. TCP 协议栈重传统计 netstat -s | grep -i retrans # 5. 网卡中断分布 cat /proc/interrupts | grep eth0 # 6. 软中断处理情况 cat /proc/softirqs | grep NET_RX # 7. 网卡队列和启用状态 ethtool -l eth0我的建议是先把这套命令存成一个小脚本丢包告警触发的时候先跑一遍拿到基础数据再进具体排查。熟练之后基本一两分钟就能定位大方向不会浪费时间在抓包上。5.4 遇到存储场景要注意的特殊细节最后说几个存储服务器特有的注意事项。存储服务器往往同时挂着 NFS、SMB、iSCSI 多种协议不同协议的流量特征完全不同。NFS 是多并发小 IOiSCSI 是大块连续写入SMB 则介于两者之间。调 Ring Buffer 的时候要按最激进的那个流量模型来定而不是取中间值。另外现在很多存储方案使用开源软件比如老牌的 NFS、Samba、TGT以及对象存储里的 MinIO。这些服务通常都不是单线程收包对网卡多队列的支持要求更高。如果跑的是 MinIO 这类对象存储还要额外关注大流量下 TCP 连接数过多导致内存占用升高这又是一个独立的问题。还有一个容易被忽略的点是交换机侧的丢包。Ring Buffer 调大只是解决了服务器侧的问题如果交换机出方向或入方向有丢包服务器端的现象会非常类似也会表现为 TCP 重传增加、业务写入超时。排查时务必在交换机端口上也看一遍discards和errors计数两边对照才不会被误判。注意如果服务器侧所有计数都正常交换机的丢弃计数却很高那问题就在网络设备侧调服务器参数再久也白搭。这个检查顺序一定要养成习惯。这次故障处理完之后我特意把所有存储服务器的 Ring Buffer 都做了统一巡检把那些还是默认值的机器都调到了 4096。后来再遇到大流量写入的告警网卡层面的丢包问题就再没出现过。作为一个长期运维存储业务的工程师我的体会是Ring Buffer 这种基础参数平时不显眼但一到流量高峰就原形毕露。提前按业务峰值把底层参数调好比等故障了再救火省心得多。
返回列表