概述
在學習docker時知道docker有四種常用的網絡模型
- bridge:橋接式網絡
- joined:聯盟式網絡,共享使用另外一個容器的網絡名稱空間
- opened:容器直接共享使用宿主機的網絡名稱空間
- none:不使用任何網絡名稱空間
無論是哪一種網絡方式都會導致如果我們跨節點之間的容器之間進行通信時必須要使用NAT機制來實現,任何pod在訪問出去之前因為自己是私有網絡中的地址,在離開本機時候必須要做源地址轉換以確保能夠拿着物理機的地址出去,而后每一個pod要想被別人所訪問或者每一個容器在上下文當中它也訪問不到我們還來做什么?從下圖中可以看到,第一個節點上我們有container1,container1自己使用虛擬網卡(純軟件)的方式生成一個網絡接口,它通常是一個v1th格式的網絡接口,一半在容器上一半在宿主機上並關聯到docker0橋上,所以他是一個私網地址,我們使用172.17.0.2這樣的地址,很顯然在這個地址出去訪問其它物理機地址的時候物理機能收到請求沒有問題因為它的網關有可能都已經通過打開核心轉換要通過eth0即宿主機的網絡發出來,但是對端主機收到以后響應給誰?是不是沒法響應了,因為這是私網地址,而且是nat背后的私網地址,或者是位於某個服務器背后的私有地址,因此為了確保能夠響應到我們任然能送回這個主機,我們需要在本地做原地址轉換,同樣的,如果container2希望被別人訪問到我們需要在宿主機上的物理接口上做dnat將服務暴露出去,因此被別人訪問時,假如有個物理機要訪問docker2時就應該訪問其宿主機上的eth0上的某一端口,再目標地址轉換至container2的eth0的地址上來,所以如果我們剛好是container1和container2通信,那就是兩級nat轉換了,首先,對container1來講,其報文離開本機的時候要做snat,然后這個報文接入物理網絡發給container2的宿主機的時候我們還需要dnat一次然后到container2,container2響應的報文也是一樣的邏輯。當前,dnat和snat都是都是自動實現的,不需要手動設置。這個過程是必不可少的
這樣的話就會導致我們網絡通信性能很差不說,並且container1其實一直不知道自己訪問的是誰,他訪問的是container2但實際上他的目標地址指向的是eth0的地址,並且container2一直不知道訪問自己的是誰,因為他收到的請求是左側eth0的,他實際上是被container1所訪問
k8s之上的網絡通訊模型
所以這種通信方式實在是讓人覺得效率低而且很難去構建我們自己真正需要的網絡通訊模型。因此k8s作為一個編排工具來講他本身就必須需要讓我們容器工作在多個節點之上,而且是pod的形式,各pod之間是需要進行通信的,而且在k8s之上要求我們pod之間通信大概存在以下情形
- 容器間通信:同一個Pod內的多個容器間的通信: lo
- pod間通信:pod間通信k8s要求他們之間的通信必須是所見即所得,即一個pod的IP到另一個pod的IP之間通信不經過任何NAT轉換,要直達
- pod與service通信:即pod IP 與cluster IP之間直接通信,他們其實不在同一個網段,但是他們通過我們本地的IPVS或者iptables規則能實現通信,而且我們知道1.11上的kube-proxy也支持IPVS類型的service,只不過我們以前沒有激活過。即pod IP與cluster IP通信是通過系統上已有的iptables或ipvs規則來實現的,這里特別提醒一下ipvs取代不了iptables,因為ipvs只能拿來做負載均衡,做nat轉換這個功能就做不到
- service與集群外部客戶端的通信;使用ingress 或nodeport或loadblance類型的service來實現
我們此前說過利用一個新的環境變量能夠在部署的時候就能夠實現IPVS后來發現這種方式不行,其實在我們使用kubeadm部署k8s集群時不需要這么做,最簡單的辦法就是直接改kube-proxy的配置文件。我們去往每一個節點都已經默認加載了ipvs內核模塊,調度算法模塊等等,還有連接追蹤模塊。我們來看一下kube-system名稱空間的configmap,可以看到一個叫做kube-proxy,這里面定義了我們kube-proxy到底使用哪種模式在工作,將其中的mode定義為ipvs再重載一下即可
kubectl get configmap -n kube-system

