【可伸缩网络服务的设计与实现】四、IP虚拟服务器的实现和性能测试

IP虚拟服务器的实现和性能测试




本章主要讲述IP负载均衡技术和连接调度算法在Linux内核中的实现,称之为IP虚拟服务器(IP Virtual Server,简写为IPVS),再叙述了实现中所遇到关键问题的解决方法和优化。最后,对IPVS软件进行性能测试,并列举该软件的应用情况。

# 一、系统实现的基本框架




我们分别在Linux 内核2.0和内核2.2中修改了TCP/IP协议栈,在IP层截取和改写/转发IP报文,实现了三种IP负载均衡技术,并提供了一个ipvsadm程序进行虚拟服务器的配置和管理。在Linux 内核2.4和2.6中,我们把它实现为NetFilter的一个模块,很多代码作了改写和进一步优化,目前版本已在网上发布,根据反馈信息该版本已经较稳定。



图5.1:系统的主要功能模块

系统的主要功能模块如图5.1所示,“VS Schedule & Control Module”是虚拟服务器的主控模块,它挂接在IP报文遍历的LOCAL_IN链和IP_FORWARD链两处,用于截取/改写IP报文;“VS Rules Table”用于存放虚拟服务器的规则,“Connections Hash Table”表是用于记录当前连接的Hash表;“Stale Connection Collector”模块用于回收已经过时的连接;“Statistics Data”表记录IPVS的统计信息。用户空间的ipvsadm管理程序通过setsockopt()函数将虚拟服务器的规则写入“VS Rules Table”表中,通过/proc文件系统把“VS Rules Table”表中的规则读出。

当一个IP报文到达时,若报文的目标地址是本地的IP地址,IP报文会转到LOCAL_IN链上,否则转到IP_FORWARD链上。IPVS模块主要挂接在LOCAL_IN链和IP_FORWARD链两处。当一个目标地址为Virtual IP Address的报文到达时,该报文会被挂接在LOCAL_IN链上的IPVS程序捕获,若该报文属于在连接Hash表中一个已建立的连接,则根据连接的信息将该报文发送到目标服务器,否则该报文为SYN时,根据连接调度算法从一组真实服务器中选出一台服务器,根据IP负载调度设置的规则将报文发送给选出的服务器,并在连接Hash表中记录这个连接。挂接在IP_FORWARD链上的IPVS程序是改写VS/NAT中服务器响应报文的地址。

连接的Hash表可以容纳几百万个并发连接,在Linux内核2.2和内核2.4的IP虚拟服务器版本中每个连接只占用128Bytes有效内存,例如一个有256M可用内存的调度器就可调度两百万个并发连接。连接Hash表的桶个数可以由用户根据实际应用来设定,来降低Hash的冲突率。

在每个连接的结构中有连接的报文发送方式、状态和超时等。报文发送方式有VS/NAT、VS/TUN、VS/DR和本地结点,报文会被以连接中设定的方式发送到目标服务器。这意味着在一个服务器集群中,我们可以用不同的方式(VS/NAT、VS/TUN或VS/DR)来调度不同的服务器。连接的状态和超时用于记录连接当前所在的状态,如SYN_REC、ESTABLISHED和FIN_WAIT等,不同的状态有不同的超时值。

# 二、系统实现的若干问题




本节讲述实现时所遇到的若干主要问题和它们的解决方法或者优化处理。

# 2.1、Hash表




在系统实现中,我们多处用到Hash表,如连接的查找和虚拟服务的查找。选择Hash表优先Tree等复杂数据结构的原因是Hash表的插入和删除的复杂度为O(1),而Tree的复杂度为O(log(n))。Hash表的查找复杂度为O(n/m),其中n为Hash表中对象的个数,m为Hash表的桶个数。当对象在Hash表中均匀分布和Hash表的桶个数与对象个数一样多时,Hash表的查找复杂度可以接近O(1)。

