本文已超過一年。較舊的文章可能包含過時的內容。檢查頁面中的資訊自發布以來是否已變得不正確。

IPVS 叢集內負載平衡深度探討

編者註:這篇文章是關於 Kubernetes 1.11 新功能的深入文章系列的一部分

簡介

根據Kubernetes 1.11 版本發布部落格文章,我們宣布基於 IPVS 的叢集內服務負載平衡升級為正式發布。在本部落格中,我們將帶您深入探討此功能。

什麼是 IPVS?

IPVS (IP 虛擬伺服器) 建構於 Netfilter 之上,並實作傳輸層負載平衡作為 Linux 核心的一部分。

IPVS 併入 LVS (Linux 虛擬伺服器) 中,它在主機上執行,並充當真實伺服器叢集前端的負載平衡器。IPVS 可以將基於 TCP 和 UDP 的服務請求導向到真實伺服器,並使真實伺服器的服務在單一 IP 位址上顯示為虛擬服務。因此,IPVS 自然支援 Kubernetes Service。

為何 Kubernetes 使用 IPVS?

隨著 Kubernetes 使用量增長,其資源的可擴展性變得越來越重要。特別是,服務的可擴展性對於開發人員/公司執行大型工作負載採用 Kubernetes 至關重要。

服務路由的建構組塊 Kube-proxy 依賴身經百戰的 iptables 來實作核心支援的服務類型,例如 ClusterIP 和 NodePort。然而,iptables 難以擴展到數萬個服務,因為它純粹為防火牆用途而設計,並且基於核心內規則列表。

即使 Kubernetes 在 v1.6 版本中已支援 5000 個節點,但具有 iptables 的 kube-proxy 實際上是將叢集擴展到 5000 個節點的瓶頸。一個例子是,在 5000 個節點的叢集中使用 NodePort 服務時,如果我們有 2000 個服務,每個服務有 10 個 Pod,這將在每個工作節點上造成至少 20000 個 iptable 記錄,這會使核心非常繁忙。

另一方面,使用基於 IPVS 的叢集內服務負載平衡在這種情況下可以提供很大幫助。IPVS 專門為負載平衡而設計,並使用更有效率的資料結構(雜湊表),允許底層幾乎無限的規模。

基於 IPVS 的 Kube-proxy

參數變更

參數:--proxy-mode 除了現有的 userspace 和 iptables 模式外,IPVS 模式是透過 --proxy-mode=ipvs 設定的。它隱含地使用 IPVS NAT 模式進行服務埠對應。

參數:--ipvs-scheduler

新增了一個 kube-proxy 參數來指定 IPVS 負載平衡演算法,參數為 --ipvs-scheduler。如果未設定,則輪詢 (rr) 為預設值。

  • rr:輪詢
  • lc:最少連線
  • dh:目標雜湊
  • sh:來源雜湊
  • sed:最短預期延遲
  • nq:永不排隊

未來,我們可以實作服務特定排程器(可能透過註解),它具有更高的優先權並覆寫該值。

參數:--cleanup-ipvs 類似於 --cleanup-iptables 參數,如果為 true,則清除在 IPVS 模式中建立的 IPVS 組態和 IPTables 規則。

參數:--ipvs-sync-period IPVS 規則重新整理的最大間隔(例如「5 秒」、「1 分鐘」)。必須大於 0。

參數:--ipvs-min-sync-period IPVS 規則重新整理的最小間隔(例如「5 秒」、「1 分鐘」)。必須大於 0。

參數:--ipvs-exclude-cidrs 以逗號分隔的 CIDR 列表,當清除 IPVS 規則時,IPVS proxier 不應觸碰這些 CIDR,因為 IPVS proxier 無法區分 kube-proxy 建立的 IPVS 規則與使用者原始 IPVS 規則。如果您在環境中使用具有您自己的 IPVS 規則的 IPVS proxier,則應指定此參數,否則您的原始規則將被清除。

設計考量

IPVS 服務網路拓撲

建立 ClusterIP 類型服務時,IPVS proxier 將執行以下三件事

  • 確保節點中存在虛擬介面,預設為 kube-ipvs0
  • 將服務 IP 位址繫結到虛擬介面
  • 分別為每個服務 IP 位址建立 IPVS 虛擬伺服器

以下範例

# kubectl describe svc nginx-service
Name:			nginx-service
...
Type:			ClusterIP
IP:			    10.102.128.4
Port:			http	3080/TCP
Endpoints:		10.244.0.235:8080,10.244.1.237:8080
Session Affinity:	None

# ip addr
...
73: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether 1a:ce:f5:5f:c1:4d brd ff:ff:ff:ff:ff:ff
    inet 10.102.128.4/32 scope global kube-ipvs0
       valid_lft forever preferred_lft forever

# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn     
TCP  10.102.128.4:3080 rr
  -> 10.244.0.235:8080            Masq    1      0          0         
  -> 10.244.1.237:8080            Masq    1      0          0   

請注意,Kubernetes 服務與 IPVS 虛擬伺服器之間的關係為 1:N。例如,考慮具有多個 IP 位址的 Kubernetes 服務。外部 IP 類型服務具有兩個 IP 位址 - ClusterIP 和外部 IP。然後 IPVS proxier 將建立 2 個 IPVS 虛擬伺服器 - 一個用於 Cluster IP,另一個用於外部 IP。Kubernetes 端點(每個 IP+Port 對)與 IPVS 虛擬伺服器之間的關係為 1:1