apiVersion: v1 data: config.conf: |- apiVersion: kubeproxy.config.k8s.io/v1alpha1 bindAddress: 0.0.0.0 clientConnection: acceptContentTypes: "" burst: 10 contentType: application/vnd.kubernetes.protobuf kubeconfig: /var/lib/kube-proxy/kubeconfig.conf qps: 5 clusterCIDR: 10.244.0.0/16 configSyncPeriod: 15m0s conntrack: max: null maxPerCore: 32768 min: 131072 tcpCloseWaitTimeout: 1h0m0s tcpEstablishedTimeout: 24h0m0s enableProfiling: false healthzBindAddress: 0.0.0.0:10256 hostnameOverride: "" iptables: masqueradeAll: false masqueradeBit: 14 minSyncPeriod: 0s syncPeriod: 30s ipvs: excludeCIDRs: null minSyncPeriod: 0s scheduler: "" syncPeriod: 30s kind: KubeProxyConfiguration metricsBindAddress: 127.0.0.1:10249 mode: "" #將此處改為ipvs即可 nodePortAddresses: null oomScoreAdj: -999 portRange: "" resourceContainer: /kube-proxy udpIdleTimeout: 250ms kubeconfig.conf: |- apiVersion: v1 kind: Config clusters: - cluster: certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt server: https://192.168.10.10:6443 name: default contexts: - context: cluster: default namespace: default user: default name: default current-context: default users: - name: default user: tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token kind: ConfigMap metadata: creationTimestamp: 2019-05-08T10:02:16Z labels: app: kube-proxy name: kube-proxy namespace: kube-system resourceVersion: "239" selfLink: /api/v1/namespaces/kube-system/configmaps/kube-proxy uid: 5c27af66-7178-11e9-be24-000c29d142be
k8s最有意思的是他的網絡實現不是自己來實現的,而是要靠網絡插件。即CNI(容器網絡接口)接口插件標准接入進來的其它插件來實現,即k8s自身並不提供網絡解決方案,他允許去使用去托管第三方的任何解決方案,代為解決k8s中能解決這四種通信模型中需要執行通信或解決任何第三方程序都可以,這種解決方案是非常非常多的,有幾十種,目前來講比較流行的就是flannel,calico,以及二者拼湊起來的canel,事實上我們k8s自己也有據說性能比較好的插件比如kube-router等等,但無論是哪一種插件他們試圖去解決這四種通信時所用到的解決方案無非就這么幾種
1 虛擬網橋:brdige,用純軟件的方式實現一個虛擬網絡,用一個虛擬網卡接入到我們網橋上去。這樣就能保證每一個容器和每一個pod都能有一個專用的網絡接口,從而實現每一主機組件有網絡接口。每一對網卡一半留在pod之上一半留在宿主機之上並接入到網橋中。甚至能接入到真實的物理網橋上能顧實現物理橋接的方式
2 多路復用: MacVLAN,基於mac的方式去創建vlan,為每一個虛擬接口配置一個獨有的mac地址,使得一個物理網卡能承載多個容器去使用。這樣子他們就直接使用物理網卡並直接使用物理網卡中的MacVLAN機制進行跨節點之間進行通信了
3 硬件交換:使用支持單根IOV(SR-IOV)的方式,一個網卡支持直接在物理機虛擬出多個接口來,所以我們稱為單根的網絡連接方式,現在市面上的很多網卡都已經支持單根IOV的虛擬化了。它是創建虛擬設備的一種很高性能的方式,一個網卡能夠虛擬出在硬件級多個網卡來。然后讓每個容器使用一個網卡
相比來說性能肯定是硬件交換的方式效果更好,不過很多情況下我們用戶期望去創建二層或三層的一些邏輯網絡子網這就需要借助於疊加的網絡協議來實現,所以會發現在多種解決方案中第一種叫使用虛擬網橋確實我們能夠實現更為強大的控制能力的解決方案,但是這種控制確實實現的功能強大但多一點,他對網絡傳輸來講有額外的性能開銷,畢竟他叫使用隧道網絡,或者我們把它稱之為疊加網絡,要多封裝IP守護或多封裝mac守護,不過一般來講我們使用這種疊加網絡時控制平面目前而言還沒有什么好的標准化,那么用起來彼此之間有可能不兼容,另外如果我們要使用VXLAN這種技術可能會引入更高的開銷,這種方式給了用戶更大的騰挪的空間
如果我們期望去使用這種所謂的CNI插件對整個k8s來講非常簡單,只需要在kubelete配置文件或在啟動時直接通過一個目錄路徑去加載插件配置文件, /etc/cni/net.d/,因此我們只要把配置文件扔到這個目錄中去他就可以被識別並加載為我們插件使用
ls /etc/cni/net.d/

