天天看點

kubernetes-flannel-14容器網絡通信flannel說明

容器網絡通信

理論來源

docker網絡說明

  • bridge: 橋接網絡, 自由使用網絡名稱空間,其主控端接口也就是docker0橋,每一個容器也會有自己的虛拟網卡,來實作容器内部的虛拟網絡通信
  • joined: 聯盟式網絡, 共享使用另外容器的IPC
  • opened: 開放式網絡,容器直接共享使用虛拟機的IPC
  • none: 無網絡, 不使用任何網絡名稱空間

    如果跨節點之間的容器通信時,必須要使用NAT,任何容器在出去之前都是私有的位址,離開本地時必須要做SIP轉換,取到實體機位址出去,每個容器要想被别人所通路,也必須做NAT

​ 在docker時代,每一個容器在建立時,容器所預設使用的網絡模型,叫做bridge,其主控端接口也就是docker0橋,每一個容器也會有自己的虛拟網卡,來實作容器内部的虛拟網絡通信;

​ 而docker0橋的網絡位址是固定的,那麼如果某一個Pod控制,編排了兩個Pod,這兩個Pod各自編排了一個容器,此時我們剝離Pod,單談容器,剛好這兩個Pod或者兩個容器本身分别被排程到了兩個節點之上,那麼這兩個Pod中的容器如果關聯到我們的docker0橋上,那麼很有可能第一個容器擷取到的位址是172.16.0.2,第二個容器在第二個節點之上擷取的IP位址還是172.16.0.2,那麼這兩個容器之間使用的位址就相同了,更何況這兩個橋上預設與節點外部的其他端點進行通信時,還需要使用SNAT方式,因為每一個節點本地就是一個區域網路,

​ 這個區域網路在節點外部預設是不能被路由的,是以節點外部流量并沒有辦法直接通路這個節點上的容器的,除非我們在節點上面做DNAT,是以出去請求别人,我們需要通過SNAT實作,如果要提供服務還需要通過DNAT實作,這樣一來就會帶來,巨大的問題,跨節點通信,要通過兩次NAT才能完成,用戶端先做SNAT,被通路的目的端要通過DNAT暴露出來,那麼如果我們有五個Pod彼此之間要通信,那得多少個NAT需要維護,且不說容器自身怎麼維護,這些NAT規則在我們容器增時就頭疼不已,是以這種網絡模型顯然在這個所謂的容器編排系統當中不是一個可取的網絡模型;

kubernetes-flannel-14容器網絡通信flannel說明

k8s網絡通信模型

  1. 對于Kubernetes來講,無論我們怎麼編排Pod,經過充分測試它的位址是不會沖突的
  2. Kubernetes要求所有的Pod應該有自己的網絡Pod Network,另外Kubernetes的設計機制當中,Pod和Pod的通信是不會直接進行的,而是通過一個Service中間層進行通信的,Service也有位址,我們把它稱為Service Network也稱為Cluster Network,在叢集内部Pod和Pod雖然,可以直接通信,但是Pod要請求某個服務一般請求的它的Service Network位址,而後由Service Network給他排程并且代理至後端的Pod
  3. 其實對于Kubernetes網絡,應該是我們的管理者手動維護的,因為節點位址還沒有進行編排範疇,那麼叢集位址是Kubernetes自己指定的,由Kubernetes内部的Service資源,通過把它定義或者轉換成iptables或ipvs規則來實作,它并不配置在任何一個網卡上,是以Kubernetes自行就能管理好Cluster Network
  4. Pod Network: 每建立一個pod, 如果都需要pod 位址,而沒有共享主控端的IPC(網絡名稱空間), 那麼就需要從這個Pod Network配置設定一個位址,更重要的是Kubernetes還要求,各Pod之間,必須能夠使用對方的位址進行通信,不能走NAT
  5. 也就是說,如果用戶端的Pod想通路某個服務端的Pod,在不考慮Service的情況下,二者之間不能經過任何NAT要能夠直接進行通信,這是Kubernetes的要求,因為在Kubernetes内部它要維持這樣幾種通信。
    1. 容器間通信: 同一個POD内的多個容器間通信: lo
    2. pod間通信: Pod IP 與其它 Pod IP 能直接進行通信,不經過任何的NAT
    3. pod與Service通信: podIP與ClusterIP通信,其通過Iptables/ipvs轉換之後就能直接進行通信了
    4. Service與叢集外部Client通信: ingress, nodeport

