節省大家時間,首先說主旨: 包級別的 TCP/UDP 負載均衡和NAT(Network Address Translate)不能並存。 這是什麼意思呢?簡單來說,如果你有動態IP的連接,一個使用私有IP地址的局域網,通過NAT(Network Address Translate)上網,現在覺得速度不夠,想再加一條使到某一Internet主機的速度加倍(現在中國大陸的典型情況),對不起,你就死了這條心吧,這是不可能的。 這裡所說的連接包括 Ethernet,PPPoE,PPPoA,PPP,以及SLIP等等。並且,這是TCP/UDP 協議的內在機制造成的,和軟/硬件無關。任何架構在 TCP/UDP 之上的應用,不論硬件還是軟件,皆不能繞過此限制。 注意,如果你的局域網使用公用IP(public IP)而不需要NAT,則不在此列。 如果你想知道原因或者質疑我的結論:),請繼續往下讀。 本文假設 1.你已經閱讀過 Linux Advanced Routing & Traffic Control HOWTO,特別是 4.2 小節的 Routing for multiple uplinks/providers,4.2.1小節Split Access 和4.2.2小節 Load balancing。此文(以下按慣例簡稱lartc)非常清楚的說明了連接級別的TCP/UDP 負載均衡。 ________ +------------+ / +--- NATed ---+ Provider 1 +------- __ IP1 100.0.0.1 / remote host 1 ___/ \__ +------+-------+ +------------+ _/ \_ if1 / / local host 1\ Local network -----+ Linux router Internet \_local host 2/ \__ __/ if2 \___/ +------+-------+ +------------+ IP2 150.0.0.1 \ remote host 2 +--- NATed ---+ Provider 2 +------- +------------+ \________ 上面這幅示意圖即摘自 lartc,官方站點是 http://lartc.org/。為行文方便,列在這裡並做輕微改動,特此說明。 2.為便於理解,以下以常見的Linux 下 ADSL PPPoE 撥號上網為例,但如前所述,討論適用於其它各種情況。用數對(IP address, port)來表示一個連結的端點。假定 Host IP Port local host 1 192.168.0.1 5000 Linux route if1 100.0.0.1 6000 Linux route if2 150.0.0.1 7000 remote host 1 200.0.0.1 80 那麼連接級別和包級別的負載均衡究竟有何不同? 1、連結級別的負載均衡 Linux 2.4內核支持連結級別的負載均衡。為理解這個問題,這裡首先簡述 Linux 的靜態路由機制。當一部 Linux 主機收到一個需要轉發的IP包時,它首先檢查Cache中是否有相關的路由信息:如果有的話,會直接使用;否則再查找路由表。Cache中一段時間不使用的路由信息會被丟棄。如果設置了 MultiPath 路由,則選擇哪一個路由是隨機的,但隨後的IP包都會走這條路由,直到Cache中的路由信息被丟棄。 比如,當LAN上的主機 local host 1 向 remote host 1 發起一次連結的時候,Linux router 在Cache中找不到有關remote host 1 的路由信息,因此轉去查找路由表,當它發現有一個 MultiPath 路由的時候,會隨機選擇一個路由,比方說 if1,並把此路由加入Cache;下一個從 local hsot 1到 remote host 1 的 IP 包到達時,Linux router 直接在Cache 中找到路由信息然後直接轉發,也就是說,所有目的地為 remote host 1 的 IP 包都會經由 if1 轉發直到 Cache 中相關路由信息失效並被丟棄為止而不會經過if2,即使if2完全空閒也是如此。 因此,和 remote host 1 之間的通信並不會從附加的第二條連接中獲益。其實際效果就是你用 Realplay 看網絡電影時效果沒有改善,當然心理作用除外:) 如果與此同時 local host 1 有發起一條到 remote host 2 的連接,則有可能這一次會使用 if2。其實際效果就是你用 Realplay 看網絡電影時用 flashget開8個並發線程到一個FTP站下載一個 600M Redhat ISO 文件,兩者不會互相影響。 那麼,下載 ISO 文件的同時 localhost 2 發起一條到 remote host 2 的連接,會出現怎樣的情況?是簡單地走if1?又或別有蹊徑?請讀者思考。 2、包級別的負載均衡 現實中這種一邊忙一邊無所事事的情況在所多有,毋庸贅述:)。那麼如何改善呢?很自然的想到,我們可以把同一條TCP/IP連接中的IP包同時從兩條上行連接中發出,左右開弓,不就可以了嗎?實際上,我們在 PPP Multilink 就是這麼干的,不過現在沒有服務器的支持,要靠自己。這是Internet,因此已經有人想到這一點並且編寫了內核補丁供下載,請搜索 equalize_2.4.18.patch。 需要特別提醒你的是,請確保該補丁的日期是 Fri Mar 22 2002,而不是有問題的 Thu Mar 21 2002 版本。另外,根據我的經驗,這個 patch 也可以用於 2.4.19 內核。 重新編譯了內核以後,你可以使用 equalize 關鍵字了。如果你的 LAN 使用公有 IP 地址,那麼恭喜你,你現在可以在雙倍速度下用 Realplay 看網絡電影了。但是正所謂人生不如意事常八九,如果你使用的是私有 IP 地址配合以NAT上網,讓我們來看看當 local host 1 對 remote host 1 發起一次連結時會發生什麼。 local host 1 第一次從(192.168.0.1,5000)向 remote host 1 (200.0.0.1, 80) 發起一條連接,現在第一個 IP 包 (SYN 包)已經到達 Linux router。既然是第一次,Cache 中當然沒有有關路由信息,Linux router 查找路由表,發現帶 equalize 標記的 MultiPath 路由,因此隨機決定如何轉發此IP 包 -- 好,這次茫茫中命裡注定從 if2 走:) 並且雁過留聲,在 Cache 中添加一條到 remote host 1 的路由信息-- 既然 local host 1 的 IP 是私有的,NAT 起作用,轉換源地址為 (150.0.0.1,7000)後發出,沒問題,remote host 1 收到並向 (150.0.0.1,7000) 發回SYN+ACK 包表示接受連結。Linux router if2 收到返回的 SYN+ACK 包,早有准備,NAT 再次轉換目的地址為 (192.168.0.1, 5000)。 第一回合。到目前為止一切 OK。 緊接著 local host 1 (192.168.0.1, 5000) 按規矩再向 (200.0.0.1, 80) 發送 ACK 包表示確認。此 ACK 包又來到Linux router 這個岔路口,它將走向何方呢?既然設定了 equalize,Linux 不會根據先前生成的路由表 Cache 中的記錄(就是由 SYN 包生成的)來決定走法,相反,它從 Cache 中刪除該記錄,然後重新查找路由表。這導致又一次重新選擇路由,可能是 if1,也可能是 if2。 如果又是 if2,很好,remote host 1 收到,握手完成,連接建立。 若是 if1,又當如何呢?很不幸,if1 將該包源地址經 NAT 轉換為 (100.0.0.1,6000),然後丟上 Internet,一路平安到達 remote host 1。remote host 1 正在苦等從 if2 (也就是 150.0.0.1,7000) 來的 ACK 包,沒想卻收到一個從(100.0.0.1,6000) 來的 ACK 包,它作何打算呢?難道說,反正是 ACK 包,馬馬虎虎將就著用用嗎?很明顯,這不是個好主意。其次,即便意欲如此,remote host 1 如何判斷這個 ACK 包和哪一個 SYN 包關聯呢?因此, 唯一合理的措施就是:忽略它,當沒看見好了。 丟棄此來路不明的 ACK 包後,remote host 1 繼續等待中......結果可想而知:不能同步,連結建立失敗。這裡 TCP/IP 的重傳機制並不能解決問題,remote host 1 並不認為這是一個正常的 TCP 包,雖然它是一個正常的 IP 包。 剛才提到如果走的是 if2,非常幸運,連接可以建立。同樣,以後 remote host 1 會收到所有數據包,不論是從 if1還是 if2 來的,只是從 if1 來的 TCP 包一概按例辦理,全部丟棄。好在local host 1 會重傳這些被丟棄的包,總有機會走到 if2 而被收到,因此連結還可以勉強維持,只是比較慢而已;當然,在某種極端情況下,也可能中途斷掉。 所以,equalize 負載均衡和 NAT 在一起工作,可能會導致建立連結失敗或者較緩慢的連結。換句話說,1+1 < 1。 最後有一個好消息,不是大好,是小好:ICMP 協議在此種情況下可以工作,也許你可以試著用它干點什麼。當然這也意味著一個使用動態IP對你發動 ICMP 攻擊的黑客有雙倍的可用帶寬:) 注1:2.4內核中 MultiPath 設定的具體的位置在 Networking options -> TCP/IP networking -> IP: advanced router -> IP: equal cost multipath,.config 文件中的對應行為 CONFIG_IP_ROUTE_MULTIPATH 注2:iproute2 的手冊明確指出,僅當內核打過補丁的情況下 equalize 關鍵字才能工作,原文"equalize only works if the kernel is patched." iproute2-ss010824, p26. 注3:本文所說的隨機並不是真正意義的隨機,但我們假定考察真正隨機的情況。 注4:對於如何處理所謂"來路不明的 ACK 包",可能是實現相關的,還請了解的朋友說一說。 附錄A 關於 lartc 中 split access 和 load-balancing 設置的一點說明: 在 load-balancing 小節中提到"如果你已經如前所述設置了 split access 的話 load-balancing 真的不是太難"。而在 split access 小節中就給出了一堆命令,T1 T2 P1 P2 IF1 IF2 什麼的。很多人不明白這些命令到底有何作用。其實這些命令是說,到 P1_NET 去的走 if1,到 P2_NET 去的走 if2,大家各行其道。那麼,我到 P1_NET 去的走if2,到 P2_NET 去的走 if1,可不可以呢?理論上也是可以的,但是這裡有個問題。如果 P1_NET 和 P2_NET 分屬兩家不同的 ISP,則從 if2 到 P1_NET 就要走遠路經過交換中心,而這可能會造成瓶頸。 至於從 P1_NET 和 P2_NET 回來的包,已經超出了我們的控制范圍,只能等它到 IF1或 IF2。如果出於某種原因路由不正常,沒有收到回音,那我們能做的也只是承認失敗而已。 因此這段命令想要防止捨近求遠,如此而已。 另外這段命令似乎有點問題。我已經在lartc郵件列表中請求幫助了,希望能盡快有一個滿意的答復。 在搞清楚這一點後,如果讀者的 P1_NET 和 P2_NET 和我一樣其實是同一家 ISP 的網絡,這段准備功夫是完全可以省去的。 意見和建議 大家認為有不正確、不妥當、不清楚的地方,請百忙中不吝賜教,以便改進。謝謝。 email : [email protected]