lvs LVS調整hash bucket及內核編譯 優化

一、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

假如沒有 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
http://vault.centos.org/6.2/updates/Source/SPackages/

下載對應的內核版本:
http://vault.centos.org/6.2/updates/Source/SPackages/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
http://www.austintek.com/LVS/LVS-HOWTO/HOWTO/LVS-HOWTO.operation.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章