因为连接的Hash表要容纳几百万个并发连接,并且连接的Hash表是系统使用最频繁的部分,任何一个报文到达都需要查找连接Hash表,所以如何选择一个高效的连接Hash函数直接影响到系统的性能。连接Hash函数的选择要考虑到两个因素,一个是尽可能地降低Hash表的冲突率,另一个是Hash函数的计算不是很复杂。

一个连接有客户的

、虚拟服务的

和目标服务器的

等元素,其中客户的

是每个连接都不相同的,后两者在不同的连接经常重叠。所以,我们选择客户的

来计算Hash Key。在IPVS版本中,我们用以下快速的移位异或Hash函数来计算。

#define IP_VS_TAB_BITS CONFIG_IP _VS_TAB_BITS
#define IP_VS_TAB_SIZE (1 << IP_VS_TAB_BITS)
#define IP_VS_TAB_MASK (IP_VS_TAB_SIZE - 1)
inline unsigned ip_vs_hash_key(unsigned proto, unsigned addr, unsigned port)
{
return (proto ^ addr ^ (addr>>IP_VS_TAB_BITS) ^ port)
& IP_VS_TAB_MASK;
}
</pre> 为了评价Hash函数的效率,我们从一个运行IPVS的真实站点上取当前连接的样本,它一共含有35652个并发连接。在有64K桶的Hash表中,连接分布如下: <pre>
桶的长度(Lj) 该长度桶的个数(Nj)
5 16
4 126
3 980
2 5614
1 20900
`

通过以下公式算出所有连接查找一次的代价:

所有连接查找一次的代价为45122,每个连接查找的平均代价为1.266(即45122/35652)。我们对素数乘法Hash函数进行分析,素数乘法Hash函数是通过乘以素数使得Hash键值达到较均匀的分布。

<pre>`
inline unsigned ip_vs_hash_key(unsigned proto, unsigned addr, unsigned port)
{
    return ((proto+addr+port)* 2654435761UL) &amp; IP_VS_TAB_MASK;
}
`</pre>

其中,2654435761UL是2到2^32间黄金分割的素数,

<pre>`
2654435761 / 4294967296 = 0.618033987

在有64K桶的Hash表中,素数乘法Hash函数的总查找代价为45287。可见,现在IPVS中使用的移位异或Hash函数还比较高效。

在最新的Linux内核2.4和2.6中,连接的Hash函数是使用Jenkins函数。

2.2、垃圾回收




为了将不再被使用的连接单元回收,我们在连接上设置一个定时器,当连接超时,将该连接回收。因为系统中有可能存在几百万个并发连接,若使用内核中的定时器,几百万个连接单元系统的定时器的列表中,系统每隔1/100秒进行单元的迁移和回收已超时的连接单元,这会占用很多系统的开销。

因为连接的回收并不需要很精确,我们可以让系统的定时器每隔1秒启动连接回收程序来回收那些超时的连接。为此,我们设计了一个慢定时器,连接的定时都是以1秒钟为单位。用三个时间大转盘,第一个转盘有1024个刻度,定时在1024秒钟之内的连接都挂接在第一个转盘的各个刻度上;第二个转盘有256个刻度,定时在[210, 218)区间的落在第二个转盘上;第三个转盘有256个刻度,定时在[218, 226)区间的落在第三个转盘上。

慢定时器处理程序每隔1秒由系统定时器启动运行一次,将第一个转盘当前指针上的连接进行回收,再将指针顺时针转一格。若指针正好转了一圈,则对第二个转盘当前指针上的连接进行操作,根据他们的定时迁移到第一个转盘上,再将指针顺时针转一格。若第二个转盘的指针正好转了一圈,则对第三个转盘当前指针上的连接进行操作,根据他们的定时迁移到第二个转盘上。

使用这种慢定时器极大地提高了过期连接的回收问题。在最初的版本中,我们是直接使用系统的定时器进行超时连接的回收,但是当并发连接数增加到500,000时,系统的CPU使用率已接近饱和,所以我们重新设计了这种高效的垃圾回收机制。

# 2.3、ICMP处理




负载调度器需要实现虚拟服务的ICMP处理,这样进来的虚拟服务ICMP报文会被改写或者转发给正确的后端服务器,出去的ICMP报文也会被正确地改写和发送给客户。ICMP处理对于客户和服务器间的错误和控制通知是非常重要的。

