kubernetes service 介紹

一、問題

首先,我們思考這樣一個問題:

訪問k8s集群中的pod, 客戶端需要知道pod地址,需要感知pod的狀態。那如何獲取各個pod的地址?若某一node上的pod故障,客戶端如何感知?

二、k8s service

什么是service

是發現后端pod服務;

是為一組具有相同功能的容器應用提供一個統一的入口地址;

是將請求進行負載分發到后端的各個容器應用上的控制器。

對service的訪問來源

訪問service的請求來源有兩種:k8s集群內部的程序(Pod)和 k8s集群外部的程序。

service類型

采用微服務架構時,作為服務所有者,除了實現業務邏輯以外,還需要考慮如何把服務發布到k8s集群或者集群外部,使這些服務能夠被k8s集群內的應用、其他k8s集群的應用以及外部應用使用。因此k8s提供了靈活的服務發布方式,用戶可以通過ServiceType來指定如何來發布服務,類型有以下幾種:

● ClusterIP:提供一個集群內部的虛擬IP以供Pod訪問(service默認類型)。

     service 結構如下: 
image

● NodePort:在每個Node上打開一個端口以供外部訪問

Kubernetes將會在每個Node上打開一個端口并且每個Node的端口都是一樣的,通過:NodePort的方式Kubernetes集群外部的程序可以訪問Service。

    service 定義如下: 

● LoadBalancer:通過外部的負載均衡器來訪問

service selector

service通過selector和pod建立關聯。

k8s會根據service關聯到pod的podIP信息組合成一個endpoint。

若service定義中沒有selector字段,service被創建時,endpoint controller不會自動創建endpoint。

service負載分發策略

service 負載分發策略有兩種:

RoundRobin:輪詢模式,即輪詢將請求轉發到后端的各個pod上(默認模式);

SessionAffinity:基于客戶端IP地址進行會話保持的模式,第一次客戶端訪問后端某個pod,之后的請求都轉發到這個pod上。

三、服務發現

k8s服務發現方式

雖然Service解決了Pod的服務發現問題,但不提前知道Service的IP,怎么發現service服務呢?

k8s提供了兩種方式進行服務發現:

● 環境變量: 當創建一個Pod的時候,kubelet會在該Pod中注入集群內所有Service的相關環境變量。需要注意的是,要想一個Pod中注入某個Service的環境變量,則必須Service要先比該Pod創建。這一點,幾乎使得這種方式進行服務發現不可用。

  例如:

  一個ServiceName為redis-master的Service,對應的ClusterIP:Port為10.0.0.11:6379,則其在pod中對應的環境變量為:

  REDIS_MASTER_SERVICE_HOST=10.0.0.11  REDIS_MASTER_SERVICE_PORT=6379  REDIS_MASTER_PORT=tcp://10.0.0.11:6379  REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379  REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
  REDIS_MASTER_PORT_6379_TCP_PORT=6379  REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11 

● DNS:可以通過cluster add-on的方式輕松的創建KubeDNS來對集群內的Service進行服務發現————這也是k8s官方強烈推薦的方式。為了讓Pod中的容器可以使用kube-dns來解析域名,k8s會修改容器的/etc/resolv.conf配置。

k8s服務發現原理

● endpoint

endpoint是k8s集群中的一個資源對象,存儲在etcd中,用來記錄一個service對應的所有pod的訪問地址。

service配置selector,endpoint controller才會自動創建對應的endpoint對象;否則,不會生成endpoint對象.

例如,k8s集群中創建一個名為k8s-classic-1113-d3的service,就會生成一個同名的endpoint對象,如下圖所示。其中ENDPOINTS就是service關聯的pod的ip地址和端口。  
image

● endpoint controller

endpoint controller是k8s集群控制器的其中一個組件,其功能如下:

負責生成和維護所有endpoint對象的控制器

負責監聽service和對應pod的變化

監聽到service被刪除,則刪除和該service同名的endpoint對象

