k8s 理解Service工作原理

iqsing發表於2022-01-08

什麼是service?


Service是將執行在一組 Pods 上的應用程式公開為網路服務的抽象方法。

簡單來說K8s提供了service物件來訪問pod。我們在《k8s網路模型與叢集通訊》中也說過k8s叢集中的每一個Pod(最小排程單位)都有自己的IP地址,都有IP了訪問起來還不簡單?

其實不然,一是k8s中pod不是永續性的,摧毀重建將獲得新的IP,客戶端通過變更IP來訪問顯然不合理。二是需要多個副本間的負載均衡。所以此時Service就冒出來了。

那麼今天我們就來學習一下service,看看它是如何工作的。

Service與endpoints、pod


當我們通過API建立/修改service物件時,endpoints控制器的informer機制 Listen到service物件,然後根據service的配置的選擇器建立一個endpoints物件,此物件將pod的IP、容器埠做記錄並儲存到etcd,這樣service只要看一下自己名下的endpoints就可以知道所對應pod資訊了。

且看下圖:

image-20220108162349356

我們在例項來看一下,先稀疏平常建立一個Deployment

#deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-demo
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: mirrorgooglecontainers/serve_hostname
        ports:
        - containerPort: 9376
          protocol: TCP

serve_hostname是k8s官方提供的debug映象,一個返回hostname的web server。這樣我們建立出了標籤為app=nginx的三個pod,當我們訪問pod的9376時會返回hostname。

接著是service清單,我們在service中指定了選擇器為app=nginx

#service.yaml
apiVersion: v1
kind: Service
metadata:
  name: service-demo
spec:
  selector:
    app: nginx
  ports:
  - name: default
    protocol: TCP
    #service port
    port: 80
    #container port
    targetPort: 9376

這樣我們獲得不變的CLUSTER-IP 10.96.148.206的service

image-20220107233553740

如果pod啟動成功,則自動建立和service同名的endpoints記錄下了三個pod的資料

image-20220108004254486

service中選擇器未指定標籤時endpoints需要手動建立對映到service的網路地址如下:

apiVersion: v1
kind: Endpoints
metadata:
  name: service
subsets:
  - addresses:
      - ip: 10.96.148.206
    ports:
      - port: 9376

此時當我們不斷訪問service的CLUSTER-IP時:

# curl 10.96.148.206:80
deployment-demo-7d94cbb55f-8mmxb
# curl 10.96.148.206:80
deployment-demo-7d94cbb55f-674ns
# curl 10.96.148.206:80
deployment-demo-7d94cbb55f-lfrm8
# curl 10.96.148.206:80
deployment-demo-7d94cbb55f-8mmxb

可以看到此時請求已被路由到後端pod,返回hostname,並且負載均衡方式是Round Robin即輪詢模式。

通過上面介紹我們好像摸到了Service其中的門道,接下來是流量到底如何通過service進入pod的?

Service與kube-proxy


涉及到流量當然是kube-proxy登場了!

kube-proxy 是叢集中每個節點上執行的網路代理, 實現 Kubernetes 服務(Service) 概念的一部分。用於處理單個主機子網劃分並向外部世界公開服務。它跨叢集中的各種隔離網路將請求轉發到正確的 pod/容器。

kube-proxy 維護節點上的網路規則。這些網路規則允許從叢集內部或外部的網路會話與 Pod 進行網路通訊。

如下圖所示:

image-20220108143313157

kube-proxy 通過 Informer知道了Service、endpoints物件的建立,然後把service身上的CLUSTER-IP 和埠已經端點資訊拿出來,建立iptable NAT規則做轉發或通過ipvs模組建立VS伺服器,這樣經過CLUSTER-IP的流量都被轉發到後端pod。

iptables模式

我們先檢視nat表的OUTPUT鏈,存在kube-proxy建立的KUBE-SERVICE鏈

iptables -nvL OUTPUT -t nat

image-20220108152150242

在KUBE-SERVICES鏈中有一條目的地為10.96.148.206即CLUSTER-IP地址跳轉到KUBE-SVC-EJUV4ZBKPDWOZNF4

iptables -nvL KUBE-SERVICES -t nat |grep service-demo

image-20220108153015622

接著是檢視這條鏈,以1/3的概率跳轉到其中一條

iptables -nvL KUBE-SVC-EJUV4ZBKPDWOZNF4 -t nat

image-20220108153839227

最後KUBE-SEP-BTFJGISFGMEBGVUF鏈終於找到了DNAT規則

iptables -nvL KUBE-SEP-BTFJGISFGMEBGVUF -t nat

image-20220108154014449

即將請求通過DNAT傳送到地址100.101.184.61:9376也就是我們其中一個Pod。

IPVS模式

與iptalbes模式相比,IPVS模式工作在核心態,在同步代理規則時具有更好的效能,同時提高網路吞吐量為大型叢集提供了更好的可擴充套件性。

IPVS 模式在工作時,當我們建立了前面的 Service 之後,kube-proxy 首先會在宿主機上建立一個虛擬網路卡kube-ipvs0,併為它分配 Service VIP 作為 IP 地址,如圖

image-20220108161050509

接著kube-proxy通過Linux的IPVS模組為這個 IP 地址新增三個 IPVS 虛擬主機,並設定這三個虛擬主機之間使用輪詢模式 來作為負載均衡策略。

通過ipvsadm檢視

ipvsadm -ln |grep -C 5 10.96.148.206

image-20220108161800771

可以看到虛擬server的IP即是Pod的地址,這樣流量即向了目的地Pod。

以上我們先認識了Service這個API物件,接著講到了service與endpoints和pod的關聯,然後是service與kube-proxy的關係,以及kube-proxy的兩種模式如何通過service的IP建立iptables、IPVS規則將流量轉發到Pod。


希望小作文對你有些許幫助,如果內容有誤請指正。

您可以隨意轉載、修改、釋出本文,無需經過本人同意。【公號:容器雲實踐】

相關文章