淺談Kubernetes Service負載均衡實現機制

 王希剛 360雲計算php

女主宣言
html

Kubernetes Serivce是一組具備相同label Pod集合的抽象(能夠簡單的理解爲集羣內的LB),集羣內外的各個服務能夠經過Service進行互相通訊。可是Service的類型有多種,每種類型的Service適合怎樣的場景以及kube-proxy是如何實現Service負載均衡的將是本文討論的重點。前端

PS:豐富的一線技術、多元化的表現形式,盡在「360雲計算」,點關注哦!node

1linux

Service和kube-proxy在kubernetes集羣中的工做原理nginx

在介紹Service和kube-proxy以前,先紹下它們在Kubernetes集羣中所起到的做用。git

圖片

讓咱們分析下上面這張圖:github

1. 運行在每一個Node節點的kube-proxy會實時的watch Services和 Endpoints對象。算法

當用戶在kubernetes集羣中建立了含有label的Service以後,同時會在集羣中建立出一個同名的Endpoints對象,用於存儲該Service下的Pod IP. 它們的關係以下圖所示:後端

圖片

2.每一個運行在Node節點的kube-proxy感知到Services和Endpoints的變化以後,會在各自的Node節點設置相關的iptables或IPVS規則,用於以後用戶經過Service的ClusterIP去訪問該Service下的服務。

3.當kube-proxy把須要的規則設置完成以後,用戶即可以在集羣內的Node或客戶端Pod上經過ClusterIP通過iptables或IPVS設置的規則進行路由和轉發,最終將客戶端請求發送到真實的後端Pod。

對於kube-proxy如何設置Iptables和IPVS策略後續會講。接下來先介紹下每種不一樣類型的Service的使用場景。

2

Service 類型

當前Kubernetes Service支持以下幾種類型,並在介紹類型的同時即可以瞭解每種類型的Service的具體使用場景。

ClusterIP

ClusterIP類型的Service是Kubernetes集羣默認的Service, 它只能用於集羣內部通訊。不能用於外部通訊。

ClusterIP Service類型的結構以下圖所示:


image.png

NodePort

若是你想要在集羣外訪問集羣內部的服務,你可使用這種類型的Service。NodePort類型的Service會在集羣內部的全部Node節點打開一個指定的端口。以後全部的流量直接發送到這個端口以後,就會轉發的Service去對真實的服務進行訪問。

NodePort Service類型的結構以下圖所示:


image.png

LoadBalancer

LoadBalancer類型的Service一般和雲廠商的LB結合一塊兒使用,用於將集羣內部的服務暴露到外網,雲廠商的LoadBalancer會給用戶分配一個IP,以後經過該IP的流量會轉發到你的Service.

LoadBalancer Service類型的結構以下圖所示:image.png

Ingress

Ingress 其實不是Service的一個類型,可是它能夠做用於多個Service,做爲集羣內部服務的入口。
Ingress 能作許多不一樣的事,好比根據不一樣的路由,將請求轉發到不一樣的Service上等等。

Ingress 的結構以下圖所示:image.png

3

Service服務發現

Service當前支持兩種類型的服務發現機制,一種是經過環境變量,另外一種是經過DNS。在這兩種方案中,建議使用後者。

環境變量

當一個Pod建立完成以後,kubelet會在該Pod中註冊該集羣已經建立的全部Service相關的環境變量,可是須要注意的是,Service建立以前的全部的POD是不會註冊該Service的環境變量的,因此在平時使用時,建議經過DNS的方式進行Service之間的服務發現。

DNS

能夠在集羣中部署CoreDNS服務(舊版本的kubernetes集羣使用的是kubeDNS), 來達到集羣內部的Pod經過DNS的方式進行集羣內部各個服務之間的通信。

當前kubernetes集羣默認使用CoreDNS做爲默認的DNS服務,主要緣由是CoreDNS是基於Plugin的方式進行擴展的簡單,靈活。而且不徹底被Kubernetes所捆綁。

4

Service負載均衡

在本文的最初已經介紹了service和kube-proxy在集羣中是如何配合來達到服務的負載均衡。kube-proxy在其中起到了關鍵性的做用,kube-proxy做爲一個控制器,做爲k8s和Linux kernel Netfilter交互的一個樞紐。監聽kubernetes集羣Services和Endpoints對象的變化,並根據kube-proxy不一樣的模式(iptables or ipvs), 對內核設置不一樣的規則,來實現路由轉發。接下來分別介紹下kube-proxy基於Iptables和IPVS兩種模式實現Service負載均衡的工做機制。