監聽到新的service被創建,則根據新建service信息獲取相關pod列表,然后創建對應endpoint對象

監聽到service被更新,則根據更新后的service信息獲取相關pod列表,然后更新對應endpoint對象

監聽到pod事件,則更新對應的service的endpoint對象,將podIp記錄到endpoint中

四、負載均衡

kube-proxy

kube-proxy負責service的實現,即實現了k8s內部從pod到service和外部從node port到service的訪問。

kube-proxy采用iptables的方式配置負載均衡,基于iptables的kube-proxy的主要職責包括兩大塊:一塊是偵聽service更新事件,并更新service相關的iptables規則,一塊是偵聽endpoint更新事件,更新endpoint相關的iptables規則(如 KUBE-SVC-鏈中的規則),然后將包請求轉入endpoint對應的Pod。如果某個service尚沒有Pod創建,那么針對此service的請求將會被drop掉。

kube-proxy的架構如下:

image

kube-proxy iptables

kube-proxy監聽service和endpoint的變化,將需要新增的規則添加到iptables中。

kube-proxy只是作為controller,而不是server,真正服務的是內核的netfilter,體現在用戶態則是iptables。

kube-proxy的iptables方式也支持RoundRobin(默認模式)和SessionAffinity負載分發策略。

kubernetes只操作了filter和nat表。

Filter:在該表中,一個基本原則是只過濾數據包而不修改他們。filter table的優勢是小而快,可以hook到input,output和forward。這意味著針對任何給定的數據包,只有可能有一個地方可以過濾它。

NAT:此表的主要作用是在PREROUTING和POSTROUNTING的鉤子中,修改目標地址和原地址。與filter表稍有不同的是,該表中只有新連接的第一個包會被修改,修改的結果會自動apply到同一連接的后續包中。

kube-proxy對iptables的鏈進行了擴充,自定義了KUBE-SERVICES,KUBE-NODEPORTS,KUBE-POSTROUTING,KUBE-MARK-MASQ和KUBE-MARK-DROP五個鏈,并主要通過為KUBE-SERVICES chain增加rule來配制traffic routing 規則。我們可以看下自定義的這幾個鏈的作用:

KUBE-MARK-DROP - [0:0] /*對于未能匹配到跳轉規則的traffic set mark 0x8000,有此標記的數據包會在filter表drop掉*/KUBE-MARK-MASQ - [0:0] /*對于符合條件的包 set mark 0x4000, 有此標記的數據包會在KUBE-POSTROUTING chain中統一做MASQUERADE*/KUBE-NODEPORTS - [0:0] /*針對通過nodeport訪問的package做的操作*/KUBE-POSTROUTING - [0:0]KUBE-SERVICES - [0:0] /*操作跳轉規則的主要chain*/ 

同時,kube-proxy也為默認的prerouting、output和postrouting chain增加規則,使得數據包可以跳轉至k8s自定義的chain,規則如下:

 -A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES

 -A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES

 -A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING 

如果service類型為nodePort,(從LB轉發至node的數據包均屬此類)那么將KUBE-NODEPORTS鏈中每個目的地址是NODE節點端口的數據包導入這個“KUBE-SVC-”鏈:

 -A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS

 -A KUBE-NODEPORTS -p tcp -m comment --comment "default/es1:http" -m tcp --dport 32135 -j KUBE-MARK-MASQ

 -A KUBE-NODEPORTS -p tcp -m comment --comment "default/es1:http" -m tcp --dport 32135 -j KUBE-SVC-LAS23QA33HXV7KBL 

Iptables chain支持嵌套并因為依據不同的匹配條件可支持多種分支,比較難用標準的流程圖來體現調用關系,建單抽象為下圖:

image

舉個例子,在k8s集群中創建了一個名為my-service的服務,其中:

service vip:10.11.97.177

對應的后端兩副本pod ip:10.244.1.10、10.244.2.10

容器端口為:80

服務端口為:80

