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服務發現方式

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地址和埠。

在這裡插入圖片描述

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的架構如下:

在這裡插入圖片描述

kube-proxy iptables

kube-proxy監聽service和endpoint的變化(service建立刪除和修改, pod的擴張與縮小),將需要新增的規則新增到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支援巢狀並因為依據不同的匹配條件可支援多種分支,比較難用標準的流程圖來體現呼叫關係,建單抽象為下圖:

在這裡插入圖片描述

舉個例子,在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%。