克服多宿主服务器上的不对称路由
不对称 TCP/IP 路由会在具有多个网络连接的服务器上引起常见的性能问题。不对称路由产生的非典型网络流最常发生在服务器环境中,在这些环境中,发送流量和接收流量使用不同的接口。这些流被认为是不寻常的,因为来自连接一端(A→B)的流量与沿相反方向(B→A)移动的流量经过不同的链路集。不对称路由有合法的用途,例如利用高带宽但单向的卫星链路,但更常见的是性能问题的根源。
这些异常的数据包流与 TCP 的拥塞控制算法交互不良。即使数据流或有效吞吐量是单向的,TCP 也会在两个方向发送数据包。TCP 的拥塞控制算法预计数据包的延迟和丢失特性与它们在反方向传输时对应的确认和控制数据包所携带的特性相似。当两种类型的数据通过物理上不同的路径传输时,这种假设不太可能成立。由此产生的不匹配通常会导致次优的 TCP 性能(参见“资源”)。
当不对称路由引入人为的带宽瓶颈时,会发生更严重的问题。如果一台具有两个容量相等的接口的服务器在两个接口上都接收流量,但始终仅通过一个接口响应,则可能会产生瓶颈。服务器通常添加多个接口,甚至连接到同一交换机的多个接口,以提高服务器的总传输容量。不对称路由是这种配置中常见的意外结果,这是因为传统的路由完全基于目标地址。
基于目标的路由在选择通过哪个接口发送数据包时,仅使用数据包的目标 IP 地址的前缀。路由表中的每个条目都包含下一跳路由器的 IP 地址(如果需要路由器)以及应通过哪个接口发送数据包。该条目还包含可变长度的 IP 地址前缀,用于匹配候选数据包。对于 IPv4 主机路由,该前缀可以长达 32 位,对于匹配所有内容的默认路由,该前缀可以短至 0 位。如果多个路由表条目匹配,则使用前缀最长的条目。
未参与动态路由协议(如 OSPF 或 BGP)的典型服务器具有简单的路由表。它包含服务器上每个接口的一个条目和一个用于访问未直接连接到其接口的所有主机的默认路由。这种简单的方法严重依赖于单个默认路由,导致传出流量集中通过单个接口,而没有考虑最初接收请求的接口。
这种情况的一个很好的例子是配备了两个 100Mb 全双工接口的 Web 服务器。这两个接口都在同一子网上配置。如果它连接到具有多千兆位背板的全双工交换机,则此设置应提供来自传入和传出流量的 200Mb/秒带宽。这种安排是一种有吸引力的服务器设计,因为它允许服务器超过 100Mb 的容量,而无需升级到千兆位网络基础设施。这是一种经济高效的方法,因为即使基于铜缆的千兆位网卡正变得越来越便宜,但使用它们的交换机端口成本仍然明显高于几个 100Mb 端口的成本。
通常,连接到此 Web 服务器的客户端首先会遇到某种负载均衡器,无论是基于 DNS 的还是可能是第 4 层交换设备,它会将一半的请求定向到一个接口,另一半定向到另一个接口。列表 1 显示了如果 Web 服务器有两个接口(都在 192.168.16.0/24 子网上配置),则默认路由表可能如下所示。
列表 1. 典型路由表
Destination Gateway Genmask Flags Iface 192.168.16.0 * 255.255.255.0 U eth0 192.168.16.0 * 255.255.255.0 U eth1 127.0.0.0 * 255.0.0.0 U lo default 192.168.16.1 0.0.0.0 UG eth0
在这种情况下,由于负载均衡器,传入负载均匀分布。但是,响应流量全部通过 eth0 发出,因为默认情况下,服务器使用基于目标的路由。

图 1. 不平衡的服务器

