本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

基於 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=trueclusterCIDR 指定時進行偽裝
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-TCPNodePort 型別服務的 TCP 埠用於偽裝到 NodePort(TCP) 的資料包
KUBE-NODE-PORT-LOCAL-TCP具有 externalTrafficPolicy=local 的 NodePort 型別服務的 TCP 埠接受到具有 externalTrafficPolicy=local 的 NodePort 服務的資料包
KUBE-NODE-PORT-UDPNodePort 型別服務的 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 故事