Kubernetes網絡三部曲之二~Service網絡

前言

在上一篇《Kubernetes網絡三部曲~Pod網絡》中,波波講解了K8s的4層網絡中的第1層Pod網絡。有了Pod網絡,K8s集羣內的所有Pods在邏輯上都可以看作在一個平面網絡內,可以正常IP尋址和互通。但是Pod僅僅是K8s雲平臺中的虛擬機抽象,最終,我們需要在K8s集羣中運行的是應用或者說服務(Service),而一個Service背後一般由多個Pods組成集羣,這時候就引入了服務發現(Service Discovery)和負載均衡(Load Balancing)等問題,這就是第2層Service網絡要解決的問題,也是本文我要展開分析的問題。

在這裏插入圖片描述

Service網絡概念模型

我們假定第1層Pod網絡已經存在,下圖是K8s的第2層Service網絡的簡化概念模型:

在這裏插入圖片描述

我們假定在K8s集羣中部署了一個Account-App應用,這個應用由4個Pod(虛擬機)組成集羣一起提供服務,每一個Pod都有自己的PodIP和端口。我們再假定集羣內還部署了其它應用,這些應用中有些是Account-App的消費方,也就說有Client Pod要訪問Account-App的Pod集羣。這個時候自然引入了兩個問題:

  1. 服務發現(Service Discovery): Client Pod如何發現定位Account-App集羣中Pod的IP?況且Account-App集羣中Pod的IP是有可能會變的(英文叫ephemeral),這種變化包括預期的,比如Account-App重新發布,或者非預期的,例如Account-App集羣中有Pod掛了,K8s對Account-App進行重新調度部署。
  2. 負載均衡(Load Balancing):Client Pod如何以某種負載均衡策略去訪問Account-App集羣中的不同Pod實例?以實現負載分攤和HA高可用。

實際上,K8s通過在Client和Account-App的Pod集羣之間引入一層Account-Serivce抽象,來解決上述問題:

  1. 服務發現:Account-Service提供統一的ClusterIP來解決服務發現問題,Client只需通過ClusterIP就可以訪問Account-App的Pod集羣,不需要關心集羣中的具體Pod數量和PodIP,即使是PodIP發生變化也會被ClusterIP所屏蔽。注意,這裏的ClusterIP實際是個虛擬IP,也稱Virtual IP(VIP)。
  2. 負載均衡:Account-Service抽象層具有負載均衡的能力,支持以不同策略去訪問Account-App集羣中的不同Pod實例,以實現負載分攤和HA高可用。K8s中默認的負載均衡策略是RoundRobin,也可以定製其它複雜策略。

K8s中爲何要引入Service抽象?背後的原理是什麼?後面我將以技術演進視角來解釋這些問題。

服務發現技術演進

DNS域名服務是一種較老且成熟的標準技術,實際上DNS可以認爲是最早的一種服務發現技術。

在這裏插入圖片描述

在K8s中引入DNS實現服務發現其實並不複雜,實際K8s本身就支持Kube-DNS組件。假設K8s引入DNS做服務發現(如上圖所示),運行時,K8s可以把Account-App的Pod集羣信息(IP+Port等)自動註冊到DNS,Client應用則通過域名查詢DNS發現目標Pod,然後發起調用。這個方案不僅簡單,而且對Client也無侵入(目前幾乎所有的操作系統都自帶DNS客戶端)。但是基於DNS的服務發現也有如下問題:

  1. 不同DNS客戶端實現功能有差異,有些客戶端每次調用都會去查詢DNS服務,造成不必要的開銷,而有些客戶端則會緩存DNS信息,默認超時時間較長,當目標PodIP發生變化時(在容器雲環境中是常態),存在緩存刷新不及時,會導致訪問Pod失效。
  2. DNS客戶端實現的負載均衡策略一般都比較簡單,大都是RoundRobin,有些則不支持負載均衡調用。

考慮到上述不同DNS客戶端實現的差異,不在K8s控制範圍內,所以K8s沒有直接採用DNS技術做服務發現。注意,實際K8s是引入Kube-DNS支持通過域名訪問服務的,不過這是建立在CusterIP/Service網絡之上,這個我後面會展開。

另外一種較新的服務發現技術,是引入Service Registry+Client配合實現,在當下微服務時代,這是一個比較流行的做法。目前主流的產品,如Netflix開源的Eureka + Ribbon,HashiCorp開源的Consul,還有阿里新開源Nacos等,都是這個方案的典型代表。

在這裏插入圖片描述

在K8s中引入Service Registry實現服務發現也不復雜,K8s自身帶分佈式存儲etcd就可以實現Service Registry。假設K8s引入Service Registry做服務發現(如上圖所示),運行時K8s可以把Account-App和Pod集羣信息(IP + Port等)自動註冊到Service Registry,Client應用則通過Service Registry查詢發現目標Pod,然後發起調用。這個方案也不復雜,而且客戶端可以實現靈活的負載均衡策略,但是需要引入客戶端配合,對客戶應用有侵入性,所以K8s也沒有直接採用這種方案。

