【華爲雲技術分享】在 K8S 大規模場景下 Service 性能如何優化?

摘要:Kubernetes 原生的 Service 負載均衡基於 Iptables 實現,其規則鏈會隨 Service 的數量呈線性增長,在大規模場景下對 Service 性能影響嚴重。本文分享了華爲雲在 Kubernetes service 性能優化方面的探索與實踐。

在企業業務推進過程中,不同業務領域的流量高峯通常都是在不同時間段來臨,而應對這些不同時間到來的業務流量往往都需要大量的額外網絡資源做保障。今天給大家帶來我們在 Kubernetes Service 上的一些優化實踐,這是一個網絡相關的話題。首先,介紹Kubernetes的Service機制。現在 Kubernetes 中 Service 的三種模式,包括原來的 Userspace 和 Iptables,以及後來我們貢獻的 IPVS;第二部分會介紹原來社區是如何使用 Iptables 來實現 Service 負載平衡的;第三部分主要是 Iptables 實現中存在的一些問題;接下來是如何使用 IPVS 來做 Service 的負載實現的;最後是一個對比。

 

Kubernetes的Service機制

先看一下 Kubernetes 裏面的 Service。在用 Kubernetes 之前,當我們有了容器網絡之後,訪問一個應用最直接的做法,就是客戶端直接去訪問一個 Backend Container。這種做法最直觀和容易,同時問題也是顯而易見的。當應用有多個後端容器的時候,怎麼做負載均衡,會話保持怎麼做,某個容器遷了之後 IP 跟着變怎麼辦,還有對應的健康檢查怎麼配,如果想用域名來做訪問入口要怎麼處理……這些其實就是 Kubernetes 的 Service 引入所要解決的問題。

01  Kubernetes Service 與 Endpoints

這張圖表現了 Service 與其它幾個對象的對應關係。首先是 Service,它保存的是服務的訪問入口信息(如 IP、端口),可以簡單理解爲 Kubernetes 內置的一個 LoadBalancer,它的作用就是給多個 Pod 提供負載均衡。

圖中是一個 Replication Controller 部署出來 2 個 pod 所對應的 Service。我們知道 RC 和 pod 的關係是通過 label-selector 來關聯的,service 也是一樣,通過 Selector 來匹配它所要做負載均衡的 Pod。實際上這中間還有一個對象,叫做 Endpoint,爲什麼要有這個對象呢?因爲在實際應用中,一個 pod 被創建,並不代表它馬上就能對外提供服務,而這個 pod 如果將被刪除,或處於其他不良狀態,我們都希望客戶端的請求不被分發到這個無法提供服務的 pod 上。Endpoint 的引入,就是用來映射那些能對外提供服務的 pod。每個 Endpoints 對象的 IP 對應一個 Kubernetes 的內部域名,可以通過這個域名直接訪問到具體的 pod。

再看 Service 和 Endpoint 的定義。這裏注意 Service 有一個 ClusterIP 的屬性字段,可以簡單理解爲是虛 IP。Service 的域名解析通常得到的就是這個 ClusterIP。另外值得注意的是 Service 支持端口映射,即 Service 暴露的端口不必和容器端口一致。

 

02  Service 內部邏輯

剛纔介紹了 Service、Pods 跟 Endpoint 三者的關係,再來看 Service 的內部邏輯。這裏主要看下 Endpoint Controller,它會 watch Service 對象、還有 pod 的變化情況,維護對應的 Endpoint 信息。然後在每一個節點上,KubeProxy 根據 Service 和 Endpoint 來維護本地的路由規則。

實際上,每當一個 Endpoint 發生變化(即 Service 以及它關聯的 Pod 狀態發生變化),Kubeproxy 都會在每個節點上做對應的規則刷新,所以這個其實更像是一個靠近客戶端的負載均衡——一個 Pod 訪問其他服務的 Pod 時,請求在出節點之前,就已經通過本地的路由規則選好了它的目的 Pod。

 

Iptables實現負載均衡

好,我們來看一下 Iptables 模式是怎麼實現的。

Iptables 主要分兩部分,一個是它的命令行工具,在用戶態;然後它也有內核模塊,但本質上還是通過 Netfilter 這個內核模塊來封裝實現的,Iptables 的特點是支持的操作比較多。

這是 IPtables 處理網絡包的一個流程圖,可以看到,每個包進來都會按順序經過幾個點。首先是 PREROUTING,它會判斷接收到的這個請求包,是訪問本地進程還是其他機器的,如果是訪問其他機器的,就要走 FORWARD 這個 chain,然後再會做一次 Routing desicion,確定它要 FORWARD 到哪裏,最後經 POSTROUTING 出去。如果是訪問本地,就會進來到 INPUT 這條線,找到對應要訪問哪個本地請求,然後就在本地處理了。處理完之後,其實會生成一個新的數據包,這個時候又會走 OUTPUT,然後經 POSTROUTING 出去。

 

