鏈路層的網卡彙總-基於Linux bonding

來源:互聯網
上載者:User

linux總是可以用一種最簡單的方式實現一個很複雜的功能,特別是網路方面的
,哪怕這個功能被認為只是在高端裝置上才有,linux也可以很容易的實現,以前的文章已經說了不少次了,比如vlan功能,比如進階路由和防火牆功能等等,本文著重說一下linux的bonding,也就是連接埠彙總的功能模組。不可否認,在網路裝置這個層面上上,linux搞出了兩個很成功的虛擬設備的概念,一個是tap網卡,另一個就是本文所講述的bonding,關於tap網卡的內容,請參閱之前關於OpenVPN的文章。
     如果有一個問題擺在眼前,那就是關於linux bonding有什麼比較好的資料,答案就是linux核心的文檔,該文檔在$KERNEL-ROOT/Documentation/networking/bonding.txt,我覺得沒有任何資料比這個更權威了。
一、bonding簡介



bonding是一個linux kernel的driver,載入了它以後,linux支援將多個物理網卡捆綁成一個虛擬bond網卡,隨著版本的升級,bond驅動可配置的參數越來越多,而且配置本身也越來越方便了。
     我們在很多地方會使用到物理網卡連接埠匯聚的功能,比如我們想提升網路速率,比如我們想提供熱備份,比如我們想把我們的主機配置成一個橋接器,並且使之支援802.3ad動態連接埠彙總協議等等,然而最重要的還是兩點,第一點是負載平衡,第二點就是熱備份啦。
二、驅動以及Changes介紹



linux的bonding驅動的最初版本僅僅提供了基本的機制,而且需要在載入模組的時候指定配置參數,如果想更改配置參數,那麼必須重新載入bonding模組;然後modprobe支援一種rename的機制,也就是在modprobe的時候支援使用-o重新為此模組命名
,這樣就可以實現一個模組以不同的配置參數載入多次了,起初比如我有4個網口,想把兩個配置成負載平衡,兩個配置成熱備,這隻能手工重新將bonding編譯成不同的名稱來解決,modprobe有了-o選項之後,就可以兩次載入相同的驅動了,比如可以使用:
modprobe bonding -o bond0 mode=0
modprobe bonding -o bond1 mode=1


載入兩次bonding驅動,用lsmod看一下,結果是bond0和bond1,並沒有bonding,這是由於modprobe載入時命名了,然而最終,這個命名機制不再被支援了,因為正如modprobe的man手冊所敘述的一樣,-o重新命名機制主要適用於test。最後,bonding支援了sysfs的配置機制,對/sys/class/net/目錄下的檔案進行讀或者寫就可以完成對驅動的配置。
     不管怎樣,在sysfs完全支援bonding配置之前,如果想往某一個bonding網卡添加裝置或者刪除裝置的時候,還是要使用經典且傳統的ioctl調用,因此必然需要一個使用者態程式與之對應,該程式就是ifenslave。
     我想,如果linux的所有關於裝置的配置都能統一於sysfs,所有的關於核心和進程配置統一於procfs(核心是所有進程共用的地址空間,也有自己的核心線程以及進程0,因此對核心的配置應該在procfs中),對所有的訊息,使用netlink通訊,這就太好了,擺脫了命令式的ioctl配置,檔案式(netlink使用的sendto之類的系統調用也可以歸為檔案系統調用相關的)的配置將更加高效,簡單以及好玩!
三、bonding配置參數



在核心文檔中,列舉了許多bonding驅動的參數,然後本文不是文檔的翻譯,因此不再翻譯文檔和介紹和主題無關的參數,僅對比較重要的參數進行介紹,並且這些介紹也不是翻譯,而是一些建議或者心得。
ad_select:

802.3ad相關。如果不明白這個,那不要緊,拋開Linux的bonding驅動,直接去看802.3ad的規範就可以了。列舉這個選項說明linux bonding驅動完全支援了動態連接埠彙總協議。
arp_interval和arp_ip_target:

以一個固定的間隔向某些固定的地址發送arp,以監控鏈路。有些配置下,需要使用arp來監控鏈路,因為這是一種三層的鏈路監控
,使用網卡狀態或者鏈路層pdu監控只能監控到雙絞線兩端的介面
的健康情況,而監控不到到下一條路由器或者目的主機之間的全部鏈路的健康情況。
primary:

表示優先權,順序排列,當出現某種選擇事件時,按照從前到後的順序選擇網口,比如802.3ad協議中的選擇行為。
fail_over_mac:

對於熱備模式是否使用同一個mac地址,如果不使用一個mac的話,就要完全依賴免費arp機制更新其它機器的arp緩衝了。比如,兩個有網卡,網卡1和網卡2處於熱備模式,網卡1的mac是mac1,網卡2的mac是mac2,網卡1一直是master,但是網卡1突然down掉了,此時需要網卡2接替,然而網卡2的mac地址與之前的網卡1不同,別的主機回複資料包的時候還是使用網卡1的mac地址來回複的,由於mac1已經不在網路上了,這就會導致資料包將不會被任何網卡接收。因此網卡2接替了master的角色之後,最好有一個回調事件,處理這個事件的時候,進行一次免費的arp廣播,廣播自己更換了mac地址。
lacp_rate:

