本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
基於 IPVS 的叢集內負載均衡深入探討
編者按:此文章是關於 Kubernetes 1.11 新功能的一系列深度文章之一
引言
根據Kubernetes 1.11 釋出部落格文章,我們宣佈基於 IPVS 的叢集內服務負載均衡已正式釋出。在這篇部落格中,我們將帶您深入瞭解該功能。
什麼是 IPVS?
IPVS (IP Virtual Server) 構建於 Netfilter 之上,作為 Linux 核心的一部分,實現了傳輸層負載均衡。
IPVS 被整合到 LVS (Linux Virtual Server) 中,在主機上執行並作為真實伺服器叢集前的負載均衡器。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 個節點的叢集中,如果 PVS 是專為負載平衡而設計的,並且使用更高效的資料結構(雜湊表),從而實現幾乎無限的底層擴充套件。
另一方面,使用基於 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:永不排隊
將來,我們可以實現特定於服務的排程器(可能透過 annotation),它具有更高的優先順序並會覆蓋該值。
引數:--cleanup-ipvs
類似於 --cleanup-iptables
引數,如果為 true,則清理 IPVS 模式下建立的 IPVS 配置和 IPTables 規則。
引數:--ipvs-sync-period
IPVS 規則重新整理頻率的最大間隔(例如 '5s','1m')。必須大於 0。
引數:--ipvs-min-sync-period
IPVS 規則重新整理頻率的最小間隔(例如 '5s','1m')。必須大於 0。
引數:--ipvs-exclude-cidrs
一個逗號分隔的 CIDR 列表,IPVS 代理在清理 IPVS 規則時不應觸及這些 CIDR,因為 IPVS 代理無法區分 kube-proxy 建立的 IPVS 規則和使用者原始的 IPVS 規則。如果您在環境中使用 IPVS 代理和自己的 IPVS 規則,則應指定此引數,否則您的原始規則將被清理。
設計考慮
IPVS 服務網路拓撲
建立 ClusterIP 型別服務時,IPVS 代理將執行以下三項操作:
- 確保節點中存在虛擬介面,預設為 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 Service 和 IPVS 虛擬伺服器之間的關係是 1:N
。例如,考慮一個具有多個 IP 地址的 Kubernetes Service。External IP 型別服務有兩個 IP 地址 - ClusterIP 和 External IP。那麼 IPVS 代理將建立 2 個 IPVS 虛擬伺服器 - 一個用於 Cluster IP,另一個用於 External IP。Kubernetes Endpoint(每個 IP+Port 對)和 IPVS 虛擬伺服器之間的關係是 1:1
。
刪除 Kubernetes 服務將觸發刪除相應的 IPVS 虛擬伺服器、IPVS 真實伺服器及其繫結到虛擬介面的 IP 地址。
埠對映
IPVS 有三種代理模式: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 代理將在 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 代理中的 Iptables 和 Ipset
IPVS 用於負載均衡,它無法處理 kube-proxy 中的其他變通方法,例如資料包過濾、髮夾偽裝技巧、SNAT 等。
IPVS 代理在上述場景中利用 iptables。具體來說,ipvs 代理將在以下 4 種場景中回退到 iptables:
- kube-proxy 啟動時帶上 --masquerade-all=true
- 在 kube-proxy 啟動時指定叢集 CIDR
- 支援 Loadbalancer 型別服務
- 支援 NodePort 型別服務
但是,我們不想建立太多的 iptables 規則。因此,我們採用 ipset 以減少 iptables 規則。以下是 IPVS 代理維護的 ipset 集合表:
集合名稱 | 成員 | 用途 |
---|---|---|
KUBE-CLUSTER-IP | 所有服務 IP + 埠 | 當 masquerade-all=true 或 clusterCIDR 指定時進行偽裝 |
KUBE-LOOP-BACK | 所有服務 IP + 埠 + IP | 用於解決髮夾問題的偽裝 |
KUBE-EXTERNAL-IP | 服務外部 IP + 埠 | 用於偽裝到外部 IP 的資料包 |
KUBE-LOAD-BALANCER | 負載均衡器入口 IP + 埠 | 用於偽裝到負載均衡器型別服務的資料包 |
KUBE-LOAD-BALANCER-LOCAL | 具有 externalTrafficPolicy=local 的負載均衡器入口 IP + 埠 | 接受到具有 externalTrafficPolicy=local 的負載均衡器的資料包 |
KUBE-LOAD-BALANCER-FW | 具有 loadBalancerSourceRanges 的負載均衡器入口 IP + 埠 | 丟棄指定了 loadBalancerSourceRanges 的負載均衡器型別服務的資料包 |
KUBE-LOAD-BALANCER-SOURCE-CIDR | 負載均衡器入口 IP + 埠 + 源 CIDR | 接受指定了 loadBalancerSourceRanges 的負載均衡器型別服務的資料包 |
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 代理,iptables 規則的數量是靜態的,無論我們有多少服務/Pod。
在 IPVS 模式下執行 kube-proxy
目前,local-up 指令碼、GCE 指令碼和 kubeadm 支援透過匯出環境變數 (KUBE_PROXY_MODE=ipvs
) 或指定標誌 (--proxy-mode=ipvs
) 來切換 IPVS 代理模式。在執行 IPVS 代理之前,請確保已安裝 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 最簡單的方法是加入眾多與其興趣相符的特別興趣小組 (SIGs) 之一。有什麼想向 Kubernetes 社群廣播的嗎?在我們的每週社群會議上以及透過以下渠道分享您的聲音。
感謝您一直以來的反饋和支援。在Stack Overflow上釋出問題(或回答問題)加入K8sPort的社群門戶關注我們的 Twitter @Kubernetesio 獲取最新更新在 Slack上與社群聊天分享您的 Kubernetes 故事