華為雲在 K8S 大規模場景下的 Service 性能優化實踐
來自專欄 KubernetesMeetup 社區
講師:王澤鋒 / 華為雲 Kubernetes 開源負責人
編輯:夏天
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 引入所要解決的問題。
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 暴露的埠不必和容器埠一致。
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 出去。
Iptables 實現流量轉發與負載均衡
我們知道,Iptables 做防火牆是專業的,那麼它是如何做流量轉發、負載均衡甚至會話保持的呢?如下圖所示:
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 實現存在的問題
Iptables 做負載均衡的問題
那麼 Iptables 做負載均衡主要有什麼缺陷呢?起初我們只是分析了原理,後來在大規模場景下實測,發現問題其實非常明顯。
- 首先是時延,匹配時延和規則更新時延。我們從剛剛的例子就能看出,每個 Kubernetes Service 的虛 IP 都會在 kube-services 下對應一條鏈。Iptables 的規則匹配是線性的,匹配的時間複雜度是 O(N)。規則更新是非增量式的,哪怕增加/刪除一條規則,也是整體修改 Netfilter 規則表。
- 其次是可擴展性。我們知道當系統中的 Iptables 數量很大時,更新會非常慢。同時因為全量提交的過程中做了保護,所以會出現 kernel lock,這時只能等待。
- 最後是可用性。服務擴容/縮容時,Iptables 規則的刷新會導致連接斷開,服務不可用。
Iptables 規則匹配時延
上圖說明了 Service 訪問時延隨著規則數的增加而增長。但其實也還能接受,因為時延最高也就 8000us(8ms),這說明真正的性能瓶頸並不在這裡。
Iptables 規則更新時延
那麼 Iptables 的規則更新,究竟慢在哪裡呢
首先,Iptables 的規則更新是全量更新,即使 --no--flush 也不行(--no--flush 只保證 iptables-restore 時不刪除舊的規則鏈)。
再者,kube-proxy 會周期性的刷新 Iptables 狀態:先 iptables-save 拷貝系統 Iptables 狀態,然後再更新部分規則,最後再通過 iptables-restore 寫入到內核。當規則數到達一定程度時,這個過程就會變得非常緩慢。
出現如此高時延的原因有很多,在不同的內核版本下也有一定的差異。另外,時延還和系統當前內存使用量密切相關。因為 Iptables 會整體更新 Netfilter 的規則表,而一下子分配較大的內核內存(>128MB)就會出現較大的時延。
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 負載均衡
什麼是 IPVS
- 傳輸層 Load Balancer,LVS 負載均衡器的實現;
- 同樣基於 Netfilter,但使用的是 hash 表;
- 支持 TCP, UDP,SCTP 協議,IPV4,IPV6;
- 支持多種負載均衡策略,如 rr, wrr, lc, wlc, sh,dh, lblc…
- 支持會話保持, persistent connection 調度演算法。
IPVS 的三種轉發模式
IPVS 有三種轉發模式,分別是:DR,隧道和 NAT。
● DR 模式工作在 L2,使用的 MAC 地址,速度最快。請求報文經過 IPVS director,轉發給後端伺服器,響應報文直接回給客戶端。缺點是不支持埠映射,於是這種模式就很可惜地 PASS 掉了。
● 隧道模式,使用 IP 包封裝 IP 包。後端伺服器接收到隧道包後,首先會拆掉封裝的 IP 地址頭,然後響應報文也會直接回給客戶端。IP 模式同樣不支持埠映射,於是這種模式也被 PASS 掉了。
● NAT 模式支持埠映射,與前面兩種模式不同的是,NAT 模式要求回程報文經過 IPVS 的 director。內核原生版本 IPVS 只做 DNAT,不做 SNAT。
使用 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
Iptables vs. IPVS 規則增加時延
通過觀察上圖很容易發現:
- 增加 Iptables 規則的時延,隨著規則數的增加呈「指數」級上升;
- 當集群中的 Service 達到 2 萬個時,新增規則的時延從 50us 變成了 5 小時;
- 而增加 IPVS 規則的時延始終保持在 100us 以內,幾乎不受規則基數影響。這中間的微小差異甚至可以認為是系統誤差。
- Iptables vs. IPVS 網路帶寬
這是我們用 iperf 實測得到兩種模式下的網路帶寬。可以看到 Iptables 模式下第一個 Service 和最後一個 Service 的帶寬有差異。最後一個 Service 帶寬明顯小於第一個,而且隨著 Service 基數的上升,差異越來越明顯。
而 IPVS 模式下,整體帶寬表現高於 Iptables。當集群中的 Service 數量達到 2.5 萬時,Iptables 模式下的帶寬已基本為零,而 IPVS 模式的服務依然能夠保持在先前一半左右的水平,提供正常訪問。
Iptables vs. IPVS CPU/內存消耗
很明顯,IPVS 在 CPU/內存兩個維度的指標都要遠遠低於 Iptables。
特性社區狀態
這個特性從 1.8 版本引入 Alpha,到 1.9 版本發布 Beta,修復了大部分的問題,目前已經比較穩定,強烈推薦大家使用。另外這個特性目前主要是我們華為雲 K8S 開源團隊在維護,大家在使用中如果發現問題,歡迎反映到社區,或者我們這邊。謝謝大家!
王澤鋒/華為雲 Kubernetes 開源負責人
多年電信領域系統軟體開發和性能調優經驗,對深度報文解析、協議識別頗有研究。華為雲 PaaS 服務團隊核心成員,專註於 PaaS 產品和容器開源社區,目前負責華為雲 K8S 開源團隊在社區貢獻的整體工作。
推薦閱讀:
※OpenStack juno版 安裝配置——第六章(Neutron)
※對象存儲OSS控制台—圖片處理
※新睿雲-讓雲服務觸手可及
※純手工搭建k8s集群-序
※分散式系統設計:PART I 單點模式
TAG:Kubernetes | 容器 | 雲計算 |