ICMP消息可以发现在客户和服务器间的MTU(Maximum Transfer Unit)值。在客户的请求被VS/DR调度到一台服务器执行,服务器将执行结果直接返回给客户。例如响应报文的MTU为1500个字节,在服务器到客户的路径中有一段线路的MTU值为512个字节,这时路由器会向报文的源地址(即虚拟服务的地址)发送一个需要分段为512个字节的ICMP消息。该ICMP消息会到达调度器,调度器需要将ICMP消息中原报文的头取出,再在Hash表中找到相应的连接,然后将该ICMP消息转发给对应的服务器。这样,服务器就会将原有的报文分段成512个字节进行发送,客户得到服务的响应。

# 2.4、可装卸的调度模块




为了提高系统的灵活性,我们将连接调度做成可装卸的模块(Loadable Modules),如ip_vs_rr.o、ip_vs_wrr.o、ip_vs_lc.o、ip_vs_wlc.o、ip_vs_lblc.o、ip_vs_lblcr.o、ip_vs_dh.o、ip_vs_sh.o、ip_vs_sed.o和ip_vs_nq.o。当虚拟服务设置时,会将相应的模块调到内核中。这样,有助于提高系统的使用效率,不装载不被使用的资源。

# 2.5、锁的处理和优化




在系统中虚拟服务规则的读和写需要锁来保证处理的一致性。在连接的Hash表中,同样需要锁来保证连接加入和删除的一致性。连接的Hash表是系统使用最频繁的资源,任何一个报文到达都需要查找连接Hash表。如果只有一个锁来管理连接Hash表的操作,锁的冲突率会很高。为此,我们引入有n个元素的锁数组,每个锁分别控制1/n的连接Hash表,增加锁的粒度,降低锁的冲突率。在两个CPU的SMP机器上,假设CPU操作Hash表的部位是随机分布的,则两个CPU同时操作同一区域的概率为1/n。在系统中n的缺省值为16。

# 2.6、连接的相关性




到现在为止,我们假设每个连接都相互独立的,所以每个连接被分配到一个服务器,跟过去和现在的分配没有任何关系。但是,有时由于功能或者性能方面的原因,一些来自同一用户的不同连接必须被分配到同一台服务器上。

FTP是一个因为功能设计导致连接相关性的例子。在FTP使用中,客户需要建立一个控制连接与服务器交互命令,建立其他数据连接来传输大量的数据。在主动的FTP模式下,客户通知FTP服务器它所监听的端口,服务器主动地建立到客户的数据连接,服务器的端口一般为20。IPVS调度器可以检查报文的内容,可以获得客户通知FTP服务器它所监听的端口,然后在调度器的连接Hash表中建立一个相应的连接,这样服务器主动建立的连接可以经过调度器。但是,在被动的FTP模式下,服务器告诉客户它所监听的数据端口,服务器被动地等待客户的连接。在VS/TUN或VS/DR下,IPVS调度器是在从客户到服务器的半连接上,服务器将响应报文直接发给客户,IPVS调度器不可能获得服务器告诉客户它所监听的数据端口。

SSL(Secure Socket Layer)是一个因为性能方面原因导致连接相关性的例子。当一个SSL连接请求建立时,一个SSL的键值(SSL Key)必须要在服务器和客户进行选择和交换,然后数据的传送都要经过这个键值进行加密,来保证数据的安全性。因为客户和服务器协商和生成SSL Key是非常耗时的,所以SSL协议在SSL Key的生命周期内,以后的连接可以用这个SSL Key和服务器交换数据。如果IPVS调度器将以后的连接调度到其他服务器,这会导致连接的失败。