Iptables 實現負載均衡

Iptables是一個用戶態程序,經過配置Netfilter規則來構建Linux內核防火牆。Netfilter是Linux內核的網絡包管理框架,提供了一整套的hook函數的管理機制,使得諸如數據包過濾,網絡地址轉換(NAT)和基於協議類型的鏈接跟蹤成爲了可能,Netfilter在內核中的位置以下圖所示。


image.png

接下來介紹kube-proxy是如何利用Iptables作負載均衡的。數據包在Iptables中的匹配流程以下圖所示:


image.png

在Iptables模式下,kube-proxy經過在目標node節點上的Iptables中的NAT表的PREROUTIN和POSTROUTING鏈中建立一系列的自定義鏈(這些自定義鏈主要是」KUBE-SERVICE」鏈, 「KUBE-POSTROUTING」鏈,每一個服務對應的」KUBE-SVC-XXXXXX」鏈和」KUBE-SEP-XXXX」鏈),而後經過這些自定義鏈對流經到該Node的數據包作DNAT和SNAT操做從而實現路由,負載均衡和地址轉化,以下圖所示:


image.png

kube-proxy中,客戶端的請求數據包在Iptables規則中具體的匹配過程爲:

1.PREROUTING鏈或者OUTPUT鏈(集羣內的Pod經過clusterIP訪問Service時通過OUTPUT鏈, 而當集羣外主機經過NodePort方式訪問Service時,經過PREROUTING鏈,兩個鏈都會跳轉到KUBE-SERVICE鏈)image.png

image.png


2.KUBE-SERVICES鏈(每個Service所暴露的每個端口在KUBE-SERVICES鏈中都會對應一條相應的規則,當Service的數量達到必定規模時,KUBE-SERVICES鏈中的規則的數據將會很是的大,而Iptables在進行查找匹配時是線性查找,這將耗費很長時間,時間複雜度O(n))image.png

3.KUBE-SVC-XXXXX鏈 (在KUBE-SVC-XXXXX鏈中(後面那串 hash 值由 Service 的虛 IP 生成),會以必定的機率匹配下面的某一條規則執行,經過statistic模塊爲每一個後端設置權重,已實現負載均衡的目的,每一個KUBE-SEP-XXXXX鏈表明Service後面的一個具體的Pod(後面那串 hash 值由後端 Pod 實際 IP 生成),這樣便實現了負載均衡的目的)image.png

4.KUBE-SEP-XXXX鏈 (經過DNAT,將數據包的目的IP修改成服務端的Pod IP)image.png5.POSTROUTING鏈

image.png

6.KUBE_POSTROUTING鏈 (對標記的數據包作SNAT)

經過上面的這個設置便實現了基於Iptables實現了負載均衡。可是Iptbles作負載均衡存在一些問題:

  • 規則線性匹配時延:
    KUBE-SERVICES鏈掛了一長串KUBE-SVC-*鏈,訪問每一個service,要遍歷每條鏈直到匹配,時間複雜度O(N)

  • 規則更新時延:
    非增量式,須要先iptables-save拷貝Iptables狀態,而後再更新部分規則,最後再經過 iptables-restore寫入到內核。當規則數到達必定程度時,這個過程就會變得很是緩慢。

  • 可擴展性:
    當系統存在大量的Iptables規則鏈時,增長/刪除規則會出現kernel lock,這時只能等待。

  • 可用性: 服務擴容/縮容時, Iptables規則的刷新會致使鏈接斷開,服務不可用。

爲了解決Iptables當前存在的這些問題,華爲開源團隊的同窗爲社區貢獻了IPVS模式,接下來介紹下IPVS是如何實現負載均衡的。

IPVS 實現負載均衡

IPVS是LVS項目的一部分,是一款運行在Linux kernel當中的4層負載均衡器,性能異常優秀。使用調優後的內核,能夠輕鬆處理每秒10萬次以上的轉發請求。

IPVS具備如下特色:

  • 傳輸層Load Balancer, LVS負載均衡器的實現。

  • 與Iptables一樣基於Netfilter, 可是使用的是hash表。

  • 支持TCP, UDP, SCTP協議,支持IPV4, IPV6。

  • 支持多種負載均衡策略:

    • rr: round-robin

    • lc: least connection

    • dh: destination hashing

    • sh: source hashing

    • sed: shortest expected delay

    • nq: never queue

  • 支持會話保持

LVS的工做原理以下圖所示:


image.png

