你真的瞭解 Load Balance 嘛

在計算中,Load Balance[1] 是指在一組資源(計算單元)上分配一組任務的過程,目的是使其整體處理更有效率。負載均衡可以優化響應時間,避免一些計算節點不均衡地超載,而其他計算節點則被閒置

同時,負載均衡, 反向代理, 網關 這些模塊功能也比較相似,所以本文寬泛的將 LB 代指所有提供類似功能的開源軟件以及設備

爲什麼需要 LB

能認識上圖的人,都是 Old Gun 了。硬件負載均衡設備 F5Netscaler

上一代互聯網基礎架構,必不可少的接入層設備,當時 lvs 剛起步,fullnat 還沒有流行起來。所以需要硬件設備做接入層 LB

在趕集網的時候,Netscaler 有兩臺設備,一主一備,後來設備到期,公司因爲成本問題,沒有購買後續維修服務,硬是撐到了和 58 合併

後來淘寶開源了 fullnat 模式的 lvs 代碼,一般公司都是 lvs + nginx 實現接入層:對外 BGP IP, Lvs+OSPF 構建 tcp layer 4 接入層, nginx 負責 offload ssl 配置各種轉發邏輯

寬泛來說,這裏面 lvs, nginx 都是 Load Balance 軟件,除了按照一定算法均衡 backend 設備的負載,LB 還要檢測後端 Server 的存活狀態,自動摘掉故障節點

基本常識

那麼問題來了,如何構建 Load Balance 設備或是軟件呢?本質還是理解 tcp/ip 模型及底層原理

如上圖所示,物理層,Mac 層,IP 層,TCP 層, HTTP/HTTPS 等七層。每層都是有不同的 header, 然後封裝好 data 後傳遞給下一層,發送數據與接收數據邏輯相反

不同的 LB 及模式工作在不同模型,比如我們常見的 Nginx 常工作在七層,在用戶態解析 http/https 協議然後轉發請求

LVS 很多種模式,工作在二,三,四層都可以

Linux Virtual Server

Linux Virtual Server (lvs) 是 Linux 內核自帶的負載均衡器,也是目前性能最好的軟件負載均衡器之一。lvs 包括 ipvs 內核模塊和 ipvsadm 用戶空間命令行工具兩部分

在 lvs 中,節點分爲 Director Server 和 Real Server 兩個角色,其中 Director Server 是負載均衡器所在節點,而 Real Server 則是後端服務節點