01  Iptables 實現流量轉發與負載均衡

我們知道,Iptables 做防火牆是專業的,那麼它是如何做流量轉發、負載均衡甚至會話保持的呢?如下圖所示:

02  Iptables 在 Kubernetes 的應用舉例

那麼,在 Kubernetes 裏面是怎麼用 Iptables 來實現負載均衡呢?來看一個實際的例子。在 Kubernetes 中,從VIP到RIP,中間經過的Iptables鏈路包括:PREROUTING/OUTPUT(取決於流量是從本機還是外機過來的)-> KUBE-SERVICES(所有 Kubernetes 自定義鏈的入口)->KUBE-SVC-XXX(後面那串 hash 值由 Service 的虛 IP 生成)->KUBE-SEP->XXX(後面那串 hash 值由後端 Pod 實際 IP 生成)。

 

當前Iptables實現存在的問題

01  Iptables 做負載均衡的問題

那麼 Iptables 做負載均衡主要有什麼缺陷呢?起初我們只是分析了原理,後來在大規模場景下實測,發現問題其實非常明顯。
 

  • 首先是時延,匹配時延和規則更新時延。我們從剛剛的例子就能看出,每個 Kubernetes Service 的虛 IP 都會在 kube-services 下對應一條鏈。Iptables 的規則匹配是線性的,匹配的時間複雜度是 O(N)。規則更新是非增量式的,哪怕增加/刪除一條規則,也是整體修改 Netfilter 規則表。
  • 其次是可擴展性。我們知道當系統中的 Iptables 數量很大時,更新會非常慢。同時因爲全量提交的過程中做了保護,所以會出現 kernel lock,這時只能等待。
  • 最後是可用性。服務擴容/縮容時,Iptables 規則的刷新會導致連接斷開,服務不可用。


02  Iptables 規則匹配時延

上圖說明了 Service 訪問時延隨着規則數的增加而增長。但其實也還能接受,因爲時延最高也就 8000us(8ms),這說明真正的性能瓶頸並不在這裏。

03  Iptables 規則更新時延

那麼 Iptables 的規則更新,究竟慢在哪裏呢

首先,Iptables 的規則更新是全量更新,即使 --no--flush 也不行(--no--flush 只保證 iptables-restore 時不刪除舊的規則鏈)。

再者,kube-proxy 會週期性的刷新 Iptables 狀態:先 iptables-save 拷貝系統 Iptables 狀態,然後再更新部分規則,最後再通過 iptables-restore 寫入到內核。當規則數到達一定程度時,這個過程就會變得非常緩慢。

出現如此高時延的原因有很多,在不同的內核版本下也有一定的差異。另外,時延還和系統當前內存使用量密切相關。因爲 Iptables 會整體更新 Netfilter 的規則表,而一下子分配較大的內核內存(>128MB)就會出現較大的時延。

 

04  Iptables 週期性刷新導致 TPS 抖動

上圖就說明了在高併發的 loadrunner 壓力測試下,kube-proxy 週期性刷新 Iptables 導致後端服務連接斷開,TPS 的週期性波動。

 

K8S Scalability

所以這個就給 Kubernetes 的數據面的性能帶來一個非常大的限制,我們知道社區管理面的規模,其實在去年就已經支持到了 5000 節點,而數據面由於缺乏一個權威的定義,沒有給出規格。

我們在多個場景下評估發現 Service 個數其實很容易達到成千上萬,所以優化還是很有必要的。當時先到的優化方案主要有兩個:

  • 用樹形結構來組織 Iptables 的規則,讓匹配和規則更新過程變成樹的操作,從而優化兩個時延。
  • 使用 IPVS,後面會講它的好處。

 

使用樹形結構組織 Iptables 規則的一個例子如下所示:

在這個例子中,樹根是 16 位地址,根的兩個子節點是 24 位地址,虛 IP 作爲葉子節點,根據不同的網段,分別掛在不同的樹節點下。這樣,規則匹配的時延就從 O(N) 降低到 O(N 的 M 次方根),M 即樹的高度。但這麼做帶來的代價是 Iptables 規則變得更加複雜。
 

IPVS實現Service負載均衡

01  什麼是 IPVS

  • 傳輸層 Load Balancer,LVS 負載均衡器的實現;
  • 同樣基於 Netfilter,但使用的是 hash 表;
  • 支持 TCP, UDP,SCTP 協議,IPV4,IPV6;
  • 支持多種負載均衡策略,如 rr, wrr, lc, wlc, sh,dh, lblc…
  • 支持會話保持, persistent connection 調度算法。

 