發送802.3ad的LACPDU,以便對端裝置自動擷取鏈路彙總的資訊。
max_bonds:

初始時建立bond裝置介面的數量,預設值是1。但是這個參數並不影響可以建立的最大的bond裝置數量。
use_carrier:

使用MII的ioctl還是使用驅動擷取保持的狀態,如果是前者的話需要自己調用mii的介面進行硬體檢測,而後者則是驅動自動進行硬體檢測(使用watchdog或者定時器),bonding驅動只是擷取結果,然而這依賴網卡驅動必須支援狀態檢測,如果不支援的話,網卡的狀態將一直是on。
mode:

這個參數最重要,配置以什麼模式運行,這個參數在bond裝置up狀態下是不能更改的,必須先down裝置(使用ifconfig bondX down)才可以配置,主要的有以下幾個:
1.balance-rr or 0:

輪轉方式的負載平衡模式,流量輪流在各個bondX的真實裝置之間分發。注意,一定要用狀態檢測機制,否則如果一個裝置down掉以後,由於沒有狀態檢測,該裝置將一直是up狀態,仍然接受發送任務,這將會出現丟包。
2.active-backup or 1:

熱備模式。在比較高的版本中,免費arp會在切換時自動發送,避免一些故障,比如fail_over_mac參數描述的故障。
3.balance-xor or 2:

我不知道既然bonding有了xmit_hash_policy這個參數,為何還要將之單獨設定成一種模式,在這個模式中,流量也是分發的,和輪轉負載不同的是,它使用源/目的mac地址為自變數通過xor|modFunction Compute出到底將資料包分發到哪一個口。
4.broadcast or 3:

向所有的口廣播資料,這個模式很XX,但是容錯性很強大。
5.802.3ad or 4:

這個就不多說了,就是以802.3ad的方式運行。
...
xmit_hash_policy:

這個參數的重要性我認為僅次於mode參數,mode參數定義了分發模式
,而這個參數定義了分發策略
,文檔上說這個參數用於mode2和mode4,我覺得還可以定義更為複雜的策略呢。
1.layer2:

使用二層幀頭作為計算分發出口的參數,這導致通過同一個網關的資料流將完全從一個連接埠發送,為了更加細化分發策略,必須使用一些三層資訊,然而卻增加了計算開銷,天啊,一切都要權衡!

2.layer2+3:

在1的基礎上增加了三層的ip前序資訊,計算量增加了,然而負載卻更加均衡了,一個個主機到主機的資料流形成並且同一個流被分發到同一個連接埠,根據這個思想,如果要使負載更加均衡,我們在繼續增加代價的前提下可以拿到4層的資訊。
3.layer3+4:

這個還用多說嗎?可以形成一個個連接埠到連接埠的流,負載更加均衡。然而且慢!
事情還沒有結束,雖然策略上我們不想將同一個tcp流的傳輸處理並行化以避免re-order或者re-transmit,因為tcp本身就是一個串列協議,比如Intel的8257X系列網卡晶片都在盡量減少將一個tcp流的包分發到不同的cpu,同樣,連接埠彙總的環境下,同一個tcp流也應該使用本policy使用同一個連接埠發送,但是不要忘記,tcp要經過ip,而ip是可能要分段的,分了段的ip資料報中直到其被重組(到達對端或者到達一個使用nat的裝置)都再也不能將之劃為某個tcp流了。ip是一個完全不需連線的協議,它只關心按照本地的mtu進行分段而不管別的,這就導致很多時候我們使用layer3+4策略不會得到完全滿意的結果。可是事情又不是那麼嚴重,因為ip只是依照本地的mtu進行分段,而tcp是端到端的,它可以使用諸如mss以及mtu發現之類的機制配合滑動視窗機制最大限度減少ip分段,因此layer3+4策略,很OK!
miimon和arp:

使用miimon僅能檢測鏈路層的狀態,也就是鏈路層的端到端串連(即交換器某個口和與之直連的本地網卡口),然而交換器的上行口如果down掉了還是無法檢測到,因此必然需要網路層的狀態檢測,最簡單也是最直接的方式就是arp了,可以直接arp網關,如果定時器到期網關還沒有回複arp reply,則認為鏈路不通了。
四、我該如何配置呢



1.首先,傳統的方式肯定不妥,核心文檔上寫的有,大家參考便是,記住,首先要裝一個ifenslave
2.最新的sysfs配置方式
首先確認你的系統上有sys這個目錄,並且mount於它的檔案系統是sysfs類型的。然後就是下面的步驟了,很簡單:
第零步:載入模組

root@zyxx:modprobe bonding
第一步:進入相應目錄

root@zyxx:cd /sys/class/net/

第二步:查看一下檔案,熟悉一下地形(該步驟可省略)