常見網絡模型網絡解析

橋接式網絡

​ ​ ​ ​ ​ ​ ​ ​ Kubernetes要求我們的網絡必須是一個平面網絡,不能經由NAT轉發,但是這種邏輯就必須要實作一種工作邏輯,比如在一個節點之上,我們運作多個容器,這些容器的位址有可能的動态指定的,那麼跨兩個節點之間的容器間通信,如果不經過NAT直接與對方位址通信,那麼隻有突破傳統的私有網絡模型來實作,隻要讓我們Pod網絡使用橋接網絡即可,每個網橋可以直接通信,比如A節點占用10.244.1.0/24,B節點占用10.244.2.0/24,位址不産生沖突即可,但是這種橋接式網絡模型也有問題,假如我們有一千個節點,每個節點跑了100個Pod,這可有十萬個Pod,十萬個Pod在同一個實體網絡,那很容易形成廣播風暴,耗盡帶寬,是以就産生VLAN式網絡

kubernetes-flannel-14容器網絡通信flannel說明

VLAN式網絡

​ ​ ​ ​ ​ ​ ​ ​ 橋接式網絡在節點Pod運作多的情況下就會很容易形成廣播風暴,進而耗盡帶寬,是以最好的辦法就是把每一個節點之上最好能做成vlan,每一個節點做成一個vlan,同一個節點的Pod互相通信就直接通信了,如果跨節點通信,通過Vlan交換機進行交換就行了,這是一種方案;

​ ​ ​ ​ ​ ​ ​ ​ 但是如果網絡很大,變動也很頻繁的時候,使用vlan也未必是一種好的解決方案,更何況vlan使用的是不同的網段位址,也雖然可以使用相同網段,但是網絡管理起來也很麻煩,是以如果必要的時候完全可以使用BGP網絡來實作,建構Kubernetes的網絡;

kubernetes-flannel-14容器網絡通信flannel說明

疊加式網絡

​ ​ ​ ​ ​ ​ ​ ​ 不管是橋接式網絡或者VLAN式網絡,Pod都隻是連接配接到節點之上的docker0橋上,而不是實體網絡,是以它依舊不能與節點之外的Pod進行通信,但是多個節點的實體接口之間可以打開一個隧道,一旦兩個Pod之間通信,所在網段是本地網段就直接本地通信,而如果不是則可以通過實體網卡封裝一個隧道協定封包,送到其它節點的實體網卡上了,但網卡将資料包解封裝之後會發現裡面還有一層封包,而這層封包是由本地轉發來的,是以就可以與其它節點的Pod進行通信了,而在虛拟網絡協定中有一種技術叫做VXlan,就是實作這麼一個功能,我們可以通過某個控制器,讓兩個區域網路之間的封包能夠借助于VXLAN的隧道協定,讓它們進行通信,是以兩個或多個區域網路可以了解為一個大網中的幾個小子網而已,也就意味着,從大的角度來看他們還是一個網段;

​ ​ ​ ​ ​ ​ ​ ​ 比如我們VXlan所管理的網絡是10.244.0.0/16的B類網,我們再把這個B類網切分成256個C類網,A節點位址為0.1/24,B節點為1.1/24,然後其内部Pod位址都由dhcp配置設定,一旦兩個節點内部的Pod需要進行通信時,可以通過VXLAN技術将兩個節點的位址它的掩碼從24位給他,聚合提升為16位,是以兩者要進行通信時,A節點就從0.1/24提升為0.1/16,B節點也從1.1/24提升為1.1/16,是以,他們就是同一個網段了,而VXLAN借助于自己的二層隧道,把封包從一個節點送到另外一個節點,但是VXlan怎麼知道哪一個節點上擁有哪一個子網呢,如果VXlan自己不能儲存這些資訊,那麼通常要借助于外部的存儲系統來儲存;

