Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3049195
  • 博文数量: 535
  • 博客积分: 15788
  • 博客等级: 上将
  • 技术积分: 6507
  • 用 户 组: 普通用户
  • 注册时间: 2007-03-07 09:11
文章分类

全部博文(535)

文章存档

2016年(1)

2015年(1)

2014年(10)

2013年(26)

2012年(43)

2011年(86)

2010年(76)

2009年(136)

2008年(97)

2007年(59)

分类: LINUX

2012-05-08 11:21:44

一、IPVS connection hash table size,该表用于记录每个进来的连接及路由去向的信息。连接的Hash表要容纳几百万个并发连接,任何一个报文到达都需要查找连接Hash表。Hash表的查找复杂度为O(n/m),其中n为Hash表中对象的个数,m为Hash表的桶个数。当对象在Hash表中均匀分布和Hash表的桶个数与对象个数一样多时,Hash表的查找复杂度可以接近O(1)。
连接跟踪表中,每行称为一个hash bucket(hash桶),桶的个数是一个固定的值CONFIG_IP_VS_TAB_BITS,默认为12(2的12次方,4096)。这个值可以调整,该值的大小应该在 8 到 20 之间,详细的调整方法见后面。每一行都是一个链表结构,包含N列(即N条连接记录),这个N是无限的,N的数量决定了决定了查找的速度。在LVS的实现说明中,有这样的一段话,可以帮助很好的理解行和列的关系:
为了评价Hash函数的效率,我们从一个运行IPVS的真实站点上取当前连接的样本,它一共含有35652个并发连接。在有64K桶的Hash表中,连接分布如下:
桶的长度(Lj) 该长度桶的个数(Nj)
5              16
4              126
3              980
2              5614
1              20900
所有连接查找一次的代价为45122,每个连接查找的平均代价为1.266(即45122/35652)。

二、LVS的调优建议将hash table的值设置为不低于并发连接数。例如,并发连接数为200,Persistent时间为200S,那么hash桶的个数应设置为尽可能接近200x200=40000,2的15次方为32768就可以了。当ip_vs_conn_tab_bits=20 时,哈希表的的大小(条目)为 pow(2,20),即 1048576,对于64位系统,IPVS占用大概16M内存,可以通过demsg看到:IPVS: Connection hash table configured (size=1048576, memory=16384Kbytes)。对于现在的服务器来说,这样的内存占用不是问题。所以直接设置为20即可。

关于最大“连接数限制”:这里的hash桶的个数,并不是LVS最大连接数限制。LVS使用哈希链表解决“哈希冲突”,当连接数大于这个值时,必然会出现哈稀冲突,会(稍微)降低性能,但是并不对在功能上对LVS造成影响。

关于连接占用内存:
每条记录用一个ip_vs_conn结构表示,这个结构使用了Linux里面的典型数据结构struct list_head构造双向链表,使得所有的记录以链表形式链接起来。
* struct ip_vs_conn 里面的其他元素就是每个连接的具体信息,在32位系统上为128字节,64位系统上为192字节 。
* struct list_head 在32位系统上为8字节,在64位系统上为16字节。
所以,hash表里面的每条记录(每个连接),在32位系统上占据136字节内存,在64位系统上占用208字节。

三、调整 ip_vs_conn_tab_bits的方法:
新的IPVS代码,允许调整 ip_vs_conn_bits 的值。而老的IPVS代码则需要通过重新编译来调整。
在发行版里,IPVS通常是以模块的形式编译的。
确认能否调整使用命令 modinfo -p ip_vs(查看 ip_vs 模块的参数),看有没有 conn_tab_bits 参数可用。假如可以用,那么说时可以调整,调整方法是加载时通过设置 conn_tab_bits参数:
在/etc/modprobe.d/目录下添加文件ip_vs.conf,内容为:
options ip_vs conn_tab_bits=20
查看
ipvsadm -l
如果显示IP Virtual Server version 1.2.1 (size=4096),则前面加的参数没有生效
modprobe -r ip_vs
modprobe ip_vs
重新查看
IP Virtual Server version 1.2.1 (size=1048576)

假如没有 conn_tab_bits 参数可用,则需要重新调整编译选项,重新编译。
Centos6.2,内核版本2.6.32-220.13.1.el6.x86_64,仍然不支持这个参数,只能自定义编译了。
另外,假如IPVS支持调整 ip_vs_conn_tab_bits,而又将IPVS集成进了内核,那么只能通过重启,向内核传递参数来调整了。在引导程序的 kernel 相关的配置行上,添加:ip_vs.conn_tab_bits=20 ,然后,重启。


