k8s 理解Service工作原理


什么是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。


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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM