十二. k8s--網絡策略flannel與canal學習筆記

k8s網絡CNI之flannel

k8s網絡模型

  • Container to Container
  • Pod to Pod
  • Service to Pod
  • external to Service

常見CNI項目

  • Flannel,提供疊加網絡,基於linux TUN/TAP,使用UDP封裝IP報
    文來建立疊加網絡,並藉助etcd維護網絡分配狀況
  • Calico,基於BGP的三層網絡,支持網絡策略實現網絡的訪問控制。在每臺機器上運行一個vRouter,利用內核轉發數據包,並藉助iptables實現防火牆等功能
  • Canal,由Flannel和Calico聯合發佈的一個統一網絡插件,支持網絡策略
  • Weave Net,多主機容器的網絡方案,支持去中心化的控制平面,數據平面上,經過UDP封裝實現L2 Overlay
  • Contiv,思科方案,直接提供多租戶網絡,支持L2(VLAN)、L3(BGP)、Overlay(VXLAN)
  • OpenContrail,Juniper開源
  • kube-router,K8s網絡一體化解決方案,可取代kube-proxy實現基於ipvs的Service,支持網絡策略、完美兼容BGP的高級特性

重點了解Flannel, Calico, Canal, kube-routermysql

Flannel插件

首先,flannel會利用Kubernetes API或者etcd用於存儲整個集羣的網絡配置,其中最主要的內容爲設置集羣的網絡地址空間,例如,設定整個集羣內全部容器的IP都取自網段「10.1.0.0/16」。接着,flannel會在每一個主機中運行flanneld做爲agent,它會爲所在主機從集羣的網絡地址空間中,獲取一個小的網段subnet,本主機內全部容器的IP地址都將從中分配。而後,flanneld再將本主機獲取的subnet以及用於主機間通訊的Public IP,一樣經過kubernetes API或者etcd存儲起來。最後,flannel利用各類backend mechanism,例如udp,vxlan等等,跨主機轉發容器間的網絡流量,完成容器間的跨主機通訊。

你們都知道Kubernetes是經過CNI標準對接網絡插件的,可是當你去看Flannel(coreos/flannel)的代碼時,並無發現它實現了CNI的接口。若是你玩過其餘CNI插件,你會知道還有一個二進制文件用來供kubele調用,而且會調用後端的網絡插件。對於Flannel(coreos/flannel)來講,這個二進制文件是什麼呢?

這個二進制文件就對應宿主機的/etc/cni/net.d/flannel

flannel原理說明

如今,咱們來簡單看一下,若是上方Machine A中IP地址爲10.1.15.2/24的容器要與下方Machine B中IP地址爲10.1.16.2/24的容器進行通訊,封包是如何進行轉發的。從上文可知,每一個主機的flanneld會將本身與所獲取subnet的關聯信息存入etcd中,例如,subnet 10.1.15.0/24所在主機可經過IP 192.168.0.100訪問,subnet 10.1.16.0/24可經過IP 192.168.0.200訪問。反之,每臺主機上的flanneld經過監聽etcd,也可以知道其餘的subnet與哪些主機相關聯。以下圖,Machine A上的flanneld經過監聽etcd已經知道subnet 10.1.16.0/24所在的主機能夠經過Public 192.168.0.200訪問,並且熟悉docker橋接模式的同窗確定知道,目的地址爲10.1.16.2/24的封包一旦到達Machine B,就能經過cni0網橋轉發到相應的pod,從而達到跨宿主機通訊的目的。

    所以,flanneld只要想辦法將封包從Machine A轉發到Machine B就OK了,而上文中的backend就是用於完成這一任務。不過,達到這個目的的方法是多種多樣的,因此咱們也就有了不少種backend。在這裏咱們舉例介紹的是最簡單的一種方式`hostgw`:由於`Machine A和Machine B處於同一個子網內`,它們本來就能直接互相訪問。所以最簡單的方法是:在Machine A中的容器要訪問Machine B的容器時,咱們能夠將Machine B當作是網關,當有封包的目的地址在subnet 10.1.16.0/24範圍內時,就將其直接轉發至B便可。而這經過下圖中那條紅色標記的路由就能完成,對於Machine B同理可得。由此,在知足仍有subnet能夠分配的條件下,咱們能夠將上述方法擴展到任意數目位於同一子網內的主機。而任意主機若是想要訪問主機X中subnet爲S的容器,只要在本主機上添加一條目的地址爲R,網關爲X的路由便可。