{ "name": "cbr0", "plugins": [ { "type": "flannel", "delegate": { "hairpinMode": true, "isDefaultGateway": true } }, { "type": "portmap", "capabilities": { "portMappings": true } } ] }
任何其它網絡插件部署上來也是如此,把配置文件扔到這個目錄下從而被kubelete所加載,從而能夠被kubelete作為必要時創建一個pod,這個pod應該有網絡和網卡,那么它的網絡和網卡怎么生成的呢?kubelet就調用這個目錄下的由配置文件指定的網絡插件,由網絡插件代為實現地址分配,接口創建,網絡創建等各種功能。這就是CNI,CNI是一個JSON格式的配置文件,另外他有必要有可能還會調IP地址管理一些二層的模塊來實現一些更為強大的管理功能。
通常來講CNI本身只是一種規范,CNI的插件是說我們的容器網絡插件應該怎么定義,網絡接口應該怎么定義等等,目前來講我們CNI插件分為三類,了解即可。不同的網絡插件在實現地址管理時可能略有不同,而flannel,calico,canel,kube-router等等都是解決方案,據統計,flannel目前來講份額還是最大的。但是其有個缺陷,在k8s上網絡插件不但要實現網絡地址分配等功能,網絡管理等功能,他還需要實現網絡策略,可以去定義pod和pod之間能不能通信等等,大家知道我們在k8s上是存在網絡名稱空間的,但是這個名稱空間他並不隔離Pod的訪問,雖然隔離了用戶的權限,比如我們定義說rolebinding以后我們一個用戶只能管理這個名稱空間的資源,但是,他卻能夠指揮着這個pod資源去訪問另一個名稱空間的pod資源,pod和pod之間在網絡上都屬於同一網段,他們彼此之間沒有任何隔離特性,萬一你的k8s集群有兩個項目,彼此之間不認識,萬一要互害的時候是沒有隔離特性的。所以我們需要確保網絡插件能夠實現輔助設置pod和pod之間是否能夠互相訪問的網絡策略,但是flannel是不支持網絡策略的。calico雖然部署和配置比flannel麻煩很多,但是calico即支持地址分配又支持網絡策略,因此其雖然復雜,但是卻很受用戶接受。還有一點就是calico在實現地址轉發的方式中可以基於bgp的方式實現三層網絡路由,因此在性能上據說比flannel要強一些。但是flannel也支持三種網絡方式。默認是疊加的,我們使用host gw其實可能比我們想象的功能要強大的多
這是介紹的網絡插件,我們經常使用時可以兼顧使用flannel的簡單,借助於calico實現網絡策略,也沒必要直接部署canel,可以直接部署flannel做平時的網絡管理功能若需要用到網絡策略時再部署一個calico,只讓其部署網絡策略,搭配起來使用,而不用第三方專門合好的插件
配置和使用flannel
flannel默認情況下是用的vxlan的方式來作為后端網絡傳輸機制的。正常情況下,兩個節點之間怎么通信呢?如果基於宿主機之間橋接直接通信那么他們應該就可以直接借助於物理網卡實現報文交換,但現在是我們要做成一個疊加網絡,使得兩邊主機之上應該各自有一個專門用於疊加報文封裝的隧道,通常稱之為flannel.0,flannel.1之類的接口,他們的ip很獨特,為10.244.0.0等等,掩碼也很獨特,為32位,是用來封裝隧道協議報文的,並且可以看到我們物理網卡mtu為1500,而他的mtu為1450要留出來一部分,因為我們要做疊加隧道封裝的話會有額外的開銷,他把額外的開銷給留出來了,但這些開銷不是沒有,這些報文額外的空間不是不被占用,而是被占用了留給我們隧道使用而已

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 00:0c:29:d1:42:be brd ff:ff:ff:ff:ff:ff inet 192.168.10.10/24 brd 192.168.10.255 scope global ens33 valid_lft forever preferred_lft forever inet6 fe80::20c:29ff:fed1:42be/64 scope link valid_lft forever preferred_lft forever 3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN link/ether 02:42:e1:e8:29:16 brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever 4: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN link/ether d2:fb:97:81:bf:7f brd ff:ff:ff:ff:ff:ff inet 10.244.0.0/32 scope global flannel.1 valid_lft forever preferred_lft forever inet6 fe80::d0fb:97ff:fe81:bf7f/64 scope link valid_lft forever preferred_lft forever 5: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP qlen 1000 link/ether 0a:58:0a:f4:00:01 brd ff:ff:ff:ff:ff:ff inet 10.244.0.1/24 scope global cni0 valid_lft forever preferred_lft forever inet6 fe80::a0f8:5eff:fe8c:cf44/64 scope link valid_lft forever preferred_lft forever 6: veth3ae34d47@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP link/ether 0a:c6:2d:90:68:d0 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::8c6:2dff:fe90:68d0/64 scope link valid_lft forever preferred_lft forever 7: veth5a6b0c93@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP link/ether ee:91:5d:55:9e:dd brd ff:ff:ff:ff:ff:ff link-netnsid 1 inet6 fe80::ec91:5dff:fe55:9edd/64 scope link valid_lft forever preferred_lft forever
另外還有cni的接口,對應在flannel.1之上對應有一個10.244.0.1的接口,被當前主機作為這個隧道協議上在本地通信時使用的接口,但只有創建完pod以后有容器運行以后這個接口才會出現,默認情況下可能只有flannel.1這個接口存在
基於此我們可以簡單理解一下,正常情況下我們的網絡兩個接口直接我們是假設期望他們能直接通信的,但是他兩之間無法直接通信,要借助於我們的隧道接口進行通信,比如我們稱為叫flannel,這也就意味着在他兩個之間我們要構建一個額外添加了開報文傳輸的一個傳輸通道,使得他兩之間的通信借助這個傳輸通道來實現,就好像他在直接通信一樣,這個后端通信我們所使用的功能就是flannel接口,每一次在這個通訊之外再封裝一個二層或三層或四層的直接報文,默認情況下我們用的是VXLAN機制,承載外層的隧道我們用的是VXLAN(擴展的虛擬局域網)協議,其在實現報文通信時他是一種類似於四層隧道一樣的協議,也就意外着我們正常傳輸一個以太網幀的時候他就能在鏈路層直接進行傳輸,那怎么傳呢?當一個幀發過來的時候,他要經過這個隧道接口再格外封裝一層報文守護。第一,他要封裝一個VXLAN守護。第二、VXLAN守護之外是UDP協議守護,他們用UDP進行傳輸。第三、UDP協議之外才是IP守護。第四、IP之外又有一個以太網守護。所以這幾種守護完全都是額外開銷。所以基於VXLAN可能性能上會有點低,但是好處就在於我們在這里可以獨立管理一個網絡,而且彼此之間和物理網絡是不相干擾的
但是這只是我們對應的flannel網絡的后端傳輸的第一種方式。所以flannel支持多種后端傳輸方式
1 VXLAN:如上所述
2 host-gw(Host Gateway):主機網關,一樣的方式,我們有多個節點,每一個節點上都需要運行pod,因此對第一個節點和第二個節點上的pod大家各自有一個專有網段,但是我們把主機自己所在的這個網絡接口當做是網關來使用,從而能給這些pod之間各自配一個ip地址並通過這個網關對外進行傳遞。我們在物理機上創建一個虛擬接口,然后我們每一個pod也有一個虛擬接口,這個虛擬接口在傳輸報文時不是通過隧道承載傳遞出去的,而是當其需要傳輸報文時一看目標主機不是本地的,他就應該把報文傳給網關,即我們本地的專用虛擬接口,這個網關看到報文以后他會查我們本地的路由表,然后再經過物理網卡就發出去了,這個路由表中記錄了到達哪個網絡就跳到哪兒。假如主機一pod網絡我們使用的是10.244.0.0/24,主機二pod網絡使用的是10.244.1.0/24。所以假設我們主機一上的pod IP為10.244.0.2,主機二上的pod IP為10.244.1.2,當我們主機一pod發報文出去時發現對端主機是10.244.1.2,不在自己的網段內,然后就將報文送到網關,網關路由表記錄了要到達10.244.1.0/24網絡需要送給對端的物理網卡。所以這個報文他會通過本機的物理網卡直接傳給主機二的物理網卡。主機二物理網卡一看這就是本機上的另外一個網卡(即這個虛擬接口),所以報文送給該接口,然后報文就到達了主機二了。通過這種方式就相當於是直接將節點當網關,不過這樣的話主機的路由表會很大,假如是一個擁有三千節點的集群,大概至少要有三千個路由條目才行。這種比VXLAN方式性能好多了,因為幾乎沒有什么額外開銷。但是其有一個缺陷就是要求各節點必須工作於同一個二層網絡中。這樣會使得同一個網段中主機非常多,萬一發一個泛洪報文,因為彼此之間沒有隔離所以每一個主機都有可能受到波及和影響。如果節點不在同一二層網絡中就不能使用該模式。我們部署k8s集群所有主機沒必要在同一網絡中,我們有時候部署很大的k8s集群時中間是有路由器隔離的
但是我們又期望用這種高性能又期望各個節點又可以不在同一網段時要怎么辦呢?其實他們還支持第二種方式,在VXLAN上也支持類似於host-gw這種模式,VXLAN還可以這樣玩,如果說節點和節點之間通信時大家在同一個網絡中,那么我們就直接使用host-gw的方式進行通信,因此我們不用隧道轉發不用隧道承載,但是有些我們目標節點與當前節點,所謂當前pod所在的節點與目標pod所在的節點中間隔了路由設備的時候那么他就會自動轉為VXLAN的方式使用疊加網絡進行通信。這就是柔和了VXLAN和host-gw的一種解決方案。即VXLAN的Directrouting模式
UDP方式:即純粹的UDP方式進行轉發,這種方式比VXLAN性能更差,因為這是用普通的UDP報文轉發的還不是用VXLAN專用的報文轉發的。因此性能比前兩種方式低很多很多,即前兩種都不支持的情況下才使用。flannel剛剛被發明出來的時候linux內核尚且不支持VXLAN,linux內核沒有VXLAN模塊,而那時候host-gw又有很高的技術門檻,所以早期flannel就用的最差的方式UDP,因此大家產生了一種偏見認為flannel性能很差
flannel配置
接下來我們來改一改flannel用法,以及了解一下他的配置參數是怎么配置的,不過要注意的是我們要配置flannel的話,我們定義他使用VXLAN之類配置的話他的信息定義起來也是JSON格式的比較簡單,我們要想使用VXLAN這種方式改為使用Directrouting,即能用直接路由就用不能用就用隧道進行轉發的方式,這個時候我們需要自己定義flannel自己的configmap配置文件。大家可以發現flannel本身和k8s沒有什么關系,他只是k8s的插件而已,都是第三方的,意思是說為了讓k8s運行他事先都得存在才行。沒有我們插件k8s沒法跑起來,他要事先存在就很頭疼。傳統方式我們是將flannel部署在集群中
我們之前講過部署k8s有兩種傳統方式,第一種方式為直接部署在節點上,使用systemctl來啟動服務之類的,這種方式啟動起來比較難。第二種方式為使用kubeadm把我們k8s自己的很多組件除了kubelete和docker以外都運行為pod,只不過他們作為k8s核心組件時都是運行為靜態pod的,叫static pod。同樣的邏輯,如果我們要把這些系統的組件部署為守護進程的話flannel一般也可以部署為系統的守護進程。因為他是被kubelet所調用的。因此但凡有kubelet的節點上都應該部署flannel,因為kubelet存在就應該運行pod,而pod就應該需要網絡,而網絡是kubelet調flannel或者其它網絡插件來實現為網絡做設置的
那么flannel自身是部署為系統級的守護進程還是部署在k8s之上的pod呢?兩種方式都支持,只不過對第二種方式來講我們必須把flannel配置為共享他所運行的節點的網絡名稱空間的pod,所以flannel自己的控制器如果作為pod來部署的話一定是一個demonset,一個節點上只運行一個pod副本,並且這個pod副本直接共享宿主機的網絡名稱空間,因為這樣的pod才能設置宿主機的網絡名稱空間。 不然他就沒法代為在pod中運行又能改宿主機的網絡接口了。比如他創建一個cni,每一個pod啟動時他要創建另外一段虛擬網卡,另外一半還要在虛擬機上,還能添加到某個橋上去,這個功能如果不能共享宿主機網絡名稱空間顯然是做不到的,另外,如果我們把flannel托管運行在k8s之上作為pod運行的話他雖然是pod但相當於模擬運行了系統級的守護進程。而既然作為pod運行我們要配置它,配置pod應用我們可以使用configmap和secret,當我們使用kubeadm部署時我們的flannel自身也是托管運行在k8s上作為pod存在的
可以看到我們flannel是被配置為daemonset控制器的,因為我們集群中的節點數量為3因此運行了三個副本
kubectl get daemonset -n kube-system
我們配置flannel的時候用的是配置文件,可以看到默認使用的是VXLAN的形式。但是我們也可以使用VXLAN中的第二種形式叫Directrouting,我們去配置他的Backend時后面還有很多可以使用的配置參數
kubectl get configmap -n kube-system

