flanneld和calico

 

  Kubernetes的網絡通信問題:
  1. 容器間通信: 即同一個Pod內多個容器間通信,通常使用loopback來實現。
  2. Pod間通信: K8s要求,Pod和Pod之間通信必須使用Pod-IP 直接訪問另一個Pod-IP
  3. Pod與Service通信: 即PodIP去訪問ClusterIP,當然,clusterIP實際上是IPVS 或 iptables規則的虛擬IP,是沒有TCP/IP協議棧支持的。但不影響Pod訪問它.
  4. Service與集羣外部Client的通信,即K8s中Pod提供的服務必須能被互聯網上的用戶所訪問到。

需要注意的是,k8s集羣初始化時的service網段,pod網段,網絡插件的網段,以及真實服務器的網段,都不能相同,如果相同就會出各種各樣奇怪的問題,而且這些問題在集羣做好之後是不方便改的,改會導致更多的問題,所以,就在搭建前將其規劃好。

CNI(容器網絡接口):
  這是K8s中提供的一種通用網絡標準規範,因爲k8s本身不提供網絡解決方案。
  目前比較知名的網絡解決方案有:
    flannel
    calico
    canel
    kube-router
    .......
等等,目前比較常用的時flannel和calico,flannel的功能比較簡單,不具備複雜網絡的配置能力,calico是比較出色的網絡管理插件,單具備複雜網絡配置能力的同時,往往意味着本身的配置比較複雜,所以相對而言,比較小而簡單的集羣使用flannel,考慮到日後擴容,未來網絡可能需要加入更多設備,配置更多策略,則使用calico更好
所有的網絡解決方案,它們的共通性:
  1. 虛擬網橋
  2. 多路複用:MacVLAN
  3. 硬件交換:SR-IOV(單根-I/O虛擬網絡):它是一種物理網卡的硬件虛擬化技術,它通過輸出VF(虛擬功能)來將網卡虛擬爲多個虛擬子接口,每個VF綁定給一個VM後,該VM就可以直接操縱該物理網卡。

kubelet來調CNI插件時,會到 /etc/cni/net.d/目錄下去找插件的配置文件,並讀取它,來加載該插件,並讓該網絡插件來爲Pod提供網絡服務。

flannel網絡插件要怎麼部署?
 1. flannel部署到那個節點上?
  因爲kubelet是用來管理Pod的,而Pod運行需要網絡,因此凡是部署kubelet的節點,都需要部署flannel來提供網絡,因爲kubelet正是通過調用flannel來實現爲Pod配置網絡的(如:添加網絡,配置網絡,激活網絡等)。

 2. flannel自身要如何部署?
  1》它支持直接運行爲宿主機上的一個守護進程。
  2》它也支持運行爲一個Pod
  對於運行爲一個Pod這種方式:就必須將flannel配置爲共享當前宿主機的網絡名稱空間的Pod,若flannel作爲控制器控制的Pod來運行的話,它的控制器必須是DaemonSet,在每一個節點上都控制它僅能運行一個Pod副本,而且該副本必須直接共享宿主機的網絡名稱空間,因爲只有這樣,此Pod才能設置宿主機的網絡名稱空間,因爲flannel要在當前宿主機的網絡名稱空間中創建CNI虛擬接口,還要將其他Pod的另一半veth橋接到虛擬網橋上,若不共享宿主機的網絡名稱空間,這是沒法做到的。