02  IPVS 的三種轉發模式

IPVS 有三種轉發模式,分別是:DR,隧道和 NAT。

  • DR 模式工作在 L2,使用的 MAC 地址,速度最快。請求報文經過 IPVS director,轉發給後端服務器,響應報文直接回給客戶端。缺點是不支持端口映射,於是這種模式就很可惜地 PASS 掉了。
  • 隧道模式,使用 IP 包封裝 IP 包。後端服務器接收到隧道包後,首先會拆掉封裝的 IP 地址頭,然後響應報文也會直接回給客戶端。IP 模式同樣不支持端口映射,於是這種模式也被 PASS 掉了。
  • NAT 模式支持端口映射,與前面兩種模式不同的是,NAT 模式要求回程報文經過 IPVS 的 director。內核原生版本 IPVS 只做 DNAT,不做 SNAT。

 

03  使用 IPVS 實現流量轉發

使用 IPVS 做流量轉發只需經過以下幾個簡單的步驟。

  • 綁定 VIP

由於 IPVS 的 DNAT 鉤子掛在 INPUT 鏈上,因此必須要讓內核識別 VIP 是本機的 IP。綁定 VIP 至少有三種方式:

1.創建一塊 dummy 網卡,然後綁定,如下所示。
# ip link add dev dummy0 type dummy # ip addr add 192.168.2.2/32 dev dummy0

2.直接在本地路由表中加上 VIP 這個 IP 地址。
# ip route add to local 192.168.2.2/32 dev eth0proto kernel

3.在本地網卡上增加一個網卡別名。
# ifconfig eth0:1 192.168.2.2netmask255.255.255.255 up

  • 爲這個虛 IP 創建一個 IPVS 的 virtual server

# ipvsadm -A -t 192.168.60.200:80 -s rr -p 600
這上面的例子中,IPVS virtual server 的虛 IP 是 192.168.60.200:80,會話保持時間 600s。

  • 爲這個 IPVS service 創建相應的 real server

# ipvsadm -a -t 192.168.60.200:80 -r 172.17.1.2:80–m
# ipvsadm -a -t 192.168.60.200:80 -r 172.17.2.3:80–m

 

這上面的例子中,爲 192.168.60.200:80 這個 IPVS 的 virtual server 創建了兩個 real server:172.17.1.2:80 和 172.17.2.3:80。

 

Iptables vs. IPVS

01  Iptables vs. IPVS 規則增加時延

通過觀察上圖很容易發現:

  • 增加 Iptables 規則的時延,隨着規則數的增加呈“指數”級上升;
  • 當集羣中的 Service 達到 2 萬個時,新增規則的時延從 50us 變成了 5 小時;
  • 而增加 IPVS 規則的時延始終保持在 100us 以內,幾乎不受規則基數影響。這中間的微小差異甚至可以認爲是系統誤差。

02  Iptables vs. IPVS 網絡帶寬

這是我們用 iperf 實測得到兩種模式下的網絡帶寬。可以看到 Iptables 模式下第一個 Service 和最後一個 Service 的帶寬有差異。最後一個 Service 帶寬明顯小於第一個,而且隨着 Service 基數的上升,差異越來越明顯。

而 IPVS 模式下,整體帶寬表現高於 Iptables。當集羣中的 Service 數量達到 2.5 萬時,Iptables 模式下的帶寬已基本爲零,而 IPVS 模式的服務依然能夠保持在先前一半左右的水平,提供正常訪問。

 

03  Iptables vs. IPVS CPU/內存消耗

很明顯,IPVS 在 CPU/內存兩個維度的指標都要遠遠低於 Iptables。

 

特性社區狀態

這個特性從 1.8 版本引入 Alpha,到 1.9 版本發佈 Beta,修復了大部分的問題,目前已經比較穩定,強烈推薦大家使用。另外這個特性目前主要是我們華爲雲 K8S 開源團隊在維護,大家在使用中如果發現問題,歡迎反映到社區,或者我們這邊。

雲原生時代已經到來,華爲雲通過 Kubernetes 邁出了雲原生基礎設施建設的第一步。雖然當前在實踐中仍然有許多挑戰在等着我們去應對,但相信隨着我們在技術上持續的投入,這些問題會一一得到解決。

 

點擊這裏→瞭解更多精彩內容

 

相關推薦

《跟唐老師學習雲網絡》 - Kubernetes網絡實現

Kubernetes增強型調度器Volcano算法分析

100 個網絡基礎知識普及,看完成半個網絡高手

華爲雲ServiceStage正式加入Spring生態大家族!

3分鐘瞭解ServiceStage 應用智能化運維

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章