本文已超過一年。較舊的文章可能包含過時的內容。檢查頁面中的資訊自發布以來是否已變得不正確。
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-TCP | NodePort 類型服務 TCP 埠 | 偽裝傳送到 NodePort(TCP) 的封包 |
KUBE-NODE-PORT-LOCAL-TCP | 具有 externalTrafficPolicy=local 的 NodePort 類型服務 TCP 埠 | 接受傳送到具有 externalTrafficPolicy=local 的 NodePort 服務的封包 |
KUBE-NODE-PORT-UDP | NodePort 類型服務 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 故事