kubernetes service 原理解析

為什么需要 service

在 kubernetes 中,當創建帶有多個副本的 deployment 時,kubernetes 會創建出多個 pod,此時即一個服務后端有多個容器,那么在 kubernetes 中負載均衡怎么做,容器漂移后 ip 也會發生變化,如何做服務發現以及會話保持?這就是 service 的作用,service 是一組具有相同 label pod 集合的抽象,集群內外的各個服務可以通過 service 進行互相通信,當創建一個 service 對象時也會對應創建一個 endpoint 對象,endpoint 是用來做容器發現的,service 只是將多個 pod 進行關聯,實際的路由轉發都是由 kubernetes 中的 kube-proxy 組件來實現,因此,service 必須結合 kube-proxy 使用,kube-proxy 組件可以運行在 kubernetes 集群中的每一個節點上也可以只運行在單獨的幾個節點上,其會根據 service 和 endpoints 的變動來改變節點上 iptables 或者 ipvs 中保存的路由規則。

service 的工作原理

service 原理

endpoints controller 是負責生成和維護所有 endpoints 對象的控制器,監聽 service 和對應 pod 的變化,更新對應 service 的 endpoints 對象。當用戶創建 service 后 endpoints controller 會監聽 pod 的狀態,當 pod 處于 running 且準備就緒時,endpoints controller 會將 pod ip 記錄到 endpoints 對象中,因此,service 的容器發現是通過 endpoints 來實現的。而 kube-proxy 會監聽 service 和 endpoints 的更新并調用其代理模塊在主機上刷新路由轉發規則。

service 的負載均衡

上文已經提到 service 實際的路由轉發都是由 kube-proxy 組件來實現的,service 僅以一種 VIP(ClusterIP) 的形式存在,kube-proxy 主要實現了集群內部從 pod 到 service 和集群外部從 nodePort 到 service 的訪問,kube-proxy 的路由轉發規則是通過其后端的代理模塊實現的,kube-proxy 的代理模塊目前有四種實現方案,userspace、iptables、ipvs、kernelspace,其發展歷程如下所示:

  • kubernetes v1.0:services 僅是一個“4層”代理,代理模塊只有 userspace
  • kubernetes v1.1:Ingress API 出現,其代理“7層”服務,并且增加了 iptables 代理模塊
  • kubernetes v1.2:iptables 成為默認代理模式
  • kubernetes v1.8:引入 ipvs 代理模塊
  • kubernetes v1.9:ipvs 代理模塊成為 beta 版本
  • kubernetes v1.11:ipvs 代理模式 GA

在每種模式下都有自己的負載均衡策略,下文會詳解介紹。

userspace 模式

在 userspace 模式下,訪問服務的請求到達節點后首先進入內核 iptables,然后回到用戶空間,由 kube-proxy 轉發到后端的 pod,這樣流量從用戶空間進出內核帶來的性能損耗是不可接受的,所以也就有了 iptables 模式。

為什么 userspace 模式要建立 iptables 規則,因為 kube-proxy 監聽的端口在用戶空間,這個端口不是服務的訪問端口也不是服務的 nodePort,因此需要一層 iptables 把訪問服務的連接重定向給 kube-proxy 服務。

iptables 模式

iptables 模式是目前默認的代理方式,基于 netfilter 實現。當客戶端請求 service 的 ClusterIP 時,根據 iptables 規則路由到各 pod 上,iptables 使用 DNAT 來完成轉發,其采用了隨機數實現負載均衡。

iptables 模式與 userspace 模式最大的區別在于,iptables 模塊使用 DNAT 模塊實現了 service 入口地址到 pod 實際地址的轉換,免去了一次內核態到用戶態的切換,另一個與 userspace 代理模式不同的是,如果 iptables 代理最初選擇的那個 pod 沒有響應,它不會自動重試其他 pod。

iptables 模式最主要的問題是在 service 數量大的時候會產生太多的 iptables 規則,使用非增量式更新會引入一定的時延,大規模情況下有明顯的性能問題。

ipvs 模式

當集群規模比較大時,iptables 規則刷新會非常慢,難以支持大規模集群,因其底層路由表的實現是鏈表,對路由規則的增刪改查都要涉及遍歷一次鏈表,ipvs 的問世正是解決此問題的,ipvs 是 LVS 的負載均衡模塊,與 iptables 比較像的是,ipvs 的實現雖然也基于 netfilter 的鉤子函數,但是它卻使用哈希表作為底層的數據結構并且工作在內核態,也就是說 ipvs 在重定向流量和同步代理規則有著更好的性能,幾乎允許無限的規模擴張。

ipvs 支持三種負載均衡模式:DR模式(Direct Routing)、NAT 模式(Network Address Translation)、Tunneling(也稱 ipip 模式)。三種模式中只有 NAT 支持端口映射,所以 ipvs 使用 NAT 模式。linux 內核原生的 ipvs 只支持 DNAT,當在數據包過濾,SNAT 和支持 NodePort 類型的服務這幾個場景中ipvs 還是會使用 iptables。

此外,ipvs 也支持更多的負載均衡算法,例如:

  • rr:round-robin/輪詢
  • lc:least connection/最少連接
  • dh:destination hashing/目標哈希
  • sh:source hashing/源哈希
  • sed:shortest expected delay/預計延遲時間最短
  • nq:never queue/從不排隊

