歡迎光臨
每天分享高質量文章

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

贊(1)

分享創造快樂