則kube-proxy為該service生成的iptables規則主要有以下幾條:

 -A KUBE-SERVICES -d 10.11.97.177/32 -p tcp -m comment --comment "default/my-service: cluster IP" -m tcp --dport 80 -j KUBE-SVC-BEPXDJBUHFCSYIC3

 -A KUBE-SVC-BEPXDJBUHFCSYIC3 -m comment --comment “default/my-service:” -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-U4UWLP4OR3LOJBXU  //50%的概率輪詢后端pod
 -A KUBE-SVC-BEPXDJBUHFCSYIC3 -m comment --comment "default/my-service:" -j KUBE-SEP-QHRWSLKOO5YUPI7O

 -A KUBE-SEP-U4UWLP4OR3LOJBXU -s 10.244.1.10/32 -m comment --comment "default/my-service:" -j KUBE-MARK-MASQ
 -A KUBE-SEP-U4UWLP4OR3LOJBXU -p tcp -m comment --comment "default/my-service:" -m tcp -j DNAT --to-destination 10.244.1.10:80
 -A KUBE-SEP-QHRWSLKOO5YUPI7O -s 10.244.2.10/32 -m comment --comment "default/my-service:" -j KUBE-MARK-MASQ
 -A KUBE-SEP-QHRWSLKOO5YUPI7O -p tcp -m comment --comment "default/my-service:" -m tcp -j DNAT --to-destination 10.244.2.10:80 

kube-proxy通過循環的方式創建后端endpoint的轉發,概率是通過probability后的1.0/float64(n-i)計算出來的,譬如有兩個的場景,那么將會是一個0.5和1也就是第一個是50%概率第二個是100%概率,如果是三個的話類似,33%、50%、100%。

kube-proxy iptables的性能缺陷

k8s集群創建大規模服務時,會產生很多iptables規則,非增量式更新會引入一定的時延。iptables規則成倍增長,也會導致路由延遲帶來訪問延遲。大規模場景下,k8s 控制器和負載均衡都面臨這挑戰。例如,若集群中有N個節點,每個節點每秒有M個pod被創建,則控制器每秒需要創建NM個endpoints,需要增加的iptables則是NM的數倍。以下是k8s不同規模下訪問service的時延:

image
image

從上圖中可以看出,當集群中服務數量增長時,因為 IPTables天生不是被設計用來作為 LB 來使用的,IPTables 規則則會成倍增長,這樣帶來的路由延遲會導致的服務訪問延遲增加,直到無法忍受。

目前有以下幾種解決方案,但各有不足:

● 將endpoint對象拆分成多個對像

優點:減小了單個endpoint大小

缺點:增加了對象的數量和請求量 

● 使用集中式負載均衡器

優點:減少了跟apiserver的連接和請求數

缺點:服務路由中又增加了一跳,并且需要集中式LB有很高的性能和高可用性 

● 定期任務,批量創建/更新endpoint

優點:減少了每秒的處理數

缺點:在定期任務執行的間隔時間內,端對端延遲明顯增加

五、K8s 1.8 新特性——ipvs

ipvs與iptables的性能差異

隨著服務的數量增長,IPTables 規則則會成倍增長,這樣帶來的問題是路由延遲帶來的服務訪問延遲,同時添加或刪除一條規則也有較大延遲。不同規模下,kube-proxy添加一條規則所需時間如下所示:

image

可以看出當集群中服務數量達到5千個時,路由延遲成倍增加。添加 IPTables 規則的延遲,有多種產生的原因,如:

添加規則不是增量的,而是先把當前所有規則都拷貝出來,再做修改然后再把修改后的規則保存回去,這樣一個過程的結果就是 IPTables 在更新一條規則時會把 IPTables 鎖住,這樣的后果在服務數量達到一定量級的時候,性能基本不可接受:在有5千個服務(4萬條規則)時,添加一條規則耗時11分鐘;在右2萬個服務(16萬條規則)時,添加一條規則需要5個小時。