​ ​ ​ ​ ​ ​ ​ ​ 比如像xvlan控制器可以借助于像etcd這類的kv存儲系統,當vxlan中每添加一個節點就配置設定一個子網,然後記錄到etcd當中,比如10.244.0.0/24(NODE1虛拟網卡)需通過192.168.1.2(NODE1實體網卡),要到達10.244.1.0/24(NODE2虛拟網卡)需通過192.168.1.3(NODE2實體網卡), 如果node1的Pod(10.244.0.2)要通路node2之上的Pod(10.244.1.2),它會先查詢xvlan,然後它查詢的目标主機是10.244.1.0/24網段,随後根據etcd當中的資訊發現要達到10.244.1.0/24這個網段那麼首先要将封包送到192.168.1.3,送到192.168.1.3之後發現目标mac位址是自己的,然後拆掉目标mac, 看到裡面mac,而裡面的mac是192.168.1.3内部的虛拟交換機的,虛拟交換機收到之後,裡面的目标IP是Pod1的IP,然後Pod1就收到了;

​ ​ ​ ​ ​ ​ ​ ​ 這種網絡我們稱之為疊加網絡,其實就是在一個網絡上承載了另外一個網絡,而疊加網絡也是讓我們能在一個較大規模的網絡當中,實作網絡虛拟化的重要基礎技術之一,而能實作疊加網絡的衆多技術當中VXLAN以性能高協定開放著稱,是以用的非常非常廣泛,甚至于有很多項目都是借助于VXLAN來完成網絡虛拟化的;

kubernetes-flannel-14容器網絡通信flannel說明

k8s網絡解析

​ ​ ​ ​ ​ ​ ​ ​ 雖然說疊加網絡看似是k8s的最優選擇,但k8s自身卻沒有實作這樣的功能,k8s雖然需要為Pod配置設定網絡,從但是它覺得各家網絡服務廠商,很可能都有自己的私有技術,是以k8s将pod網絡功能基于插件接口提供功能,将它交給第三方網絡插件來實作,而K8s隻保留出一個插件接口,隻要遵循restful風格的網絡插件,都可以作為k8s叢集之上的Pod網絡元件, 叢集網絡系統, flannel

CNI插件

k8s網絡不是由自己來實作,而是靠CNI (網絡插件) 接口,如果想使用CNI插件,我們在每一個節點啟動kubelet的時候要給定參數–network-plugin=cni啟用這個插件,CNI還需要一個外部的節點級的應用程式來對接,是以還需要使用–cni-conf-dir來指定應用程式位址,預設配置檔案

/etc/cni/net.d/

,程式檔案預設在/opt/cni/bin目錄,我們在使用kubeadm部署的時候不需要手動管理CNI插件
  • flannel, CoreOS所研發的一種輕量級網絡實作方案,使用者人數多,占用面積大,簡單易用
  • 實作方式:基于VXLAN技術的,跨級節點通信,借助于疊加網絡來實作的
  • 網絡:隧道網絡,但是它不是二層,而是三層,ipip,用一個ip封包承載另外一個ip封包
  • 缺點: 不支援網絡政策,無法定義Pod之間的政策
  • 優點: 簡單且快速的網絡模型
  • calico, 一般可用于與flannel搭配,隻需要其網絡政策功能
    • 實作方式:
    • 網絡:使用BGP協定的,大二層網絡解決方案,利用一個mac封包承載另一個mac封包
    • 缺點: 網絡配置複雜
    • 優點: 強悍的網絡政策
  • cannel
  • flannel + calico的揉合體, 簡單且易配的網絡、以及網絡政策
  • kube-route
  • k8s自帶的網絡功能, 一般使用少,
  • 網絡: 既能實作網絡也能夠借助于lvs/ipvs來管理Service的kube-proxy ,還能夠借助于核心轉發功能來建構Pod和Pod之間的網絡解決方案,而且不使用疊加網絡,直接利用核心級的某些特性就能完成大二層網絡之間的Pod通信,是以kube-proxy從這個角度來講也是一個後起之秀,但是一直處于bate這個級别,沒有stabe;