apiVersion: v1 data: cni-conf.json: | { "name": "cbr0", "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: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"v1","data":{"cni-conf.json":"{\n \"name\": \"cbr0\",\n \"plugins\": [\n {\n \"type\": \"flannel\",\n \"delegate\": {\n \"hairpinMode\": true,\n \"isDefaultGateway\": true\n }\n },\n {\n \"type\": \"portmap\",\n \"capabilities\": {\n \"portMappings\": true\n }\n }\n ]\n}\n","net-conf.json":"{\n \"Network\": \"10.244.0.0/16\",\n \"Backend\": {\n \"Type\": \"vxlan\"\n }\n}\n"},"kind":"ConfigMap","metadata":{"annotations":{},"labels":{"app":"flannel","tier":"node"},"name":"kube-flannel-cfg","namespace":"kube-system"}} creationTimestamp: 2019-05-09T01:33:03Z labels: app: flannel tier: node name: kube-flannel-cfg namespace: kube-system resourceVersion: "16138" selfLink: /api/v1/namespaces/kube-system/configmaps/kube-flannel-cfg uid: 6378828a-71fa-11e9-be24-000c29d142be
flannel的配置參數:大體上來講配置參數如下(上述json字符串中也可看到)
a、Network:flannel使用的CIDR格式的網絡地址,用於為Pod配置網絡功能。他通常會是全局的,他通常會是16位或8位掩碼的,然后這個全局網絡會划分成子網,每一個節點使用一個子網,比如他使用10.244.0.0/16,但是我們的節點1,節點2,節點3分別分一個子網,比如master:10.244.0.0/24。node01:10.244.1.0/24。...直到node255:10.244.255.0/24,不過這就意味着我們整個集群支持255個節點,想要更多的節點那就需要使用更大的網絡,比如10.0.0.0/8,這樣就能有10.0.0.0/24-10.255.255.0/24個節點。就可以有2的16次方,即65536個。因此我們一般不會用這么大的網段,比如我們可以使用16或12位掩碼,最起碼要留出來service使用的網絡。比如service使用10.9.0.0/12,而pod使用10.10.0.0/12。
b、subnetLen:把Network切分子網供各節點使用時,使用多長的掩碼進行切分,默認為24位。默認為什么是使用上述方式切子網呢?那是因為我們的子網默認使用24位掩碼去切分子網。 留24位就意味着8位是可以當主機地址的,也就意味着將來在一個節點上可以同時運行兩百多個Pod,如果我們運行不了那么多運行20個就夠了那么我們的掩碼可以再長一點,比如使用28位掩碼,這樣就能支持更多節點了。
c、SubnetMin:指明我們子網中的地址段中從最大最小是多少可以分給節點使用,默認就是從第一個開始,比如10/244.0.0/16中就是從10.244.0.0/24開始,10.244.255.0/24結束。但是我們可以限制,從10.244.10.0/24開始,前面10個不讓其使用
d、SubnetMAx:同c中,指明最大可以以哪個網段結束,比如10.244.100.0/24,后面155個不讓其使用。
e、Backend:指明各pod之間通信時使用什么樣的方式來進行通信。有三種方式,vxlan,host-gw,udp。vxlan還有兩種方式:默認的vxlan和Directrouting類型的VXLAN。
現在我們將默認的VXLAN配置為Directrouting類型的VXLAN
首先我們分別在兩個節點上啟動一個pod副本,用pod1 ping pod2並在node1 cni0接口進行抓包
kubectl get pods -o wide
kubectl exec -it myapp-deploy-67f6f6b4dc-76ddb /bin/sh ping 10.244.2.159
在node1 cni0 接口我們進行抓包
tcpdump -i cni0 -nn icmp
實際上這個報文被送達到flannel這個接口時他要先從cni0進來,從flannel.1出去,然后再借助於物理網卡發出去,到達flannel.1的時候就已經被封裝為vxlan報文了,因此我們再來抓flannel.1接口的包
tcpdump -i flannel.1 -nn icmp
然后我們看我們node1物理網卡和node2之間通信的報文,可以看到有overlay的報文
接下來我們把我們剛才用到的flannel的配置信息改成使用Directrouting的方式進行通信
- 我們首先定義一個json文件

