目錄html
在Kubernetes中設計了一種網絡模型,要求不管容器運行在集羣中的哪一個節點,全部容器都能經過一個扁平的網絡平面進行通訊,即在同一IP網絡中。須要注意的是:在K8S集羣中,IP地址分配是以Pod對象爲單位,而非容器,同一Pod內的全部容器共享同一網絡名稱空間。node
Docker容器的原生網絡模型主要有3種:Bridge(橋接)、Host(主機)、none。linux
[root@master ~]# docker network ls NETWORK ID NAME DRIVER SCOPE ac364a575cb8 bridge bridge local e3bdb1c29df6 host host local 4161accaf430 none null local #none網絡,在該網絡下的容器僅有lo網卡,屬於封閉式網絡,一般用於對安全性要求較高而且不須要聯網的應用 #host網絡,共享宿主機的網絡名稱空間,容器網絡配置和host一致,可是存在端口衝突的問題 #bridge網絡,Docker安裝完成時會建立一個名爲docker0的linux bridge,不指定網絡時,建立的網絡默認爲橋接網絡,都會橋接到docker0上。
橋接式網絡是目前較爲流行和默認的解決方案。可是這種方案的弊端是沒法跨主機通訊的,僅能在宿主機本地進行,而解決該問題的方法就是NAT。全部接入到該橋接設備上的容器都會被NAT隱藏,它們發往Docker主機外部的全部流量都會通過源地址轉換後發出,而且默認是沒法直接接受節點以外的其餘主機發來的請求。當須要接入Docker主機外部流量,就須要進行目標地址轉換甚至端口轉換將其暴露在外部網絡當中。以下圖:
容器內的屬於私有地址,須要在左側的主機上的eth0上進行源地址轉換,而右側的地址須要被訪問,就須要將eth0的地址進行NAT轉換。SNAT---->DNAT
這樣的通訊方式會比較麻煩,從而須要藉助第三方的網絡插件實現這樣的跨主機通訊的網絡策略。
容器內的屬於私有地址,須要在左側的主機上的eth0上進行源地址轉換,而右側的地址須要被訪問,就須要將eth0的地址進行NAT轉換。SNAT---->DNAT
這樣的通訊方式會比較麻煩,從而須要藉助第三方的網絡插件實現這樣的跨主機通訊的網絡策略。nginx
咱們知道的是,在K8S上的網絡通訊包含如下幾類:git
K8S網絡的實現不是集羣內部本身實現,而是依賴於第三方網絡插件----CNI(Container Network Interface)
flannel、calico、canel等是目前比較流行的第三方網絡插件。github
這三種的網絡插件須要實現Pod網絡方案的方式一般有如下幾種:
虛擬網橋、多路複用(MacVLAN)、硬件交換(SR-IOV)docker
K8S支持CNI插件進行編排網絡,以實現Pod和集羣網絡管理功能的自動化。每次Pod被初始化或刪除,kubelet都會調用默認的CNI插件去建立一個虛擬設備接口附加到相關的底層網絡,爲Pod去配置IP地址、路由信息並映射到Pod對象的網絡名稱空間。json
在配置Pod網絡時,kubelet會在默認的/etc/cni/net.d/目錄中去查找CNI JSON配置文件,而後經過type屬性到/opt/cni/bin中查找相關的插件二進制文件,以下面的"portmap"。而後CNI插件調用IPAM插件(IP地址管理插件)來配置每一個接口的IP地址:vim
[root@master ~]# cat /etc/cni/net.d/10-flannel.conflist { "name": "cbr0", "plugins": [ { "type": "flannel", "delegate": { "hairpinMode": true, "isDefaultGateway": true } }, { "type": "portmap", "capabilities": { "portMappings": true } } ] }
CNI主要是定義容器網絡模型規範,連接容器管理系統和網絡插件,二者主要經過上面的JSON格式文件進行通訊,實現容器的網絡功能。CNI的主要核心是:在建立容器時,先建立好網絡名稱空間(netns),而後調用CNI插件爲這個netns配置網絡,最後在啓動容器內的進程
常見的CNI網絡插件包含如下幾種:後端
在各節點上的Docker主機在docker0上默認使用同一個子網,不一樣節點的容器都有可能會獲取到相同的地址,那麼在跨節點通訊時就會出現地址衝突的問題。而且在多個節點上的docker0使用不一樣的子網,也會由於沒有準確的路由信息致使沒法準確送達報文。
而爲了解決這一問題,Flannel的解決辦法是,預留一個使用網絡,如10.244.0.0/16,而後自動爲每一個節點的Docker容器引擎分配一個子網,如10.244.1.0/24和10.244.2.0/24,並將分配信息保存在etcd持久存儲。
第二個問題的解決,Flannel是採用不一樣類型的後端網絡模型進行處理。其後端的類型有如下幾種:
UDP:使用普通的UDP報文封裝完成隧道轉發。
跨節點的Pod之間的通訊就是以上的一個過程,整個過程當中通訊雙方對物理網絡是沒有感知的。以下網絡圖:
flannel運行後,在各Node宿主機多了一個網絡接口:
#master [root@master ~]# ifconfig flannel.1 flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450 inet 10.244.0.0 netmask 255.255.255.255 broadcast 0.0.0.0 inet6 fe80::bc1b:fdff:fece:7506 prefixlen 64 scopeid 0x20<link> ether be:1b:fd:ce:75:06 txqueuelen 0 (Ethernet) RX packets 1274 bytes 1105723 (1.0 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 668 bytes 275033 (268.5 KiB) TX errors 0 dropped 8 overruns 0 carrier 0 collisions 0 #node01 [root@node01 ~]# ifconfig flannel.1 flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450 inet 10.244.1.0 netmask 255.255.255.255 broadcast 0.0.0.0 inet6 fe80::10f0:d4ff:fe41:bd69 prefixlen 64 scopeid 0x20<link> ether 12:f0:d4:41:bd:69 txqueuelen 0 (Ethernet) RX packets 2867 bytes 280059 (273.4 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 2886 bytes 353550 (345.2 KiB) TX errors 0 dropped 8 overruns 0 carrier 0 collisions 0 #node02 [root@node02 ~]# ifconfig flannel.1 flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450 inet 10.244.2.0 netmask 255.255.255.255 broadcast 0.0.0.0 inet6 fe80::e8de:4ff:fe2a:cadc prefixlen 64 scopeid 0x20<link> ether ea:de:04:2a:ca:dc txqueuelen 0 (Ethernet) RX packets 3512 bytes 605029 (590.8 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 3386 bytes 1333288 (1.2 MiB) TX errors 0 dropped 8 overruns 0 carrier 0 collisions 0
從上面的結果能夠知道 :
舉個實際例子
#啓動一個nginx容器,副本爲3 [root@master manifests]# kubectl run nginx --image=nginx:1.14-alpine --port=80 --replicas=3 [root@master manifests]# kubectl get pods -o wide |grep nginx nginx-7849c4bbcd-8srph 1/1 Running 0 22s 10.244.1.62 node01 <none> <none> nginx-7849c4bbcd-brsrv 1/1 Running 0 22s 10.244.2.74 node02 <none> <none> nginx-7849c4bbcd-vjh4w 1/1 Running 0 22s 10.244.2.73 node02 <none> <none>
查看網絡接口能夠發如今各個節點上多了一個虛擬接口cni0,其ip地址爲10.244.0.1。它是由flanneld建立的一個虛擬網橋叫cni0,在Pod本地通訊使用。 這裏須要注意的是,cni0虛擬網橋,僅做用於本地通訊.
[root@node02 ~]# ifconfig cni0 cni0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450 inet 10.244.2.1 netmask 255.255.255.0 broadcast 0.0.0.0 inet6 fe80::3c3d:dcff:fe84:c3a4 prefixlen 64 scopeid 0x20<link> ether 0a:58:0a:f4:02:01 txqueuelen 1000 (Ethernet) RX packets 125902 bytes 13768322 (13.1 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 128191 bytes 12079793 (11.5 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
容器跨主機是能夠正常通訊的,那麼容器的跨主機通訊是如何實現的呢?????master上查看路由表信息:
[root@master manifests]# ip route default via 10.0.0.2 dev ens33 proto static metric 100 10.0.0.0/24 dev ens33 proto kernel scope link src 10.0.0.10 metric 100 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 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
#在宿主機和容器內都進行ping另一臺主機上的Pod ip並進行抓包 [root@master manifests]# kubectl exec nginx-7849c4bbcd-8srph -it -- /bin/sh #進入node01的10.244.1.62 IP的pod內 [root@master ~]# kubectl exec -it nginx-7849c4bbcd-brsrv -- /bin/sh #另外開終端進入node02的10.244.2.74 IP的pod內 #使用node01的pod ping10.244.2.74 在node02節點抓包 [root@node02 ~]# tcpdump -i flannel.1 -nn host 10.244.2.74 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on flannel.1, link-type EN10MB (Ethernet), capture size 262144 bytes 17:16:06.779840 IP 10.244.1.62 > 10.244.2.74: ICMP echo request, id 2816, seq 66, length 64 17:16:06.779904 IP 10.244.2.74 > 10.244.1.62: ICMP echo reply, id 2816, seq 66, length 64 17:16:07.780045 IP 10.244.1.62 > 10.244.2.74: ICMP echo request, id 2816, seq 67, length 64 17:16:07.780080 IP 10.244.2.74 > 10.244.1.62: ICMP echo reply, id 2816, seq 67, length 64 17:16:08.780127 IP 10.244.1.62 > 10.244.2.74: ICMP echo request, id 2816, seq 68, length 64 17:16:08.780173 IP 10.244.2.74 > 10.244.1.62: ICMP echo reply, id 2816, seq 68, length 64 17:16:09.780576 IP 10.244.1.62 > 10.244.2.74: ICMP echo request, id 2816, seq 69, length 64 #使用master節點ping 10.244.2.74 在node02節點抓包 [root@node02 ~]# tcpdump -i flannel.1 -nn host 10.244.2.74 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on flannel.1, link-type EN10MB (Ethernet), capture size 262144 bytes 17:17:51.003946 IP 10.244.0.0 > 10.244.2.74: ICMP echo request, id 30700, seq 4, length 64 17:17:51.004017 IP 10.244.2.74 > 10.244.0.0: ICMP echo reply, id 30700, seq 4, length 64 17:17:52.004634 IP 10.244.0.0 > 10.244.2.74: ICMP echo request, id 30700, seq 5, length 64 17:17:52.004688 IP 10.244.2.74 > 10.244.0.0: ICMP echo reply, id 30700, seq 5, length 64 17:17:53.005045 IP 10.244.0.0 > 10.244.2.74: ICMP echo request, id 30700, seq 6, length 64 17:17:53.005098 IP 10.244.2.74 > 10.244.0.0: ICMP echo reply, id 30700, seq 6, length 64 17:17:54.005302 IP 10.244.0.0 > 10.244.2.74: ICMP echo request, id 30700, seq 7, length 64 17:17:54.005359 IP 10.244.2.74 > 10.244.0.0: ICMP echo reply, id 30700, seq 7, length 64 #能夠看到報文都是通過flannel.1網絡接口進入2層隧道進而轉發
發送到10.244.1.0/24和10.244.20/24網段的數據報文發給本機的flannel.1接口,即進入二層隧道,而後對數據報文進行封裝(封裝VxLAN首部-->UDP首部-->IP首部-->以太網首部),到達目標Node節點後,由目標Node上的flannel.1進行解封裝。
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模式
修改kube-flannel.yml文件,將flannel的configmap對象改成:
[root@master manifests]# wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml #下載原版配置文件進行修改後apply執行 [root@master manifests]# vim kube-flannel.yml ...... net-conf.json: | { "Network": "10.244.0.0/16", "Backend": { "Type": "vxlan", #注意,號 "Directrouting": true #增長字段 } } ...... #此時須要刪除flannel網絡後重建,可是會影響全部pod,生產中不建議這麼使用kubectl delete -f kube-flannel.yaml ,kubectl apply -f kube-flannel.yaml #查看已經生成相應規則 [root@master manifests]# ip route show default via 10.0.0.2 dev ens33 proto static metric 100 10.0.0.0/24 dev ens33 proto kernel scope link src 10.0.0.10 metric 100 10.244.1.0/24 via 10.0.0.11 dev ens33 10.244.2.0/24 via 10.0.0.12 dev ens33 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
配置完成後,各節點會生成相似directrouting同樣的 路由和iptables規則,用於實現二層轉發Pod網絡的通訊報文,省去了隧道轉發模式的額外開銷。可是存在的問題點是,對於不在同一個二層網絡的報文轉發,host-gw是沒法實現的。延續上面的例子,進行抓包查看:
[root@master manifests]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES myapp-deploy-65df64765c-85k8x 1/1 Running 0 28s 10.244.1.66 node01 <none> <none> myapp-deploy-65df64765c-8trf2 1/1 Running 0 28s 10.244.1.65 node01 <none> <none> myapp-deploy-65df64765c-cxgq4 1/1 Running 0 28s 10.244.2.78 node02 <none> <none> myapp-deploy-65df64765c-l646w 1/1 Running 0 28s 10.244.2.79 node02 <none> <none> pod-demo 2/2 Running 0 43s 10.244.2.77 node02 <none> <none> #進入myapp-deploy-65df64765c-85k8x 容器ping另外一個節點的容器10.244.2.79 [root@master manifests]# kubectl exec -it myapp-deploy-65df64765c-85k8x -- /bin/sh / # ping 10.244.2.79 PING 10.244.2.79 (10.244.2.79): 56 data bytes 64 bytes from 10.244.2.79: seq=0 ttl=62 time=2.812 ms 64 bytes from 10.244.2.79: seq=1 ttl=62 time=0.398 ms 64 bytes from 10.244.2.79: seq=2 ttl=62 time=0.362 ms #node02節點抓包 [root@node02 ~]# 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 08:56:37.256880 IP 10.244.1.66 > 10.244.2.79: ICMP echo request, id 2816, seq 38, length 64 08:56:37.256948 IP 10.244.2.79 > 10.244.1.66: ICMP echo reply, id 2816, seq 38, length 64 08:56:38.256295 IP 10.244.1.66 > 10.244.2.79: ICMP echo request, id 2816, seq 39, length 64 08:56:38.256371 IP 10.244.2.79 > 10.244.1.66: ICMP echo reply, id 2816, seq 39, length 64 08:56:39.255704 IP 10.244.1.66 > 10.244.2.79: ICMP echo request, id 2816, seq 40, length 64
從上面的結果能夠看到,發往10.244.1.0/24和10.244.1.0/24的包都是直接通過eth0網絡接口直接發出去的,這就是Directrouting。若是兩個節點是跨網段的,則flannel自動降級爲VxLAN模式。
此時,在各個集羣節點上執行「iptables -nL」命令能夠看到,iptables filter表的FORWARD鏈上由其生成了以下兩條轉發規則,它顯示放行了10.244.0.0/16網絡進出的全部報文,用於確保由物理接收或發送的目標地址或源地址爲10.244.0.0/16網絡的全部報文均可以正常通行。這些是 Direct Routing模式得以實現的必要條件:
再在此以前建立的Pod和宿主機上進行ping測試,能夠看到在flannel.1接口上已經抓不到包了,在eth0上能夠用抓到ICMP的包
Flannel除了上面2種數據傳輸的方式之外,還有一種是host-gw的方式,host-gw後端是經過添加必要的路由信息使用節點的二層網絡直接發送Pod的通訊報文。它的工做方式相似於Directrouting的功能,可是其並不具有VxLan的隧道轉發能力。
編輯kube-flannel的配置清單,將ConfigMap資源kube-flannel-cfg的data字段中網絡配置進行修改,以下:
[root@master ~]# vim kube-flannel.yml ...... net-conf.json: | { "Network": "10.244.0.0/16", "Backend": { "Type": "host-gw" } } ......
配置完成後,各節點會生成相似directrouting同樣的 路由和iptables規則,用於實現二層轉發Pod網絡的通訊報文,省去了隧道轉發模式的額外開銷。可是存在的問題點是,對於不在同一個二層網絡的報文轉發,host-gw是沒法實現的
[root@master manifests]# ip route default via 10.0.0.2 dev ens33 proto static metric 100 10.0.0.0/24 dev ens33 proto kernel scope link src 10.0.0.10 metric 100 10.244.1.0/24 via 10.0.0.11 dev ens33 10.244.2.0/24 via 10.0.0.12 dev ens33 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 / # ping -c 2 10.244.1.66 PING 10.244.1.66 (10.244.1.66): 56 data bytes 64 bytes from 10.244.1.66: seq=0 ttl=62 time=0.433 ms 64 bytes from 10.244.1.66: seq=1 ttl=62 time=0.334 ms --- 10.244.1.66 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.334/0.383/0.433 ms [root@master mainfest]# 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 09:09:39.496407 IP 10.244.2.78 > 10.244.1.66: ICMP echo request, id 3072, seq 0, length 64 09:09:39.496588 IP 10.244.1.66 > 10.244.2.78: ICMP echo reply, id 3072, seq 0, length 64 09:09:40.497129 IP 10.244.2.78 > 10.244.1.66: ICMP echo request, id 3072, seq 1, length 64 09:09:40.497356 IP 10.244.1.66 > 10.244.2.78: ICMP echo reply, id 3072, seq 1, length 64
該模式下,報文轉發的相關流程以下:
以上就是Flannel網絡模型的三種工做模式,可是flannel自身並不具有爲Pod網絡實現網絡策略和網絡通訊隔離的功能,爲此只能藉助於Calico聯合統一的項目Calnal項目進行構建網絡策略的功能。
網絡策略(Network Policy )是 Kubernetes 的一種資源。Network Policy 經過 Label 選擇 Pod,並指定其餘 Pod 或外界如何與這些 Pod 通訊。
Pod的網絡流量包含流入(Ingress)和流出(Egress)兩種方向。默認狀況下,全部 Pod 是非隔離的,即任何來源的網絡流量都可以訪問 Pod,沒有任何限制。當爲 Pod 定義了 Network Policy,只有 Policy 容許的流量才能訪問 Pod。
Kubernetes的網絡策略功能也是由第三方的網絡插件實現的,所以,只有支持網絡策略功能的網絡插件才能進行配置網絡策略,好比Calico、Canal、kube-router等等。
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。
#下載文件到本地 [root@master calico]# wget https://docs.projectcalico.org/v3.2/getting-started/kubernetes/installation/hosted/canal/rbac.yaml [root@master calico]# wget https://docs.projectcalico.org/v3.2/getting-started/kubernetes/installation/hosted/canal/canal.yaml #應用 [root@master calico]# kubectl apply -f rbac.yaml [root@master calico]# kubectl apply -f canal.yaml
Canal做爲DaemonSet部署到每一個節點,屬於kube-system這個名稱空間。須要注意的是,Canal只是直接使用了Calico和flannel項目,代碼自己沒有修改,Canal只是一種部署的模式,用於安裝和配置項目。
[root@master calico]# kubectl get pods -n kube-system -w NAME READY STATUS RESTARTS AGE canal-nbspn 0/3 ContainerCreating 0 2m16s canal-pj6rx 2/3 Running 0 2m16s canal-rgsnp 3/3 Running 0 2m16s
在Kubernetes系統中,報文的流入和流出的核心組件是Pod資源,它們也是網絡策略功能的主要應用對象。NetworkPolicy對象經過podSelector選擇 一組Pod資源做爲控制對象。NetworkPolicy是定義在一組Pod資源之上用於管理入站流量,或出站流量的一組規則,有能夠是出入站規則一塊兒生效,規則的生效模式一般由spec.policyTypes進行 定義。以下圖:
默認狀況下,Pod對象的流量控制是爲空的,報文能夠自由出入。在附加網絡策略以後,Pod對象會由於NetworkPolicy而被隔離,一旦名稱空間中有任何NetworkPolicy對象匹配了某特定的Pod對象,則該Pod將拒絕NetworkPolicy規則中不容許的全部鏈接請求,可是那些未被匹配到的Pod對象依舊能夠接受全部流量。
對特定的Pod集合來講,入站和出站流量默認是放行狀態,除非有規則能夠進行匹配。還有一點須要注意的是,在spec.policyTypes中指定了生效的規則類型,可是在networkpolicy.spec字段中嵌套定義了沒有任何規則的Ingress或Egress時,則表示拒絕入站或出站的一切流量。定義網絡策略的基本格式以下:
apiVersion: networking.k8s.io/v1 #定義API版本 kind: NetworkPolicy #定義資源類型 metadata: name: allow-myapp-ingress #定義NetwokPolicy的名字 namespace: default spec: #NetworkPolicy規則定義 podSelector: #匹配擁有標籤app:myapp的Pod資源 matchLabels: app: myapp policyTypes ["Ingress"] #NetworkPolicy類型,能夠是Ingress,Egress,或者二者共存 ingress: #定義入站規則 - from: - ipBlock: #定義能夠訪問的網段 cidr: 10.244.0.0/16 except: #排除的網段 - 10.244.3.0/24 - podSelector: #選定當前default名稱空間,標籤爲app:myapp能夠入站 matchLabels: app: myapp ports: #開放的協議和端口定義 - protocol: TCP port: 80
該網絡策略就是將default名稱空間中擁有標籤"app=myapp"的Pod資源開放80/TCP端口給10.244.0.0/16網段,並排除10.244.3.0/24網段的訪問,而且也開放給標籤爲app=myapp的全部Pod資源進行訪問。
建立
[root@master manifests]# mkdir networkpolicy #建立名稱空間 dev prod [root@master manifests]# kubectl create namespace dev namespace/dev created [root@master manifests]# kubectl create namespace prod namespace/prod created [root@master manifests]# kubectl get ns NAME STATUS AGE default Active 12d dev Active 6s ingress-nginx Active 7d16h kube-public Active 12d kube-system Active 12d prod Active 3s #定義規則 [root@master manifests]# cd networkpolicy/ [root@master networkpolicy]# vim ingress-def.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all-ingress spec: podSelector: {} policyTypes: - Ingress #執行 指定在dev名稱空間生效 [root@master networkpolicy]# kubectl apply -f ingress-def.yaml -n dev networkpolicy.networking.k8s.io/deny-all-ingress created [root@master networkpolicy]# kubectl get netpol -n dev NAME POD-SELECTOR AGE deny-all-ingress <none> 33s
運行Pod
[root@master networkpolicy]# vim pod-a.yaml apiVersion: v1 kind: Pod metadata: name: pod1 spec: containers: - name: myapp image: ikubernetes/myapp:v1 [root@master networkpolicy]# kubectl apply -f pod-a.yaml -n dev pod/pod1 created [root@master networkpolicy]# kubectl get pods -n dev -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod1 1/1 Running 0 21s 10.244.2.3 node02 <none> <none>
嘗試訪問剛剛建立的pod1
[root@master networkpolicy]# curl 10.244.2.3 curl: (7) Failed connect to 10.244.2.3:80; 鏈接超時
嘗試訪問在prod名稱空間的pod1
[root@master networkpolicy]# kubectl apply -f pod-a.yaml -n prod pod/pod1 created [root@master networkpolicy]# kubectl get pods -o wide -n prod NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod1 1/1 Running 0 11s 10.244.2.4 node02 <none> <none> [root@master networkpolicy]# curl 10.244.2.4 Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
總結
此時dev名稱空間使用了Ingress規則,可是爲空,表明拒絕全部入站請求,而prod名稱空間沒有定義Ingress規則,容許全部訪問。
定義dev名稱空間能夠對外被訪問
[root@master networkpolicy]# vim ingress-def.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all-ingress spec: podSelector: {} ingress: - {} policyTypes: - Ingress #應用後嘗試訪問dev名稱空間中的pod1 [root@master networkpolicy]# kubectl apply -f ingress-def.yaml -n dev networkpolicy.networking.k8s.io/deny-all-ingress configured [root@master networkpolicy]# kubectl get pods -n dev -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod1 1/1 Running 0 11m 10.244.2.3 node02 <none> <none> [root@master networkpolicy]# curl 10.244.2.3 Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
dev名稱空間中的pod1能夠被訪問,由於增長了ingress規則 {}表示映射全部。
放行特定的入棧流量:
可使用標籤選擇器來選擇指定的一組pod來定義規則
#給pod1打標籤 [root@master networkpolicy]# kubectl label pods pod1 app=myapp -n dev pod/pod1 labeled #定義 [root@master networkpolicy]# vim allow-netpol-demo.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: all-myapp-ingress spec: podSelector: matchLabels: #使用標籤選擇器匹配 app: myapp ingress: - from: - ipBlock: #容許網段 cidr: 10.244.0.0/16 except: #排除IP 也但是網段 - 10.244.1.88/32 ports: #定義入棧規則容許被訪問的端口和協議 - protocol: TCP port: 80 #應用 [root@master networkpolicy]# kubectl get netpol -n dev NAME POD-SELECTOR AGE allow-myapp-ingress app=myapp 38s deny-all-ingress <none> 26m
嘗試訪問dev中pod1的80和443端口,驗證,(myapp沒有443端口,若是被阻擋應提示鏈接超時,若是能夠訪問應提示 拒絕鏈接)
[root@master networkpolicy]# curl 10.244.2.3 Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a> [root@master networkpolicy]# curl 10.244.2.3:443 curl: (7) Failed connect to 10.244.2.3:443; 鏈接超時
一般,出站的流量默認策略應該是容許經過的,可是當有精細化需求,僅放行那些有對外請求須要的Pod對象的出站流量,也能夠先爲名稱空間設置「禁止全部」的默認策略,再細化制定准許的策略。networkpolicy.spec中嵌套的Egress字段用來定義出站流量規則。
實踐中,須要進行嚴格隔離的環境一般將默認的策略設置爲拒絕全部出站流量,再去細化配置容許到達的目標端點的出站流量。
舉例,禁止prod名稱空間全部出站規則,入棧所有容許
#配置禁止出站規則 [root@master networkpolicy]# vim egress-def.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all-egress spec: podSelector: {} policyTypes: - Egress #應用並查看 [root@master networkpolicy]# kubectl apply -f egress-def.yaml -n prod networkpolicy.networking.k8s.io/deny-all-egress created [root@master networkpolicy]# kubectl get netpol -n prod NAME POD-SELECTOR AGE deny-all-egress <none> 13s #在prod內建立pod [root@master networkpolicy]# kubectl get pods -n prod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod1 1/1 Running 0 29m 10.244.2.4 node02 <none> <none> #進入pod1內部,嘗試ping集羣內dashboard的ip地址 10.244.2.72 [root@master networkpolicy]# kubectl exec -it pod1 -n prod -- /bin/sh / # ping 10.244.2.72 PING 10.244.2.72 (10.244.2.72): 56 data bytes #沒法ping通,如今修改配置清單容許全部出站流量 [root@master networkpolicy]# vim egress-def.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all-egress spec: podSelector: {} egress: - {} policyTypes: - Egress #應用後再次進入pod1內嘗試pingdashboard的ip地址 10.244.2.72 [root@master networkpolicy]# kubectl apply -f egress-def.yaml -n prod networkpolicy.networking.k8s.io/deny-all-egress configured [root@master networkpolicy]# kubectl exec -it pod1 -n prod -- /bin/sh / # ping 10.244.2.72 PING 10.244.2.72 (10.244.2.72): 56 data bytes 64 bytes from 10.244.2.72: seq=144 ttl=63 time=0.189 ms 64 bytes from 10.244.2.72: seq=145 ttl=63 time=0.084 ms 64 bytes from 10.244.2.72: seq=146 ttl=63 time=0.086 ms 64 bytes from 10.244.2.72: seq=147 ttl=63 time=0.092 ms 64 bytes from 10.244.2.72: seq=148 ttl=63 time=0.079 ms
實踐中,一般須要彼此隔離全部的名稱空間,可是又須要容許它們能夠和kube-system名稱空間中的Pod資源進行流量交換,以實現監控和名稱解析等各類管理功能。下面的配置清單示例在default名稱空間定義相關規則,在出站和入站都默認均爲拒絕的狀況下,它用於放行名稱空間內部的各Pod對象之間的通訊,以及和kube-system名稱空間內各Pod間的通訊。
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-allow 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"]
有一些額外的系統附件可能會單獨部署到獨有的名稱空間中,好比將prometheus監控系統部署到prom名稱空間等,這類具備管理功能的附件所在的名稱空間和每個特定的名稱空間的出入流量也是須要被放行的。
https://www.cnblogs.com/linuxk 馬永亮. Kubernetes進階實戰 (雲計算與虛擬化技術叢書) Kubernetes-handbook-jimmysong-20181218