CNI基本解決方案

  • 虛拟網橋 (bridge):純軟體方式實作一個虛拟網卡,如docker的橋接,容器、主機各一半接口;
  • 多路複用(MacVlan):基于mac的方式建立vlan,為每個虛拟接口建立一個獨有的MAC位址,使得一個實體網卡能承載多個容器去使用, 基于實體網卡中的VLAN機制進行跨節點之間通信;
  • 硬體交換:SR-IOV (單根IO虛拟化), 一個網卡支援能直接在實體機虛拟出多個接口;
~]# cat /etc/cni/net.d/10-flannel.conflist
{
  "name": "cbr0",            # 網卡名稱
  "cniVersion": "0.3.1",     # 插件版本
  "plugins": [
    {
      "type": "flannel",     # 插件類型
      "delegate": {
        "hairpinMode": true,
        "isDefaultGateway": true   # 預設網關
      }
    },
    {
      "type": "portmap",
      "capabilities": {
        "portMappings": true
      }
    }
  ]
}
           

CNI配置與使用

​​ ​ ​ ​ ​ ​ ​ ​ 我們可以看到每個節點上都可以看到cni0的網絡接口,同時還有一個接口flannel.1,這是flannel自己的隧道接口橋(隧道網絡有自己專用的封裝隧道協定的接口在這裡叫做flannel.1),任何Pod在建立以後會自動關聯到cni0這個網段上來,是以關聯的位址和cni0的位址,同時也是一個24為的掩碼 ,如果目标主機IP和cni0位址不在同一個網段,那就不是本地cni0的子網,那麼就會通過核心級的路由轉發到flannel.1,而後flannel.1給它打上隧道封包,送給另外一個節點,進而完成跨節點的隧道間協定轉發,因為flannel預設情況下使用的是vxlan的方式,來做為後端網絡傳輸機制的,

​​ ​ ​ ​ ​ ​ ​ ​ 兩個節點之間的Pod通信:組成一個疊加網絡,兩個主機之上應該應該有一個專門用于疊加封包封裝的隧道flannel-0, flannel-1這樣的接口, 如: 兩個節點pod網絡之間是無法通信的 需要借助于flannel接口的封包封裝通道,

kubernetes-flannel-14容器網絡通信flannel說明

flannel支援多種後端:

  1. VxLAN: 擴充的虛拟區域網路
  2. vxlan
    • vxlan在實作封包通信時,它是一種類型與四層隧道的協定,當幀經由flannel隧道時其頭部會封裝一個vxlan首部在到其前面加一個udp首部外層會在加一層IP首部,
    • 以太網首部 POD2 <-- [以太網首部|IP首部|udp首部|vxlan] <-- flannel隧道 <-- POD1
  3. DirectRouting
    • 直接路由,源和目标節點在同一網絡, 使用路由轉發,不用隧道疊加,如果大家在同一個網絡中就使用Host-gw類型,如果不在同一個網絡中就工作為VXLAN模型
  4. host-gw: Host Gateway (主機網關)
    • 如:2個節點,節點之上各自有一個專用網段, 主機自己所在的網絡接口當做網關使用,進而給pod接口各自配置位址,網關設定為主機位址
    • 通過實體網橋直接接入實體網橋,網橋不是路由,所有Pod直接通信,但是這種方式雖然性能很好,但是有局限性,既然大家都是直接通過mac位址通信的,那也就意味着所有節點必須在同一個實體網絡中不能跨路由器
    • 不支援跨節點
      kubernetes-flannel-14容器網絡通信flannel說明
  5. UDP

兩台主機在同一個網段\同一個2層交換機下使用host-gw, 如果跨網段\跨路由就自動降級為Vxlan

flannel說明