{ "Network": "10.244.0.0/16", "Backend": { "Type": "vxlan", "Directrouting": "true" } }
定義完成以后我們需要將它重新應用到集群中,怎么應用呢?這其實是我們configmap中的數據,所以我們需要把它定義成一個獨立的configmap,或者是合並到flannel的kube-flannel-cfg 這個configmap中去,當然我們其實也可以直接使用kubectl edit的方式進行編輯,直接編輯配置中的net-conf.json的配置

apiVersion: v1 data: cni-conf.json: | { "name": "cbr0", "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" "Directrouting": true #此字段是添加字段 } } kind: ConfigMap metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"v1","data":{"cni-conf.json":"{\n \"name\": \"cbr0\",\n \"plugins\": [\n {\n \"type\": \"flannel\",\n \"delegate\": {\n \"hairpinMode\": true,\n \"isDefaultGateway\": true\n }\n },\n {\n \"type\": \"portmap\",\n \"capabilities\": {\n \"portMappings\": true\n }\n }\n ]\n}\n","net-conf.json":"{\n \"Network\": \"10.244.0.0/16\",\n \"Backend\": {\n \"Type\": \"vxlan\"\n }\n}\n"},"kind":"ConfigMap","metadata":{"annotations":{},"labels":{"app":"flannel","tier":"node"},"name":"kube-flannel-cfg","namespace":"kube-system"}} creationTimestamp: 2019-05-09T01:33:03Z labels: app: flannel tier: node name: kube-flannel-cfg namespace: kube-system resourceVersion: "16138" selfLink: /api/v1/namespaces/kube-system/configmaps/kube-flannel-cfg uid: 6378828a-71fa-11e9-be24-000c29d142be
此時查看路由表沒生效
ip route
經驗證直接edit是不會重載配置文件使生效的,因此我們必須重新部署flannel插件,注意,在生產環境中千萬不要刪掉然后重建,而需要在一開始部署集群的時候就要將flannel部署好
接下來我們開干,首先從flannel官網下載flannel配置文件並將文件中net-conf.json配置添加"Directrouting": true內容
wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml cat kube-flannel.yml |grep -A7 "net-conf"
然后我們先干掉flannel插件,注意我們干掉后我們所有的pod都運行不了了,因為沒有網絡插件因此我們系統節點是不就緒的
kubectl delete -f kube-flannel.yml
接下來我們重載我們修改好的flannel插件配置文件,然后查看路由表會發現我們pod之間通信直接會走物理網卡了
kubectl apply -f kube-flannel.yml ip route
接下來我們開始驗證,在node1中的pod ping node2中的pod,然后在node1的物理網卡上抓包
kubectl get pods -o wide kubectl exec -it myapp-deploy-67f6f6b4dc-76ddb /bin/sh ping 10.244.2.159
tcpdump -i ens33 -nn icmp
在物理接口上抓包可以看到並沒有顯示說二者是被overlay網絡重載,和我們物理橋接是很想象的,所以我們flannel也能實現兩個pod之間的橋接網絡。這種通信性能肯定是很ok的,但是如果兩個節點是跨網段的,他會自動降級為overlay
若我們將flannel的配置文件的net-conf.json中的Type內容改為host-gw時那它就是host-gw模式,host-gw是不支持Directrouting的,所以部署時將vxlan改為host-gw即可,不過host-gw各主機不能跨網段,跨網段是通信不了的
cat kube-flannel.yml |grep -A6 "net-conf"