flannel配置參數

Network,全局CIDR格式的IPv4網絡,字符串格式,必選
SubnetLen,子網,默認爲24位
SubnetMin,分配給節點的起始子網
SubnetMax,分配給節點的最大子網
Backend,flannel要使用的後端

flannel初始配置

[root@master bin]# cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

flannel後端實現原理

host-gw

hostgw是最簡單的backend,它的原理很是簡單,直接添加路由,將目的主機當作網關,直接路由原始封包。例如,咱們從etcd中監聽到一個EventAdded事件:subnet爲10.1.15.0/24被分配給主機Public IP 192.168.0.100,hostgw要作的工做很是簡單,在本主機上添加一條目的地址爲10.1.15.0/24,網關地址爲192.168.0.100,輸出設備爲上文中選擇的集羣間交互的網卡便可。對於EventRemoved事件,刪除對應的路由便可。

VxLAN

當有一個EventAdded到來時,flanneld如何進行配置的,以及封包是如何在flannel網絡中流動的。

如上圖所示,當主機B加入flannel網絡時,和其餘全部backend同樣,它會將本身的subnet 10.1.16.0/24和Public IP 192.168.0.101寫入etcd中,和其餘backend不同的是,它還會將vtep設備flannel.1的mac地址也寫入etcd中。linux

以後,主機A會獲得EventAdded事件,並從中獲取上文中B添加至etcd的各類信息。這個時候,它會在本機上添加三條信息:nginx

  1. 路由信息:全部通往目的地址10.1.16.0/24的封包都經過vtep設備flannel.1設備發出,發往的網關地址爲10.1.16.0,即主機B中的flannel.1設備。sql

  2. fdb信息:MAC地址爲MAC B的封包,都將經過vxlan首先發往目的地址192.168.0.101,即主機Bdocker

  3. arp信息:網關地址10.1.16.0的地址爲MAC Bshell

如今有一個容器網絡封包要從A發往容器B,和其餘backend中的場景同樣,封包首先經過網橋轉發到主機A中。此時經過,查找路由表,該封包應當經過設備flannel.1發往網關10.1.16.0。經過進一步查找arp表,咱們知道目的地址10.1.16.0的mac地址爲MAC B。到如今爲止,vxlan負載部分的數據已經封裝完成。因爲flannel.1是vtep設備,會對經過它發出的數據進行vxlan封裝(這一步是由內核完成的,至關於udp backend中的proxy),那麼該vxlan封包外層的目的地址IP地址該如何獲取呢?事實上,對於目的mac地址爲MAC B的封包,經過查詢fdb,咱們就能知道目的主機的IP地址爲192.168.0.101。 最後,封包到達主機B的eth0,經過內核的vxlan模塊解包,容器數據封包將到達vxlan設備flannel.1,封包的目的以太網地址和flannel.1的以太網地址相等,三層封包最終將進入主機B並經過路由轉發達到目的容器。

虛擬網絡數據幀添加到VxLAN首部後,封裝在物理網絡UDP報文中,到達目地主機後,去掉物理網絡報文頭部及VxLAN首部,再將報文交付給目的終端後端

VxLAN後端使用隧道網絡轉發會致使必定和流量開銷,VxLAN DirectRouting模式,經過添加必要的路由信息使用節點的二層網絡直接發送Pod通訊報文,僅在跨IP網絡時,才啓用隧道方式。這樣,在不跨IP網絡時,性能基本接近二層物理網絡api

已經建立的flannel的網絡配置修改後不會生效, 只能刪掉flannel, 修改yaml文件後從新建立, 因此必定要提早肯定好網絡配置

k8s網絡策略之canal

canal安裝

curl https://docs.projectcalico.org/v3.9/manifests/canal.yaml -O
kubectl apply -f canal.yaml

NetworkPolicy相關術語

kubectl explain networkpolicy.spec講解:

  • egress 出站流量規則 能夠根據ports和to去定義規則。ports下能夠指定目標端口和協議。to(目標地址):目標地址分爲ip地址段、pod、namespace
  • ingress 入站流量規則 能夠根據ports和from。ports下能夠指定目標端口和協議。from(來自那個地址能夠進來):地址分爲ip地址段、pod、namespace
  • podSelector 定義NetworkPolicy的限制範圍。直白的說就是規則應用到那個pod上。podSelector: {},留空就是定義對當前namespace下的全部pod生效。沒有定義白名單的話 默認就是Deny ALL (拒絕全部)
  • policyTypes 指定那個規則 那個規則生效,不指定就是默認規則。