為了讓k8s運作, flannel事先應該是先存在的,也就是說在運作之前就應該先部署flannel。

​ 部署k8s有兩種方式: k8s直接部署在節點上,使用kubeadm将服務都運作在pod之上, 但凡有kubenet的節點都需要安裝flannel插件,Kubenet要借助flannel為pod設定網絡接口,添加網絡以及激活網絡等功能;

​ 而flannel也同于k8s安裝方式, 部署于節點之上的守護程序 或 運作于pod之中,運作在pod之中就必須把flannel配置為共享它所運作的節點網絡名稱空間的pod, 一旦做為Pod運作那它一定是一個daemonSet, 在每一個節點之上隻運作一個副本,而這個副本是直接共享主控端的網絡名稱空間,這樣pod才能設定主控端的網絡名稱空間,

flannel狀态資訊

  • 如運作于pod之上的flannel, 叢集有多少個 flannel 就會安裝多少
~]# kubectl get daemonsets.apps -n kube-system 
NAME                      DESIRED   CURRENT   READY   UP-TO-DATE  
kube-flannel-ds-amd64     3         3         3       3     
           
  • 節點數
    ~]# kubectl get pods -n kube-system -o wide
    NAME                             READY   STATUS   IP              NODE  
    kube-flannel-ds-amd64-cb9h5      1/1     Running  192.168.2.222   slave2
    kube-flannel-ds-amd64-wd7tv      1/1     Running  192.168.2.220   master
    kube-flannel-ds-amd64-z96rw      1/1     Running  192.168.2.221   slave1
               
  • configmap用來配置這三個pod是如何運作的
    ~]# kubectl get configmaps -n kube-system 
    NAME                                 DATA   AGE
    kube-flannel-cfg                     2      8d
    
    ~]# kubectl get configmaps -n kube-system kube-flannel-cfg -o yaml
    apiVersion: v1
    data:
      cni-conf.json: |
        {
          "name": "cbr0",
          "cniVersion": "0.3.1",
          "plugins": [
            {
              "type": "flannel",
              "delegate": {
                "hairpinMode": true,
                "isDefaultGateway": true
              }
            },
            {
              "type": "portmap",
              "capabilities": {
                "portMappings": true
              }
            }
          ]
        }
      net-conf.json: |
        {
          "Network": "10.244.0.0/16",
          "Backend": {
            "Type": "vxlan"
          }
        }
    kind: ConfigMap
    metadata:
      name: kube-flannel-cfg
      namespace: kube-system
               

flannel配置參數

  • Network: flannel使用的CIDR格式的網絡位址,用于為Pod配置網絡功能
  • 如大網段是:10.244.0.0/16 node1:10.244.1.0/24 node2: 10.244.2.0/24 …
  • SubnetLen: 把Network切分子網借節點使用時,使用多長的掩碼劃分,預設: 24;
  • SubnetMin: 指明網段從多少開始劃分 比如: 10.244.20.0/24開始
  • SubnetMax: 指明網段最大是多少 比如: 10.244.25.0/24
  • Backend: pod與pod之間的通信協定, vxlan, host-gw, udp
  • flannel配置說明

測試隧道協定

​ 主機網關模式: 假設有兩個節點,分别是node1和node2,node1節點Pod位址為10.244.1.1/24, node2節點Pod位址為10.244.2.1/24,此時我們分别将node1與node2網卡位址做為node1\node2的網關, 這時當Pod與外部通信時就可以直接使用自己主控端網關IP位址進行通信了;

​ 比如我們在實體機上建立一個虛拟邏輯接口lo1,用于連接配接Pod網絡并将其做為Pod網絡的網關,此時Pod在傳輸封包的時候不是通過隧道承載傳遞的,此時這個Pod的ip位址為10.244.1.1/24,傳遞封包目标位址為10.244.2.1/24,此時應該将它的封包傳遞比網關lo1, 位址為10.244.1.254/24,當網關收到該封包時,先查詢本地的路由表,發現要到達指定網絡需要經過本地的實體網卡 eth0(位址為172.16.1.3/24),此時實體網卡會再查詢本地路由表,發現rs表中記錄要到達指定的10.244.2.0/24網絡要送給對端的實體網卡位址為172.16.1.4/24,對端位址收到封包之後發現目标位址是10.244.2.0/24,那麼就會發給10.244.2.254/24(lo1),最終在通過查詢本地路由表最終到達10.244.2.1;