3. flannel的工作方式有3種:
  1) VxLAN:
   而VxLAN有兩種工作方式:
    a. VxLAN: 這是原生的VxLAN,即直接封裝VxLAN首部,UDP首部,IP,MAC首部這種的。
    b. DirectRouting: 這種是混合自適應的方式, 即它會自動判斷,若當前是相同二層網絡
       (即:不垮路由器,二層廣播可直達),則直接使用Host-GW方式工作,若發現目標是需要跨網段
       (即:跨路由器)則自動轉變爲使用VxLAN的方式。
  2) host-GW: 這種方式是宿主機內Pod通過虛擬網橋互聯,然後將宿主機的物理網卡作爲網關,當需要訪問其它Node上的Pod時,只需要將報文發給宿主機的物理網卡,由宿主機通過查詢本地路由表,來做路由轉發,實現跨主機的Pod通信,這種模式帶來的問題時,當k8s集羣非常大時,會導致宿主機上的路由表變得非常巨大,而且這種方式,要求所有Node必須在同一個二層網絡中,否則將無法轉發路由,這也很容易理解,因爲如果Node之間是跨路由的,那中間的路由器就必須知道Pod網絡的存在,它才能實現路由轉發,但實際上,宿主機是無法將Pod網絡通告給中間的路由器,因此它也就無法轉發理由。
  3) UDP: 這種方式性能最差的方式,這源於早期flannel剛出現時,Linux內核還不支持VxLAN,即沒有VxLAN核心模塊,因此flannel採用了這種方式,來實現隧道封裝,其效率可想而知,因此也給很多人一種印象,flannel的性能很差,其實說的是這種工作模式,若flannel工作在host-GW模式下,其效率是非常高的,因爲幾乎沒有網絡開銷。

4. flannel的網絡配置參數:
  1) Network: flannel使用的CIDR格式的網絡地址,主要用於爲Pod配置網絡功能。
   如: 10.10.0.0/16 --->
    master: 10.10.0.0/24
    node01: 10.10.1.0/24
    .....
    node255: 10.10.255.0/24

  2) SubnetLen: 把Network切分爲子網供各節點使用時,使用多長的掩碼來切分子網,默認是24位.
  3) SubnetMin: 若需要預留一部分IP時,可設置最小從那裏開始分配IP,如:10.10.0.10/24 ,這樣就預留出了10個IP
  4) SubnetMax: 這是控制最多分配多個IP,如: 10.10.0.100/24 這樣在給Pod分配IP時,最大分配到10.10.0.100了。
  5) Backend: 指定後端使用的協議類型,就是上面提到的:vxlan( 原始vxlan,directrouter),host-gw, udp

flannel的配置:
  .....
  net-conf.json: |
    {
     "Network": "10.10.0.0/16",
     "Backend": {
     "Type": "vxlan",    #當然,若你很確定自己的集羣以後也不可能跨網段,你完全可以直接設置爲 host-gw.
     "Directrouting": true  #默認是false,修改爲true就是可以讓VxLAN自適應是使用VxLAN還是使用host-gw了。
     }
    }

#在配置flannel時,一定要注意,不要在半道上,去修改,也就是說要在你部署k8s集羣后,就直接規劃好,而不要在k8s集羣已經運行起來了,你再去修改,雖然可能也不會出問題,但一旦出問題,你就!!

  

  #在配置好,flannel後,一定要測試,創建新Pod,看看新Pod是否能從flannel哪裏獲得IP地址,是否能通信。

 

Calico:
  Calico是一種非常複雜的網絡組件,它需要自己的etcd數據庫集羣來存儲自己通過BGP協議獲取的路由等各種所需要持久保存的網絡數據信息,因此在部署Calico時,早期是需要單獨爲Calico部署etcd集羣的,因爲在k8s中,訪問etcd集羣只有APIServer可以對etcd進行讀寫,其它所有組件都必須通過APIServer作爲入口,將請求發給APIServer,由APIServer來從etcd獲取必要信息來返回給請求者,但Caclico需要自己寫,因此就有兩種部署Calico網絡插件的方式,一種是部署兩套etcd,另一種就是Calico不直接寫,而是通過APIServer做爲代理,來存儲自己需要存儲的數據。通常第二種使用的較多,這樣可降低系統複雜度。
  當然由於Calico本身很複雜,但由於很多k8s系統可能存在的問題是,早期由於各種原因使用了flannel來作爲網絡插件,但後期發現需要使用網絡策略的需求,怎麼辦?
  目前比較成熟的解決方案是:flannel + Calico, 即使用flannel來提供簡單的網絡管理功能,而使用Calico提供的網絡策略功能。


