全域性負載均衡方案
譯自:Global Load Balancer Approaches
本文經驗更適用於混合雲場景,公有云一般直接使用供應商提供的LB即可。
簡介
當在多雲(可能是混合雲)中使用Kubernetes或Openshift部署應用時,需要考慮到如何跨叢集分發應用流量。為了解決該問題,我們設計了一個全域性負載均衡器。
上圖描述了一個稱為"app"的應用,並將其部署到了兩個Kubernetes叢集中,通過 Ingress 或 Gateway APIs資源(本地負載均衡器,簡稱LLB)進行訪問,LLB對外暴露了兩個虛擬IP:VIP1和VIP2。全域性負載均衡器位於LLB之上,用於將消費者(指一個全域性的FQDN,上例中為app.globaldomain.io)定向到某個叢集的應用中。
在本文發表時(October 5, 2021),Kubernetes 社群中並沒有明確的指導方案來告訴使用者如何設計和實現Kubernetes叢集的全域性負載均衡器。
在本文中,我們會討論如何設計一個合理的全域性負載均衡器,以及如何根據Kubernetes叢集中執行的負載來實現自動配置。
在選擇具體的架構前,首先看下常用的全域性負載均衡器的特點。
全域性負載均衡器的要求
Kubernetes叢集的全域性負載均衡器負責將到服務的連線路由到某個Kubernetes叢集中執行的例項上。這些叢集可以是物理上分開的,也可以位於相同的區域。
Kubernetes提供了兩種主要的方式來控制進入叢集的流量:Ingresses(Ingress, Gateway API 和 OpenShift Routes) 和LoadBalancer 服務。
我們的全域性負載均衡器兩種都要支援,需要注意的是,對於ingresses(L7反向代理)的場景,我們提供了一個暴露VIP的多應用共享的本地負載均衡器。
再者,全域性負載均衡器應該提供除輪詢之外的複雜的負載均衡策略。實踐中,一個策略通常能夠根據某些指標(延遲、地理距離等)來給嘗試連線的消費者返回最近的端點。在地理上分佈的場景中,該策略允許消費者以最小的延遲連線到後端。在相同區域的場景中,允許在同一資料中心內路由連線。
另一個常見的策略是路由權重,通過一次只給一個叢集分配權重來達到正向/反向配置的效果。
最後,一個全域性負載均衡器應該在應用不可達時將流量匯入健康的後端,這通常需要配置健康檢查。
概況一下一個全域性負載均衡器的要求:
- 能夠支援LoadBalancer Services 和Ingresses的負載均衡
- 複雜的負載均衡策略
- 支援健康檢查,並能夠從負載均衡池中移除不健康的後端
全域性負載均衡器架構選型
基於我們的經驗,可以通過如下兩種架構方案來設計全域性負載均衡器:
- 基於DNS
- 基於任播(Anycast)
基於DNS方案
在基於DNS的方案中,通過一個DNS服務來決定負載均衡。
在上圖中可以看到,一個服務消費者首先會向DNS服務請求app.globaldomain.io,然後DNS服務會返回其中一個叢集的地址。
鑑於DNS服務是所有網路基礎設定必不可少的一部分,因此基於DNS的負載均衡的好處是相對容易實現。但簡單的同時也帶來了一系列的限制:
- 客戶端需要在一定時間內(通常為DNS TTL,但不保證)快取DNS的響應結果。但如果快取的地區出現中斷,則在該地區問題解決之前或DNS快取重新整理之前,客戶端可能無法繼續運作。
- 當DNS服務配置為返回多個IP值時,客戶端需要負責選擇連線的後端,這有可能導致流量不均衡。即使DNS伺服器將返回IP的順序隨機化,也會發生這種情況。這是因為DNS規範中沒有規定在響應返回DNS服務層次結構時必須保留IP的順序,或者服務使用者必須遵守該順序。
除了上述限制,典型的DNS服務並不支援複雜的負載均衡策略,也不支援對後端的健康檢查。
但有一些高階的DNS實現可以支援這些特性:
- 網路裝置,如F5 BigIP 和 Citrix ADC,通常位於本地資料中心
- DNS託管服務,如Infoblox 和 CloudFlare
- 大型公有云服務商的DNS服務:AWS Route53, Azure Traffic Manager
注意,當實現這些全域性負載均衡器方案時,如果Ingresses (Ingress v1, v2 或OpenShift Routes)正在路由到應用的流量,則ingress中配置的主機名必須與消費者在查詢DNS伺服器時使用的全域性FQDN相同。該主機名與應用的預設地址不同,預設地址通常是叢集特定的,必須顯式設定。
概括
基於DNS的負載均衡 | 簡單DNS 服務 | 高階DNS 服務 |
---|---|---|
支援負載均衡服務 | yes | yes |
支援Ingress | yes | yes |
支援複雜的負載策略 | no | yes |
支援健康檢查 | no | yes |
例子 | 所有DNS 服務 | 本地: F5 BigIP, Citrix ADC等 |
限制 | 有可能會出現負載不均衡 當後端出現中斷時,反應較慢(30s) |
當後端出現中斷時,反應較慢(30s) |
基於任播方案
基於任播的方案是一種架構模式,即很多服務會宣告相同的IP,路由器可以通過不同的網路路徑將報文路由到不同的服務。
有兩種方式可以實現這種架構:
- 基於IP/BGP的任播服務:設計並使用IP/BGP技術來實現任播負載均衡功能。
- 使用任播/CDN雲服務:使用現有的啟用任播的網路(如特定的公有云任播服務(可以提供以選播為中心的負載平衡)和CDN服務)。這類公有云服務包括Google Cloud Network Load balancer, AWS Global Accelerator 和其他供應商的簡單全域性負載均衡服務。
基於IP/BGP的方案
首先看下基於IP/BGP的方案。可以使用配置 BGP 和ECMP了的IPv4,或本身就支援任播的IPv6來實現。
最終的架構如下:
上圖展示了一個部署到兩個叢集的應用,並通過負載均衡器對外提供服務。兩個負載均衡器的VIP是相同的(即上圖中的VIPG)。通過BGP對路由器進行程式設計,並給兩條路徑設定相同的開銷(ECMP),以此來實現雙活任播負載均衡方案。可以使用備選的BGP指標,如設定不同的開銷或本地偏好來實現不同的負載均衡策略,如主備。
可以讓一個叢集宣告的路由指標比其他叢集好得多的方式來實現主備。通常情況下,特定VIP上的所有流量都會路由到該叢集(無論該叢集與客戶端的跳數)。當發生災難時,會使用優先順序較低的路由。
這種方式的好處是依賴路由器而非客戶端進行故障切換,其故障切換遠比基於DNS的方案快得多(毫秒級別)。
為了獲得更好的故障切換體驗,路由器需要基於3元組或5元組雜湊以及一致性雜湊(有時稱為resilient hashing)來感知多路負載均衡。如果沒有這些功能,則故障切換和可用性可能會受到影響,這是因為在因網路故障事件導致會話從一個後端重新雜湊和重新路由到另一個後端時,可能會重置大量TCP會話。現代路由器通常會支援這些特性,但需要確保其配置能夠支援一致性雜湊。
這種方式有如下限制:
- 本地資料中心中的BGP/任播功能不一定可用。
- 由於BGP執行在L3/L4層,因此無法直接獨立切換共享相同VIP的多個應用程式(如上圖多個APP使用了同一個VIPG,要切換隻能一組同時切換,切換粒度比較大)。
Kubernetes的參考實現
實現上述方案的一個可選方式是在BGP模式下使用MetalLB來實現Kubernetes負載平衡器服務,並將群集節點連線到支援BGP的物理網路。
該拓撲結構與上圖類似,部署到每個叢集節點的MetalLB 代理與外部BGP網路形成對等網路,並通過多個叢集的多個節點宣告相同VIP的可達性。
注意上述方案中,無需使用支援BGP路由的CNI外掛來實現叢集內pod到pod的路由。MetalLB 僅僅使用BGP來向外部網路宣告負載均衡器的VIP。MetalLB 遵循Kubernetes服務規範的負載均衡器API,因此可以用於雲供應商不支援或雲供應商不遵循負載均衡器API的場景。
為了讓全域性負載均衡器運作,需要給多個叢集配置相同的VIP。
可以引入自動化來同步多個叢集的配置。例如,查詢全域性負載均衡器服務或強制它們配置相同的負載均衡器 VIP。可以通過集中自動化來協調支援BGP路由的指標(如 本地偏好),以此來配置叢集的優先順序。
基於雲服務的任播負載均衡方案
很多公共雲提供商支援將全域性負載均衡作為一種實現選播方法的服務,因為租戶可以使用單個靜態全域性IP地址,並且可以將其作為多叢集多區域服務的前端。
這類服務包括 Google Cloud Network Load Balancer, AWS Global Accelerator, Fastly Anycast 和 CloudFlare Anycast。這些雲服務通常會繫結CDN和/或 API閘道器,但大多數情況下會單純作為一個全域性任播負載均衡服務。
這些服務通常位於邊緣雲,並宣告全域性靜態VIP。傳送到全域性VIP的流量會被定向到離請求者最近的邊緣雲,然後進行負載均衡並在雲供應商內部進行路由(到後端例項)。
這些技術細節由雲供應商實現,對終端使用者不可見,但通常會涉及基於IP/BGP的任播和彈性ECMP的內部組合。雲供應商可以使用其私有網路來提供高階的負載均衡策略和流量控制,但這些特性在不同供應商的實現中有可能會不一致。
每個任播雲服務提供的特性各不相同,包括是否允許客戶使用自帶的IP,以及全域性IP是內部的還是對外的。
概況
任播負載均衡 | 簡單BGP+ECMP 方案 | 任播LBaaS-based 方案 |
---|---|---|
支援負載均衡器服務 | yes | yes |
支援Ingress | no | yes |
支援複雜的負載均衡策略 | 一部分 | yes |
支援健康檢查 | no | yes |
例子 | 所有 BGP+ECMP 實現 | LBaaS: Akamai, F5 silverline |
限制 | 需要實現滿足BGP的負載均衡器服務 | 通常不是本地部署的,每個特定的實現都有額外的限制 |
自帶全域性負載均衡配置
至此,我們已經調研了幾種構建全域性負載均衡器的方案。但想象一種場景,如果需要選擇一種方式來為部署到上百個叢集的應用實現負載均衡,是否存在某種途徑來為這些應用自動化建立全域性負載均衡配置?
有人可能會使用傳統的自動化工具來自動配置網路,但在Kubernetes上下文中,最好的方式是使用operator。
一個全域性負載均衡器operator需要監控叢集的配置,並基於叢集狀態來配置全域性負載均衡器。這裡的一個難點是,現今用於構建operator的生態工具主要面向單個叢集,因此大部分operator都是繫結到叢集的。
部分出於這個原因,部分出於可以通過多種方式來實現全域性負載平衡器,因此Kubernetes社群中似乎沒有針對這類operator的官方實現。但可以參考下面兩個實現:k8gb 和global-loadbalancer-operator
K8gb
K8gb是一個基於DNS的全域性負載均衡operator。K8gb比較有意思的是,DNS服務使用了叢集自建的CoreDNS pods。這種方式本身提供了災難恢復能力以及獨立於外部網路的能力。
k8gb 的侷限是它限制了對高階負載均衡策略的支援。目前正在努力通過使用CoreDNS外掛來緩解這些缺點。請參閱最近新增的geoip負載平衡策略來了解正在進行工作。
Global-loadbalancer-operator
global-loadbalancer-operator是一個執行在控制叢集(使用全域性負載均衡器配置),並監控多個被控制叢集的operator。
它可以使用所有external-dns operator 中支援的DNS服務來實現基於DNS的全域性負載均衡。
它還被設計為同時支援基於DNS和基於雲任播的全域性負載均衡。當執行在公有云中時,還可以新增高階配置項。
在本文發表時,支援AWS Route53, Azure Traffic Manager 和 Google Global IPs,這些供應商提供了更高階的能力,如不同的負載均衡策略和健康檢查等。
需要使用控制叢集和僅支援在OpenShift中部署是global-loadbalancer-operator的兩個限制。