做公有云的人,迟早都要啃一遍负载均衡。传统IDC时代,我们习惯用F5或者Nginx扛流量,规则简单、模型清晰。可一旦把场景搬到公有云,尤其是像Azure这种超大规模多租户环境,事情就完全变了:租户的IP段互相重叠、业务规模从几十台虚机到几十万台跨度极大、LB的API还要深度嵌进云平台的编排体系。这个时候再看那些传统方案,几乎处处碰壁。
Ananta就是Azure在这条路上磨出来的答案。它是Azure内部那套软件定义负载均衡系统的代号,和F5这类设备完全不同,Ananta不是一台设备,也不是一组集群,而是一套分布式的、控制面和数据面完全分离的横向扩展架构。它最早作为学术论文对外公开过,但真正看懂这套架构,再回头去看今天各种云负载均衡产品,会发现很多产品其实都是Ananta思路的变体。对做云网络、做中间件、做大规模分布式系统的人来说,这套系统的价值不是“某个公司内部的实现细节”,而是一整套在企业级流量入口场景下如何做分布式设计的方法论。这篇文章我就把Ananta的核心架构、转发链路和落地运维经验拆开揉碎来讲,希望对正在设计负载均衡系统或者想在云上选型LB的你有参考价值。
1. 为什么企业级负载均衡必须走软件定义这条路
先摆一个场景。你在云平台上卖虚拟机,用户买了10台VM,他自己在控制台上点几下就创建了一个负载均衡器,把流量分发到这10台VM上。这个动作看起来稀松平常,但对底层网络来说,它意味着:你要在某个网络位置接入一个“分发节点”,并且针对这组VM动态维护分发规则。
传统机房怎么解决?在机架上放两台F5,做VRRP主备,配一条VIP,后面挂真实服务器池。这个模型稳定,但在云环境下根本无法复制——原因很直接:租户数量是海量的,每个租户都可能开十几个LB实例,你不可能给每个LB实例分配两台专用硬件设备;租户的业务是动态的,VM创建销毁随时发生,硬件LB的配置变更速度跟不上云平台的编排速度;还有成本问题,硬件设备的采购周期和单位成本,决定了它没法成为云内的一种“可编程资源”。所以从第一天起,云平台的负载均衡就必须是软件形态的,并且必须能像虚拟机一样被调度、被生命周期管理、被多租户复用。
再说单机软件LB。很多人会想,既然硬件不行,那我在普通的x86服务器上装个Nginx或者用DPDK写个转发程序,做成集群总可以吧?这个思路在中小规模完全成立,但放在Azure那种规模上会卡在几个点上:一是单机能力有限,无论怎么优化,一台通用服务器的转发性能都比不上专门的硬件,而云平台的LB流量是几十T级别的;二是管理麻烦,如果LB是“一台台服务器”,那每台的配置同步、版本升级、故障替换都成了运维噩梦;最致命的是缺乏弹性——LB本身也应该是弹性资源,流量涨了要能扩容,租户多了要能分片,单机模型做不到这一点。
Ananta的思路是彻底换一个框架:不在某个固定位置放一台“LB设备”,而是把负载均衡拆成控制逻辑和转发逻辑两块,分别用不同的分布式组件承载。控制面负责管理VIP、DIP、健康状态这些配置信息;数据面则由一组分布在不同物理位置的转发组件协同完成,流量不再依赖某台固定设备,而是由整个网络基础设施建设共同承担。这个思路本质上就是把负载均衡从“设备”变成“网络的一种属性”,这也是SDN思想在负载均衡领域最典型的落地。
2. Ananta分层架构与核心组件拆解
2.1 总体控制流:从VIP分配到DIP摘除的完整链路
Ananta把整个系统的管理逻辑拆成了几个独立角色,每个角色只干一件专门的事,相互之间通过内部总线通信。这套设计最大的好处是每一个模块都可以独立扩展、独立替换,运维上也能按模块粒度去排查问题。
先明确一组角色关系。Ananta池部署在每个区域的多个PoP(入网点)中,每个PoP里都跑着一组服务实例,共同完成区域内的流量负载均衡。系统里有LBS(负载均衡服务)、LBM(负载均衡管理器)、以及一个用分布式表格实现的流表(Flow Table)。另外还有一个基础网络组件TAP,它部署在宿主机网络上,负责实际的隧道封包解包和最终投递。角色之间通过消息机制完成信息同步:LBM负责计算状态并下发指令,LBS负责具体转发,流表保存实时转发规则。
要理解这套架构,关键看两次创建过程。第一次是创建一个LB实例(即VIP)时,控制面的VIP管理器会为这个VIP分配一个IP地址,并且为它生成若干条“流表项”(Flow Entry)。流表项不是存在某个集中式数据库里,而是分布到多个LBS所在的节点的流表中。这些流表项的核心字段包括VIP、DIP池、协议、端口映射关系和哈希策略。LBM后台还有个组件专门负责把新生成的流表项批量推送到相关的LBS节点,并最终写入每台LBS本地的流表缓存。
第二次是配置后端服务器(DIP)时。用户把VM加入负载均衡器的后端池,控制面就对这组DIP做健康检查,检查失败的DIP会被自动从流表项中摘除。这个“摘除”动作不是删除整条流表项,而是修改该条流表项中的DIP列表字段,再通过增量同步的方式推给所有LBS节点——所以LBS永远不需要理解业务细节,它只需要根据流表项干活。
2.2 数据平面三大角色:LBS、LBM、流表的分工协作
LBS是真正跑流量的组件,运行在PoP的若干台服务器上。它的职责非常纯粹:接收进入PoP的流量包,解析五元组,在本地流表中查找匹配的流表项,根据流表项做目的地址转换(NAT),然后封装转发。为了让LBS尽可能轻,Ananta特意不让LBS维护任何VIP到DIP的一对一状态,一切转发决策都通过查流表完成,流表项本身是控制面算好推下来的,LBS只做匹配和执行。
LBM是控制面的核心,负责生成流表项、管理VIP和DIP池的全局视图、感知健康状态变化并触发流表项的更新。它自己不参与任何数据包的转发。为了应对大量租户和频繁变更,LBM需要和所有LBS建立可靠的指令通道,确保每条流表更新都能到达所有相关节点。
流表(Flow Table)是整个系统里最容易被低估的组件。它存放的是“当前所有活跃转发规则”的分布式视图。默认情况下,LBS本地缓存了全量的流表项,而全局的流表数据通过一个可水平扩展的分布式表格存储。由于不同VIP的流量可能命中同一台LBS,流表项的分布天然是按哈希散列的,这种设计避免了集中式数据库的瓶颈。
2.3 TAP与Underlay网络如何协同
TAP是Ananta架构中很容易被忽略但极其关键的角色。它运行在计算节点的宿主机网络上,承担两个职责:一是接收从LBS发来的封装流量并解包,把原始报文送到目标VM;二是处理VM发出的出向流量,做必要的映射后走underlay网络发出去。
理解TAP的存在,需要先理解一个网络上的难题。LBS和VM并不在同一个二层网络里,数据包从LBS出去之后要穿越underlay网络才能到达VM所在的宿主机,中间可能经过多跳路由。Ananta给LBS和TAP之间设计了一个标准的网络隧道方案:LBS封装后的报文,源是LBS的IP,目标是TAP所在宿主机网络的IP,TAP收到后解封装,从封装头里取出VIP、协议、端口和真实DIP的信息,再以正确的形式投递给宿主机上的VM。
这个设计把LBS和计算解耦了。LBS不需要知道自己把流量发给了哪个宿主机,只需要知道TAP的地址;TAP也不需要关心VIP的全局拓扑,只需要处理本地VM的隧道报文。转发路径上每一环的“知识”都被限制在最小范围,这也是Ananta能扩展到超大规模的原因之一。
3. 核心转发链路与关键功能实现
3.1 从入向流量到DIP的完整转发路径
现在我们把一个具体的入向流量包跟完整走一遍。客户端的请求目的是VIP的IP和端口,这个VIP是Azure负载均衡器的前端地址。数据包到达PoP入口后,首先由入口交换机按某种负载分摊策略(通常是一致性哈希)把流量发给某台LBS。LBS收到包后,解析出五元组,用这个五元组在本地流表里做最长匹配,找到对应的流表项。
比如流表项内容可能是:VIP=1.2.3.4:80,协议TCP,DIP列表=[10.0.0.1:8080, 10.0.0.2:8080],哈希策略是五元组哈希。LBS根据哈希策略选择其中一个DIP,把这个DIP的地址替换到数据包的目的地址字段,然后再做一次地址映射(把VIP的地址转换为TAP可识别的内部路由地址),接着把报文封装进新的IP报文,目标是TAP的地址。此时数据包已经是一个全新的封装包:外层源IP是LBS的地址,外层目的IP是TAP所在宿主机的地址。
TAP收到这个封装包后,去掉外层IP头,露出包含DIP地址的内层报文。TAP接下来做的事情是看这个DIP对应的是哪台VM。如果VM就在本宿主机,TAP直接把报文交给虚拟交换机,之后进入VM网卡。如果DIP不在本宿主机,TAP还要再转发一次,此时它扮演的是宿主机侧的分布式路由角色。这里有一个很实用的细节:TAP本地维护了一张DIP到宿主机的映射表,这个表是控制面同步下来的,不是现查现学的。
从性能上看,这条路径上唯一可能成为瓶颈的就是LBS的流表查询和封装操作。为此LBS做了几件事:流表用哈希索引,匹配过程不遍历;封装操作直接操作内存中的mbuf,不经过协议栈;转发路径全程无锁或细粒度锁。实测下来,这种纯用户态转发路径的单机性能可以扛到千万级并发连接,足够满足PoP级别的流量要求。
3.2 NAT语义与会话保持背后的哈希设计
很多人会忽略负载均衡器和NAT的关系,但Ananta的很多设计巧思恰恰体现在NAT语义上。入向流量从客户端到VIP,再被转发到DIP,这个过程里VIP地址会被翻译成DIP地址,这是标准的DNAT。但关键的问题是回程流量。DIP回程的目的地址是客户端的IP,如果DIP直接把包发给客户端,这个包的源IP就是DIP的IP,客户端不认。所以回程流量必须经过LB,做一次反向NAT,把源IP改回VIP。
Ananta的巧妙之处在于它不需要DIP把回程流量专门绕回同一台LBS。它设计了一个“对称哈希”策略:对五元组做哈希,不管是入向还是回向,只要五元组不变(客户端IP端口、VIP端口和DIP IP端口这些字段在NAT前后有确定映射),哈希到的LBS位置就是一致的。这意味着从DIP回程的流量只要进入PoP,经过同一套哈希算法处理,就必然能落到处理入向流量的同一台LBS上。这既保证了会话的闭环,又避免了给每台LBS维护复杂会话状态表,还天然支持无状态重启——因为一切都可以通过哈希重新计算。
这里要给一个实操建议:如果你自己在设计类似的分布式LB,不要把会话保持直接做成“记录连接属于哪台机器”的有状态模式,而是优先考虑“五元组在NAT前后的映射关系 + 一致性哈希”的无状态方案。前者在单机故障时会把所有会话打散,而后者只要哈希算法不变,故障迁移只影响极少量连接。Ananta在这件事上已经把路蹚平了。
3.3 健康探测的层次拆分:VIP探测与DIP探测
负载均衡器的健康检查是刚需,但很多系统把健康检查做成一种简单的心跳,这在高可用下是不够的。Ananta把健康检查拆成了两个层面:VIP层面的探测和DIP层面的探测,两者各自的用途和实现完全不同。
VIP探测是为了验证这个VIP的LB服务本身是否可用。探测请求从某个探测源发出,目的地址是VIP,期望得到响应。如果探测连续失败,说明VIP对应的整条数据链路(可能某台LBS或者某种映射关系)出了问题,控制面会把这个问题上报,并触发对LBS或流表项的重新分配。这里有一个关键细节:VIP探测的响应不是DIP返回的,而是LBS直接返回的,所以它验证的是LBS的存活和转发能力,而不是后端业务。
DIP探测才是真正检查后端VM健康状态的机制。Ananta允许租户自定义DIP的探测方式(HTTP、TCP或自定义端口),控制面根据探测结果更新流表项中的DIP状态,摘除异常DIP、恢复后重新加入。DIP探测的频率不用太高,通常在5秒~15秒之间,控制面把探测结果汇总后统一推送给LBS,所以LBS上的流表项更新是批量式的,不会因为探测频繁变动而产生大量控制面消息。
我自己在折腾软负载均衡时踩过一个坑:把健康检查和自动摘除做得太激进,探测超时设为1秒,结果后端有个做批量计算的VM偶发CPU跑满,每秒都可能超时,导致流表项里的DIP频繁上下线,连接成功率反而下降了。后来把超时调整到3秒且连续三次失败才摘除,稳定性立竿见影。Ananta把探测设计成分层双通道,本身也是在防御这类问题。
4. 从Ananta出发的企业级LB设计启示
4.1 水平扩展的秘密:无状态转发+分布式控制面
整个Ananta最核心的设计理念,就是“转发无状态”。一台LBS宕机了会怎样?它本地缓存的那部分流表项丢了,但全局流表的数据仍然存在分布式存储中,新上线的LBS会从全局拉取全量流表项重建缓存。由于流表项本身是控制面计算好的静态数据,LBS不保留任何连接级的动态状态,从故障中恢复就只是一个“重新加载配置”的过程,业务影响极小。
这一点对任何想做高可用LB的人都有借鉴意义。很多LB系统把安全性或复杂逻辑放进转发路径,结果转发节点一旦故障,除了丢连接还要丢状态,恢复起来异常痛苦。Ananta的选择是把所有智能都收拢到控制面,让数据面“傻”下来。转发路径越傻,系统越不容易出错;出了问题,恢复得也越快。
4.2 多租户隔离与云原生集成的边界技术
作为公有云的内部系统,Ananta天生要面对多租户问题。不同租户的VIP可能落在同一台LBS上,DIP的IP段也可能重叠。Ananta解决这个问题靠的是全局唯一的内部标识:每个VIP都有一个全局唯一的ID,即使在不同租户里看到了相同的IP地址,内部标识也不同。流表项中的匹配字段用的是“全局ID + 五元组”,而不是纯粹的IP地址,这样就从根本上避免了地址冲突。
这个设计对自建云平台的人来说等于是一份参考答案。如果你在OpenStack或者K8s里搭建自己的负载均衡插件,遇到租户IP重叠的问题时,不要试图在IP层面硬隔离,学Ananta的做法,引入一层虚拟化标识(VNI,类似于VXLAN的VNI),把租户的流量空间从IP层抽离出来。用ID而非地址做匹配,是一切虚拟化内服务化的基础。
Ananta的另一个云原生特性体现在它和SDN控制器、元数据服务的对接上。VM把自身IP上报到元数据服务,Ananta把LB的分配结果下发到VM侧的网络代理(即TAP),整个过程是完全自动化的,不需要运维人员手工登录任何一台设备。这给云平台的Serverless化、弹性伸缩策略提供了天然支撑——负载均衡不再是一个独立配置项,而是和VM生命周期紧密绑定的基础设施服务。
4.3 从硬件LB迁移到Ananta式LB时的注意事项
如果你所在团队正打算从传统硬件LB迁移到软件定义LB,或者是想自研一套类似Ananta的控制面,有几个经验值得分享。
先考虑数据面性能该如何规划。硬件LB的吞吐量是标称的购买指标,而软件LB的吞吐和配置强相关。Ananta的LBS是纯用户态转发、依赖流表查询和封装操作,实际性能受限于CPU核数、内存带宽和网卡队列数。我建议在规划阶段就做好基准测试,不要照搬硬件标称值。另外,软件LB的瓶颈往往不是吞吐而是小包率的PPS,规格选择时一定要看小包性能。
再考虑运维视角的差异。硬件LB的日常操作是配策略、盯CPU、看日志,而Ananta式LB的日常操作是盯流表同步延迟、查分布式存储的负载、观察LBS之间的哈希均不均衡。很多工程师刚上手时完全不适应——他们习惯看“设备状态”,但在这里需要改看“同步状态”和“一致性状态”。如果你正在搭建这类系统,建议把“流表同步延迟”和“LBS哈希偏差率”加入到核心巡检指标里,这两个指标直接决定了故障时的RTO。
最后说说成本。Ananta式的LB本质上是用通用服务器+分布式算法取代专用设备,单台成本可能不比F5低多少,但它是横向扩展的——流量翻了10倍,你就加10倍的LBS节点,而不是再买两台更贵的新设备。而且新增容量的边际成本是线性的,不会像硬件设备一样出现跳变。从企业CIO视角看,这不仅是技术方案替换,更是成本模型从“资本支出”向“运营支出”转变的过程。
5. 常见故障排查与运维经验实录
5.1 流表不一致导致的部分连接失败
我见过最多的故障表现是:同一个VIP,一部分客户端访问完全正常,另一部分客户端间歇性超时。在Ananta式架构里,这类问题八成出在流表不一致上。某台LBS没收到最新的流表更新,或者收到了但本地缓存没刷新,它还在往一个已经被摘除的DIP转发流量。
排查思路是先确认“故障是否和LBS有关”:看故障客户端的流量哈希到了哪台LBS,手动在这台LBS上查当前流表项的内容,跟LBM下发的期望状态做比对。如果发现不一致,优先检查LBS到LBM的同步通道,以及流表存储在批量同步时是否发生过超时。针对流表同步,我在实操中养成了一个习惯:不仅是消费端校验,还在LBS入口加一个流表版本号,每次同步都会携带全局版本,一旦发现版本落后就主动全量拉取,不依赖增量的最终到达。
这类问题的根因往往是控制面在下发时没有设置可靠的确认机制。如果你在设计自己的同步机制,建议加一个简单的两步提交:第一步下发“准备更新”信号,第二步下发“生效更新”信号,LBS只有在收到第二步后才切换新流表项。虽多一次交互,但能彻底避免中间态导致的转发错乱。
5.2 哈希不均造成单点LBS过载
另一个常见现象是某台LBS的CPU冲到100%,但其他LBS还很空闲。Ananta里的LBS选择用的是一致性哈希,正常情况下流量会均匀分摊,但存在两类例外:一是数据包流量的五元组集中在个别哈希值上,比如某个大客户业务的特征导致哈希碰撞集中;二是流表项更新频繁时,哈希环上的节点临时出现过热。
处理上先做短期缓解,把过载LBS上的一部分VIP重新哈希到其他LBS。Ananta支持在控制面动态调整VIP到LBS的映射,这就是SDN控制面带来的运维红利,传统硬件LB根本不给你在线改拓扑的机会。长期优化则是让LBS的哈希因子里加入“虚拟机IP + VM唯一标识”的混合字段,而不是只依赖IP,这样业务特征的聚集度会被VM的分布打散。
5.3 DIP健康检查的假阴性问题
健康检查的假阴性(误判后端故障)是每个LB运维都会碰到的坑。Ananta的DIP探测机制本身做了分层,但假阴性仍然常见,典型场景是后端VM的防火墙把健康检查源IP段给拦了。TAP发起的探测请求源地址往往是宿主机的内部保留网段,很多租户会在VM里配置防火墙规则放行“所有外部网段的来源”,忽略了内部网段,导致探测包被丢,后端被误摘除。
排查健康检查问题时,第一步永远不是看后端应用日志,而是看LB到DIP的探测包到底有没有被接收。可以在DIP上临时抓包,确认探测包的源IP,再检查防火墙规则。我见过不只一个团队在这个问题上折腾两三天,最后发现只是iptables少了一条放行规则。建议在系统上线前就把LB探测源网段固化成文档并写进安全组的初始化脚本,这种问题一次定位、永久根治。
注意:如果你在真实环境中遇到DIP频繁上下线,但后端应用明明正常,先检查宿主机安全组、VM防火墙、以及云平台的默认网络ACL,不要先怀疑LB的健康检查逻辑。
5.4 安全组与Ananta返回路径的交互冲突
最后一个值得单独写一段的坑,是安全组与TAP返回路径的交互。TAP在宿主机上承接了Ananta的下行流量,把它投递给VM。但VM发出的出向流量走的是裸机网卡,不经TAP。在默认配置里,如果有安全组规则只对“经过TAP的流量”做校验,就会出现一种奇怪现象:入向流量正常到达VM,但VM的响应包丢失,因为出向路径没经过处理。
这类问题最容易在从传统NAT网关切换到Ananta式LB时出现。传统NAT网关是强制绕行的,出向也过网关;但Ananta式LB只在入向介入了流量,出向走的是另一条链路。迁移时要在安全组里同时放行两条路径。你自己做方案设计时,脑子里一定要有一张流量流程图,标清楚入向和出向分别走的哪条链路、经过哪些安全节点,再核对每一段的安全组规则是否有对应放行策略。踩过一次这个坑之后,你会发现自己对网络路径的理解会上一个台阶,很多奇怪的“单向通”问题都能一眼看出答案。
Ananta这套架构落到今天,已经不只是Azure内部的实现细节了。从很多云厂商的LB产品里都能看到类似的分层影子,区别只是组件命名和数据结构的差异。对从业者来说,掌握这套思路最大的价值不是照搬它的代码,而是理解它做每个决策时的思考过程:为什么转发要无状态、为什么控制面要独立、为什么用哈希解决一切绑定问题。想清楚这些,再回头看你手头的Nginx、LVS、云LB,视角会完全不一样。
最后分享一个个人的心得体会:学习这类大型系统,别一上来就啃源码或者看论文里枯燥的架构图,先动手画一条流量的流向图,从客户端到VIP、到LBS、到TAP、再到VM,把每一跳上发生的动作标注出来。这个流程图能覆盖的问题比任何文档都多——不管是做性能分析、故障排查还是新功能设计,回到这张图上理一遍思路,方案自然就清晰了。这也算是我啃完Ananta相关材料之后,最想留给你的一个实操技巧。