Calico網絡策略:

  

  Egress:是出站的流量,即自己是源,遠端爲服務端,因此我自己的源IP可確定,但端口不可預知, 目標的端口和IP都是確定的,因此to 和 ports都是指目標的IP和端口。
  Ingress:是入站的流量,即自己爲目標,而遠端是客戶端,因此要做控制,就只能對自己的端口 和 客戶端的地址 做控制。
我們通過Ingress 和 Egress定義的網絡策略是對一個Pod生效 還是 對一組Pod生效?
這個就要通過podSelector來實現了。
而且在定義網絡策略時,可以很靈活,如:入站都拒絕,僅允許出站的; 或 僅允許指定入站的,出站都允許等等。
另外,在定義網絡策略時,也可定義 在同一名稱空間中的Pod都可以自由通信,但跨名稱空間就都拒絕。

網絡策略的生效順序:
  越具體的規則越靠前,越靠前,越優先匹配

網絡策略的定義:
  kubectl explain networkpolicy
  spec:
   egress: <[]Object> :定義出站規則
   ingress: <[]Object>: 定義入站規則
   podSelector: 如論是入站還是出站,這些規則要應用到那些Pod上。
   policyType:[Ingress|Egress| Ingress,Egress] :
     它用於定義若同時定義了egress和ingress,到底那個生效?若僅給了ingress,則僅ingress生效,若設置爲Ingress,Egress則兩個都生效。
        注意:policyType在使用時,若不指定,則當前你定義了egress就egress生效,若egress,ingress都定義了,則兩個都生效!!
    還有,若你定義了egress, 但policyType: ingress, egress ; egress定義了,但ingress沒有定義,這種要會怎樣?
    其實,這時ingress的默認規則會生效,即:若ingress的默認規則爲拒絕,則會拒絕所有入站請求,若爲允許,則會允許所有入站請求,
    所以,若你只想定義egress規則,就明確寫egress !!

  egress:<[]Object>
      ports: <[]Object> :因爲ports是有端口號 和 協議類型的,因此它也是對象列表
       port :
    protocol: 這兩個就是用來定義目標端口和協議的。
   to :<[]Object>
       podSelector: <Object> : 在控制Pod通信時,可控制源和目標都是一組Pod,然後控制這兩組Pod之間的訪問。
      ipBlock:<[]Object> : 指定一個Ip地址塊,只要在這個IP範圍內的,都受到策略的控制,而不區分是Pod還是Service。
      namespaceSelector: 這是控制對指定名稱空間內的全部Pod 或 部分Pod做訪問控制。

  Ingress:
      from: 這個from指訪問者訪問的IP
      ports: 也是訪問者訪問的Port

複製代碼
複製代碼
#定義網絡策略:
vim   networkpolicy-demo.yaml
apiVersion:  networking.k8s.io/v1  
    #注意:雖然kubectl  explain networkpolicy中顯示爲 extensions/v1beta1 ,但你要注意看說明部分.
kind:  NetworkPolicy
metadata:
     name:  deny-all-ingress
     namespace:  dev
spec:
     podSelector:  {}  #這裏寫空的含義是,選擇指定名稱空間中所有Pod
     policyTypes:
     -  Ingress        #這裏指定要控制Ingress(進來的流量),但又沒有指定規則,就表示全部拒絕,只有明確定義的,纔是允許的。
                       #egress: 出去的流量不控制,其默認規則就是允許,因爲不關心,所以愛咋咋地的意思。
    
#寫一個簡單的自主式Pod的定義:
vim  pod1.yaml
    apiVersion:  v1
    kind: Pod
    metadata: 
      name:  pod1
    spec:
      containers:
      - name:  myapp
        image:  harbor.zcf.com/k8s/myapp:v1

#創建dev名稱空間,並應用規則
kubectl apply -f networkpolicy-demo.yaml -n dev