我们现在解决连接相关性的方法是持久服务(Persistent Service)的处理。使用两个模板来表示客户和服务器之间的持久服务,模板〈protocol, client_ip, 0, virtual_ip, virtual_port, dest_ip, dest_port〉表示来自同一客户client_ip到虚拟服务〈virtual_ip, virtual_port〉的任何连接都会被转发到目标服务器〈dest_ip, dest_port〉,模板〈protocol, client_ip, 0, virtual_ip, 0 dest_ip, 0〉表示来自同一客户client_ip到虚拟服务器virtual_ip的任何连接都会被转发到目标服务器dest_ip,前者用于单一的持久服务,后者用于所有端口的持久服务。当一个客户访问一个持久服务时,IPVS调度器会在连接Hash表中建立一个模板,这个模板会在一个可设置的时间内过期,如果模板有所控制的连接没有过期,则这个模板不会过期。在这个模板没有过期前,所有来自这个客户到相应服务的任何连接会被发送到同一台服务器。

持久服务还可设置持久的粒度,即可设置将来自一个C类地址范围的所有客户请求发送到同一台服务器。这个特征可以保证当使用多个代理服务器的客户访问集群时,所有的连接会被发送到同一服务器。

虽然持久服务可能会导致服务器间轻微的负载不平衡,因为持久服务的一般调度粒度是基于每个客户机的,但是这有效地解决连接相关性问题,如FTP、SSL和HTTP Cookie等。

# 2.7、本地结点




本地结点(Local Node)功能是让调度器本身也能处理请求,在调度时就相当一个本地结点一样,在实现时就是根据配置将部分连接转交给在用户空间的服务进程,由服务进程处理完请求将结果返回给客户。该功能的用处如下:

当集群中服务器结点较少时,如只有三、四个结点,调度器在调度它们时,大部分的CPU资源是闲置着,可以利用本地结点功能让调度器也能处理一部分请求,来提高系统资源的利用率。

在分布式服务器中,我们可以利用IPVS调度的本地结点功能,在每台服务器上加载IPVS调度模块,在一般情况下,利用本地结点功能服务器处理到达的请求,当管理程序发现服务器超载时,管理程序将其他服务器加入调度序列中,将部分请求调度到其他负载较轻的服务器上执行。

在地理上分布的服务器镜像上,镜像服务器利用本地结点功能功能处理请求,当服务器超载时,服务器通过VS/TUN将请求调度到邻近且负载较轻的服务器上。

# 2.8、数据统计




在IPVS虚拟服务使用情况的统计上,我们实现了如下计数器:

调度器所处理报文的总数 调度器所处理连接的总数
调度器中所有并发连接的数目 每个虚拟服务处理连接的总数
每个服务器所有并发连接的数目

在单位时间内,我们可以根据调度器所处理报文总数之差得出调度器的报文处理速率,根据调度器所处理连接总数之差得出调度器的连接处理速率。同样,我们可以算出每个虚拟服务的连接处理速率。

# 2.9、防卫策略




IPVS调度器本身可以利用Linux内核报文过滤功能设置成一个防火墙,只许可虚拟服务的报文进入,丢掉其他报文。调度器的脆弱之处在于它需要记录每个连接的状态,每个连接需要占用128个字节,一些恶意攻击可能使得调度器生成越来越多的并发连接,直到所有的内存耗尽,系统出现拒绝服务(Denial of Service)。但是,一般SYN-Flooding攻击调度器是非常困难的,假设系统有128Mbytes可用内存,则系统可以容纳一百万个并发连接,每个处理接受SYN连接的超时(Timeout)为60秒,SYN-Flooding主机需要生成16,666 Packets/Second的流量,这往往需要分布式SYN-Flooding工具,由许多个SYN-Flooding主机同时来攻击调度器。

为了避免此类大规模的恶意攻击,我们在调度器中实现三种针对DoS攻击的防卫策略。它们是随机丢掉连接、在调度报文前丢掉1/rate的报文、使用更安全的TCP状态转换和更短的超时。在系统中有三个开关分别控制它们,开关处于0表示该功能完全关掉。1和2表示自动状态,当系统的有效内存低于设置的阀值时,该防卫策略被激活,开关从1状态迁移到2状态;当系统的有效内存高于设置的阀值时,该防卫策略被关掉,开关从2状态迁移到1状态。3表示该策略永远被激活。

