以個人經驗來說,理解K8S集羣服務的概念,是比較不容易的一件事情。尤爲是當咱們基於似是而非的理解,去排查服務相關問題的時候,會很是不順利。前端
這體如今,對於新手來講,ping不通服務的IP地址這樣基礎的問題,都很難理解;而就算對經驗很豐富的工程師來講,看懂服務相關的iptables配置,也是至關的挑戰。算法
今天這邊文章,我來深刻解釋一下K8S集羣服務的原理與實現,便於你們理解。編程
概念上來說,K8S集羣的服務,其實就是負載均衡、或反向代理。這跟阿里雲的負載均衡產品,有不少相似的地方。和負載均衡同樣,服務有它的IP地址以及前端端口;服務後邊會掛載多個容器組Pod做爲其「後端服務器」,這些「後端服務器」有本身的IP以及監聽端口。後端
當這樣的負載均衡和後端的架構,與K8S集羣結合的時候,咱們能夠想到的最直觀的實現方式,就是集羣中某一個節點專門作負載均衡(相似LVS)的角色,而其餘節點則用來負載後端容器組。服務器
這樣的實現方法,有一個巨大的缺陷,就是單點問題。K8S集羣是Google多年來自動化運維實踐的結晶,這樣的實現顯然與其智能運維的哲學相背離的。網絡
邊車模式(Sidecar)是微服務領域的核心概念。邊車模式,換一句通俗一點的說法,就是自帶通訊員。熟悉服務網格的同窗確定對這個很熟悉了。可是可能比較少人注意到,其實K8S集羣原始服務的實現,也是基於Sidecar模式的。架構
在K8S集羣中,服務的實現,其實是爲每個集羣節點上,部署了一個反向代理Sidecar。而全部對集羣服務的訪問,都會被節點上的反向代理轉換成對服務後端容器組的訪問。基本上來講,節點和這些Sidecar的關係以下圖所示。負載均衡
前邊兩節,咱們看到了,K8S集羣的服務,本質上是負載均衡,即反向代理;同時咱們知道了,在實際實現中,這個反向代理,並非部署在集羣某一個節點上,而是做爲集羣節點的邊車,部署在每一個節點上的。框架
在這裏把服務照進反向代理這個現實的,是K8S集羣的一個控制器,即kube-proxy。關於K8S集羣控制器的原理,請參考我另一篇關於控制器的文章。簡單來講,kube-proxy做爲部署在集羣節點上的控制器,它們經過集羣API Server監聽着集羣狀態變化。當有新的服務被建立的時候,kube-proxy則會把集羣服務的狀態、屬性,翻譯成反向代理的配置。運維
那剩下的問題,就是反向代理,即上圖中Proxy的實現。
K8S集羣節點實現服務反向代理的方法,目前主要有三種,即userspace、iptables以及ipvs。今天咱們只深刻分析iptables的方式,底層網絡基於阿里雲flannel集羣網絡。
過濾器框架
如今,咱們來設想一種場景。咱們有一個屋子。這個屋子有一個入水管和出水管。從入水管進入的水,是不能直接飲用的,由於有雜質。而咱們指望,從出水管流出的水,能夠直接飲用。爲了達到目的,咱們切開水管,在中間加一個雜質過濾器。
過了幾天,咱們的需求變了,咱們不止要求從屋子裏流出來的水能夠直接飲用,咱們還但願水是熱水。因此咱們不得再也不在水管上增長一個切口,而後增長一個加熱器。
很明顯,這種切開水管,增長新功能的方式是很醜陋的。由於需求可能隨時會變,咱們甚至很難保證,在通過一年半載以後,這跟水管還能找獲得能夠被切開的地方。
因此咱們須要從新設計。首先咱們不能隨便切開水管,因此咱們要把水管的切口固定下來。以上邊的場景爲例,咱們確保水管只能有一個切口位置。其次,咱們抽象出對水的兩種處理方式:物理變化和化學變化。
基於以上的設計,若是咱們須要過濾雜質,就能夠在化學變化這個功能模塊裏增長一條過濾雜質的規則;若是咱們須要增長溫度的話,就能夠在物理變化這個功能模塊裏增長一條加熱的規則。
以上的過濾器框架,顯然比切水管的方式,要優秀不少。設計這個框架,咱們主要作了兩件事情,一個是固定水管切口位置,另一個是抽象出兩種水處理方式。
理解這兩件事情以後,咱們能夠來看下iptables,或者更準確的說法,netfilter的工做原理。netfilter實際上就是一個過濾器框架。netfilter在網絡包收發及路由的管道上,一共切了5個口,分別是PREROUTING,FORWARD,POSTROUTING,INPUT以及OUTPUT;同時netfilter定義了包括nat、filter在內的若干個網絡包處理方式。
須要注意的是,routing和forwarding很大程度上增長了以上netfilter的複雜程度,若是咱們不考慮routing和forwarding,那麼netfilter會變得更咱們的水質過濾器框架同樣簡單。
節點網絡大圖
如今咱們看一下K8S集羣節點的網絡全貌。橫向來看,節點上的網絡環境,被分割成不一樣的網絡命名空間,包括主機網絡命名空間和Pod網絡命名空間;縱向來看,每一個網絡命名空間包括完整的網絡棧,從應用到協議棧,再到網絡設備。
在網絡設備這一層,咱們經過cni0虛擬網橋,組建出系統內部的一個虛擬局域網。Pod網絡經過veth對鏈接到這個虛擬局域網內。cni0虛擬局域網經過主機路由以及網口eth0與外部通訊。
在網絡協議棧這一層,咱們能夠經過編程netfilter過濾器框架,來實現集羣節點的反向代理。
實現反向代理,歸根結底,就是作DNAT,即把發送給集羣服務IP和端口的數據包,修改爲發給具體容器組的IP和端口。
參考netfilter過濾器框架的圖,咱們知道,在netfilter裏,能夠經過在PREROUTING,OUTPUT以及POSTROUGING三個位置加入NAT規則,來改變數據包的源地址或目的地址。
由於這裏須要作的是DNAT,即改變目的地址,這樣的修改,必須在路由(ROUTING)以前發生以保證數據包能夠被路由正確處理,因此實現反向代理的規則,須要被加到PREROUTING和OUTPUT兩個位置。
其中,PREOURTING的規則,用來處理從Pod訪問服務的流量。數據包從Pod網絡veth發送到cni0以後,進入主機協議棧,首先會通過netfilter PREROUTING來作處理,因此發給服務的數據包,會在這個位置作DNAT。通過DNAT處理以後,數據包的目的地址變成另一個Pod的地址,從而通過主機路由,轉發到eth0,發送給正確的集羣節點。
而添加在OUTPUT這個位置的DNAT規則,則用來處理從主機網絡發給服務的數據包,原理也是相似,即通過路由以前,修改目的地址,以方便路由轉發。
升級過濾器框架
在過濾器框架一節,咱們看到netfilter是一個過濾器框架。netfilter在數據「管到」上切了5個口,分別在這5個口上,作一些數據包處理工做。雖然固定切口位置以及網絡包處理方式分類已經極大的優化了過濾器框架,可是有一個關鍵的問題,就是咱們仍是得在管道上作修改以知足新的功能。換句話說,這個框架沒有作到管道和過濾功能二者的完全解耦。
爲了實現管道和過濾功能二者的解耦,netfilter用了表這個概念。表就是netfilter的過濾中心,其核心功能是過濾方式的分類(表),以及每種過濾方式中,過濾規則的組織(鏈)。
把過濾功能和管道解耦以後,全部對數據包的處理,都變成了對錶的配置。而管道上的5個切口,僅僅變成了流量的出入口,負責把流量發送到過濾中心,並把處理以後的流量沿着管道繼續傳送下去。
如上圖,在表中,netfilter把規則組織成爲鏈。表中有針對每一個管道切口的默認鏈,也有咱們本身加入的自定義鏈。默認鏈是數據的入口,默認鏈能夠經過跳轉到自定義鏈來完成一些複雜的功能。這裏容許增長自定義鏈的好處是顯然的。爲了完成一個複雜過濾功能,好比實現K8S集羣節點的反向代理,咱們可使用自定義鏈來模塊化咱們規則。
用自定義鏈實現服務的反向代理
集羣服務的反向代理,實際上就是利用自定義鏈,模塊化地實現了數據包的DNAT轉換。KUBE-SERVICE是整個反向代理的入口鏈,其對應全部服務的總入口;KUBE-SVC-XXXX鏈是具體某一個服務的入口鏈,KUBE-SERVICE鏈會根據服務IP,跳轉到具體服務的KUBE-SVC-XXXX鏈;而KUBE-SEP-XXXX鏈表明着某一個具體Pod的地址和端口,即endpoint,具體服務鏈KUBE-SVC-XXXX會以必定算法(通常是隨機),跳轉到endpoint鏈。
而如前文中提到的,由於這裏須要作的是DNAT,即改變目的地址,這樣的修改,必須在路由以前發生以保證數據包能夠被路由正確處理。因此KUBE-SERVICE會被PREROUTING和OUTPUT兩個默認鏈所調用。
經過這篇文章,你們應該對K8S集羣服務的概念以及實現,有了更深層次的認識。咱們基本上須要把握三個要點。1、服務本質上是負載均衡;2、服務負載均衡的實現採用了與服務網格相似的Sidecar的模式,而不是LVS類型的獨佔模式;3、kube-proxy本質上是一個集羣控制器。除此以外,咱們思考了過濾器框架的設計,並在此基礎上,理解使用iptables實現的服務負載均衡的原理。
本文做者:阿里雲支持與服務
本文爲雲棲社區原創內容,未經容許不得轉載。