實驗

建立兩個namespace

kubectl create namespace dev
kubectl create namespace prod

建立pod

apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1

管理入站流量

kubectl explain networkpolicy.spec.ingress
NetworkPolicy屬於名稱空間級別
參數:
from,源地址對象列表,多個項目間邏輯關係爲或,若爲空,表示匹配一切源地址;若至少有一個值,則僅容許列表中流量經過
ports,可被訪問的端口對象列表,多個項目間爲邏輯或,若爲空,表示匹配Pod的全部端口;若至少有一個值,則僅容許訪問指定的端口

拒絕全部入站流量的規則:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-policy
spec:
  podSelector: {}
  policyTypes:
  - Ingress
沒有定義Ingress規則, 可是寫到了policyTypes裏, 就表示默認拒絕入方向訪問, 沒有寫Egress, 表示默認容許出方向訪問
[root@master networkpolicy]# kubectl apply -f deny-all.yaml -n dev
networkpolicy.networking.k8s.io/deny-all-policy created

容許全部入站流量的規則:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}   # 匹配全部Pod
  ingress:
  - {}              # 定義爲空, 表示容許訪問
  policyTypes: ["Ingress"]

放入特定入站流量

僅定義from將默認容許本地Pod全部端口;僅定義ports將默認容許全部源端點;同時定義from和ports時,是邏輯與關係
多個from之間是邏輯或關係
多個ports之間是邏輯或關係
from與ports間是邏輯與關係
from下ipBlock、namespaceSelector、podSelector同時使用多個時,爲邏輯或關係
# 爲pod打標籤
[root@master manifests]# kubectl label pod myapp -n dev app=myapp --overwrite
pod/myapp labeled
[root@master manifests]# kubectl get pod -n dev --show-labels
NAME    READY   STATUS    RESTARTS   AGE   LABELS
myapp   1/1     Running   0          29m   app=myapp
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-myapp-ingress
  name: default
spec:
  podSelector:         # 該規則只在當前的namespace下,攜帶app: myapp標籤的pod生效。限制請求的類型包括Ingress和Egress
    matchLabels:
      app: myapp
  policyTypes: ["Ingress"]
  ingress:
  - from:
    - ipBlock:                  # 網絡地址塊
        cidr: 10.244.0.0/16     # 容許某個網段訪問
        except:                 # 排除某個網段或ip訪問(只拒絕掉10.244.1.5)          
        - 10.244.1.5/32
    - podSelector:              # 攜帶了app: myapp標籤的pod能夠訪問
        matchLabels:
          app: myapp
    ports:
    - protocol: TCP
      port: 80

管理出站流量

拒絕全部出站流量

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-egress
spec:
  podSelector: {}
  policyTypes: ["Egress"]

放行特定的出站流量

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-tomcat-egress
spec:
  podSelector:
    matchLabels:
      app: tomcat
  policyTypes: ["Egress"]
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: nginx
  - ports:
    - protocol: TCP
      port: 80
  - to:
    - podSelector:
        matchLabels:
          app: mysql
    ports:
    - protocol: TCP
      port: 3306
對app=tomcat的Pod,限制只能訪問app=nginx的80端口和app=mysql的3306端口

隔離名稱空間

隔離名稱空間,應該放行與kube-system名稱空間中Pod的通訊,以實現監控和名稱解析等各類管理功能tomcat

kubectl explain networkpolicy.spec.ingress.from.namespaceSelector.matchExpressions
kubectl explain networkpolicy.spec.egress.to.namespaceSelector.matchExpressions
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: namespace-deny-all
  namespace: default
spec:
  policyTypes: ["Ingress","Egress"]
  podSelector: {}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: namespace-
  namespace: default
spec:
  policyTypes: ["Ingress","Egress"]
  podSelector: {}
  ingress:
  - from:
    - namespaceSelector:
        matchExpressions:
        - key: name
          operator: In
          values: ["default","kube-system"]
  egress:
  - to:
    - namespaceSelector:
        matchExpressions:
        - key: name
          operator: In
          values: ["default","kube-system"]

參考連接

https://pdf.us/2019/03/27/3129.html

https://my.oschina.net/jxcdwangtao/blog/1624486

https://www.cnblogs.com/YaoDD/p/7681811.html

https://www.cnblogs.com/xzkzzz/p/9952716.html

相關文章
相關標籤/搜索