當用戶的請求到達 Director Server 時,內核 netfilter 機制的 PREROUTING 鏈會將發往本地 IP 的包轉發給 INPUT 鏈(也就是 ipvs 的工作鏈,在 INPUT 鏈上,ipvs 根據用戶定義的規則對數據包進行處理(如修改目的 IP 和端口等),並把新的包發送到 POSTROUTING 鏈,進而再轉發給 Real Server

阿里開源 LVS 很久了,但是最新的 fullnat 代碼一直沒放出,七牛使用的 lvs 內核版本過低,只能用 linux 2.7 kernel, 連硬件支持都很差了。所以後面會講到 dpvs[2]

Nat 是將修改數據包的目的 IP 和端口,將包轉發給 RealServer (rs 不需要做任務設置), 此時 Director Server 即 LVS 是做爲網關的,處於同一個局域網。包進來做 DNAT,出去做 SNAT

所有流量都經過 LVS, 很容易成爲瓶頸。一般用於運維 OP 性質的多一些,服務正常業務流量有問題。

DR (Direct Route) 模式性能最好,工作在二層,通過修改數據包的 Mac 地址來實現轉發,屬於單臂 one-arm 模式,所以要求二層可達

進來的流量經過 LVS, 出去的直接返回給 client, 以前在趕集網時 MySQL 多用 DR 模式

這個模式需要修改 Real Server, 配置 arp_ignore 和 arp_announce 忽略對 vip 的 ARP 解析請求,同時 lo 添加對應的 VIP

Tunnel 是典型的隧道模式

Fullnat 解決了 Nat 的不足,機器可以無限擴展,當然也要受限於單機 lvs 的網卡及 cpu 性能

同時爲了讓服務獲取 client 真實 IP, 需要加載 TOA 模塊,將 IP 寫到 tcp option 中

DPDK + LVS = DPVS

IQIYI 前幾年開源了 DPVS, 主流公司都有自己的 DPDK LB 輪子,你不造一個都不好意思說是大公司

另外,阿里開源的 fullnat 模塊還停留在 linux 2.6.32 kernel, 很多現代機器支持不好,而且 2021 年了,linux 主流內核都是 4.0 及以上

主要優化就是由 DPDK bypass 內核,完全用戶態接管網卡,同時重寫 tcp/ip 協議棧代碼,這樣避免了內核空間與用戶空間的來回拷貝

上面是 dpvs 的整體架構,裏面細節超多,感興趣可以網上搜我的文章,以前寫過一系列

性能 Benchmark 據說可以達到線速,公司用的話還得調研一下。開源產品宣傳的很好,實際測起來數據可能不是那麼回事,需要有專人調優

調度算法

上面是主流 LB 設備的調度算法,面試八股文必備,一般 RR 簡單的輪詢就夠了。複雜一些的需要加個權,一般都是輾轉相除實現的 GCD 最大公約數

這就夠了嘛?其實不夠的,比如我們線上遇到過預熱的問題,服務流量特別大,新起來的機器 RR 過來的話瞬間 QPS 超高,影響服務性能,表現爲 GC 特別頻繁,同時請求的 latency 非常高

怎麼解決呢?參考 Nginx 的 Smooth Weighted Round-Robin (SWRR) 平滑移動加權算法

雲廠商的 LB

暫時只看 AWS Cloud, 一般我們都用 elb 做入口,涉及到 elb, alb, clb, nlb 概念巨多

大家可以參考官網看下區別,無外乎是否支持 layer 4、layer 7, 是否支持按 path 路由,還有一些高級功能等等的區別

具體實現,因爲是黑盒,可能 c/c++ 自己寫,也可能是 nginx 魔改,誰知道呢

K8S 的 LB

K8S 裏面主要是三類:Service 四層,Ingress 七層,以及所謂的 Service Mesh 模式

Service 允許指定你所需要的 Service 類型,默認是 ClusterIP, 主要類型有:

ClusterIP:通過集羣的內部 IP 暴露服務,選擇該值時服務只能夠在集羣內部訪問。這也是默認的 ServiceType

NodePort:通過每個節點上的 IP 和靜態端口(NodePort)暴露服務。NodePort 服務會路由到自動創建的 ClusterIP 服務。通過請求 <節點 IP>:< 節點端口 >,你可以從集羣的外部訪問一個 NodePort 服務

LoadBalancer:使用雲提供商的負載均衡器向外部暴露服務。外部負載均衡器可以將流量路由到自動創建的 NodePort 服務和 ClusterIP 服務上

ExternalName:通過返回 CNAME 和對應值,可以將服務映射到 externalName 字段的內容(例如,foo.bar.example.com), 無需創建任何類型代理

除了類型主要用三種工作模式:userspace proxy, iptables model, ipvs model. 現在性能最好默認的就是 ipvs 模式,如果機器不支持會 fallback 到 iptables

網上有性能對比,差距還是蠻明顯的。Service 工作在四層,有些需求是按照 path 把請求轉發到不同服務,這時就需要用到 Ingress

上圖的 Ingress 由 nginx 實現,感興趣的可以參考官網

對於流行的 Service Mesh, 每個 POD 都有 sidecar 容器來劫持流量,好處是業務無需配置服務發現,熔斷,限流等等,這些都由 SM 的控制面來配

缺點是中間引入過多的 proxy, 服務可觀測性是個挑戰,A 調 B 服務慢了,中間有多個 sidecar, 扯皮就很難受了

小結

今天的分享就這些,寫文章不容易,如果對大家有所幫助和啓發,請大家幫忙點擊再看點贊分享 三連

關於 Load Balance 大家有什麼看法,或是文章有內容錯誤,歡迎留言一起討論,大牛多留言 ^_^

參考資料

[1]

Load Balance From wikipedia: https://en.wikipedia.org/wiki/Load_balancing(computing),_

[2]

dpvs: https://github.com/iqiyi/dpvs,

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/nif6blio5ksQNbOF5hfsSQ