1.當客戶端的請求到達負載均衡器的內核空間時,首先會達到PREROUTING鏈。
2.當內核發現請求的數據包的目的地址是本機時,將數據包送往INPUT鏈。
3.當數據包達到INPUT鏈時, 首先會被IPVS檢查,若是數據包裏面的目的地址及端口沒有在IPVS規則裏面,則這條數據包將被放行至用戶空間。
4.若是數據包裏面的目的地址和端口在IPVS規則裏面,那麼這條數據報文的目的地址會被修改成經過負責均衡算法選好的後後端服務器(DNAT),併發往POSROUTING鏈。
5.最後經由POSTROUTING鏈發日後端的服務器。

LVS主要由三種工做模式, 分別是NAT, DR, Tunnel模式,而在kube-proxy中,IPVS工做在NAT模式,因此下面主要對NAT模式進行介紹:

仍是分析上面的那張圖:

1. 客戶端將請求發往前端的負載均衡器,請求報文源地址是CIP(客戶端IP), 目的地址是VIP(負載均衡器前端地址)

2. 負載均衡器收到報文以後,發現請求的是在規則裏面存在的地址,那麼它將請求的報文的目的地址改成後端服務器的RIP地址,並將報文根據響應的負責均衡策略發送出去

3. 報文發送到Real Server後,因爲報文的目的地址是本身,全部會響應請求,並將響應的報文返回給LVS

4. 而後LVS將此報文的源地址修改本機的IP地址併發送給客戶端


介紹完基本的工做原理以後,下面咱們看看如何在kube-proxy中使用IPVS模式進行負載均衡。首先須要在啓動kube-proxy的參數中指定以下參數:

--proxy-mode=ipvs //將kube-proxy的模式設置爲IPVS--ipvs-scheduler=rr //設置ipvs的負載均衡算法,默認是rr--ipvs-min-sync-period=5s // 刷新IPVS規則的最小時間間隔--ipvs-sync-period=30s // 刷新IPVS規則的最大時間間隔

設置完這些參數以後,重啓啓動kube-proxy服務便可。當建立ClusterIP類型的Service時,IPVS模式的kube-proxy會作下面幾件事兒:

  • 建立虛擬網卡,默認是kube-ipvs0

  • 綁定service IP地址到虛擬網卡kube-ipvs0

image.png

  • 爲每個Service IP地址建立IPVS虛擬服務器

image.png

同時IPVS還支持會話保持功能,經過在建立Srevice對象時,指定service.spec.sessionAffinity參數爲ClusterIP默認是None 和 指定service.spec.sessionAffinityConfig.clientIP.timeoutSeconds參數爲須要的時間,默認是10800s。

下面是一個建立Service,指定會話保持的一個具體的例子:

kind: ServiceapiVersion: v1metadata:  name: nginx-servicespec:  type: ClusterIP  selector:    app: nginx  sessionAffinity: ClientIP  sessionAffinityConfig:    clientIP:      timeoutSeconds: 50  ports:  -name: http   protocol: TCP   port: 80   targetPort: 80

以後經過ipvsadm -L便可查看會話保持功能是否設置成功。這樣kube-proxy即可以經過IPVS的模式實現負載均衡。image.png

5

總結

kube-proxy在使用iptables和ipvs實現對Service的負載均衡,可是經過iptables的實現方式,因爲Iptables自己的特性,新增規則,更新規則是非增量式的,須要先iptables-save而後在內存中更新規則,在內核中修改規則,在iptables-restore,而且Iptables在進行規則查找匹配時是線性查找,這將耗費很長時間,時間複雜度O(n)。而使用IPVS的實現方式,其鏈接過程的時間複雜度是O(1)。基本就是說鏈接的效率與集羣Service的數量是無關的。所以隨着集羣內部Service的不斷增長,IPVS的性能優點就體現出來了。

相關文章

  • https://zhuanlan.zhihu.com/p/37230013

  • https://zhuanlan.zhihu.com/p/39909011

  • https://www.projectcalico.org/comparing-kube-proxy-modes-iptables-or-ipvs/

  • https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0

  • https://wiki.archlinux.org/index.php/Iptables_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)

  • https://kubernetes.io/docs/concepts/services-networking/service/

  • http://blog.chinaunix.net/uid-23069658-id-3160506.html

  • https://tonydeng.github.io/sdn-handbook/linux/loadbalance.html

  • https://www.josedomingo.org/pledin/2018/11/recursos-de-kubernetes-services/

相關文章
相關標籤/搜索