userspace、iptables、ipvs 三種模式中默認的負載均衡策略都是通過 round-robin 算法來選擇后端 pod 的,在 service 中可以通過設置 service.spec.sessionAffinity 的值實現基于客戶端 ip 的會話親和性,service.spec.sessionAffinity 的值默認為"None",可以設置為 "ClientIP",此外也可以使用 service.spec.sessionAffinityConfig.clientIP.timeoutSeconds 設置會話保持時間。kernelspace 主要是在 windows 下使用的,本文暫且不談。

service 的類型

service 支持的類型也就是 kubernetes 中服務暴露的方式,默認有四種 ClusterIP、NodePort、LoadBalancer、ExternelName,此外還有 Ingress,下面會詳細介紹每種類型 service 的具體使用場景。

ClusterIP

ClusterIP 類型的 service 是 kubernetes 集群默認的服務暴露方式,它只能用于集群內部通信,可以被各 pod 訪問,其訪問方式為:

pod ---> ClusterIP:ServicePort --> (iptables)DNAT --> PodIP:containePort

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

image
NodePort

如果你想要在集群外訪問集群內部的服務,可以使用這種類型的 service,NodePort 類型的 service 會在集群內部署了 kube-proxy 的節點打開一個指定的端口,之后所有的流量直接發送到這個端口,然后會被轉發到 service 后端真實的服務進行訪問。Nodeport 構建在 ClusterIP 上,其訪問鏈路如下所示:

client ---> NodeIP:NodePort ---> ClusterIP:ServicePort ---> (iptables)DNAT ---> PodIP:containePort

其對應具體的 iptables 規則會在后文進行講解。

NodePort service 類型的結構如下圖所示:

image
LoadBalancer

LoadBalancer 類型的 service 通常和云廠商的 LB 結合一起使用,用于將集群內部的服務暴露到外網,云廠商的 LoadBalancer 會給用戶分配一個 IP,之后通過該 IP 的流量會轉發到你的 service 上。

LoadBalancer service 類型的結構如下圖所示:

image
ExternelName

通過 CNAME 將 service 與 externalName 的值(比如:foo.bar.example.com)映射起來,這種方式用的比較少。

Ingress

Ingress 其實不是 service 的一個類型,但是它可以作用于多個 service,被稱為 service 的 service,作為集群內部服務的入口,Ingress 作用在七層,可以根據不同的 url,將請求轉發到不同的 service 上。

Ingress 的結構如下圖所示:

image

service 的服務發現

雖然 service 的 endpoints 解決了容器發現問題,但不提前知道 service 的 Cluster IP,怎么發現 service 服務呢?service 當前支持兩種類型的服務發現機制,一種是通過環境變量,另一種是通過 DNS。在這兩種方案中,建議使用后者。

環境變量

當一個 pod 創建完成之后,kubelet 會在該 pod 中注冊該集群已經創建的所有 service 相關的環境變量,但是需要注意的是,在 service 創建之前的所有 pod 是不會注冊該環境變量的,所以在平時使用時,建議通過 DNS 的方式進行 service 之間的服務發現。

DNS

可以在集群中部署 CoreDNS 服務(舊版本的 kubernetes 群使用的是 kubeDNS), 來達到集群內部的 pod 通過DNS 的方式進行集群內部各個服務之間的通訊。

當前 kubernetes 集群默認使用 CoreDNS 作為默認的 DNS 服務,主要原因是 CoreDNS 是基于 Plugin 的方式進行擴展的,簡單,靈活,并且不完全被Kubernetes所捆綁。

Service 的使用

ClusterIP 方式
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
spec:
  clusterIP: 10.105.146.177
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: my-nginx
  sessionAffinity: None
  type: ClusterIP
NodePort 方式
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
spec:
  ports:
  - nodePort: 30090
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: my-nginx
  sessionAffinity: None
  type: NodePort

其中 nodeport 字段表示通過 nodeport 方式訪問的端口,port 表示通過 service 方式訪問的端口,targetPort 表示 container port。

Headless service(就是沒有 Cluster IP 的 service )

當不需要負載均衡以及單獨的 ClusterIP 時,可以通過指定 spec.clusterIP 的值為 None 來創建 Headless service,它會給一個集群內部的每個成員提供一個唯一的 DNS 域名來作為每個成員的網絡標識,集群內部成員之間使用域名通信。

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
spec:
  clusterIP: None
  ports:
  - nodePort: 30090
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: my-nginx

總結

本文主要講了 kubernetes 中 service 的原理、實現以及使用方式,service 目前主要有 5 種服務暴露方式,service 的容器發現是通過 endpoints 來實現的,其服務發現主要是通過 DNS 實現的,其負載均衡以及流量轉發是通過 kube-proxy 實現的。在后面的文章我會繼續介紹 kube-proxy 的設計及實現。

參考:

https://www.cnblogs.com/xzkzzz/p/9559362.html

https://xigang.github.io/2019/07/21/kubernetes-service/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
禁止轉載,如需轉載請通過簡信或評論聯系作者。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,743評論 2 370