1. 程式人生 > 其它 >基於IPVS的叢集內負載均衡深入解讀

基於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