root@zyxx:/sys/class/net# ls
bond0 bonding_masters  eth0  eth1  eth2  eth3  eth4  eth5  lo

第三步:看看當前有哪些bond裝置

root@zyxx:/sys/class/net# cat bonding_masters
bond0

第四步:從一個bond裝置添加或者刪除一個乙太網路卡裝置

root@zyxx:/sys/class/net# echo +(-)X > bonding_masters 

#注釋:上一條命令中的+號表示添加裝置,而-號表示刪除裝置,+X中的X表示任意一個你喜歡的名字,-X中的X表示bonding_masters中已經存在的一個名字
第五步:進入新建立的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.設定鏈路監控間隔
root@zyxx:/sys/class/net/bondMy/bonding# echo 100 > miimon

3.設定mode為熱備
root@zyxx:/sys/class/net/bondMy/bonding# echo 1 > mode

...
第七步:感慨

整個配置步驟很簡單,模組只需要載入一次,以後動態配置就一切OK了。
五、bonding驅動的實現



在看了精彩的配置並且實際上已經配置出一個很好用的網路後,我肯定會急切的看一下原始碼的實現,這也是我喜歡linux的原因,因為它可以讓你隨意patch。實際上bonding的驅動非常簡單,和tap一樣的,基本就是三大部分:
第一部分:初始化


這部分很簡單,就是初始化一個net_device,然後註冊進去,這就不多說了
第二部分:實現使用者配置介面


該介面有兩種,第一種就是傳統的基於ioctl的方式配置,就是實現一個ioctl即可,另一種就是通過sysfs實現,也很簡單,實現一些attitude的store/show方法即可,不管採用哪種方式,最終都要調用一個函數,那就是netdev_set_master,該函數中最重要的事就一個,那就是將物理網卡的master設定成我們在第一部分初始化的那個bond裝置:
slave->master = master;
第三部分:初始化傳輸和接收介面


對於傳輸介面,很簡單,和多連接埠橋接器類似。bond裝置在初始化時將start_xmit初始化成bond_start_xmit,在bond_start_xmit中有一個switch-case,switch什麼呢?當然是bonding的mode了,比如在mode0,也就是輪轉負載的mode下,bond_start_xmit調用如下程式碼片段:
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;
    }
}
對於接收介面,所有的裝置都從netif_receive_skb開始資料包的協議分發,在該函數的開始處有下面代碼:
if (orig_dev->master) {
    if (skb_bond_should_drop(skb))
        null_or_orig = orig_dev;
    else
        skb->dev = orig_dev->master;
}
orig_dev是物理網卡,如果不出意外,在其擁有master的情況下,也就是使用使用者介面將物理網卡綁到一個bond裝置上後,物理網卡的master欄位將被設定,經過這段代碼,skb的dev將會被設定成master,也就是說bond裝置。接下來的向上層協議的傳輸就由bond裝置來完成了,因為所有的三層資訊完全都在bond裝置上,而沒有在物理網卡orig_dev上。
六、進階主題



不可否認,本文的解析是極其淺顯的,如果想獲得更進階的主題和一些很深入細節的主題,或者想瞭解一些關於交換器的知識以及效能方面的問題,請參考Linux關於bonding的文檔,該文檔在$KERNEL-ROOT/Documentation/networking/bonding.txt,強烈建議閱讀,讀了該文檔之後,你就是bonding方面的專家啦。
附:談談配置方式


linux的sysfs和proc(sysctl等)機制可以實現檔案io的方式配置系統參數和裝置,回想當初學習思科/華為裝置配置時,花了上萬元RMB之後,最後的收穫就是只會打“?”就OK了,後來隨著學習的深入,發現windows的netsh也是這種配置方式,甚至感覺比思科/華為裝置的配置方式更加簡單,但是歸根結底都是命令式的,所謂的命令式其實就是類似英語的,主-謂-賓(定-狀-補),主語就是系統的當前user,謂語就是命令,賓語就是目標裝置或者系統本身,定狀補描述命令參數,這類命令對於習慣於自然語言的人們來講是很方便的,然而記憶的開銷卻不小。linux通過檔案讀寫的方式可以實作類別似的配置,不知道是好是壞,反正我在初用sysfs配置bonding的時候第一個感覺就是“好極了”,再也不用使用man ifenslave了,再也不用vim文檔了,kobject將bonding的相關內容組織在$sysfstoor/class/net/目錄下,只要你會訪問檔案,你就能進行配置,而且bonding的新版本幾乎廢棄了原來的ifenslave的ioctl方式,任何關於bonding配置都可以用sysfs來進行。
     最重要的是,檔案式或者netlink的配置相比ioctl式的經典命令式配置,可以減少裝置vfs層的膨脹,我們可以看看ioctl層次的代碼,是多麼的淩亂不堪,每添加一個新命令就需要修改甚至好幾層ioctl的代碼,其實就是添加一個case語句,用以分發新添加的命令,這樣就會導致ioctl代碼雖然分層但是卻解決不了裝置驅動和介面過度耦合的問題。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.