K8S的網絡中主要存在4種類型的通信:
①同一Pod內的容器間通信
②各個Pod彼此間的通信
③Pod和Service間的通信
④集群外部流量和Service之間的通信
K8S為Pod和Service資源對象分別使用了各自的專有網絡,Pod網絡由K8S的網絡插件配置實現,而Service網絡則由K8S集群進行指定。
K8S使用的網絡插件需要為每個Pod配置至少一個特定的地址,即Pod IP。Pod IP地址實際存在於某個網卡(可以是虛擬機設備)上。
而Service的地址卻是一個虛擬IP地址,沒有任何網絡接口配置在此地址上,它由Kube-proxy借助iptables規則或ipvs規則重定向到本地端口,再將其調度到后端的Pod對象。Service的IP地址是集群提供服務的接口,也稱為Cluster IP。
Pod網絡和IP由K8S的網絡插件負責配置和管理,具體使用的網絡地址可以在管理配置網絡插件時進行指定,如10.244.0.0/16網絡。而Cluster網絡和IP是由K8S集群負責配置和管理,如10.96.0.0/12網絡。
一個K8S集群包含是三個網絡。
(1)節點網絡:各主機(Master、Node、ETCD等)自身所屬的網絡,地址配置在主機的網絡接口,用於各主機之間的通信,又稱為節點網絡。
(2)Pod網絡:專用於Pod資源對象的網絡,它是一個虛擬網絡,用於為各Pod對象設定IP地址等網絡參數,其地址配置在Pod中容器的網絡接口上。Pod網絡需要借助kubenet插件或CNI插件實現。
(3)Service網絡:專用於Service資源對象的網絡,它也是一個虛擬網絡,用於為K8S集群之中的Service配置IP地址,但是該地址不會配置在任何主機或容器的網絡接口上,而是通過Node上的kube-proxy配置為iptables或ipvs規則,從而將發往該地址的所有流量調度到后端的各Pod對象之上。
k8s的網路模型和網絡策略
1、Kubernetes網絡模型和CNI插件
1.1、Docker網絡模型
1.2、Kubernetes網絡模型
1.3、Flannel網絡插件
1.4、VxLAN后端和direct routing
1.5、Host-gw后端
2、網絡策略
2.1、部署Canal提供網絡策略功能
2.2、配置網絡策略
2.3、管控入站流量
2.4、管控出站流量
2.5、隔離名稱空間
1、Kubernetes網絡模型和CNI插件
在Kubernetes中設計了一種網絡模型,要求無論容器運行在集群中的哪個節點,所有容器都能通過一個扁平的網絡平面進行通信,即在同一IP網絡中。需要注意的是:在K8S集群中,IP地址分配是以Pod對象為單位,而非容器,同一Pod內的所有容器共享同一網絡名稱空間。
1.1、Docker網絡模型
了解Docker的友友們都應該清楚,Docker容器的原生網絡模型主要有3種:Bridge(橋接)、Host(主機)、none。
Bridge:借助虛擬網橋設備為容器建立網絡連接。
Host:設置容器直接共享當前節點主機的網絡名稱空間。
none:多個容器共享同一個網絡名稱空間。
從上可以看到容器內有一個網卡eth0@if39,實際上eth0@if39和veth3f1b114是一對veth pair。veth pair是一種成對出現的特殊網絡設備,可以想象它們由一根虛擬的網線進行連接的一對網卡,eth0@if39在容器中,veth3f1b114掛在網橋docker0上,最終的效果就是eth0@if39也掛在了docker0上。
橋接式網絡是目前較為流行和默認的解決方案。但是這種方案的弊端是無法跨主機通信的,僅能在宿主機本地進行,而解決該問題的方法就是NAT。所有接入到該橋接設備上的容器都會被NAT隱藏,它們發往Docker主機外部的所有流量都會經過源地址轉換后發出,並且默認是無法直接接受節點之外的其他主機發來的請求。當需要接入Docker主機外部流量,就需要進行目標地址轉換甚至端口轉換將其暴露在外部網絡當中。
容器內的屬於私有地址,需要在左側的主機上的eth0上進行源地址轉換,而右側的地址需要被訪問,就需要將eth0的地址進行NAT轉換。SNAT---->DNAT
這樣的通信方式會比較麻煩,從而需要借助第三方的網絡插件實現這樣的跨主機通信的網絡策略。
1.2、Kubernetes網絡模型
我們知道的是,在K8S上的網絡通信包含以下幾類:
容器間的通信:同一個Pod內的多個容器間的通信,它們之間通過lo網卡進行通信。
Pod之間的通信:通過Pod IP地址進行通信。
Pod和Service之間的通信:Pod IP地址和Service IP進行通信,兩者並不屬於同一網絡,實現方式是通過IPVS或iptables規則轉發。
Service和集群外部客戶端的通信,實現方式:Ingress、NodePort、Loadbalance
K8S網絡的實現不是集群內部自己實現,而是依賴於第三方網絡插件----CNI(Container Network Interface)
flannel、calico、canel等是目前比較流行的第三方網絡插件。
這三種的網絡插件需要實現Pod網絡方案的方式通常有以下幾種:
虛擬網橋、多路復用(MacVLAN)、硬件交換(SR-IOV)
無論是上面的哪種方式在容器當中實現,都需要大量的操作步驟,而K8S支持CNI插件進行編排網絡,以實現Pod和集群網絡管理功能的自動化。每次Pod被初始化或刪除,kubelet都會調用默認的CNI插件去創建一個虛擬設備接口附加到相關的底層網絡,為Pod去配置IP地址、路由信息並映射到Pod對象的網絡名稱空間。
在配置Pod網絡時,kubelet會在默認的/etc/cni/net.d/目錄中去查找CNI JSON配置文件,然后通過type屬性到/opt/cni/bin中查找相關的插件二進制文件,如下面的"portmap"。然后CNI插件調用IPAM插件(IP地址管理插件)來配置每個接口的IP地址:
CNI主要是定義容器網絡模型規范,鏈接容器管理系統和網絡插件,兩者主要通過上面的JSON格式文件進行通信,實現容器的網絡功能。CNI的主要核心是:在創建容器時,先創建好網絡名稱空間(netns),然后調用CNI插件為這個netns配置網絡,最后在啟動容器內的進程。
常見的CNI網絡插件包含以下幾種:
Flannel:為Kubernetes提供疊加網絡的網絡插件,基於TUN/TAP隧道技術,使用UDP封裝IP報文進行創建疊 加網絡,借助etcd維護網絡的分配情況,缺點:無法支持網絡策略訪問控制。
Calico:基於BGP的三層網絡插件,也支持網絡策略進而實現網絡的訪問控制;它在每台主機上都運行一個虛擬路由,利用Linux內核轉發網絡數據包,並借助iptables實現防火牆功能。實際上Calico最后的實現就是將每台主機都變成了一台路由器,將各個網絡進行連接起來,實現跨主機通信的功能。
Canal:由Flannel和Calico聯合發布的一個統一網絡插件,提供CNI網絡插件,並支持網絡策略實現。
1.3、Flannel網絡插件
在各節點上的Docker主機在docker0上默認使用同一個子網,不同節點的容器都有可能會獲取到相同的地址,那么在跨節點通信時就會出現地址沖突的問題。並且在多個節點上的docker0使用不同的子網,也會因為沒有准確的路由信息導致無法准確送達報文。
而為了解決這一問題,Flannel的解決辦法是,預留一個使用網絡,如10.244.0.0/16,然后自動為每個節點的Docker容器引擎分配一個子網,如10.244.1.0/24和10.244.2.0/24,並將分配信息保存在etcd持久存儲。
第二個問題的解決,Flannel是采用不同類型的后端網絡模型進行處理。其后端的類型有以下幾種:
VxLAN:使用內核中的VxLAN模塊進行封裝報文。也是flannel推薦的方式,
host-gw:即Host GateWay,通過在節點上創建目標容器地址的路由直接完成報文轉發,要求各節點必須在同一個2層網絡,對報文轉發性能要求較高的場景使用。
UDP:使用普通的UDP報文封裝完成隧道轉發。
1.4、VxLAN后端和direct routing
VxLAN(Virtual extensible Local Area Network)虛擬可擴展局域網,采用MAC in UDP封裝方式,具體的實現方式為:
1、將虛擬網絡的數據幀添加到VxLAN首部,封裝在物理網絡的UDP報文中
2、以傳統網絡的通信方式傳送該UDP報文
3、到達目的主機后,去掉物理網絡報文的頭部信息以及VxLAN首部,並交付給目的終端
跨節點的Pod之間的通信就是以上的一個過程,整個過程中通信雙方對物理網絡是沒有感知的。
從上面的結果可以知道 :
flannel默認就是VXLAN模式,即Overlay Network。
flanneld創建了一個flannel.1接口,它是專門用來封裝隧道協議的,默認分給集群的Pod網段為10.244.0.0/16。
flannel給k8s-master節點配置的Pod網絡為10.244.0.0段,給k8s-node01節點配置的Pod網絡為10.244.1.0段,給k8s-node01節點配置的Pod網絡為10.244.2.0段,如果有更多的節點,以此類推。
VXLAN是Linux內核本身支持的一種網絡虛擬化技術,是內核的一個模塊,在內核態實現封裝解封裝,構建出覆蓋網絡,其實就是一個由各宿主機上的Flannel.1設備組成的虛擬二層網絡。
由於VXLAN由於額外的封包解包,導致其性能較差,所以Flannel就有了host-gw模式,即把宿主機當作網關,除了本地路由之外沒有額外開銷,性能和calico差不多,由於沒有疊加來實現報文轉發,這樣會導致路由表龐大。因為一個節點對應一個網絡,也就對應一條路由條目。
host-gw雖然VXLAN網絡性能要強很多。,但是種方式有個缺陷:要求各物理節點必須在同一個二層網絡中。物理節點必須在同一網段中。這樣會使得一個網段中的主機量會非常多,萬一發一個廣播報文就會產生干擾。在私有雲場景下,宿主機不在同一網段是很常見的狀態,所以就不能使用host-gw了。
VXLAN還有另外一種功能,VXLAN也支持類似host-gw的玩法,如果兩個節點在同一網段時使用host-gw通信,如果不在同一網段中,即 當前pod所在節點與目標pod所在節點中間有路由器,就使用VXLAN這種方式,使用疊加網絡。 結合了Host-gw和VXLAN,這就是VXLAN的Direct routing模式
Flannel VxLAN的Direct routing模式配置
1.5、Host-gw后端
Flannel除了上面2種數據傳輸的方式以外,還有一種是host-gw的方式,host-gw后端是通過添加必要的路由信息使用節點的二層網絡直接發送Pod的通信報文。它的工作方式類似於Directrouting的功能,但是其並不具備VxLan的隧道轉發能力。
編輯kube-flannel的配置清單,將ConfigMap資源kube-flannel-cfg的data字段中網絡配置進行修改,
該模式下,報文轉發的相關流程如下:
1、Pod(10.244.0.17)向Pod(10.244.1.146)發送報文,查看到報文中的目的地址為:10.244.1.146,並非本地網段,會直接發送到網關(192.168.56.11);
2、網關發現該目標地址為10.244.1.146,要到達10.244.1.0/24網段,需要送達到node2 的物理網卡,node2接收以后發現該報文的目標地址屬於本機上的另一個虛擬網卡,然后轉發到相對應的Pod(10.244.1.146)
以上就是Flannel網絡模型的三種工作模式,但是flannel自身並不具備為Pod網絡實現網絡策略和網絡通信隔離的功能,為此只能借助於Calico聯合統一的項目Calnal項目進行構建網絡策略的功能。
2、網絡策略
網絡策略(Network Policy )是 Kubernetes 的一種資源。Network Policy 通過 Label 選擇 Pod,並指定其他 Pod 或外界如何與這些 Pod 通信。
Pod的網絡流量包含流入(Ingress)和流出(Egress)兩種方向。默認情況下,所有 Pod 是非隔離的,即任何來源的網絡流量都能夠訪問 Pod,沒有任何限制。當為 Pod 定義了 Network Policy,只有 Policy 允許的流量才能訪問 Pod。
Kubernetes的網絡策略功能也是由第三方的網絡插件實現的,因此,只有支持網絡策略功能的網絡插件才能進行配置網絡策略,比如Calico、Canal、kube-router等等。
PS:Kubernetes自1.8版本才支持Egress網絡策略,在該版本之前僅支持Ingress網絡策略。
2.1、部署Canal提供網絡策略功能
Calico可以獨立地為Kubernetes提供網絡解決方案和網絡策略,也可以和flannel相結合,由flannel提供網絡解決方案,Calico僅用於提供網絡策略,此時將Calico稱為Canal。結合flannel工作時,Calico提供的默認配置清單式以flannel默認使用的10.244.0.0/16為Pod網絡,因此在集群中kube-controller-manager啟動時就需要通過--cluster-cidr選項進行設置使用該網絡地址,並且---allocate-node-cidrs的值應設置為true。
2.2、配置網絡策略
在Kubernetes系統中,報文的流入和流出的核心組件是Pod資源,它們也是網絡策略功能的主要應用對象。NetworkPolicy對象通過podSelector選擇 一組Pod資源作為控制對象。NetworkPolicy是定義在一組Pod資源之上用於管理入站流量,或出站流量的一組規則,有可以是出入站規則一起生效,規則的生效模式通常由spec.policyTypes進行 定義。
默認情況下,Pod對象的流量控制是為空的,報文可以自由出入。在附加網絡策略之后,Pod對象會因為NetworkPolicy而被隔離,一旦名稱空間中有任何NetworkPolicy對象匹配了某特定的Pod對象,則該Pod將拒絕NetworkPolicy規則中不允許的所有連接請求,但是那些未被匹配到的Pod對象依舊可以接受所有流量。
就特定的Pod集合來說,入站和出站流量默認是放行狀態,除非有規則可以進行匹配。還有一點需要注意的是,在spec.policyTypes中指定了生效的規則類型,但是在networkpolicy.spec字段中嵌套定義了沒有任何規則的Ingress或Egress時,則表示拒絕入站或出站的一切流量。
2.3、管控入站流量
NetworkPolicy資源屬於名稱空間級別,它的作用范圍為其所屬的名稱空間。
1、設置默認的Ingress策略
用戶可以創建一個NetworkPolicy來為名稱空間設置一個默認的隔離策略,該策略選擇所有的Pod對象,然后允許或拒絕任何到達這些Pod的入站流量,
2.4、管控出站流量
通常,出站的流量默認策略應該是允許通過的,但是當有精細化需求,僅放行那些有對外請求需要的Pod對象的出站流量,也可以先為名稱空間設置“禁止所有”的默認策略,再細化制定准許的策略。networkpolicy.spec中嵌套的Egress字段用來定義出站流量規則。
1、設定默認Egress策略
和Igress一樣,只需要通過policyTypes字段指明生效的Egress類型規則,然后不去定義Egress字段,就不會去匹配到任何目標端點,從而拒絕所有的出站流量。
2.5、隔離名稱空間
實踐中,通常需要彼此隔離所有的名稱空間,但是又需要允許它們可以和kube-system名稱空間中的Pod資源進行流量交換,以實現監控和名稱解析等各種管理功能。下面的配置清單示例在default名稱空間定義相關規則,在出站和入站都默認均為拒絕的情況下,它用於放行名稱空間內部的各Pod對象之間的通信,以及和kube-system名稱空間內各Pod間的通信。
需要注意的是,有一些額外的系統附件可能會單獨部署到獨有的名稱空間中,比如將prometheus監控系統部署到prom名稱空間等,這類具有管理功能的附件所在的名稱空間和每一個特定的名稱空間的出入流量也是需要被放行的。