四、重新编译内核:
使用yum安装了kernel.x86_64、kernel-devel.x86_64、kernel-headers.x86_64 后,在/usr/src/kernel下会存在相应的内核目录,如2.6.32-220.13.1.el6.x86_64,执行make menuconfig也可以配置内核,但是在执行make时会有报错:“make[1]: *** No rule to make target `missing-syscalls'.  Stop.”
出现这个错误的原因是:
You're trying to compile something that isn't a source tree (it's a headers tree from kernel-devel).  Building a kernel requires that you install the srpm.
即没有在源码树下make,所存在的内核的目录只是headers,解决的办法是安装kernel src的rpm包。

1、下载:
例如,CentOS6.2

下载对应的内核版本:
kernel-2.6.32-220.13.1.el6.src.rpm

2、安装必要的程序
yum install wget rng-tools xmlto asciidoc newt-devel perl-ExtUtils-Embed.x86_64

3、安装并为内核打补丁
3.1、准备rebuild的工作目录
默认rebulid的目录为~/rpmbuild,重新编译内核,需要十几G的空间,所以,选择一个空间足够的目录。
不建议使用root用户来bulid packages,刚才下载的kernel-2.6.32-220.13.1.el6.src.rpm就是使用mockbuild用户来build,这里,我也使用这个用户。

useradd mockbuild

mkdir -p /tmp/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
chown -R mockbuild.mockbuild  /tmp/rpmbuild
更改rebuild的默认目录:
echo "%_topdir /tmp/rpmbuild">/home/mockbuild/.rpmmacros

yum install rpmdevtools yum-utils 
对于Centos5,需要安装unifdef

以下非特别说明,都使用mockbuild用户

3.2、解压kernel源码,准备源码树(如果需要重新编译内核为rpm包,直接跳到4
rpm -ivh kernel-2.6.32-220.13.1.el6.src.rpm 
cd /tmp/rpmbuild/SPECS
rpmbuild -bp --target=$(uname -m) kernel.spec
运行过程中,当程序停止在某处,要求生成随机数时,使用root用户,执行:rngd -r /dev/urandom用于生成随机数,如果后面有报错,没有足够的随机数,再次执行该命令即可。

cd /tmp/rpmbuild/BUILD/kernel-2.6.32-220.13.1.el6
mv linux-2.6.32-220.13.1.el6.x86_64/ /usr/src/kernels/
到这里,我们有了一个完整的源码树了。

现在,可以看出源码树和只含有header的内核目录在名称上的区别了:
2.6.32-220.13.1.el6.x86_64
linux-2.6.32-220.13.1.el6.x86_64/


3.3、编译和rebuild内核
cd /usr/src/kernels/linux-2.6.32-220.13.1.el6.x86_64
make clean && make mrproper
#使用当前系统的 config ,这样编译后的内核只不过是改变了 hash table 的值,并没有改变其它的东西。
cp  /boot/config-2.6.32-220.13.1.el6.x86_64 ./.config

修改.config,第一行#改成# x86_64
Add a new line to the top of the config file that contains the hardware platform the kernel is built for (the output of uname -i). The line is preceded by a # sign. 
For example, an x86_64 machine would have the following line added to the top of the config file。

修改 CONFIG_IP_VS_TAB_BITS=12 为
CONFIG_IP_VS_TAB_BITS=20

make && make modules_install  && make install

3.4、后续
修改grub.conf
reboot
# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=1048576)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn

4、重新编译为RPM包,方便安装到其他服务器:
rpm -ivh kernel-2.6.32-220.13.1.el6.src.rpm
内核配置文件由两个文件合并而成,修改/tmp/rpmbuild/SOURCES/config-generic
注:在2.6.18上,不是这个文件,简单的办法就是使用sed把目录下所有的文件都改掉:
CONFIG_IP_VS_TAB_BITS=12 改为 CONFIG_IP_VS_TAB_BITS=20

sed -i 's/CONFIG_IP_VS_TAB_BITS=12/CONFIG_IP_VS_TAB_BITS=20/g' ./*

注:网上有的文档说将/boot下的config配置文件直接拷贝到目录中以后再做修改,实际上是不生效的,因为下面的rpmbuild bb或ba会重新合并生成内核配置文件。

cd /tmp/rpmbuild/SPECS
修改kernel.spec:
注意:对于内核,建议使用rpm -ivh来进行安装。所以,我们要给新的内核一个唯一的名称以区别现在正在运行的内核,然后,进行安装。
# % define buildid .local修改为%define buildid .ipvs_20bit

rpmbuild -bb --target=$(uname -m) --with firmware kernel.spec
参数说明
bb: Build a binary package (after doing the %prep, %build, and %install stages).
kernel.spec:控制文件
因为修改了内核的名字,所以firmware将不匹配,解决的办法就是在编译的时候添加参数--with firmware。

bb和ba的区别,ba编译后做成*.rpm和src.rpm,bb编译后做成*.rpm,如果以后需要在这个内核的基础上这次修改,建议使用ba。
ba: Build binary and source packages (after doing the %prep, %build, and %install stages).

这个过程比较长,大概需要10G的硬盘空间,编译完成后,在RPM/目录下生成以下文件

  1. kernel-2.6.32-220.13.1.el6.ipvs_20bit.x86_64.rpm
  2. kernel-devel-2.6.32-220.13.1.el6.ipvs_20bit.x86_64.rpm
  3. kernel-debug-2.6.32-220.13.1.el6.ipvs_20bit.x86_64.rpm
  4. kernel-firmware-2.6.32-220.13.1.el6.ipvs_20bit.x86_64.rpm
  5. kernel-debug-debuginfo-2.6.32-220.13.1.el6.ipvs_20bit.x86_64.rpm
  6. kernel-headers-2.6.32-220.13.1.el6.ipvs_20bit.x86_64.rpm
  7. kernel-debug-devel-2.6.32-220.13.1.el6.ipvs_20bit.x86_64.rpm
  8. perf-2.6.32-220.13.1.el6.ipvs_20bit.x86_64.rpm
  9. kernel-debuginfo-2.6.32-220.13.1.el6.ipvs_20bit.x86_64.rpm
  10. perf-debuginfo-2.6.32-220.13.1.el6.ipvs_20bit.x86_64.rpm
  11. kernel-debuginfo-common-x86_64-2.6.32-220.13.1.el6.ipvs_20bit.x86_64.rpm
  12. python-perf-2.6.32-220.13.1.el6.ipvs_20bit.x86_64.rpm
根据需要安装后,重启
IP Virtual Server version 1.2.1 (size=1048576)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn

五、其他调整
5.1Network
增加LVS主机的网络吞吐能力,有利于提高LVS的处理速度和能力。
使用更快的网卡,比如使用千兆、万兆的网卡。
可以进一步将两块或多块网卡绑定(多块网卡的绑定有待验证),bonding 时 mode=0 (balance-rr)或者 mode=4(802.3ad,需要交换机支持聚合端口),miimon=80或者 miimon=100(毫秒)。

打开文件数限制
/etc/security/limits.conf
root soft nofile 65535
root hard nofile 65535
daemon soft nofile 65535
daemon hard nofile 65535

TCP/IP
/etc/sysctl.conf
net.core.netdev_max_backlog = 60000
该文件表示在每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目,默认为1000。
net.ipv4.tcp_synack_retries =2

5.2、Hardware
IPVS的运行,使用的服务器资源主要是 CPU、内存I/O、网络I/O;IPVS完全运行在内存中,并且运行在内核态。

当IPVS的应用在DR模式时,即不耗CPU,也不耗I/O,运行非常快,所以系统负载非常的低,跟据我的经验,一般负载总是0。
其实我们可以做一下计算:
以64位系统为例,一个哈希表条目,16个字节,一个 ip_vs_conn 结构 192字节。以哈希表的冲突尽可能的少为场景(将 ip_vs_conn_tab_bits 设置为最大值 20 ),那么:
pow(2,20)=1048576
pow(2,20)*(16+192)/1024/1024 = 208 M
就是说,当系统当前有100 万连接的时候,才用去内存 208 M,所以  IPVS 的主机,即使是1G的内存,也足以承载负载。

考虑到连接的持续时间:
4G内存的服务器,不考虑系统消耗的内存,连接持续时间为120S:
4G=4x1024x1024x1024字节
4x1024x1024x1024/(16+192)/120=172074即并发为17W。




参考:
https://fedoraproject.org/wiki/Building_a_custom_kernel
http://blog.csdn.net/yanziguishi/article/details/7284793
阅读(5320) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~