​ 兩種方式:通過直接将主機節點當成網關性能會比邏輯接口更高;

VxLAN- xvlan

  • 部署兩個測試pod
]# cat myapp.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deploy
  namespace: default
  labels:
    app: myapp
spec:
  replicas: 2
  selector: 
    matchLabels:
      app: myapp
  template:
    metadata:
      name: myapp
      namespace: default
      labels: 
        app: myapp
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          containerPort: 80

~]# kubectl get pods -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP            NODE  
my-deploy-7b59496b7d-9qpjx   1/1     Running   0          11m   10.244.1.22   slave1
my-deploy-7b59496b7d-v6fmj   1/1     Running   0          11m   10.244.2.26   slave2

# 分别連入
]# kubectl exec -it my-deploy-7b59496b7d-9qpjx /bin/sh ping
           
  • 檢視路由
]#  ip route
default via 192.168.2.1 dev ens33 proto static metric 100 
10.244.0.0/24 dev cni0 proto kernel scope link src 10.244.0.1 
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink 
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink 


# 10.244.1.22 -->  10.244.1.0 (flannel1.1接口) --> cni0 --> 實體網卡節點1  --> 節點2 --> cni0 --> flannel1.1 --> 10.244.2.26
           
  • 測試icmp
# xvlan資料流  cni0進, flannel1 出, 最後借助于ens網口到其它網口, 1.1的時候會封裝成xvaln封包

 ~]# tcpdump -i cni0 -nn icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on cni0, link-type EN10MB (Ethernet), capture size 262144 bytes
11:00:40.294152 IP 10.244.2.26 > 10.244.1.22: ICMP echo request, id 3072, seq 298, length 64
11:00:40.294224 IP 10.244.1.22 > 10.244.2.26: ICMP echo reply, id 3072, seq 298, length 64

 ~]# tcpdump -i flannel.1 -nn icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on flannel.1, link-type EN10MB (Ethernet), capture size 262144 bytes
11:01:58.651415 IP 10.244.1.22 > 10.244.2.26: ICMP echo request, id 4352, seq 421, length 64
11:01:58.652113 IP 10.244.2.26 > 10.244.1.22: ICMP echo reply, id 4352, seq 421, length 64

# xvlan是從虛拟接口出的, 從ens接口是直接看不到icmp協定的, 被xvlan封裝了
           

VxLAN-DirectRouting

前期需要分析,如果不存在跨網關可以直接使用,網關模式 減少資料包封裝提高性能,如果跨網段是 預設降級為vxlan模式
]# wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

# 需要net-conf.json段, 
net-conf.json: | {
  "Network": "10.244.0.0/16",
  "Backend": {
    "Type": "vxlan", 
    "DirectRouting": true   # 增加 Directouting 使flannel直接通過實體網卡連接配接通信
  }
}

]# kubectl get configmaps -n kube-system kube-flannel-cfg -o json
# 添加 DirectRouting 改為直連後 檢視路由連接配接

# 需要先删除 pods(deployment), flannel,然後在建立, 直接修改json段可能不會生效


 ~]# ip r s
10.244.0.0/24 via 192.168.2.220 dev ens33 
10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1 
10.244.2.0/24 via 192.168.2.222 dev ens33 

# 檢視 icmp協定
 ~]# tcpdump -i ens33 -nn icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
11:34:59.889258 IP 10.244.2.27 > 10.244.1.23: ICMP echo request, id 4352, seq 46, length 64
11:34:59.889374 IP 10.244.1.23 > 10.244.2.27: ICMP echo reply, id 4352, seq 46, length 64
           

繼續閱讀