刪除 Kubernetes 服務將觸發刪除對應的 IPVS 虛擬伺服器、IPVS 真實伺服器及其繫結到虛擬介面的 IP 位址。

埠對應

IPVS 中有三種 Proxy 模式:NAT (masq)、IPIP 和 DR。只有 NAT 模式支援埠對應。Kube-proxy 利用 NAT 模式進行埠對應。以下範例顯示 IPVS 將服務埠 3080 對應到 Pod 埠 8080。

TCP  10.102.128.4:3080 rr
  -> 10.244.0.235:8080            Masq    1      0          0         
  -> 10.244.1.237:8080            Masq    1      0       

工作階段親和性

IPVS 支援用戶端 IP 工作階段親和性(持久連線)。當服務指定工作階段親和性時,IPVS proxier 將在 IPVS 虛擬伺服器中設定逾時值(預設為 180 分鐘 = 10800 秒)。例如

# kubectl describe svc nginx-service
Name:			nginx-service
...
IP:			    10.102.128.4
Port:			http	3080/TCP
Session Affinity:	ClientIP

# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.102.128.4:3080 rr persistent 10800

IPVS Proxier 中的 Iptables 和 Ipset

IPVS 用於負載平衡,但它無法處理 kube-proxy 中的其他變通方案,例如,封包篩選、髮夾偽裝技巧、SNAT 等。

IPVS proxier 在上述情況下利用 iptables。具體而言,ipvs proxier 將在以下 4 種情況下退回 iptables

  • kube-proxy 以 --masquerade-all=true 啟動
  • 在 kube-proxy 啟動中指定叢集 CIDR
  • 支援 Loadbalancer 類型服務
  • 支援 NodePort 類型服務

但是,我們不想建立太多 iptables 規則。因此,我們採用 ipset 以減少 iptables 規則。以下是 IPVS proxier 維護的 ipset 集合表

集合名稱成員用途
KUBE-CLUSTER-IP所有服務 IP + 埠在 masquerade-all=true 或指定 clusterCIDR 的情況下進行偽裝
KUBE-LOOP-BACK所有服務 IP + 埠 + IP偽裝以解決髮夾問題
KUBE-EXTERNAL-IP服務外部 IP + 埠偽裝傳送到外部 IP 的封包
KUBE-LOAD-BALANCER負載平衡器 Ingress IP + 埠偽裝傳送到 Load Balancer 類型服務的封包
KUBE-LOAD-BALANCER-LOCAL具有 externalTrafficPolicy=local 的負載平衡器 Ingress IP + 埠接受傳送到具有 externalTrafficPolicy=local 的負載平衡器的封包
KUBE-LOAD-BALANCER-FW具有 loadBalancerSourceRanges 的負載平衡器 Ingress IP + 埠捨棄針對指定 loadBalancerSourceRanges 的 Load Balancer 類型服務的封包
KUBE-LOAD-BALANCER-SOURCE-CIDR負載平衡器 Ingress IP + 埠 + 來源 CIDR接受針對指定 loadBalancerSourceRanges 的 Load Balancer 類型服務的封包
KUBE-NODE-PORT-TCPNodePort 類型服務 TCP 埠偽裝傳送到 NodePort(TCP) 的封包
KUBE-NODE-PORT-LOCAL-TCP具有 externalTrafficPolicy=local 的 NodePort 類型服務 TCP 埠接受傳送到具有 externalTrafficPolicy=local 的 NodePort 服務的封包
KUBE-NODE-PORT-UDPNodePort 類型服務 UDP 埠偽裝傳送到 NodePort(UDP) 的封包
KUBE-NODE-PORT-LOCAL-UDP具有 externalTrafficPolicy=local 的 NodePort 類型服務 UDP 埠接受傳送到具有 externalTrafficPolicy=local 的 NodePort 服務的封包

一般而言,對於 IPVS proxier,iptables 規則的數量是靜態的,無論我們有多少服務/Pod。

在 IPVS 模式中執行 kube-proxy

目前,local-up 腳本、GCE 腳本和 kubeadm 支援透過匯出環境變數 (KUBE_PROXY_MODE=ipvs) 或指定旗標 (--proxy-mode=ipvs) 來切換 IPVS Proxy 模式。在執行 IPVS proxier 之前,請確保已安裝 IPVS 要求的核心模組。

ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
nf_conntrack_ipv4

最後,對於 Kubernetes v1.10,功能閘道 SupportIPVSProxyMode 預設設定為 true。對於 Kubernetes v1.11,功能閘道已完全移除。但是,對於 v1.10 之前的 Kubernetes,您需要明確啟用 --feature-gates=SupportIPVSProxyMode=true

參與其中

參與 Kubernetes 最簡單的方式是加入許多與您的興趣一致的特殊興趣小組 (SIG) 之一。有什麼想向 Kubernetes 社群廣播的嗎?在我們的每週社群會議上以及透過以下管道分享您的聲音。

感謝您持續的回饋與支持。在 Stack Overflow 上發布問題(或回答問題)加入 K8sPort 上倡導者的社群入口網站 在 Twitter 上追蹤我們 @Kubernetesio 以取得最新更新 在 Slack 上與社群聊天 分享您的 Kubernetes 故事