最近半年我接了不少RoCE网络的排障和优化需求,有做存储的、有跑AI训练的,从100G到400G都有。说实话,大部分问题的根源不是网卡也不是交换机硬件性能,而是大家对RoCE这套机制“七分懂三分不懂”:知道要开PFC,但不知道PFC为什么会带来死锁;知道要开ECN,但阈值怎么定完全凭感觉。这篇内容我把RoCE里最关键的细节拆开来讲,重点放在“为什么这么设计”,而不是只给一堆命令参数。希望能帮到正在折腾RoCE的兄弟,少走点弯路。
RoCE全称是RDMA over Converged Ethernet,翻译过来就是在融合以太网上跑远程直接内存访问。它解决的问题很直白:传统TCP/IP通信中,数据要经过内核协议栈,CPU要参与中断、拷贝、校验,延迟高、CPU开销大。RoCE让网卡直接读写远端内存,绕过内核、绕过CPU参与数据搬移,把延迟从几十微秒压到微秒级,把CPU算力释放给业务本身,同时还能保留以太网的生态和成本优势。如果你正在搭HPC集群、GPU训练集群或者全闪存储网络,RoCE基本上绕不开。就算你暂时只打算跑普通TCP,把RoCE这套机制吃透,也会帮你看清数据中心网络里“拥塞控制”和“流控”的真正边界。
1. RoCE是什么,为什么需要它
1.1 RDMA的基本模型
要理解RoCE,先得理解RDMA到底允许多“暴力”。传统网络收数据是这么走的:对端网卡收到数据后触发中断,CPU把包从网卡缓冲区拷贝到内核协议栈,再拷贝到用户空间的应用缓冲区,期间还要做TCP校验、ACK确认、内存拷贝。一次完整的数据接收,CPU要参与好几趟,数据在内存里被搬来搬去,这就是所谓的“多拷贝、内核旁路没做好”。到了高速网络时代,尤其是存储和AI这类大流量、小延迟敏感的场景,这套路径就成了瓶颈。
RDMA的做法完全不同。应用程序在注册内存时,把内存区域告诉网卡,网卡直接通过DMA访问这块内存,数据从对端网卡进入本地网卡后,直接被DMA写进应用缓冲区,中间不需要CPU参与数据搬运,也不需要内核做第二次拷贝。CPU只在连接建立、内存注册、完成事件处理时介入。这个模型被称为“内核旁路+零拷贝”,延迟能降到1-3微秒的量级,CPU占用也低到可以忽略。
我用一个比较土的类比来帮助理解:传统网络像是发快递,发件人把包裹送到快递站,快递车再送到收件人楼下的驿站,最后由收件人自己下楼取。RDMA相当于网卡揣着钥匙直接开了对端的内存库房,把东西放在指定的货架上,全程不需要主人下来取件。听起来很爽,但问题也随之而来:快递车之间要是堵在路上,没有交通调度,就很容易出事。RoCE要解决的正是“如何在以太网上保证这种直连不丢包、不堵车”。
1.2 RoCE在RDMA方案里的位置
RDMA有三个主要实现路径:InfiniBand、RoCE、iWARP。InfiniBand是原生的RDMA网络,从物理层到协议层全部为RDMA设计,自带无损传输和拥塞控制,性能最稳,但成本高、生态相对封闭,通常只有在高端HPC里才会整套采用。iWARP走TCP协议栈,兼容性最好,只要配好网卡就可以在有丢包的传统以太网上工作,但因为TCP栈的处理开销大,延迟和CPU占用都不太理想,这几年已经不太有人重点推。
RoCE站在中间位置:它继承了RDMA的低延迟零拷贝特性,又依赖标准以太网,网卡和交换机的供应链庞杂、成本可控。所以RoCE v2发布之后,整个数据中心行业基本形成了共识:想要便宜、够用、性能接近IB的RDMA方案,就是RoCE。今天你看到的NVMe-oF存储、GPU集群里跑GPUDirect RDMA、分布式数据库的共享内存通信,绝大多数底层用的都是RoCE v2。
1.3 适合谁来读
这篇内容主要面向三类人:第一类是网络工程师,需要掌握PFC、ECN的配置原理,能处理交换机侧的调优;第二类是存储或HPC运维工程师,系统里已经挂了RoCE网卡,遇到性能问题想自查;第三类是应用开发者,需要知道RDMA通信中哪些参数影响很大,好和网络团队顺畅沟通。不管你是哪一类,建议先通读一遍机制部分,再对照后面的排查章节查自己的网络。如果只想着抄配置,遇到复杂问题还是会抓瞎。
2. 版本、协议栈和硬件基础
2.1 RoCE v1 vs RoCE v2,核心差异在哪里
RoCE v1是早期版本,直接把RDMA数据封装在以太网链路层上,用的以太网类型是0x8915。它没有任何路由能力,只能在一个二层广播域内工作,也就是说所有RoCE v1节点必须在同一个VLAN里,中间不能跨三层设备。这意味着大规模组网基本没戏,只能在小集群里玩玩。另一个麻烦是,二层以太网上没有标准的拥塞反馈通道,拥塞控制很难做精细。所以v1基本被行业淘汰了,只有一些老环境还在跑。
RoCE v2把报文封进了UDP/IP,外层是IP头加UDP头,UDP目的端口固定为4791,再往里才是IB的BTH(基础传输头)、数据载荷等。这样一来,RoCE流量就能像普通UDP一样跨三层路由,交换机只要支持IP转发就能转发RoCE报文,不需要感知RDMA语义。这对数据中心来说极其关键:你说要搭几千台GPU的集群,不可能全塞进一个二层VLAN里,必须能跨Leaf-Spine架构做三层收敛。
这里有个细节值得强调:RoCE v2虽然用UDP封装,但它并不像TCP那样在内核里跑拥塞控制。它的可靠性、流量控制、拥塞处理都是在网卡和交换机的配合下完成的,这也是为什么需要专门的机制来处理。你在抓包的时候,看到大量4791端口的UDP包,不用惊讶,那就是RoCE v2数据面在跑。
| 对比项 | RoCE v1 | RoCE v2 |
|---|---|---|
| 封装层 | 以太网链路层 | IP + UDP |
| UDP端口 | 无 | 4791 |
| 是否可路由 | 不可路由 | 可跨三层路由 |
| 拥塞控制能力 | 弱 | 依赖ECN/DCQCN |
| 当前使用情况 | 基本淘汰 | 主流方案 |
2.2 无损网络:RoCE的“前提条件”
很多人把RoCE和无损网络划等号,这不完全对,但离真相不远。RoCE的硬件设计假设是“网络不丢包”。因为RDMA的出站流量不经过内核协议栈,一旦网络里发生丢包,发送端不会像TCP那样通过重传超时来恢复,而是靠网卡的可靠连接重传机制,这个机制在包丢失比较严重时效率非常低,会大幅拉高延迟,甚至导致QoS大幅下降。换句话说,RoCE可以在有损网络里“跑”,但跑得很难看,一旦丢包率达到千分之一,吞吐可能就掉到一半以下。
为了不让RoCE流量在拥塞时被交换机直接丢弃,业界引入了无损网络的概念。无损并不是保证绝对不丢包,而是通过一系列队列和流控机制,把“拥塞导致丢包”尽量转移到“源端降速”和“队列吸收”。核心手段是PFC按优先级暂停,配合ECN标记让发送端主动减速。只有在所有交换机都正确配置、所有网卡都开启ECN的时候,RoCE才能发挥正常性能。
有一点必须讲透:无损网络是针对指定流量类别而言的,不是全网络所有流量都必须无损。通常只给RoCE所在的服务等级开启无损队列,TCP/UDP这类传统流量继续走传统有损队列。这样既保护了RoCE,又不会让所有流量都被流控拖累。很多第一次做RoCE的人,图省事全局开启PFC,最后所有TCP业务莫名变卡,就是因为没搞懂这个边界。
2.3 硬件选型与检查清单
RoCE能不能跑好,硬件底子占一大半。网卡端建议直接选NVIDIA Mellanox系列,从ConnectX-4之后的型号都对RoCE v2有很好的支持,固件和驱动也成熟。其他厂商也有支持RoCE的网卡,但如果预算允许,尽量不要在RoCE这种细节极多、参数敏感的技术上为了省成本用不成熟硬件,后期排障会痛苦很多。CPU的PCIe通道、网卡所在槽位速率也要检查,很多标称100G的网卡插在PCIe 3.0 x8上实际就跑不满。
交换机端要重点看buffer容量。无损网络之所以能吸收突发,靠的就是芯片里的包缓冲区。入门级TOR交换机如果共享buffer只有几MB,跑100G无损网络会非常吃力,因为一拥塞就会直接buffer溢出丢包;数据中心级别的交换机单端口buffer最好在几MB以上,总buffer十几MB甚至几十MB。另一个看点是是否支持ECN的WRED配置,以及PFC的优先级数量,基本主流厂商的云级交换机都支持,但老旧的千兆柜顶设备就别指望了。
检查清单列一下:网卡型号和固件;交换机芯片型号、buffer大小;使用的光模块和线缆信号质量(RoCE在长距离链路上对FEC依赖很强,建议开RS-FEC或FC-FEC);服务器BIOS里PCle最大负载设置;BIOS里网卡SR-IOV和IOMMU设置,在多数性能场景下建议关闭IOMMU以减少地址转换开销。别小看这些底层设置,我踩过一台机器因为光模块信号劣化,RoCE带宽直接折半,换线后瞬间恢复。
3. 三大机制拆解:PFC、ECN、DCQCN
3.1 PFC:按优先级暂停,而不是全局刹车
PFC(Priority Flow Control,优先级流控)是IEEE 802.1Qbb定义的标准。它把以太网链路按802.1P优先级分成8个队列,允许接收端在某个队列的缓冲将满时,向对端发送一个PFC Pause帧,要求对端在指定时间内暂停发送该优先级的数据。Pause帧是MAC控制帧,ethertype是0x8808,里面带一个8位的使能位图,逐优先级指示需要暂停的队列,以及每个优先级的暂停时长。
为什么要按优先级暂停而不是像老式802.3x那样一个Pause帧暂停整条链路?因为老式全局暂停会造成头端阻塞:一条链路上可能同时跑着TCP业务和RoCE业务,如果RoCE突发导致缓冲区压力,把整条链路暂停,TCP流量也跟着遭殃,所有流互相拖累。PFC按队列暂停,只有RoCE所属的队列被暂停,TCP流量还能继续走,实现了“无损通道隔离”。
但PFC不是万能药,它有两个要命的副作用。第一是流量死锁,如果两个方向都在发送PFC暂停帧,而双方的处理逻辑又形成环路,就可能出现“你等我、我等你”的局面,链路完全卡死。第二是PFC风暴,上游收到pause后,数据堆积在上游switch的buffer里,上游switch又向上游发pause,一层层传导,拥塞从Points of Congestion蔓延到整个网络,这就是经典的“拥塞扩散”。所以在生产环境里,PFC是最后一道防线,不是第一道。真正要让网络顺畅,得靠ECN提前让源端降速,PFC只应该处理协议栈没照顾到的突发现象。
3.2 ECN:让发送端自己知道“现在很挤”
ECN(Explicit Congestion Notification,显式拥塞通知)在IP头里用了两个bit,一个表示是否支持ECN,另一个表示是否遇到拥塞。RoCE v2的UDP报文里,IP头的ECN位会被置成可拥塞标记的状态。交换机在转发时如果发现某个队列的深度超过阈值,就在IP头里把“拥塞经历”位标记为CE,然后继续转发这个包,而不是直接丢包。这个动作非常关键,代表着“我不丢你的包,但我告诉你拥塞了,你自觉点”。
终端网卡收到带CE标记的报文后,并不是直接自己降速,而是需要一个反馈环节。RoCE v2定义了CNP(Congestion Notification Packet)报文:接收端网卡检测到ECN标记,就会构造一个CNP,发给真正的数据源端,告诉源端“你发到我这边的流量已经在拥塞了”。源端网卡收到CNP后,通过内部的DCQCN算法降低发送速率。另外有种优化是交换机直接向源端发CNP,减少反馈往返时间,有些厂商实现了这种方案,我建议在兼容性确认后优先开启,反应更及时。
ECN的阈值设置是很多团队拿不准的点。阈值太低,交换机一有点流量就疯狂给包打标记,源端会被异常压制,带宽上不去;阈值太高,拥塞已经在队列里积累到很深,虽然包没丢,但延迟已经上去了,PFC会频繁介入。差不多的起步值是:把RoCE队列的最小ECN阈值设为该队列buffer的50%左右,最大阈值设为80%左右,然后再通过业务实测去微调。不同厂商的概率设置逻辑有差异,关键是以“ECN标记先于PFC触发”为目标,让端侧先降速,而不是等缓冲区满了再靠PFC去暂停。
3.3 DCQCN:动态拥塞控制算法
DCQCN(Data Center Quantized Congestion Notification)是目前RoCE v2上最主流的拥塞控制算法,最初由微软提出,目标是把ECN反馈和QCN(Quantized Congestion Notification)思路结合起来,让源端能够在毫秒级响应拥塞,同时多流之间尽量公平。它的核心思想是:收到一个CNP后,立刻把当前发送速率降低到一半;在之后的一定周期内没有继续收到CNP,就认为拥塞已经缓解,逐步恢复速率,但又不能猛增,必须小步爬升,避免再次触发拥塞。
具体拆开讲,DCQCN包含两段状态机。先是Timer-based Decrease阶段:一旦源端收到第一个CNP,速率立即减半,这个动作非常激进,目的是快速刹住拥塞恶化;随后如果持续收到CNP,说明拥塞仍存在,就继续降低,或者进入加性减小的阶段,每次按比例降一点。当拥塞开始消退,进入Fast Recovery阶段:源端在一个固定时间窗口内没有收到新的CNP,就试图更快恢复一点;再往后进入Additive Increase阶段,每个恢复周期只增加一个很小的步长,避免一跳恢复到全速又引起下一个拥塞峰。
这里面参数不少:初次降速比例、速率恢复周期、加性增量步长、对多个CNP的聚合窗口。网卡驱动默认值通常能跑出不错的性能,但不同场景有差异。比如存储小包流量为主,对拥塞响应要更激进,否则小流容易被大流欺负;而AI训练大流多,持续时间长,恢复太慢会浪费带宽。我在生产环境里,一般先把驱动和固件升级到厂商建议版本,用perftest跑吞吐验证默认参数,再通过长时间业务压测决定要不要调整恢复周期的长短。不要一上来就背一堆参数,先能稳定复现问题再优化。
3.4 三个机制如何协同
这三个机制的分工可以归纳成一句话:ECN负责提前预警,DCQCN负责源端降速,PFC负责最后的真无损兜底。正常运行时,理想状态是ECN能把大多数拥塞控制在队列早期,源端已经降速,PFC很少触发;当突发流量来得太快、源端来不及响应时,队列深度冲破ECN最大阈值,PFC才开始发暂停,封锁交换机与交换机之间的队列,避免尾部丢包;如果PFC都无法抑制,最终才会走到净丢包,这时候整个RoCE网络的问题排查就开始了。
集成协同还有一个要点:配置必须全网一致。一条RoCE路径上,接入交换机、汇聚交换机、核心交换机的ECN阈值、PFC使能状态必须保持一致,否则就会出现“在A交换机提前降速,到B交换机却直接丢了”的情况。这也意味着,每次改配置都要全网变更,不能只动局部。我见过某客户只把接入交换机PFC开了,汇聚端没开,结果出现偶发的微突发丢包,排查了整整一周,最后是用统计计数器发现汇聚端rx_pause帧为0,才锁定了不一致问题。全网视角,而不是单点视角,是RoCE运维的基本素养。
4. 端到端部署实操
4.1 交换机侧配置框架
先声明一下,这里不给特定品牌的逐条命令,因为厂商差异太大,直接贴命令会让读者复制出问题。我给的是一个配置思路框架,你拿到自己的交换机上对着型号找对应命令即可。第一步,把RoCE流量映射到专门的服务等级。通常把802.1p优先级3(或者4)留给RoCE,其他优先级留给普通流量。映射关系确定了,后面的PFC、ECN、buffer策略都围绕这个优先级展开。
第二步,开启该优先级的PFC。开启的动作要具体到端口和优先级,而不是全局。在开启前要确认LED环路和链路对端都支持PFC,否则暂停帧发出去了对端不识别。第三步,在相应的队列上配置ECN WRED。需要填最小阈值、最大阈值和标记概率,建议起步按buffer容量50%-80%设置,最大阈值不能超过buffer上限,否则直接丢包就失去了ECN“边标记边转”的意义。第四步,为RoCE队列分配足够的buffer空间,包括动态buffer的百分比限制。有些交换机还能对headroom buffer做独立配置,也就是给PFC暂停帧生效期间预留的额外buffer,这个值要结合链路时延和端口速率计算,我建议先用厂商默认值,再通过丢包计数器判断是否需要加。
具体配置动作并不复杂,真正复杂的是端口角色不同、配置策略也不同。对于接入交换机连接到服务器的端口,一般把该端口配置为信任报文优先级或强制优先级;对于交换机互联端口,则要保证两端PFC和ECN属性一致。核心多做一层路由转发,还要确认UDP 4791端口没有被ACL误拦截。不少集群因为核心交换机上有一条deny all的ACL把4791封了,导致跨POD的RoCE全断,排障时很容易忽略。
4.2 网卡侧配置与工具
网卡侧配置重点有两个:一是让RoCE v2能正常发出,二是让ECN/DCQCN生效。以Mellanox网卡为例,建议先装好驱动和固件,并确认接口工作在以太网模式下而不是IB模式。如果用的是系统自带驱动,建议通过ethtool确认接口支持roce特性,并检查LRO(Large Receive Offload)是否被关闭,有些场景下LRO会和RDMA内存注册产生冲突。
ECN在网卡侧通常会转成一组内部参数,比如初始化速率、降速因子、恢复周期。驱动版本不同,可调接口位置也不同。一般可以在modprobe配置里,或者通过mlxconfig工具查看“DCQCN”相关项,也可以用内核sysfs接口直接改。如果厂商默认值已经能跑满带宽,不建议再动这些参数;只有在性能调优阶段,配合交换机的ECN阈值一起调整才有意义。
验证工具方面,我常用perftest家族。先在服务端执行ib_write_bw -d mlx5_0,再在客户端执行ib_write_bw -d mlx5_0 <服务端IP>,就能看到实测带宽和延迟。注意测试时关闭系统防火墙,否则UDP端口被拦,测试完全跑不通。对于延迟测试,ib_write_lat更能反映端到端的真实体验。还有一个小工具rping可以测RC传输的连通性,比ping更贴近RDMA路径。
4.3 参数调优和验证方法
参数调优最忌讳的是“开盲盒式改值”。我来给一个标准调优流程:先做连通性和基线测试,确认两台机器在默认参数下能达到多少带宽;然后把交换机的ECN阈值和PFC配置逐项上线,对比开启前后吞吐与延迟曲线;最后把业务压测接入,跑半小时长稳,同时采集交换机的ECN计数器、PFC计数器、网卡侧的降速事件。
如果发现ECN标记事件几乎没有,说明阈值可能设高了,拥塞主要通过PFC缓解,这不是理想状态。如果ECN事件非常多,图形像锯齿一样,带宽也波动,说明降速过猛,恢复跟不上,需要调大恢复周期或减小加性增长步长。还有一种情况是PFC和ECN事件都很少,但吞吐就是不达标,那就要回到物理层检查,看光模块光功率、链路FEC协商是否一致,大概率是物理层信号问题而不是流控问题。
另外,RoCE的MTU建议直接开9000巨型帧。RoCE报文封装层次多,默认1500MTU会拆包严重,既增加转发处理开销,也容易踩到交换机分片丢弃的坑。全网MTU必须一致,包括服务器网卡和交换机所有接口,忽略这点会导致“延迟高、带宽低”的诡异现象。
5. 常见问题与排查实录
5.1 丢包和PFC风暴排查
Top 1问题就是RoCE吞吐不稳定、周期性掉坑。排查步骤先看交换机队列丢包计数:如果丢包集中在RoCE队列,先确认ECN是否已经开启,如果没开,那拥塞就全靠PFC硬扛,PFC计数会持续增长,此时应该把ECN阈值调低。另一个经典问题叫做PFC风暴:一旦某个RoCE队列的PFC暂停时间过长,上游Buffer被占满,pause层层上游传递,最后可能把整棵网络的RoCE流量全部憋住。检查方法是在每台交换机上看PFC tx/rx计数,如果某个端口tx pause帧速率极高,同时下游端口rx pause帧极高,说明拥塞在这条链路上扩散了。
解决PFC风暴的方向只有两个:要么降低突发流量本身,要么提高网络吸收突发的能力。前者可以调整业务端的发送时机,后者则要求把ECN阈值设置得更敏感,同时把buffer分配向RoCE队列倾斜。如果风暴已经导致卡死,可能需要临时手动disable PFC几秒钟,让堆积流量先消化,再重新开启。这个方法只能在极端情况下用,生产环境要谨慎,因为关PFC的这段时间RoCE会开始丢包。
5.2 性能不达标排查案例
有一回客户报“100G网络只能跑30G”,测iperf TCP正常,roce带宽就是上不去。我上去先看网卡速率是否协商到100G,正常;再看FEC,是RS-FEC,正常;然后看交换机端口ECN标记,发现CE标记数量极高,说明交换机反复在报拥塞。继续查,发现该客户在交换机上把ECN最小阈值设成了5%,相当于一有流量抖动就被标记,源端DCQCN层反复降速,导致带宽严重压抑。把阈值从5%调到40%后,带宽立刻恢复了。这就是典型的“ECN过度敏感导致性能塌陷”。
另一个常见原因是网卡驱动版本不一致。RoCE这种依赖端到端握手和精确参数的协议,驱动版本不一致时,ECN状态机行为可能完全不同,轻则性能暴跌,重则连接不稳定。所以我给客户立的规矩是:全网服务器必须统一RoCE驱动和固件版本,不允许“这批次是5.x,那批次是6.x”的混跑状态。听起来像洁癖,但真能避免大量玄学问题。
5.3 实用命令速查
网卡侧:用mlxlink -m检查物理链路,用ibstat或ibv_devinfo看端口状态;用ethtool -S查丢包和暂停帧计数,重点看rx_missed、rx_errors、tx_dropped、rx_pause、tx_pause;用ib_write_bw和ib_write_lat做性能基线。交换机侧:根据厂商不同,用display qos pfc statistics或show qos pfc counters查看PFC帧,用display qos ecn statistics查看ECN标记数量,用show qos buffer查看buffer高水位。如果你要快速判断一条路径是否健康,我有个简单的经验:正常运行时PFC帧应该是低频偶发,ECN标记也应该呈现“偶发、能消退”的趋势,而不是持续高位。
再补一个容易被忽略的检查点:VLAN和优先级映射。RoCE v2报文本身带802.1p优先级,如果交换机端口上没有配置信任或不信任策略,优先级在入方向可能被重写,导致PFC和ECN针对的队列根本不是业务真正使用的队列。遇到“配置都对但不生效”的场景,优先检查入方向优先级信任。这一点很容易花掉半天时间,但排查过程非常简单,喊厂商解一下转发表项即可。
6. 写在最后的个人体会
做了这么多年网络,我最大的感受是RoCE把“网络质量”和“应用性能”死死绑在一起。以前TCP网络有点丢包,应用层面可能只是慢一点,大家感知不强;但RoCE对丢包极敏感,网络抖动会直接变成存储时延飙升、GPU训练断断续续。这种设计是有意为之,因为它换来的是极致的CPU卸载和低延迟。代价就是运维必须更精细化,不能像以前那样“通了就行”。
我个人的建议是,RoCE项目启动时就把三个事做在前面:一是全网规格统一,包括固件、驱动、MTU、ECN参数、PFC配置;二是建立监控看板,把PFC帧计数、ECN标记计数、RoCE队列深度作为日常指标采集;三是准备一套压测脚本,每次变更后都跑一轮,用数据说话而不是凭感觉说没问题。这三件事做好,RoCE后期排障能少掉一大半的坑。最后再分享一个小细节,给网卡预留大一点的ring buffer和完成队列深度,在很多微突发场景下比调ECN参数更管用,但极少有人会主动去调它。细节决定成败,RoCE尤其如此。