1. <em id="vzzs9"></em>
      <tbody id="vzzs9"></tbody>

    2. <span id="vzzs9"></span>
      <progress id="vzzs9"></progress>
      首頁 運維干貨Kubernetes學習筆記之kube-proxy service實現原理

      Kubernetes學習筆記之kube-proxy service實現原理

      運維派隸屬馬哥教育旗下專業運維社區,是國內成立最早的IT運維技術社區,歡迎關注公眾號:yunweipai
      領取學習更多免費Linux云計算、Python、Docker、K8s教程關注公眾號:馬哥linux運維

      Kubernetes學習筆記之kube-proxy service實現原理插圖

      1. Overview

      我們生產k8s對外暴露服務有多種方式,其中一種使用external-ips?clusterip?service?ClusterIP Service方式對外暴露服務,kube-proxy使用iptables mode。這樣external ips可以指定固定幾臺worker節點的IP地址(worker節點服務已經被驅逐,作為流量轉發節點不作為計算節點),并作為lvs vip下的rs來負載均衡。根據vip:port來訪問服務,并且根據port不同來區分業務。相比于NodePort Service那樣可以通過所有worker節點的node_ip:port來訪問更高效,也更容易落地生產。但是,traffic packet是怎么根據集群外worker節點的node_ip:port或者集群內cluster_ip:port訪問方式找到pod ip的?

      并且,我們生產k8s使用calico來作為cni插件,采用 Peered with TOR (Top of Rack) routers方式部署,每一個worker node和其置頂交換機建立bgp peer配對,置頂交換機會繼續和上層核心交換機建立bgp peer配對,這樣可以保證pod ip在公司內網可以直接被訪問。

      但是,traffic packet知道了pod ip,又是怎么跳到pod的呢?

      以上問題可以歸并為一個問題:數據包是怎么一步步跳轉到pod的?很長時間以來,一直在思考這些問題。

      2. 原理分析

      實際上答案很簡單:訪問業務服務vip:port或者說node_ip:port,當packet到達node_ip所在機器如worker A節點時,會根據iptable rules一步步找到pod ip;找到了pod ip后,由于使用calico bgp部署方式,核心交換機和置頂交換機都有該pod ip所在的ip段的路由,packet最后會跳轉到某一個worker節點比如worker B,而worker B上有calico早就寫好的路由規則route和虛擬網卡virtual interface,再根據veth pair從而由host network namespace跳轉到pod network namespace,從而跳轉到對應的pod。

      首先可以本地部署個k8s集群模擬測試下,這里使用 install minikube with calico

      minikube start --network-plugin=cni --cni=calico
      # 或者
      minikube start --network-plugin=cni
      kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
      

      然后部署個業務pod,這里使用nginx為例,副本數為2,并創建ClusterIP Service with ExternalIPs和NodePort Service:

      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: nginx-demo-1
        labels:
          app: nginx-demo-1
      spec:
        replicas: 2
        template:
          metadata:
            name: nginx-demo-1
            labels:
              app: nginx-demo-1
          spec:
            containers:
              - name: nginx-demo-1
                image: nginx:1.17.8
                imagePullPolicy: IfNotPresent
                livenessProbe:
                  httpGet:
                    port: 80
                    path: /index.html
                  failureThreshold: 10
                  initialDelaySeconds: 10
                  periodSeconds: 10
            restartPolicy: Always
        selector:
          matchLabels:
            app: nginx-demo-1
      ---
      apiVersion: v1
      kind: Service
      metadata:
        name: nginx-demo-1
      spec:
        selector:
          app: nginx-demo-1
        ports:
          - port: 8088
            targetPort: 80
            protocol: TCP
        type: ClusterIP
        externalIPs:
          - 192.168.64.57 # 這里worker節點ip可以通過 minikube ip 查看,這里填寫你自己的worker節點ip地址
      ---
      apiVersion: v1
      kind: Service
      metadata:
        name: nginx-demo-2
      spec:
        selector:
          app: nginx-demo-1
        ports:
          - port: 8089
            targetPort: 80
        type: NodePort
      ---
      

      部署完成后,就可以通過 ExternalIP ClusterIP Service或者NodePort Service兩種方式訪問業務服務:

      3. iptables寫自定義規則

      當數據包通過node_ip:port或者cluster_ip:port訪問服務時,會在當前worker節點被內核DNAT(Destination?Network?Address?Translation)為pod?ip,反向packet又會被SNAT(Source Network Address Translation)。這里借用calico官網的非常生動的兩張圖說明About?Kubernetes?Services?:

      cluster-ip service 訪問流程:

      Kubernetes學習筆記之kube-proxy service實現原理插圖1

      node-port service 訪問流程:

      Kubernetes學習筆記之kube-proxy service實現原理插圖2

      由于我們生產k8s的kube-proxy使用iptables mode,所以這些snat/dnat規則是kube-proxy進程通過調用iptables命令來實現的。iptables使用各種chain來管理大量的iptable rules,主要是五鏈四表,五鏈包括:prerouting/input/output/forward/postrouting chain,四表包括:

      raw/mangle/nat/filter table,同時也可以用戶自定義chain。數據包packet進過內核時經過五鏈四表流程圖如下:

      Kubernetes學習筆記之kube-proxy service實現原理插圖3

      而kube-proxy進程會在nat table內自定義KUBE-SERVICES chain,并在PREROUTING內生效,可以通過命令查看,然后在查看KUBE-SERVICES chain中的規則:

      sudo iptables -v -n -t nat -L PREROUTING | grep KUBE-SERVICES
      
      sudo iptables -v -n -t nat -L KUBE-SERVICES
      
      sudo iptables -v -n -t nat -L KUBE-NODEPORTS
      

      可以看到,如果在集群內通過cluster_ip:port即10.196.52.1:8088,或者在集群外通過external_ip:port即192.168.64.57:8088方式訪問服務,都會在內核里匹配到?KUBE-SVC-JKOCBQALQGD3X3RT chain的規則,這個對應nginx-demo-1 service;如果是在集群內通過cluster_ip:port即10.196.89.31:8089,或者集群外通過nodeport_ip:port即192.168.64.57:31755方式訪問服務,會匹配到 KUBE-SVC-6JCCLZMUQSW27LLD chain的規則,這個對應nginx-demo-2 service:

      Kubernetes學習筆記之kube-proxy service實現原理插圖4

      然后繼續查找 KUBE-SVC-JKOCBQALQGD3X3RT chain和 KUBE-SVC-6JCCLZMUQSW27LLD chain的規則,發現每一個 KUBE-SVC-xxx 都會跳轉到 KUBE-SEP-xxx chain上,并且因為pod副本數是2,這里就會有兩個 KUBE-SEP-xxx chain,并且以50\%概率跳轉到任何一個 KUBE-SEP-xxx chain,即rr(round robin)負載均衡算法,這里kube-proxy使用iptables statistic module來設置的,最后就會跳轉到pod ip 10.217.120.72:80(這里假設訪問這個pod)??傊?,經過kube-proxy調用iptables命令,根據service/endpoint設置對應的chain,最終一步步跳轉到pod ip,從而數據包packet下一跳是該pod ip:

      sudo iptables -v -n -t nat -L KUBE-SVC-JKOCBQALQGD3X3RT
      sudo iptables -v -n -t nat -L KUBE-SEP-CRT5ID3374EWFAWN
      
      sudo iptables -v -n -t nat -L KUBE-SVC-6JCCLZMUQSW27LLD
      sudo iptables -v -n -t nat -L KUBE-SEP-SRE6BJUIAABTZ4UR
      

      Kubernetes學習筆記之kube-proxy service實現原理插圖5

      總之,不管是通過cluster_ip:port、external_ip:port還是node_ip:port方式訪問業務服務,packet通過kube-proxy進程自定義的各種chain找到了下一跳pod ip地址。

      但是,packet如何知道這個pod ip在哪個節點呢?

      4. calico寫自定義routers和virtual interface

      上文已經說過,我們部署calico方式可以保證pod ip在集群外是可以被路由的,這是因為交換機上會有node level的路由規則,在交換機上執行 dis bgp routing-table會有類似如下路由規則。表示如果訪問 10.20.30.40/26 pod網段下一跳是worker B的IP地址。這些路由規則是部署在每一個worker節點的bird進程(bgp client)分發的,交換機通過BGP學習來的:

      # 這里是隨機編造的地址
      Network                 NextHop         ...
      10.20.30.40/26          10.203.30.40    ...
      

      所以,packet在知道了pod ip 10.217.120.72:80?后(這里假設訪問了pod nginx-demo-1-7f67f8bdd8-fxptt),很容易找到了worker B節點,本文章示例即是minikube節點。查看該節點的路由表和網卡,找到了在host network namespace這一側是網卡 cali1087c975dd9,編號是13,這個編號很重要,可以通過編號知道這個veth pair的另一端在哪個pod network namespace。發現 pod nginx-demo-1-7f67f8bdd8-fxptt 的網卡eth0就是veth pair的另一端,并且編號也是13:

      # 因為該nginx容器沒有ifconfig命令和ip命令,可以創建nicolaka/netshoot:latest 容器并加入到該nginx container的namespace中
      docker ps -a | grep nginx
      export CONTAINER_ID=f2ece695e8b9 # 這里是nginx container的container id
      # nicolaka/netshoot:latest鏡像地址github.com/nicolaka/netshoot
      docker run -it --network=container:$CONTAINER_ID --pid=container:$CONTAINER_ID --ipc=container:$CONTAINER_ID nicolaka/netshoot:latest ip -c addr
      ip -c addr
      

      Kubernetes學習筆記之kube-proxy service實現原理插圖6

      以上路由表規則和虛擬網卡是calico cni的calico network plugin創建的,而pod ip以及每一個node的pod ip cidr網段都是由calico ipam plugin創建管理的,并且這些數據會寫入calico datastore內。至于calico network plugin和calico ipam plugin具體是如何做的,后續有時間再記錄學習。

      5. 總結

      不管集群內cluster_ip:port,還是集群外external_ip:port或node_ip:port方式訪問服務,都是會通過kube-proxy進程設置的各種iptables?rules后跳轉到對應的pod ip,然后借助于calico bgp部署方式跳轉到目標pod所在worker節點,并通過該節點的路由表和虛擬網卡,找到對應的那個pod,packet由host network namespace再跳轉到pod network namespace。一直以來的有關service和calico疑問也算是搞明白了。

      參考鏈接

      • https://docs.projectcalico.org/about/about-kubernetes-service

      • https://mp.weixin.qq.com/s/bYZJ1ipx7iBPw6JXiZ3Qu

      • https://mp.weixin.qq.com/s/oaW87xLnlUYYrwVjBnqee

      • https://mp.weixin.qq.com/s/RziLRPYqNoQEQuncm47rHg

      文章轉載:360云計算
      (版權歸原作者所有,侵刪)

      本文鏈接:http://www.abandonstatusquo.com/39904.html

      網友評論comments

      發表評論

      您的電子郵箱地址不會被公開。

      暫無評論

      Copyright ? 2012-2022 YUNWEIPAI.COM - 運維派 京ICP備16064699號-6
      掃二維碼
      掃二維碼
      返回頂部
      久久久久亚洲国内精品|亚洲一区二区在线观看综合无码|欧洲一区无码精品色|97伊人久久超碰|一级a爱片国产亚洲精品