前言

上篇文章中老顧介紹了相關pod、容器、node之間的通信,通過pod的ip進行通信,存在一定的問題。

Kubernetes Pod是有生命週期的,它們可以被創建,也可以被銷燬,然而一旦被銷燬生命就永遠結束。 通過ReplicationController能夠動態地創建和銷燬Pod(例如,需要進行擴縮容,或者執行滾動升級)。 每個 Pod 都會獲取它自己的 IP 地址,可一旦銷燬後,重新創建後,IP地址會產生改變。 這會導致一個問題:在 Kubernetes 集羣中,如果一組 Pod(稱爲 backend)爲其它 Pod (稱爲 frontend)提供服務,一旦backend的Pod重新創建,那麼frontend的Pod該如何發現,並連接到這組 Pod 中的哪些 backend 呢?

Service

Service資源用於爲pod對象提供一個固定、統一的訪問接口及負載均衡的能力,並藉助新一代DNS系統的服務發現功能,解決客戶端發現並訪問容器化應用的問題。

注意:service只是在k8s集羣內部起作用,集羣外部訪問是無效的

實現原理

Service通過關注定義出多個POD對象組合而成的邏輯集合,以及訪問這組POD的策略,Service關聯POD需要標籤選擇器完成,其基於標籤選擇器將一組POD定義成一個邏輯集合,並通過自己的IP地址和端口調度代理請求至後端POD之上。

apiVersion: v1kind: Servicemetadata: name: a-servicespec: selector: app: pod-label ports: - protocol: TCP port: 80 targetPort: 9376

上面的例子服務a-service關聯着label爲【app:pod-label】的pod,這時候另一個服務B可以訪問跟a-service服務綁定的service,service信息是固定的提前告訴B就行了,service通過Label Selector跟a服務的pod綁定,無論a的pod如何變化對b來說都是透明的。

虛擬IP

service對象的IP地址稱爲cluster IP,位於K8S集羣配置指定的專用IP地址範圍內,其是一種虛擬IP地址,其在service對象創建後保持不變,並且能夠被同一集羣中的POD資源訪問,service端口接受客戶端的請求並將其轉發至後端POD中的相應端口,因此,其又被稱爲四層代理,因其工作在TCP/IP層。

一個service對象就是工作節點上的一些iptables或ipvs,用於將到達service對象的IP地址的流量轉發到相應的endpoint對象指定的IP地址和端口上,kube-proxy組件通過api-server持續監控着各個service及其相關的POD對象,並將其創建或變動實時反映到工作節點的iptable或ipvs上

服務代理

k8s羣集中的每個節點都運行一個kube-proxy的組件,kube-proxy其實是一個代理層負責實現service

userspace模式

客戶端訪問ServiceIP(clusterIP)請求會先從用戶空間到內核中的iptables,然後回到用戶空間kube-proxy,kube-proxy負責代理工作。

具體細節:

請求到達service後,其被轉發到內核,經由套接字送往用戶空間的kube-proxy,而後經由kube-proxy送回內核空間,並調度至後端POD,其傳輸方式效率太低。在1.1 版本之前,其是默認的轉發策略。

iptables模式

客戶端訪問ServiceIP(clusterIP)請求會由iptables直接重定向到後端

具體細節:

客戶端IP請求時,直接請求本地內核service ip,根據iptables的規則直接將請求轉發到到各pod上,因爲使用iptable NAT來完成轉發,也存在不可忽視的性能損耗。另外,如果集羣中存在上萬的Service/Endpoint,那麼Node上的iptables rules將會非常龐大,性能還會再打折扣

Kubernetes v1.2之前默認是userspace之後是iptables模式,iptables模式性能和可靠性更好,但是iptables模式依賴健康檢查,在沒有健康檢查的情況下如果一個pod不響應,iptables模式不會切換另一個pod上

ipvs模型

此模型跟蹤API service上的service和endpoints對象的變動,據此來調用netlink接口創建IPVS規則,並確保API server中的變動保持同步,其流量調度策略在IPVS中實現,其餘的在iptables中實現。

ipvs 支持衆多調度算法,如rr、lc、dh、sh、sed和nq 等。

集羣外部訪問

我們如何在集羣外訪問service呢?k8s提供了幾種方式

NodePort

通過每個 Node 上的 IP 和靜態端口(NodePort)暴露服務。NodePort 服務會路由到 ClusterIP 服務,這個 ClusterIP 服務會自動創建。通過請求 NodeIP:Port,可以從集羣的外部訪問一個 NodePort 服務。

這時要訪問這個Service的話,只需要通過訪問

<任何一臺宿主機器的IP>:Port

LoadBalancer

在NodePort基礎上,Kubernetes可以請求底層雲平臺cloud provider 創建一個外部的負載均衡器,並將請求轉發到每個Node作爲後端,進行服務分發。

該模式需要底層雲平臺(例如GCE、AWS)支持。

ExternalName

創建一個dns別名指到service name上,主要是防止service name發生變化,要配合dns插件使用。通過返回 CNAME 和它的值,可以將服務映射到 externalName 字段的內容。

這隻有 Kubernetes 1.7 或更高版本的 kube-dns 才支持

Ingress

上面我們提到幾種方式,但是當集羣服務很多的時候,NodePort方式最大的缺點是會佔用很多集羣機器的端口;LB方式最大的缺點則是每個service一個LB又有點浪費和麻煩,並且需要k8s之外的支持; 而ingress則只需要一個NodePort或者一個LB就可以滿足所有service對外服務的需求。工作機制大致可以用下圖表示:

Ingress是基於service實現7層路由轉發能力的

相關文章