基於IPVS的叢集內負載均衡深入解讀
在Kubernetes官方部落格之前的文章《Kubernetes1.11: In-Cluster Load Balancingand CoreDNS Plugin Graduate to General Availability中我們宣佈了基於IPVS的叢集內負載均衡已經實現了GA(General Availability),在這篇文章中我們將詳細介紹該特性的實現細節。
什麼是IPVS
IPVS (IP Virtual Server)是基於Netfilter的、作為linux核心的一部分實現傳輸層負載均衡的技術。
IPVS整合在LVS(Linux Virtual Server)中,它在主機中執行,並在真實伺服器叢集前充當負載均衡器。IPVS可以將對TCP/UDP服務的請求轉發給後端的真實伺服器,因此IPVS天然支援Kubernetes Service。
為什麼選擇IPVS
隨著kubernetes使用量的增長,其資源的可擴充套件性變得越來越重要。特別是對於使用kubernetes執行大型工作負載的開發人員或者公司來說,service的可擴充套件性至關重要。
kube-proxy是為service構建路由規則的模組,之前依賴iptables來實現主要service型別的支援,比如(ClusterIP和NodePort)。但是iptables很難支援上萬級的service,因為iptables純粹是為防火牆而設計的,並且底層資料結構是核心規則的列表。
kubernetes早在1.6版本就已經有能力支援5000多節點,這樣基於iptables的kube-proxy就成為叢集擴容到5000節點的瓶頸。舉例來說,如果在一個5000節點的叢集,我們建立2000個service,並且每個service有10個pod,那麼我們就會在每個節點上有至少20000條iptables規則,這會導致核心非常繁忙。
基於IPVS的叢集內負載均衡就可以完美的解決這個問題。IPVS是專門為負載均衡設計的,並且底層使用雜湊表這種非常高效的資料結構,幾乎可以允許無限擴容。
基於IPVS的kube-proxy
IPVS (IP Virtual Server)是基於Netfilter的、作為linux核心的一部分實現傳輸層負載均衡的技術。
IPVS整合在LVS(Linux Virtual Server)中,它在主機中執行,並在真實伺服器叢集前充當負載均衡器。IPVS可以將對TCP/UDP服務的請求轉發給後端的真實伺服器,因此IPVS天然支援Kubernetes Service。
1.啟動引數變更
啟動引數:--proxy-mode。除了userspace模式和iptables模式外,現在使用者可以通過--proxy-mode=ipvs使用IPVS模式。它預設使用IPVS的NAT模式,用於實現service埠對映。
啟動引數:--ipvs-scheduler。引入新的kube-proxy引數以支援IPVS的負載均衡演算法,使用者可以通過--IPVS-scheduler配置,預設使用輪詢模式(rr)。下面是另外支援的幾種負載均衡演算法:
• rr:輪詢
• lc:最少連線數
• dh:目的地址雜湊
• sh:源地址雜湊
• sed:最短期望延時
• nq:從不排隊
未來還可以實現通過service定義排程策略(可能是基於annotation),覆蓋kube-proxy預設的排程策略。
啟動引數:-cleanup-ipvs。與iptables的--cleanup-iptables引數類似,如果設定為true,清理IPVS相關配置,以及IPVS模式建立的iptables規則。
啟動引數:--ipvs-sync-period。重新整理IPVS規則的最大時間間隔,必須大於0。
啟動引數:--ipvs-min-sync-period。重新整理IPVS規則的最小時間間隔,必須大於0。
啟動引數:--ipvs-exclude-cidrs。指定一個用逗號分隔的CIDR列表,這個列表規定了IPVS重新整理時不能清理的規則。因為基於IPVS的kube-proxy不能區分自身建立的規則和系統中使用者自帶的規則,因此如果您要使用基於IPVS的kube-proxy並且系統中原來存在一些IPVS規則,那麼應該加上這個啟動引數,否則系統中原來的規則會被清理掉。
2. 設計細節
a)IPVS service網路拓撲
當我們建立ClusterIP型別的service時,IPVS模式的kube-proxy會做下面幾件事:
• 確認節點中的虛擬網絡卡,預設是kube-ipvs0
• 繫結service IP地址到虛擬網絡卡
• 為每個service 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
需要注意的是,service與IPVS虛擬伺服器應該是1:N的對應關係,因為service可能有多個IP地址(比如ExternalIP型別的service,就有clusterIP和ExternalIP兩個地址)。而endpoint與IPVS真實伺服器的對應關係應該是1:1。
刪除kubernetes的service將會觸發刪除相應的IPVS虛擬伺服器、IPVS真實伺服器並且解綁虛擬網絡卡上的IP。
b)埠對映
IPVS有三種代理模式:NAT(masq),IPIP 和DR,其中只有NAT模式支援埠對映。kube-proxy使用NAT模式進行埠對映,以下示例是IPVS對映service埠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
c)會話保持
IPVS支援會話保持,當kubernetes的service指定會話保持時,IPVS會設定超時時間,預設180分鐘,下面是會話保持示例:
# 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
d)IPVS模式中的iptables和ipset
IPVS是專門為負載均衡設計的,因此IPVS自身不能實現kube-proxy的其他功能,比如包過濾、hairpin、源地址轉換等。
IPVS模式會在上述場景中使用iptables,具體來說分為下面四種場景:
• kube-proxy啟動引數中帶有--masquerade-all=true,即所有流量作源地址轉換。
• kube-proxy啟動引數中指定CIDR。
• 支援LoadBalancer型別的service。
• 支援NodePort型別的service。
但是IPVS模式不會像iptables模式,建立太多iptables規則。所以我們引入了ipset來減少iptables規則。以下是IPVS模式維護的ipset表:
ipset名稱 | set成員 | 功能 |
---|---|---|
KUBE-CLUSTER-IP | 所有Service IP + port | 如果啟動引數中加了masquerade-all=true或clusterCIDR,用來做masquerade |
KUBE-LOOP-BACK | 所有Service IP + port + IP | 針對hairpin問題做masquerade |
KUBE-EXTERNAL-IP | Service External IP + port | 對方問外部IP的流量做masquerade |
KUBE-LOAD-BALANCER | lb型service的ingress IP + port | 對訪問lb型別service的流量做masquerade |
KUBE-LOAD-BALANCER-LOCAL | 規定了externalTrafficPolicy=local的lb型的service的ingress IP + port | 接收規定了externalTrafficPolicy=local的lb型service |
KUBE-LOAD-BALANCER-FW | 規定了loadBalancerSourceRanges的lb型的service的ingress IP + port | 針對規定了loadBalancerSourceRanges的lb型service,用於過濾流量 |
KUBE-LOAD-BALANCER-SOURCE-CIDR | lb型的service的ingress IP + port + source CIDR | 針對規定了loadBalancerSourceRanges的lb型service,用於過濾流量 |
KUBE-NODE-PORT-TCP | NodePort型Service TCP port | 對訪問NodePort(TCP)的流量作masquerade |
KUBE-NODE-PORT-LOCAL-TCP | 規定了externalTrafficPolicy=local的NodePort型Service TCP port | 接收規定了externalTrafficPolicy=local的NodePort型service |
KUBE-NODE-PORT-UDP | NodePort型Service UDP port | 對訪問NodePort(UDP)的流量作masquerade |
KUBE-NODE-PORT-LOCAL-UDP | 規定了externalTrafficPolicy=local的NodePort型Service UDP port | 接收規定了externalTrafficPolicy=local的NodePort型service |
通常來說,對於IPVS模式的kube-proxy,無論有多少pod/service,iptables的規則數都是固定的。
e)使用基於IPVS的kube-proxy
目前,local-up-cluster指令碼,GCE部署叢集指令碼,kubeadm都已經支援通過環境變數KUBE_PROXY_MODE=ipvs自動部署IPVS模式的叢集。
另外可以通過在kube-proxy的啟動引數中新增--proxy=mode=ipvs啟動IPVS模式的kube-proxy,不過需要保證事先載入了IPVS依賴的核心模組。
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
nf_conntrack_ipv4
最後,在kubernetes之前的版本中,需要通過設定特性開關SupportIPVSProxyMode來使用IPVS。在kubernetes v1.10版本中,特性開關SupportIPVSProxyMode預設開啟,在1.11版本中該特性開關已經被移除。但是如果您使用kubernetes 1.10之前的版本,需要通過--feature-gates=SupportIPVSProxyMode=true開啟SupportIPVSProxyMode才能正常使用IPVS