# 2.10、调度器间的状态同步




尽管IPVS虚拟服务器软件已被证明相当鲁棒,但调度器有可能因为其他原因而失效,如机器的硬件故障和网络线路故障等。所以,我们引入一个从调度器作为主调度器的备份,当主调度器失效时,从调度器将接管VIP等地址进行负载均衡调度。在现在的解决方案中,当主调度器失效时,调度器上所有已建立连接的状态信息将丢失,已有的连接会中断,客户需要向重新连接,从调度器才会将新连接调度到各个服务器上。这对客户会造成一定的不便。为此,我们考虑一种高效机制将主调度器的状态信息及时地复制到从调度器,当从调度器接管时,绝大部分已建立的连接会持续下去。

因为调度器的连接吞吐率是非常高的,如每秒处理一万多个连接,如何将这些变化非常快的状态信息高效地复制到另一台服务器?我们设计利用内核线程实现主从同步进程,在操作系统的内核中,直接将状态信息发送到从调度器上,可以避免用户空间和核心的切换开销。其结构如图5.2所示:



图5.2:主从调度器间的状态同步

在主从调度器的操作系统内核中分别有两个内核线程ConnSyncd,主调度器上的ConnSyncd每隔1/10秒钟唤醒一次从更新队列中将更新信息读出,将更新信息发给从调度器上的ConnSyncd,然后在从调度器内核中生成相应的状态信息。为了减少主从调度器间的通讯开销,在主调度器的更新队列中只放新连接生成的信息,在从调度器中生成连接信息,设置定时器,当连接超时,该连接会自动被删除。

主从调度器间状态复制的代码正在编写中。因为主从调度器间的状态复制会降低调度器的吞吐率,所以主从调度器间状态复制会以模块的形式出现,当用户特别需要时,可以将该模块加入内核中。

# 三、性能测试




在美国VA Linux公司的高级工程师告诉我,他们在实验室中用一个IPVS调度器(VS/DR方式)和58台WEB服务器组成一个WEB集群,想测试在真实网络服务负载下IPVS调度器的性能,但是他们没有测试IPVS调度器的性能,当58台WEB服务器都已经满负荷运行时,IPVS调度器还处于很低的利用率(小于0.2)。他们认为系统的瓶颈可能在网卡的速度和报文的转发速度,估计要到几百台服务器时,IPVS调度器会成为整个系统的瓶颈。

我们没有足够的物理设备来测试在真实网络服务负载下IPVS调度器的性能,况且在更高的硬件配置下(如两块1Gbps网卡和SMP机器),调度器肯定会有更高的性能。为了更好地估计在VS/DR和VS/TUN方式下IPVS调度器的性能,我们专门写一个测试程序testlvs,程序不断地生成SYN的报文发送给调度器上的虚拟服务,调度器会生成一个连接并将SYN报文转发给后端服务器,我们设置后端服务器在路由时将这些SYN报文丢掉,在后端服务器的网卡上我们可以获得进来的报文速率,从而估计出调度器的报文处理速率。

我们的测试环境如图5.3所示:有四台客户机、三台服务器和一个Pentium III 500MHz、128M内存和2块100M网卡的调度器,四台客户机和调度器通过一个100M交换机相连,三台服务器和调度器也通过一个100M交换机相连。它们都运行Linux操作系统。



图5.3:IPVS调度器的性能测试环境

在VS/NAT的性能测试中,我们分别在三台服务器启动三个Netpipe服务进程,在调度器上开启三个虚拟服务通过网络地址转换到三台服务器,用三台客户机运行Netpipe分别向三个虚拟服务进行测试,调度器已经满负荷运行,获得三个Netpipe的累计吞吐率为89Mbps。在正常网络服务下,我们假设每个连接的平均数据量为10Kbytes,VS/NAT每秒处理的连接数为1112.5 Connections/Second。

在VS/DR和VS/TUN的性能测试中,我们设置后端服务器在路由时将这些SYN报文丢掉,后端服务器就像一个黑洞将报文吸掉,它的处理开销很小,所以我们在后端服务器只用两台。我们在后端服务器上运行程序来测试进来报文的速率,在调度器上将一虚拟服务负载均衡到两台后端服务器,然后在四台客户机上运行testlvs不断地向虚拟服务发SYN报文,报文的源地址是随机生成的,每个报文的大小为40个字节。测试得VS/DR的处理速率为150,100 packets/second,VS/TUN的处理速率为141,000packets/second,可见将IP隧道的开销要比修改MAC地址要大一些。在实际实验中,我们测得平均文件长度为10K的HTTP连接,从客户到服务器方向的报文为6个。这样,我们可以推出VS/DR或VS/TUN调度器的最大吞吐率为25,000 Connections/Second。

# 四、LVS集群的应用



Linux虚拟服务器项目(Linux Virtual Server Project)的网址是http://www.LinuxVirtualServer.org/, LVS中的IPVS第一个版本源程序于1998年5月在网上发布。至今,本项目受到不少关注,LVS系统已被用于很多重负载的站点,就我们所知该系统已在美、中、英、德、澳等国的近百个站点上正式使用。

我们没有上百台机器和高速的网络来测试LVS的终极性能,所以举LVS的应用实例来说明LVS的高性能和稳定性。我们所知的一些大型LVS应用实例如下:

英国国家JANET Cache Service(wwwcache.ja.net)是为英国150所以上的大学提供Web Cache服务。他们用28个结点的LVS集群代替了原有现50多台相互独立的Cache服务器,用他们的话说现在速度就跟夏天一样,因为夏天是放假期间没有很多人使用网络。
Linux的门户站点(www.linux.com)用LVS将很多台VA Linux SMP服务器组成高性能的WEB服务,已使用将近一年。 SourceForge(sourceforge.net)是在全球范围内为开发源码项目提供WEB、FTP、Mailing List和CVS等服务,他们也使用LVS将负载调度到十几台机器上。
世界上最大的PC制造商之一采用了两个LVS集群系统,一个在美洲,一个在欧洲,用于网上直销系统。 以RealPlayer提供音频视频服务而闻名的Real公司(www.real.com)使用由20台服务器组成的LVS集群,为其全球用户提供音频视频服务。在2000年3月时,整个集群系统已收到平均每秒20,000个连接的请求流。
NetWalk(www.netwalk.com)用多台服务器构造LVS系统,提供1024个虚拟服务,其中本项目的一个美国镜像站点(www.us.linuxvirtualserver.org)。 RedHat(www.redhat.com)从其6.1发行版起已包含LVS代码,他们开发了一个LVS集群管理工具叫Piranha,用于控制LVS集群,并提供了一个图形化的配置界面。
VA Linux(www.valinux.com)向客户提供基于LVS的服务器集群系统,并且提供相关的服务和支持。 TurboLinux的“世界一流Linux集群产品”TurboCluster实际上是基于LVS的想法和代码的,只是他们在新闻发布和产品演示时忘了致谢 。
* 红旗Linux和中软都提供基于LVS的集群解决方案,并在2000年9月召开的Linux World China 2000上展示。

五、小结




本章主要讲述了IP虚拟服务器在Linux内核中的实现、关键问题的解决方法和优化处理、以及IP虚拟服务器的性能测试和应用情况。IP虚拟服务器具有以下特点:

三种IP负载均衡技术,在一个服务器集群中,不同的服务器可以使用不同的IP负载均衡技术。 可装卸连接调度模块,共有五种连接调度算法。
高效的Hash函数 高效的垃圾回收机制
虚拟服务的数目没有限制,每个虚拟服务有自己的服务器集。 支持持久的虚拟服务
正确的ICMP处理 拥有本地结点功能
提供系统使用的统计数据 针对大规模DoS攻击的三种防卫策略

通过IP虚拟服务器软件和集群管理工具可以将一组服务器组成一个高性能、高可用的网络服务。该系统具有良好的伸缩性,支持几百万个并发连接。无需对客户机和服务器作任何修改,可适用任何Internet站点。该系统已经在很多大型的站点得到很好的应用。































加载评论框需要翻墙