图 2. 为了有效地使用两个接口,我们需要使用基于策略的路由。
Web 服务器上的大部分流量是传出的,因为 HTTP 响应往往比请求大得多。因此,即使此服务器具有两个负载均衡的接口,其有效带宽仍然限制为 100Mb/秒。仅对请求进行负载均衡无济于事,因为瓶颈在响应端。数据包要么使用通过 eth0 的默认规则,要么如果它们的目标是本地子网,则必须在两个权重相等的路由之间进行选择。在这种情况下,选择第一个路由(再次到 eth0)。最终结果是 Web 请求在 eth0 和 eth1 之间均匀平衡,但更大且更重要的响应都通过 eth0 上的瓶颈进行传输。
解决这种不平衡问题的一个好方法是使用 iproute2 软件包。iproute2 允许管理员抛弃传统的网络配置工具(如 ifconfig),并使用名为 ip 的程序来处理更复杂的情况。ip 是内核开发人员 Alexey Kuznetsov 编写的 iproute2 软件包的一部分。大多数发行版都预装了 iproute2。如果您的系统上没有 ip 命令,可以从 ftp.inr.ac.ru/ip-routing 下载该软件包。与许多与 Linux 内核紧密集成的软件包一样,iproute2 需要在编译期间提供内核源代码副本。
一般的 iproute2 功能还需要将 netlink 套接字支持编译到正在运行的内核中。此外,本文概述的特定策略需要正在运行的内核中配置 IP: 高级路由器和 IP: 策略路由选项。这些功能在整个 2.4 内核系列中都可用,并且也包含在 2.6 中。内核配置脚本仍然将策略路由标记为 NEW,但这更多的是内核帮助屏幕更新缓慢的一个因素,而不是反映高级路由和策略路由的成熟度。
iproute2 的完整故事过于复杂,无法在本文中介绍。除了控制路由行为外,iproute2 还可以用于设置接口、控制 arp 行为、执行 NAT 和建立隧道。
iproute2 的路由控制的主要思想是将路由决策分为两个步骤。第二步是传统的基于目标的路由表。iproute2 世界中的关键区别在于,系统可以包含许多不同的基于目标的路由表,而不是单个全局系统表。第一个 iproute2 步骤用于标识在第二步期间应使用这些表中的哪一个。此表识别步骤称为规则选择或策略选择。规则选择被认为比传统路由更灵活,因为它在进行策略选择时使用的因素比仅数据包的目标地址更广泛。
这种两阶段的基础架构为解决上述多宿主 Web 服务器上的瓶颈问题奠定了基础。首先,我们需要创建两个路由表;每个表通过不同的接口路由出去。其次,我们需要创建决策步骤,使其选择路由表,该路由表将服务器的响应流量发送到请求到达的同一接口。传出数据包的源地址可用于将数据包与会话发起的接口相关联。在网络术语中,此技术称为基于源的路由。
首先创建两个路由表。这些表只需要到我们主网关的默认路由,但每个表都使用不同的接口来访问该网关。不同的表在 iproute2 配置中由唯一的整数表示。表编号可以通过 /etc/iproute2/rt_tables 文件给出字符串别名,但简单的数字对于此示例来说就足够了。这些数字只是标识符,它们的大小没有意义。默认系统路由表(使用传统 route 命令时看到的正常表)的编号为 254。编号 1 到 252 可供本地使用。我们在此处将示例表称为表 1 和表 2
#ip route add default via 192.168.16.1 dev eth0 tab 1 #ip route add default via 192.168.16.1 dev eth1 tab 2
显示任何表的内容都是使用 ip route show 命令完成的
#ip route show table 1 default via 192.168.16.1 dev eth0 #ip route show table 2 default via 192.168.16.1 dev eth1
我们的简单表看起来不错;它们唯一的区别是它们传输的接口。让我们继续创建在运行时动态选择两个表之间的策略。在示例服务器上,接口 eth0 绑定到地址 192.168.16.20,接口 eth1 绑定到 192.168.16.21。将传出数据包的源地址与使用接口的表相匹配的选择策略可以实现我们的目标,该接口又绑定到该源地址。这听起来比实际过程复杂。它的真正含义是我们需要一个策略,该策略声明源地址为 192.168.16.20 的数据包应使用表 1,因为表 1 使用 eth0,而 eth0 绑定到 192.168.16.20。类似的逻辑适用于将 eth1 和 192.168.16.21 绑定在一起的策略。
每个路由策略都有一个关联的优先级。优先级编号较低的策略优先于也可能匹配候选数据包但优先级值较高的策略。优先级是一个 32 位无符号数字,因此永远不会出现找不到足够的优先级级别来详细表达任何算法的问题。我们的示例算法只需要两个策略。
在启动时,内核会创建几个默认规则来控制服务器的正常路由。这些规则的优先级为 0、32766 和 32767。优先级为 0 的规则是用于控制盒内流量的特殊规则,不会影响我们。但是,我们确实希望我们的新规则优先于其他默认规则,因此它们应使用小于 32766 的优先级编号。如果您确定您的替换路由永远不需要回退到服务器的默认行为,也可以删除这两个默认规则。
新策略规则是使用 ip rule add 命令添加的。from 属性用于生成基于源地址的路由策略。
#ip rule add from 192.168.16.20/32 tab 1 priority 500 #ip rule add from 192.168.16.21/32 tab 2 priority 600
在此设置下,传出数据包首先检查源地址是否为 192.168.16.20。如果匹配,则它们使用路由表 1,该表将所有流量发送到 eth0。否则,将检查数据包的源地址是否与 192.168.16.21 匹配。与该规则的匹配将使用表 2,该表将所有流量发送到 eth1。任何其他数据包都将使用规则 32766 和 32767 详细说明的默认系统规则。
#ip rule show 0: from all lookup local 500: from 192.168.16.20 lookup 1 600: from 192.168.16.21 lookup 2 32766: from all lookup main 32767: from all lookup 253
对策略数据库所做的更改不会动态生效。要告知内核它需要重新解析策略数据库,请发出 ip route flush cache 命令
#ip route flush cache
iproute2 允许您在执行策略选择时使用源地址以外的因素。候选数据包的服务类型位、目标地址和任何区分服务标记以及其他一些属性也可用。有关所有 iproute2 参数和功能的详细说明,请参阅 www.compendium.com.ar/policy-routing.txt 和 www.linuxgrill.com/iproute2.doc.html。
现在让我们看看在真实的 Web 服务测试中,这种技术的结果如何。该测试包括传输一个 90KB 的文件 20,000 次。HTTP 事务在服务器的两个 IP 地址之间进行负载均衡,平均并行执行 40 个连接。
ifconfig 命令报告接口的数据包计数器。列表 2 显示了在未采用基于源的路由方法的普通 Web 服务器上运行测试后 ifconfig 命令的输出。
列表 2. 基于目标的路由的接口计数器
eth0 Link encap:Ethernet HWaddr 00:E1:AA:7C:51:2C inet addr:192.168.16.20 Bcast:192.168.16.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:328008 errors:0 dropped:0 overruns:0 frame:0 TX packets:1341151 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:100 RX bytes:23963417 (22.8 Mb) TX bytes:1908125938 (1819.7 Mb) Interrupt:19 Base address:0xe400 Memory:dff80000-dffa0000 eth1 Link encap:Ethernet HWaddr 00:E1:AA:7C:51:2D inet addr:192.168.16.21 Bcast:192.168.16.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:346430 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:100 RX bytes:25250075 (24.0 Mb) TX bytes:0 (0.0 b) Interrupt:16 Base address:0xec00 Memory:dffa0000-dffc0000
服务器接收到的流量(包括 HTTP 请求和 HTTP 响应的 TCP 确认)非常均衡,每个接口接收到大约 330,000 个数据包。但是,传输流量已成为异步路由问题的牺牲品:接口 eth0 传输了 130 万个数据包,而 eth1 没有传输任何数据包。
列表 3 包含在重新启动服务器以清除接口计数器并采用本文讨论的 iproute2 策略后 ifconfig 的输出。然后以与上述相同的方式再次运行测试。
列表 3. 基于策略的路由的接口计数器
eth0 Link encap:Ethernet HWaddr 00:E1:AA:7C:51:2C inet addr:192.168.16.20 Bcast:192.168.16.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:332371 errors:0 dropped:0 overruns:0 frame:0 TX packets:670341 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:100 RX bytes:24270910 (23.1 Mb) TX bytes:954045844 (909.8 Mb) Interrupt:19 Base address:0xe400 Memory:dff80000-dffa0000 eth1 Link encap:Ethernet HWaddr 00:E1:AA:7C:51:2D inet addr:192.168.16.21 Bcast:192.168.16.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:334110 errors:0 dropped:0 overruns:0 frame:0 TX packets:670152 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:100 RX bytes:24387875 (23.2 Mb) TX bytes:954032082 (909.8 Mb) Interrupt:16 Base address:0xec00 Memory:dffa0000-dffc0000
服务器接收到的流量仍然非常均衡,但传输流量现在已均衡,每个接口为 670,000 个数据包。
基于源的路由功能在高端网络设备上很常见,但在服务器环境中很少见或使用。Linux 具有出色但鲜为人知的基于源的路由支持。关于高级 Linux 路由和流量整形的整个领域在 lartc.org 上有很好的描述。
资源
网络不对称对 TCP 性能的影响:www.eecs.berkeley.edu/IPRO/Summary/97abstracts/padmanab.1.html
Linux 高级路由和流量控制:www.lartc.org
Patrick McManus (mcmanus@ducksong.com) 在 Datapower Technology 担任软件工程师,靠近他在马萨诸塞州波士顿的家。他目前痴迷于阅读每位美国总统的传记。