這樣的延遲時間,對生產環境是不可以的,那該性能問題有哪些解決方案呢?從根本上解決的話,可以使用 “IP Virtual Server”(IPVS )來替換當前 kube-proxy 中的 IPTables 實現,這樣能帶來顯著的性能提升以及更智能的負載均衡功能如支持權重、支持重試等等。

那什么是 “IP Virtual Server”(IPVS ) 呢?

ipvs 簡介

k8s 1.8 版本中,社區 SIG Network 增強了 NetworkPolicy API,以支持 Pod 出口流量策略,以及允許策略規則匹配源或目標 CIDR 的匹配條件。這兩個增強特性都被設計為 beta 版本。 SIG Network 還專注于改進 kube-proxy,除了當前的 iptables 和 userspace 模式,kube-proxy 還引入了一個 alpha 版本的 IPVS 模式。

作為 Linux Virtual Server(LVS) 項目的一部分,IPVS 是建立于 Netfilter之上的高效四層負載均衡器,支持 TCP 和 UDP 協議,支持3種負載均衡模式:NAT、直接路由(通過 MAC 重寫實現二層路由)和IP 隧道。ipvs(IP Virtual Server)安裝在LVS(Linux Virtual Server)集群作為負載均衡主節點上,通過虛擬出一個IP地址和端口對外提供服務。客戶端通過訪問虛擬IP+端口訪問該虛擬服務,之后訪問請求由負載均衡器調度到后端真實服務器上。

ipvs相當于工作在netfilter中的input鏈。

image

配置方法:IPVS 負載均衡模式在 kube-proxy 處于測試階段還未正式發布,完全兼容當前 Kubernetes 的行為,通過修改 kube-proxy 啟動參數,在 mode=userspace 和 mode=iptables 的基礎上,增加 mode=IPVS 即可啟用該功能。

ipvs轉發模式

● DR模式(Direct Routing)

特點:

<1> 數據包在LB轉發過程中,源/目的IP和端口都不會變化。LB只修改數據包的MAC地址為RS的MAC地址

<2> RS須在環回網卡上綁定LB的虛擬機服務IP

<3> RS處理完請求后,響應包直接回給客戶端,不再經過LB

缺點:

<1> LB和RS必須位于同一子網

image

● NAT模式(Network Address Translation

特點:

<1> LB會修改數據包地址:對于請求包,進行DNAT;對于響應包,進行SNAT

<2> 需要將RS的默認網關地址配置為LB的虛擬IP地址

缺點:

<1> LB和RS必須位于同一子網,且客戶端和LB不能位于同一子網

image

● FULLNAT模式

特點:

<1> LB會對請求包和響應包都做SNAT+DNAT

<2> LB和RS對于組網結構沒有要求

<3> LB和RS必須位于同一子網,且客戶端和LB不能位于同一子網

image

● 三種轉發模式性能從高到低:DR > NAT >FULLNAT

ipvs 負載均衡器常用調度算法

● 輪詢(Round Robin)

LB認為集群內每臺RS都是相同的,會輪流進行調度分發。從數據統計上看,RR模式是調度最均衡的。

● 加權輪詢(Weighted Round Robin)

LB會根據RS上配置的權重,將消息按權重比分發到不同的RS上。可以給性能更好的RS節點配置更高的權重,提升集群整體的性能。

● 最少連接調度

LB會根據和集群內每臺RS的連接數統計情況,將消息調度到連接數最少的RS節點上。在長連接業務場景下,LC算法對于系統整體負載均衡的情況較好;但是在短連接業務場景下,由于連接會迅速釋放,可能會導致消息每次都調度到同一個RS節點,造成嚴重的負載不均衡。

● 加權最少連接調度

最小連接數算法的加權版。

● 原地址哈希,鎖定請求的用戶

根據請求的源IP,作為散列鍵(Hash Key)從靜態分配的散列表中找出對應的服務器。若該服務器是可用的且未超載,將請求發送到該服務器。

原文首發于網易云。

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

推薦閱讀更多精彩內容