K8s雖然沒有直接採用上述方案,但是它的Service網絡實現是在上面兩種技術的基礎上擴展演進出來的。它融合了上述方案的優點,同時解決了上述方案的不足,下節我會詳細剖析K8s的Service網絡的實現原理。

K8s的Service網絡原理

前面提到,K8s的服務發現機制是在上節講的Service Registry + DNS基礎上發展演進出來的,下圖展示K8s服務發現的簡化原理:

在這裏插入圖片描述

在K8s平臺的每個Worker節點上,都部署有兩個組件,一個叫Kubelet,另外一個叫Kube-Proxy,這兩個組件+Master是K8s實現服務註冊和發現的關鍵。下面我們看下簡化的服務註冊發現流程。

  • 首先,在服務Pod實例發佈時(可以對應K8s發佈中的Kind: Deployment),Kubelet會負責啓動Pod實例,啓動完成後,Kubelet會把服務的PodIP列表彙報註冊到Master節點。
  • 其次,通過服務Service的發佈(對應K8s發佈中的Kind: Service),K8s會爲服務分配ClusterIP,相關信息也記錄在Master上。
  • 第三,在服務發現階段,Kube-Proxy會監聽Master並發現服務ClusterIP和PodIP列表映射關係,並且修改本地的linux iptables轉發規則,指示iptables在接收到目標爲某個ClusterIP請求時,進行負載均衡並轉發到對應的PodIP上。
  • 運行時,當有消費者Pod需要訪問某個目標服務實例的時候,它通過ClusterIP發起調用,這個ClusterIP會被本地iptables機制截獲,然後通過負載均衡,轉發到目標服務Pod實例上。

實際消費者Pod也並不直接調服務的ClusterIP,而是先調用服務名,因爲ClusterIP也會變(例如針對TEST/UAT/PROD等不同環境的發佈,ClusterIP會不同),只有服務名一般不變。爲了屏蔽ClusterIP的變化,K8s在每個Worker節點上還引入了一個KubeDNS組件,它也監聽Master並發現服務名和ClusterIP之間映射關係,這樣, 消費者Pod通過KubeDNS可以間接發現服務的ClusterIP。

注意,K8s的服務發現機制和目前微服務主流的服務發現機制(如Eureka + Ribbon)總體原理類似,但是也有顯著區別(這些區別主要體現在客戶端):

  1. 首先,兩者都是採用客戶端代理(Proxy)機制。和Ribbon一樣,K8s的代理轉發和負載均衡也是在客戶端實現的,但Ribbon是以Lib庫的形式嵌入在客戶應用中的,對客戶應用有侵入性,而K8s的Kube-Proxy是獨立的,每個Worker節點上有一個,它對客戶應用無侵入。K8s的做法類似ServiceMesh中的邊車(sidecar)做法。
  2. 第二,Ribbon的代理轉發是穿透的,而K8s中的代理轉發是iptables轉發,雖然K8s中有Kube-Proxy,但它只是負責服務發現和修改iptables(或ipvs)規則,實際請求是不穿透Kube-Proxy的。注意早期K8s中的Kube-Proxy代理是穿透的,考慮到有性能損耗和單點問題,後續的版本就改成不穿透了。
  3. 第三,Ribbon實現服務名到服務實例IP地址的映射,它只有一層映射。而K8s中有兩層映射,Kube-Proxy實現ClusterIP->PodIP的映射,Kube-DNS實現ServiceName->ClusterIP的映射。

個人認爲,對比目前微服務主流的服務發現機制,K8s的服務發現機制抽象得更好,它通過ClusterIP統一屏蔽服務發現和負載均衡,一個服務一個ClusterIP,這個模型和傳統的IP網絡模型更貼近和易於理解。ClusterIP也是一個IP,但這個IP後面跟的不是一個服務實例,而是一個服務集羣,所以叫集羣ClusterIP。同時,它對客戶應用無侵入,且不穿透沒有額外性能損耗。

總結

  1. K8s的Service網絡構建於Pod網絡之上,它主要目的是解決服務發現(Service Discovery)和負載均衡(Load Balancing)問題。
  2. K8s通過一個ServiceName+ClusterIP統一屏蔽服務發現和負載均衡,底層技術是在DNS+Service Registry基礎上發展演進出來。
  3. K8s的服務發現和負載均衡是在客戶端通過Kube-Proxy + iptables轉發實現,它對應用無侵入,且不穿透Proxy,沒有額外性能損耗。
  4. K8s服務發現機制,可以認爲是現代微服務發現機制和傳統Linux內核機制的優雅結合。

有了Service抽象,K8s中部署的應用都可以通過一個抽象的ClusterIP進行尋址訪問,並且消費方不需要關心這個ClusterIP後面究竟有多少個Pod實例,它們的PodIP是什麼,會不會變化,如何以負載均衡方式去訪問等問題。但是,K8s的Service網絡只是一個集羣內可見的內部網絡,集羣外部是看不到Service網絡的,也無法直接訪問。而我們發佈應用,有些是需要暴露出去,要讓外網甚至公網能夠訪問的,這樣才能對外提供服務。K8s如何將內部服務暴露出去?這個是波波在下一篇《Kubernetes網絡三部曲~外部接入網絡》要展開的問題,敬請期待。

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