链路层的网卡聚合-基于Linux bonding
linux總是可以用一種最簡單的方式實現(xiàn)一個很復(fù)雜的功能,特別是網(wǎng)絡(luò)方面的 ,哪怕這個功能被認(rèn)為只是在高端設(shè)備上才有,linux也可以很容易的實現(xiàn),以前的文章已經(jīng)說了不少次了,比如vlan功能,比如高級路由和防火墻功能等等,本文著重說一下linux的bonding,也就是端口聚合的功能模塊。不可否認(rèn),在網(wǎng)絡(luò)設(shè)備這個層面上上,linux搞出了兩個很成功的虛擬設(shè)備的概念,一個是tap網(wǎng)卡,另一個就是本文所講述的bonding,關(guān)于tap網(wǎng)卡的內(nèi)容,請參閱之前關(guān)于Open×××的文章。
???? 如果有一個問題擺在眼前,那就是關(guān)于linux bonding有什么比較好的資料,答案就是linux內(nèi)核的文檔,該文檔在$KERNEL-ROOT/Documentation/networking/bonding.txt,我覺得沒有任何資料比這個更權(quán)威了。
一、bonding簡介
bonding是一個linux kernel的driver,加載了它以后,linux支持將多個物理網(wǎng)卡捆綁成一個虛擬的bond網(wǎng)卡,隨著版本的升級,bond驅(qū)動可配置的參數(shù)越來越多,而且配置本身也越來越方便了。
???? 我們在很多地方會使用到物理網(wǎng)卡端口匯聚的功能,比如我們想提升網(wǎng)絡(luò)速率,比如我們想提供熱備份,比如我們想把我們的主機配置成一個網(wǎng)橋,并且使之支持802.3ad動態(tài)端口聚合協(xié)議等等,然而最重要的還是兩點,第一點是負(fù)載均衡,第二點就是熱備份啦。
二、驅(qū)動以及Changes介紹
linux的bonding驅(qū)動的最初版本僅僅提供了基本的機制,而且需要在加載模塊的時候指定配置參數(shù),如果想更改配置參數(shù),那么必須重新加載bonding模塊;然后modprobe支持一種rename的機制,也就是在modprobe的時候支持使用-o重新為此模塊命名 ,這樣就可以實現(xiàn)一個模塊以不同的配置參數(shù)加載多次了,起初比如我有4個網(wǎng)口,想把兩個配置成負(fù)載均衡,兩個配置成熱備,這只能手工重新將bonding編譯成不同的名稱來解決,modprobe有了-o選項之后,就可以兩次加載相同的驅(qū)動了,比如可以使用:
modprobe bonding -o bond0 mode=0
modprobe bonding -o bond1 mode=1
加載兩次bonding驅(qū)動,用lsmod看一下,結(jié)果是bond0和bond1,并沒有bonding,這是由于modprobe加載時命名了,然而最終,這個命名機制不再被支持了,因為正如modprobe的man手冊所敘述的一樣,-o重命名機制主要適用于test。最后,bonding支持了sysfs的配置機制,對/sys/class/net/目錄下的文件進行讀或者寫就可以完成對驅(qū)動的配置。
???? 不管怎樣,在sysfs完全支持bonding配置之前,如果想往某一個bonding網(wǎng)卡添加設(shè)備或者刪除設(shè)備的時候,還是要使用經(jīng)典且傳統(tǒng)的ioctl調(diào)用,因此必然需要一個用戶態(tài)程序與之對應(yīng),該程序就是ifenslave。
???? 我想,如果linux的所有關(guān)于設(shè)備的配置都能統(tǒng)一于sysfs,所有的關(guān)于內(nèi)核和進程配置統(tǒng)一于procfs(內(nèi)核是所有進程共享的地址空間,也有自己的內(nèi)核線程以及進程0,因此對內(nèi)核的配置應(yīng)該在procfs中),對所有的消息,使用netlink通信,這就太好了,擺脫了命令式的ioctl配置,文件式(netlink使用的sendto之類的系統(tǒng)調(diào)用也可以歸為文件系統(tǒng)調(diào)用相關(guān)的)的配置將更加高效,簡單以及好玩!
三、bonding配置參數(shù)
在內(nèi)核文檔中,列舉了許多bonding驅(qū)動的參數(shù),然后本文不是文檔的翻譯,因此不再翻譯文檔和介紹和主題無關(guān)的參數(shù),僅對比較重要的參數(shù)進行介紹,并且這些介紹也不是翻譯,而是一些建議或者心得。
ad_select: 802.3ad相關(guān)。如果不明白這個,那不要緊,拋開Linux的bonding驅(qū)動,直接去看802.3ad的規(guī)范就可以了。列舉這個選項說明linux bonding驅(qū)動完全支持了動態(tài)端口聚合協(xié)議。
arp_interval和arp_ip_target: 以一個固定的間隔向某些固定的地址發(fā)送arp,以監(jiān)控鏈路。有些配置下,需要使用arp來監(jiān)控鏈路,因為這是一種三層的鏈路監(jiān)控 ,使用網(wǎng)卡狀態(tài)或者鏈路層pdu監(jiān)控只能監(jiān)控到雙絞線兩端的接口 的健康情況,而監(jiān)控不到到下一條路由器或者目的主機之間的全部鏈路的健康狀況。
primary: 表示優(yōu)先權(quán),順序排列,當(dāng)出現(xiàn)某種選擇事件時,按照從前到后的順序選擇網(wǎng)口,比如802.3ad協(xié)議中的選擇行為。
fail_over_mac: 對于熱備模式是否使用同一個mac地址,如果不使用一個mac的話,就要完全依賴免費arp機制更新其它機器的arp緩存了。比如,兩個有網(wǎng)卡,網(wǎng)卡1和網(wǎng)卡2處于熱備模式,網(wǎng)卡1的mac是mac1,網(wǎng)卡2的mac是mac2,網(wǎng)卡1一直是master,但是網(wǎng)卡1突然down掉了,此時需要網(wǎng)卡2接替,然而網(wǎng)卡2的mac地址與之前的網(wǎng)卡1不同,別的主機回復(fù)數(shù)據(jù)包的時候還是使用網(wǎng)卡1的mac地址來回復(fù)的,由于mac1已經(jīng)不在網(wǎng)絡(luò)上了,這就會導(dǎo)致數(shù)據(jù)包將不會被任何網(wǎng)卡接收。因此網(wǎng)卡2接替了master的角色之后,最好有一個回調(diào)事件,處理這個事件的時候,進行一次免費的arp廣播,廣播自己更換了mac地址。
lacp_rate: 發(fā)送802.3ad的LACPDU,以便對端設(shè)備自動獲取鏈路聚合的信息。
max_bonds: 初始時創(chuàng)建bond設(shè)備接口的數(shù)量,默認(rèn)值是1。但是這個參數(shù)并不影響可以創(chuàng)建的最大的bond設(shè)備數(shù)量。
use_carrier: 使用MII的ioctl還是使用驅(qū)動獲取保持的狀態(tài),如果是前者的話需要自己調(diào)用mii的接口進行硬件檢測,而后者則是驅(qū)動自動進行硬件檢測(使用watchdog或者定時器),bonding驅(qū)動只是獲取結(jié)果,然而這依賴網(wǎng)卡驅(qū)動必須支持狀態(tài)檢測,如果不支持的話,網(wǎng)卡的狀態(tài)將一直是on。
mode: 這個參數(shù)最重要,配置以什么模式運行,這個參數(shù)在bond設(shè)備up狀態(tài)下是不能更改的,必須先down設(shè)備(使用ifconfig bondX down)才可以配置,主要的有以下幾個:
1.balance-rr or 0: 輪轉(zhuǎn)方式的負(fù)載均衡模式,流量輪流在各個bondX的真實設(shè)備之間分發(fā)。注意,一定要用狀態(tài)檢測機制,否則如果一個設(shè)備down掉以后,由于沒有狀態(tài)檢測,該設(shè)備將一直是up狀態(tài),仍然接受發(fā)送任務(wù),這將會出現(xiàn)丟包。
2.active-backup or 1: 熱備模式。在比較高的版本中,免費arp會在切換時自動發(fā)送,避免一些故障,比如fail_over_mac參數(shù)描述的故障。
3.balance-xor or 2: 我不知道既然bonding有了xmit_hash_policy這個參數(shù),為何還要將之單獨設(shè)置成一種模式,在這個模式中,流量也是分發(fā)的,和輪轉(zhuǎn)負(fù)載不同的是,它使用源/目的mac地址為自變量通過xor|mod函數(shù)計算出到底將數(shù)據(jù)包分發(fā)到哪一個口。
4.broadcast or 3: 向所有的口廣播數(shù)據(jù),這個模式很XX,但是容錯性很強大。
5.802.3ad or 4: 這個就不多說了,就是以802.3ad的方式運行。
...
xmit_hash_policy: 這個參數(shù)的重要性我認(rèn)為僅次于mode參數(shù),mode參數(shù)定義了分發(fā)模式 ,而這個參數(shù)定義了分發(fā)策略 ,文檔上說這個參數(shù)用于mode2和mode4,我覺得還可以定義更為復(fù)雜的策略呢。
1.layer2: 使用二層幀頭作為計算分發(fā)出口的參數(shù),這導(dǎo)致通過同一個網(wǎng)關(guān)的數(shù)據(jù)流將完全從一個端口發(fā)送,為了更加細(xì)化分發(fā)策略,必須使用一些三層信息,然而卻增加了計算開銷,天啊,一切都要權(quán)衡!
2.layer2+3: 在1的基礎(chǔ)上增加了三層的ip報頭信息,計算量增加了,然而負(fù)載卻更加均衡了,一個個主機到主機的數(shù)據(jù)流形成并且同一個流被分發(fā)到同一個端口,根據(jù)這個思想,如果要使負(fù)載更加均衡,我們在繼續(xù)增加代價的前提下可以拿到4層的信息。
3.layer3+4: 這個還用多說嗎?可以形成一個個端口到端口的流,負(fù)載更加均衡。然而且慢! 事情還沒有結(jié)束,雖然策略上我們不想將同一個tcp流的傳輸處理并行化以避免re-order或者re-transmit,因為tcp本身就是一個串行協(xié)議,比如Intel的8257X系列網(wǎng)卡芯片都在盡量減少將一個tcp流的包分發(fā)到不同的cpu,同樣,端口聚合的環(huán)境下,同一個tcp流也應(yīng)該使用本policy使用同一個端口發(fā)送,但是不要忘記,tcp要經(jīng)過ip,而ip是可能要分段的,分了段的ip數(shù)據(jù)報中直到其被重組(到達對端或者到達一個使用nat的設(shè)備)都再也不能將之劃為某個tcp流了。ip是一個完全無連接的協(xié)議,它只關(guān)心按照本地的mtu進行分段而不管別的,這就導(dǎo)致很多時候我們使用layer3+4策略不會得到完全滿意的結(jié)果。可是事情又不是那么嚴(yán)重,因為ip只是依照本地的mtu進行分段,而tcp是端到端的,它可以使用諸如mss以及mtu發(fā)現(xiàn)之類的機制配合滑動窗口機制最大限度減少ip分段,因此layer3+4策略,很OK!
miimon和arp: 使用miimon僅能檢測鏈路層的狀態(tài),也就是鏈路層的端到端連接(即交換機某個口和與之直連的本地網(wǎng)卡口),然而交換機的上行口如果down掉了還是無法檢測到,因此必然需要網(wǎng)絡(luò)層的狀態(tài)檢測,最簡單也是最直接的方式就是arp了,可以直接arp網(wǎng)關(guān),如果定時器到期網(wǎng)關(guān)還沒有回復(fù)arp reply,則認(rèn)為鏈路不通了。
四、我該如何配置呢
1.首先,傳統(tǒng)的方式肯定不妥,內(nèi)核文檔上寫的有,大家參考便是,記住,首先要裝一個ifenslave
2.最新的sysfs配置方式
首先確認(rèn)你的系統(tǒng)上有sys這個目錄,并且mount于它的文件系統(tǒng)是sysfs類型的。然后就是下面的步驟了,很簡單:
第零步:加載模塊
root@zyxx:modprobe bonding
第一步:進入相應(yīng)目錄
root@zyxx:cd /sys/class/net/
第二步:查看一下文件,熟悉一下地形(該步驟可省略)
root@zyxx:/sys/class/net# ls
bond0 bonding_masters? eth0? eth1? eth2? eth3? eth4? eth5? lo
第三步:看看當(dāng)前有哪些bond設(shè)備
root@zyxx:/sys/class/net# cat bonding_masters
bond0
第四步:從一個bond設(shè)備添加或者刪除一個以太網(wǎng)卡設(shè)備
root@zyxx:/sys/class/net# echo +(-)X > bonding_masters?
#注釋:上一條命令中的+號表示添加設(shè)備,而-號表示刪除設(shè)備,+X中的X表示任意一個你喜歡的名字,-X中的X表示bonding_masters中已經(jīng)存在的一個名字
第五步:進入新創(chuàng)建的bondMy,并且盡情配置吧
root@zyxx:/sys/class/net/bondMy/bonding# ls
active_slave?? ad_num_ports??? ad_select????? arp_validate?? lacp_rate?? mode????????? primary? use_carrier
ad_actor_key?? ad_partner_key? arp_interval?? downdelay????? miimon????? num_grat_arp? slaves?? xmit_hash_policy
ad_aggregator? ad_partner_mac? arp_ip_target? fail_over_mac? mii_status? num_unsol_na? updelay
1.增加eth2到bondMy
root@zyxx:/sys/class/net/bondMy/bonding# echo +eth2 > slaves
2.設(shè)置鏈路監(jiān)控間隔
root@zyxx:/sys/class/net/bondMy/bonding# echo 100 > miimon
3.設(shè)置mode為熱備
root@zyxx:/sys/class/net/bondMy/bonding# echo 1 > mode
...
第七步:感慨
整個配置步驟很簡單,模塊只需要加載一次,以后動態(tài)配置就一切OK了。
五、bonding驅(qū)動的實現(xiàn)
在看了精彩的配置并且實際上已經(jīng)配置出一個很好用的網(wǎng)絡(luò)后,我肯定會急切的看一下源代碼的實現(xiàn),這也是我喜歡linux的原因,因為它可以讓你隨意patch。實際上bonding的驅(qū)動非常簡單,和tap一樣的,基本就是三大部分:
第一部分:初始化
這部分很簡單,就是初始化一個net_device,然后注冊進去,這就不多說了
第二部分:實現(xiàn)用戶配置接口
該接口有兩種,第一種就是傳統(tǒng)的基于ioctl的方式配置,就是實現(xiàn)一個ioctl即可,另一種就是通過sysfs實現(xiàn),也很簡單,實現(xiàn)一些attitude的store/show方法即可,不管采用哪種方式,最終都要調(diào)用一個函數(shù),那就是netdev_set_master,該函數(shù)中最重要的事就一個,那就是將物理網(wǎng)卡的master設(shè)置成我們在第一部分初始化的那個bond設(shè)備:
slave->master = master;
第三部分:初始化傳輸和接收接口
對于傳輸接口,很簡單,和多端口網(wǎng)橋類似。bond設(shè)備在初始化時將start_xmit初始化成bond_start_xmit,在bond_start_xmit中有一個switch-case,switch什么呢?當(dāng)然是bonding的mode了,比如在mode0,也就是輪轉(zhuǎn)負(fù)載的mode下,bond_start_xmit調(diào)用如下代碼段:
switch-case:bond_xmit_roundrobin...
bond_for_each_slave_from(bond, slave, i, start_at) {
??? if (IS_UP(slave->dev) && (slave->link == BOND_LINK_UP) && (slave->state == BOND_STATE_ACTIVE)) {
??? ??? res = bond_dev_queue_xmit(bond, skb, slave->dev);
??? ??? break;
??? }
}
對于接收接口,所有的設(shè)備都從netif_receive_skb開始數(shù)據(jù)包的協(xié)議分發(fā),在該函數(shù)的開始處有下面代碼:
if (orig_dev->master) {
??? if (skb_bond_should_drop(skb))
??? ??? null_or_orig = orig_dev;
??? else
??? ??? skb->dev = orig_dev->master;
}
orig_dev是物理網(wǎng)卡,如果不出意外,在其擁有master的情況下,也就是使用用戶接口將物理網(wǎng)卡綁到一個bond設(shè)備上后,物理網(wǎng)卡的master字段將被設(shè)置,經(jīng)過這段代碼,skb的dev將會被設(shè)置成master,也就是說bond設(shè)備。接下來的向上層協(xié)議的傳輸就由bond設(shè)備來完成了,因為所有的三層信息完全都在bond設(shè)備上,而沒有在物理網(wǎng)卡orig_dev上。
六、高級主題
不可否認(rèn),本文的解析是極其淺顯的,如果想獲得更高級的主題和一些很深入細(xì)節(jié)的主題,或者想了解一些關(guān)于交換機的知識以及性能方面的問題,請參考Linux關(guān)于bonding的文檔,該文檔在$KERNEL-ROOT/Documentation/networking/bonding.txt,強烈建議閱讀,讀了該文檔之后,你就是bonding方面的專家啦。
附:談?wù)勁渲梅绞?/strong>
linux的sysfs和proc(sysctl等)機制可以實現(xiàn)文件io的方式配置系統(tǒng)參數(shù)和設(shè)備,回想當(dāng)初學(xué)習(xí)思科/華為設(shè)備配置時,花了上萬元RMB之后,最后的收獲就是只會打“?”就OK了,后來隨著學(xué)習(xí)的深入,發(fā)現(xiàn)windows的netsh也是這種配置方式,甚至感覺比思科/華為設(shè)備的配置方式更加簡單,但是歸根結(jié)底都是命令式的,所謂的命令式其實就是類似英語的,主-謂-賓(定-狀-補),主語就是系統(tǒng)的當(dāng)前user,謂語就是命令,賓語就是目標(biāo)設(shè)備或者系統(tǒng)本身,定狀補描述命令參數(shù),這類命令對于習(xí)慣于自然語言的人們來講是很方便的,然而記憶的開銷卻不小。linux通過文件讀寫的方式可以實現(xiàn)類似的配置,不知道是好是壞,反正我在初用sysfs配置bonding的時候第一個感覺就是“好極了”,再也不用使用man ifenslave了,再也不用vim文檔了,kobject將bonding的相關(guān)內(nèi)容組織在$sysfstoor/class/net/目錄下,只要你會訪問文件,你就能進行配置,而且bonding的新版本幾乎廢棄了原來的ifenslave的ioctl方式,任何關(guān)于bonding配置都可以用sysfs來進行。
???? 最重要的是,文件式或者netlink的配置相比ioctl式的經(jīng)典命令式配置,可以減少設(shè)備vfs層的膨脹,我們可以看看ioctl層次的代碼,是多么的凌亂不堪,每添加一個新命令就需要修改甚至好幾層ioctl的代碼,其實就是添加一個case語句,用以分發(fā)新添加的命令,這樣就會導(dǎo)致ioctl代碼雖然分層但是卻解決不了設(shè)備驅(qū)動和接口過度耦合的問題。
轉(zhuǎn)載于:https://blog.51cto.com/fastkknd/710032
總結(jié)
以上是生活随笔為你收集整理的链路层的网卡聚合-基于Linux bonding的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java源程序加密解决方案(基于Clas
- 下一篇: word中格式化姓名的输出