# kubectl describe -n dev networkpolicies
    Name:         deny-all-ingress
    Namespace:    dev
   ........................
    Spec:
      PodSelector:     <none> (Allowing the specific traffic to all pods in this namespace)
      Allowing ingress traffic:
        <none> (Selected pods are isolated for ingress connectivity)
      Allowing egress traffic:
        <none> (Selected pods are isolated for egress connectivity)


#查看dev名稱空間中的網絡規則:
kubectl get networkpolicy -n dev     
 或   
 kubectl  get  netpol  -n  dev

#然後在dev 和 prod 兩個名稱空間中分別創建pod
kubectl  apply   -f  pod1.yaml   -n   dev
kubectl  apply   -f  pod1.yaml   -n   prod

#接着測試訪問這兩個名稱空間中的pod
kubectl  get  pod  -n  dev   -o   wide
    
    #測試訪問:
        curl   http://POD_IP
        
kubectl  get  pod  -n  prod   -o   wide

    #測試訪問:
        curl   http://POD_IP

#通過以上測試,可以看到,dev名稱空間中的pod無法被訪問,而prod名稱空間中的pod則可被訪問。
複製代碼
複製代碼
複製代碼
複製代碼
#測試放行所有dev的ingress入站請求。
# vim  networkpolicy-demo.yaml
    apiVersion:  networking.k8s.io/v1   
    kind:  NetworkPolicy
    metadata:
       name: allow-all-ingress
       namespace:  dev
    spec:
       podSelector:  {}
       ingress:
       -  {}      #這就表示允許所有,因爲定義了規則,但規則是空的,即允許所有。
       policyTypes:
       -  Ingress

#接着測試,和上面測試一樣,也是訪問dev 和 prod兩個名稱空間中的pod,若能訪問,則成功。
# kubectl describe -n dev netpol
    Name:         deny-all-ingress
    Namespace:    dev
    .....................
    Spec:
      PodSelector:     <none> (Allowing the specific traffic to all pods in this namespace)
      Allowing ingress traffic:
        To Port: <any> (traffic allowed to all ports)
        From: <any> (traffic not restricted by source)
      Allowing egress traffic:
        <none> (Selected pods are isolated for egress connectivity)
      Policy Types: Ingress
複製代碼
複製代碼

  #測試定義一個僅允許訪問dev名稱空間中,pod標籤 app=myapp 的一組pod的80端口

  

複製代碼
複製代碼
#先給pod1打上app=myapp的標籤
#kubectl label pod pod1 app=myapp -n dev
    
vim  allow-dev-80.yaml
 apiVersion: networking.k8s.io/v1
 kind: NetworkPolicy
 metadata:
    name: allow-myapp-ingress
 spec:
   podSelector:
      matchLabels:
        app: myapp
     ingress:
     - from:
        - ipBlock:
            cidr:  10.10.0.0/16
            except:
            -  10.10.1.2/32
       ports:
       - protocol:  TCP
         port:  88
       - protocol:  TCP
         port:  443

#查看定義的ingress規則
kubectl  get  netpol  -n  dev

#然後測試訪問 dev 名稱空間中的pod
curl  http://Pod_IP
curl  http://Pod_IP:443

curl   http://Pod_IP:88
複製代碼
複製代碼

複製代碼
複製代碼
上圖測試:
1. 先給dev名稱空間打上標籤
  kubectl   label  namespace   dev   ns=dev

2. 編寫網絡策略配置清單
  vim  allow-ns-dev.yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-ns-dev
    spec:
      podSelector: {}
      ingress:
      - from:
         - namespaceSelector:
              matchLabels:
                ns: dev
      egress:
      - to:
         - namespaceSelector:
             matchLabels:
               ns: dev



#要控制egress,也是如此,只是將ingress替換爲egress即可,然後在做測試。
另外,關於網絡策略,建議:
 名稱空間內:
     拒絕所有出站,入站流量
     僅放行出站目標爲當前名稱空間內各Pod間通信,因爲網絡策略控制的顆粒度是Pod級別的,不是名稱空間級別。
     具體的網絡策略,要根據實際需求,來定義ingress 和 egress規則。
複製代碼
複製代碼

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章