Kubernetes之(十七)网络模型和网络策略
目錄
- Kubernetes之(十七)網(wǎng)絡(luò)模型和網(wǎng)絡(luò)策略
- Kubernetes網(wǎng)絡(luò)模型和CNI插件
- Docker網(wǎng)絡(luò)模型
- Kubernetes網(wǎng)絡(luò)模型
- Flannel網(wǎng)絡(luò)插件
- Direct routing模式配置
- Host-gw后端
- 網(wǎng)絡(luò)策略
- 部署Canal提供網(wǎng)絡(luò)策略功能
- 配置策略
- Ingress管控
- Egress管控
- 隔離名稱空間
- Kubernetes網(wǎng)絡(luò)模型和CNI插件
Kubernetes之(十七)網(wǎng)絡(luò)模型和網(wǎng)絡(luò)策略
Kubernetes網(wǎng)絡(luò)模型和CNI插件
在Kubernetes中設(shè)計(jì)了一種網(wǎng)絡(luò)模型,要求無論容器運(yùn)行在集群中的哪個(gè)節(jié)點(diǎn),所有容器都能通過一個(gè)扁平的網(wǎng)絡(luò)平面進(jìn)行通信,即在同一IP網(wǎng)絡(luò)中。需要注意的是:在K8S集群中,IP地址分配是以Pod對(duì)象為單位,而非容器,同一Pod內(nèi)的所有容器共享同一網(wǎng)絡(luò)名稱空間。
Docker網(wǎng)絡(luò)模型
Docker容器的原生網(wǎng)絡(luò)模型主要有3種:Bridge(橋接)、Host(主機(jī))、none。
- Bridge:借助虛擬網(wǎng)橋設(shè)備為容器建立網(wǎng)絡(luò)連接。
- Host:設(shè)置容器直接共享當(dāng)前節(jié)點(diǎn)主機(jī)的網(wǎng)絡(luò)名稱空間。
- none:多個(gè)容器共享同一個(gè)網(wǎng)絡(luò)名稱空間。
橋接式網(wǎng)絡(luò)是目前較為流行和默認(rèn)的解決方案。但是這種方案的弊端是無法跨主機(jī)通信的,僅能在宿主機(jī)本地進(jìn)行,而解決該問題的方法就是NAT。所有接入到該橋接設(shè)備上的容器都會(huì)被NAT隱藏,它們發(fā)往Docker主機(jī)外部的所有流量都會(huì)經(jīng)過源地址轉(zhuǎn)換后發(fā)出,并且默認(rèn)是無法直接接受節(jié)點(diǎn)之外的其他主機(jī)發(fā)來的請(qǐng)求。當(dāng)需要接入Docker主機(jī)外部流量,就需要進(jìn)行目標(biāo)地址轉(zhuǎn)換甚至端口轉(zhuǎn)換將其暴露在外部網(wǎng)絡(luò)當(dāng)中。如下圖:
容器內(nèi)的屬于私有地址,需要在左側(cè)的主機(jī)上的eth0上進(jìn)行源地址轉(zhuǎn)換,而右側(cè)的地址需要被訪問,就需要將eth0的地址進(jìn)行NAT轉(zhuǎn)換。SNAT---->DNAT
這樣的通信方式會(huì)比較麻煩,從而需要借助第三方的網(wǎng)絡(luò)插件實(shí)現(xiàn)這樣的跨主機(jī)通信的網(wǎng)絡(luò)策略。
容器內(nèi)的屬于私有地址,需要在左側(cè)的主機(jī)上的eth0上進(jìn)行源地址轉(zhuǎn)換,而右側(cè)的地址需要被訪問,就需要將eth0的地址進(jìn)行NAT轉(zhuǎn)換。SNAT---->DNAT
這樣的通信方式會(huì)比較麻煩,從而需要借助第三方的網(wǎng)絡(luò)插件實(shí)現(xiàn)這樣的跨主機(jī)通信的網(wǎng)絡(luò)策略。
Kubernetes網(wǎng)絡(luò)模型
我們知道的是,在K8S上的網(wǎng)絡(luò)通信包含以下幾類:
- 容器間的通信:同一個(gè)Pod內(nèi)的多個(gè)容器間的通信,它們之間通過lo網(wǎng)卡進(jìn)行通信。
- Pod之間的通信:通過Pod IP地址進(jìn)行通信。
- Pod和Service之間的通信:Pod IP地址和Service IP進(jìn)行通信,兩者并不屬于同一網(wǎng)絡(luò),實(shí)現(xiàn)方式是通過IPVS或iptables規(guī)則轉(zhuǎn)發(fā)。
- Service和集群外部客戶端的通信,實(shí)現(xiàn)方式:Ingress、NodePort、Loadbalance
K8S網(wǎng)絡(luò)的實(shí)現(xiàn)不是集群內(nèi)部自己實(shí)現(xiàn),而是依賴于第三方網(wǎng)絡(luò)插件----CNI(Container Network Interface)
flannel、calico、canel等是目前比較流行的第三方網(wǎng)絡(luò)插件。
這三種的網(wǎng)絡(luò)插件需要實(shí)現(xiàn)Pod網(wǎng)絡(luò)方案的方式通常有以下幾種:
虛擬網(wǎng)橋、多路復(fù)用(MacVLAN)、硬件交換(SR-IOV)
K8S支持CNI插件進(jìn)行編排網(wǎng)絡(luò),以實(shí)現(xiàn)Pod和集群網(wǎng)絡(luò)管理功能的自動(dòng)化。每次Pod被初始化或刪除,kubelet都會(huì)調(diào)用默認(rèn)的CNI插件去創(chuàng)建一個(gè)虛擬設(shè)備接口附加到相關(guān)的底層網(wǎng)絡(luò),為Pod去配置IP地址、路由信息并映射到Pod對(duì)象的網(wǎng)絡(luò)名稱空間。
在配置Pod網(wǎng)絡(luò)時(shí),kubelet會(huì)在默認(rèn)的/etc/cni/net.d/目錄中去查找CNI JSON配置文件,然后通過type屬性到/opt/cni/bin中查找相關(guān)的插件二進(jìn)制文件,如下面的"portmap"。然后CNI插件調(diào)用IPAM插件(IP地址管理插件)來配置每個(gè)接口的IP地址:
[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主要是定義容器網(wǎng)絡(luò)模型規(guī)范,鏈接容器管理系統(tǒng)和網(wǎng)絡(luò)插件,兩者主要通過上面的JSON格式文件進(jìn)行通信,實(shí)現(xiàn)容器的網(wǎng)絡(luò)功能。CNI的主要核心是:在創(chuàng)建容器時(shí),先創(chuàng)建好網(wǎng)絡(luò)名稱空間(netns),然后調(diào)用CNI插件為這個(gè)netns配置網(wǎng)絡(luò),最后在啟動(dòng)容器內(nèi)的進(jìn)程
常見的CNI網(wǎng)絡(luò)插件包含以下幾種:
- Flannel:為Kubernetes提供疊加網(wǎng)絡(luò)的網(wǎng)絡(luò)插件,基于TUN/TAP隧道技術(shù),使用UDP封裝IP報(bào)文進(jìn)行創(chuàng)建疊 加網(wǎng)絡(luò),借助etcd維護(hù)網(wǎng)絡(luò)的分配情況,缺點(diǎn):無法支持網(wǎng)絡(luò)策略訪問控制。
- Calico:基于BGP的三層網(wǎng)絡(luò)插件,也支持網(wǎng)絡(luò)策略進(jìn)而實(shí)現(xiàn)網(wǎng)絡(luò)的訪問控制;它在每臺(tái)主機(jī)上都運(yùn)行一個(gè)虛擬路由,利用Linux內(nèi)核轉(zhuǎn)發(fā)網(wǎng)絡(luò)數(shù)據(jù)包,并借助iptables實(shí)現(xiàn)防火墻功能。實(shí)際上Calico最后的實(shí)現(xiàn)就是將每臺(tái)主機(jī)都變成了一臺(tái)路由器,將各個(gè)網(wǎng)絡(luò)進(jìn)行連接起來,實(shí)現(xiàn)跨主機(jī)通信的功能。
- Canal:由Flannel和Calico聯(lián)合發(fā)布的一個(gè)統(tǒng)一網(wǎng)絡(luò)插件,提供CNI網(wǎng)絡(luò)插件,并支持網(wǎng)絡(luò)策略實(shí)現(xiàn)。
- 其他的還包括Weave Net、Contiv、OpenContrail、Romana、NSX-T、kube-router等等。而Flannel和Calico是目前最流行的選擇方案。
Flannel網(wǎng)絡(luò)插件
在各節(jié)點(diǎn)上的Docker主機(jī)在docker0上默認(rèn)使用同一個(gè)子網(wǎng),不同節(jié)點(diǎn)的容器都有可能會(huì)獲取到相同的地址,那么在跨節(jié)點(diǎn)通信時(shí)就會(huì)出現(xiàn)地址沖突的問題。并且在多個(gè)節(jié)點(diǎn)上的docker0使用不同的子網(wǎng),也會(huì)因?yàn)闆]有準(zhǔn)確的路由信息導(dǎo)致無法準(zhǔn)確送達(dá)報(bào)文。
而為了解決這一問題,Flannel的解決辦法是,預(yù)留一個(gè)使用網(wǎng)絡(luò),如10.244.0.0/16,然后自動(dòng)為每個(gè)節(jié)點(diǎn)的Docker容器引擎分配一個(gè)子網(wǎng),如10.244.1.0/24和10.244.2.0/24,并將分配信息保存在etcd持久存儲(chǔ)。
第二個(gè)問題的解決,Flannel是采用不同類型的后端網(wǎng)絡(luò)模型進(jìn)行處理。其后端的類型有以下幾種:
- VxLAN:使用內(nèi)核中的VxLAN模塊進(jìn)行封裝報(bào)文。也是flannel推薦的方式.
- host-gw:即Host GateWay,通過在節(jié)點(diǎn)上創(chuàng)建目標(biāo)容器地址的路由直接完成報(bào)文轉(zhuǎn)發(fā),要求各節(jié)點(diǎn)必須在同一個(gè)2層網(wǎng)絡(luò),對(duì)報(bào)文轉(zhuǎn)發(fā)性能要求較高的場景使用。
UDP:使用普通的UDP報(bào)文封裝完成隧道轉(zhuǎn)發(fā)。
VxLAN后端和direct routing
VxLAN(Virtual extensible Local Area Network)虛擬可擴(kuò)展局域網(wǎng),采用MAC in UDP封裝方式,具體的實(shí)現(xiàn)方式為:
跨節(jié)點(diǎn)的Pod之間的通信就是以上的一個(gè)過程,整個(gè)過程中通信雙方對(duì)物理網(wǎng)絡(luò)是沒有感知的。如下網(wǎng)絡(luò)圖:
flannel運(yùn)行后,在各Node宿主機(jī)多了一個(gè)網(wǎng)絡(luò)接口:
#master [root@master ~]# ifconfig flannel.1 flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450inet 10.244.0.0 netmask 255.255.255.255 broadcast 0.0.0.0inet6 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 0TX 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 1450inet 10.244.1.0 netmask 255.255.255.255 broadcast 0.0.0.0inet6 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 0TX 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 1450inet 10.244.2.0 netmask 255.255.255.255 broadcast 0.0.0.0inet6 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 0TX packets 3386 bytes 1333288 (1.2 MiB)TX errors 0 dropped 8 overruns 0 carrier 0 collisions 0從上面的結(jié)果可以知道 :
舉個(gè)實(shí)際例子
#啟動(dòng)一個(gè)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>查看網(wǎng)絡(luò)接口可以發(fā)現(xiàn)在各個(gè)節(jié)點(diǎn)上多了一個(gè)虛擬接口cni0,其ip地址為10.244.0.1。它是由flanneld創(chuàng)建的一個(gè)虛擬網(wǎng)橋叫cni0,在Pod本地通信使用。 這里需要注意的是,cni0虛擬網(wǎng)橋,僅作用于本地通信.
[root@node02 ~]# ifconfig cni0 cni0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450inet 10.244.2.1 netmask 255.255.255.0 broadcast 0.0.0.0inet6 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 0TX packets 128191 bytes 12079793 (11.5 MiB)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0容器跨主機(jī)是可以正常通信的,那么容器的跨主機(jī)通信是如何實(shí)現(xiàn)的呢?????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 #在宿主機(jī)和容器內(nèi)都進(jìn)行ping另外一臺(tái)主機(jī)上的Pod ip并進(jìn)行抓包 [root@master manifests]# kubectl exec nginx-7849c4bbcd-8srph -it -- /bin/sh #進(jìn)入node01的10.244.1.62 IP的pod內(nèi)[root@master ~]# kubectl exec -it nginx-7849c4bbcd-brsrv -- /bin/sh #另外開終端進(jìn)入node02的10.244.2.74 IP的pod內(nèi)#使用node01的pod ping10.244.2.74 在node02節(jié)點(diǎn)抓包[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節(jié)點(diǎn)ping 10.244.2.74 在node02節(jié)點(diǎn)抓包 [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#可以看到報(bào)文都是經(jīng)過flannel.1網(wǎng)絡(luò)接口進(jìn)入2層隧道進(jìn)而轉(zhuǎn)發(fā)發(fā)送到10.244.1.0/24和10.244.20/24網(wǎng)段的數(shù)據(jù)報(bào)文發(fā)給本機(jī)的flannel.1接口,即進(jìn)入二層隧道,然后對(duì)數(shù)據(jù)報(bào)文進(jìn)行封裝(封裝VxLAN首部-->UDP首部-->IP首部-->以太網(wǎng)首部),到達(dá)目標(biāo)Node節(jié)點(diǎn)后,由目標(biāo)Node上的flannel.1進(jìn)行解封裝。
VXLAN是Linux內(nèi)核本身支持的一種網(wǎng)絡(luò)虛擬化技術(shù),是內(nèi)核的一個(gè)模塊,在內(nèi)核態(tài)實(shí)現(xiàn)封裝解封裝,構(gòu)建出覆蓋網(wǎng)絡(luò),其實(shí)就是一個(gè)由各宿主機(jī)上的Flannel.1設(shè)備組成的虛擬二層網(wǎng)絡(luò)。
由于VXLAN由于額外的封包解包,導(dǎo)致其性能較差,所以Flannel就有了host-gw模式,即把宿主機(jī)當(dāng)作網(wǎng)關(guān),除了本地路由之外沒有額外開銷,性能和calico差不多,由于沒有疊加來實(shí)現(xiàn)報(bào)文轉(zhuǎn)發(fā),這樣會(huì)導(dǎo)致路由表龐大。因?yàn)橐粋€(gè)節(jié)點(diǎn)對(duì)應(yīng)一個(gè)網(wǎng)絡(luò),也就對(duì)應(yīng)一條路由條目。
host-gw雖然VXLAN網(wǎng)絡(luò)性能要強(qiáng)很多。,但是種方式有個(gè)缺陷:要求各物理節(jié)點(diǎn)必須在同一個(gè)二層網(wǎng)絡(luò)中。物理節(jié)點(diǎn)必須在同一網(wǎng)段中。這樣會(huì)使得一個(gè)網(wǎng)段中的主機(jī)量會(huì)非常多,萬一發(fā)一個(gè)廣播報(bào)文就會(huì)產(chǎn)生干擾。在私有云場景下,宿主機(jī)不在同一網(wǎng)段是很常見的狀態(tài),所以就不能使用host-gw了。
VXLAN還有另外一種功能,VXLAN也支持類似host-gw的玩法,如果兩個(gè)節(jié)點(diǎn)在同一網(wǎng)段時(shí)使用host-gw通信,如果不在同一網(wǎng)段中,即 當(dāng)前pod所在節(jié)點(diǎn)與目標(biāo)pod所在節(jié)點(diǎn)中間有路由器,就使用VXLAN這種方式,使用疊加網(wǎng)絡(luò)。 結(jié)合了Host-gw和VXLAN,這就是VXLAN的Direct routing模式
Direct routing模式配置
修改kube-flannel.yml文件,將flannel的configmap對(duì)象改為:
[root@master manifests]# wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml #下載原版配置文件進(jìn)行修改后apply執(zhí)行[root@master manifests]# vim kube-flannel.yml ......net-conf.json: |{"Network": "10.244.0.0/16","Backend": {"Type": "vxlan", #注意,號(hào)"Directrouting": true #增加字段}} ......#此時(shí)需要?jiǎng)h除flannel網(wǎng)絡(luò)后重建,但是會(huì)影響所有pod,生產(chǎn)中不建議這么使用kubectl delete -f kube-flannel.yaml ,kubectl apply -f kube-flannel.yaml #查看已經(jīng)生成相應(yīng)規(guī)則 [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配置完成后,各節(jié)點(diǎn)會(huì)生成類似directrouting一樣的 路由和iptables規(guī)則,用于實(shí)現(xiàn)二層轉(zhuǎn)發(fā)Pod網(wǎng)絡(luò)的通信報(bào)文,省去了隧道轉(zhuǎn)發(fā)模式的額外開銷。但是存在的問題點(diǎn)是,對(duì)于不在同一個(gè)二層網(wǎng)絡(luò)的報(bào)文轉(zhuǎn)發(fā),host-gw是無法實(shí)現(xiàn)的。延續(xù)上面的例子,進(jìn)行抓包查看:
[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>#進(jìn)入myapp-deploy-65df64765c-85k8x 容器ping另一個(gè)節(jié)點(diǎn)的容器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節(jié)點(diǎn)抓包 [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從上面的結(jié)果可以看到,發(fā)往10.244.1.0/24和10.244.1.0/24的包都是直接經(jīng)過eth0網(wǎng)絡(luò)接口直接發(fā)出去的,這就是Directrouting。如果兩個(gè)節(jié)點(diǎn)是跨網(wǎng)段的,則flannel自動(dòng)降級(jí)為VxLAN模式。
此時(shí),在各個(gè)集群節(jié)點(diǎn)上執(zhí)行“iptables -nL”命令可以看到,iptables filter表的FORWARD鏈上由其生成了如下兩條轉(zhuǎn)發(fā)規(guī)則,它顯示放行了10.244.0.0/16網(wǎng)絡(luò)進(jìn)出的所有報(bào)文,用于確保由物理接收或發(fā)送的目標(biāo)地址或源地址為10.244.0.0/16網(wǎng)絡(luò)的所有報(bào)文均能夠正常通行。這些是 Direct Routing模式得以實(shí)現(xiàn)的必要條件:
再在此之前創(chuàng)建的Pod和宿主機(jī)上進(jìn)行ping測試,可以看到在flannel.1接口上已經(jīng)抓不到包了,在eth0上可以用抓到ICMP的包
Host-gw后端
? Flannel除了上面2種數(shù)據(jù)傳輸?shù)姆绞揭酝?#xff0c;還有一種是host-gw的方式,host-gw后端是通過添加必要的路由信息使用節(jié)點(diǎn)的二層網(wǎng)絡(luò)直接發(fā)送Pod的通信報(bào)文。它的工作方式類似于Directrouting的功能,但是其并不具備VxLan的隧道轉(zhuǎn)發(fā)能力。
? 編輯kube-flannel的配置清單,將ConfigMap資源kube-flannel-cfg的data字段中網(wǎng)絡(luò)配置進(jìn)行修改,如下:
[root@master ~]# vim kube-flannel.yml ......net-conf.json: |{"Network": "10.244.0.0/16","Backend": {"Type": "host-gw"}} ......配置完成后,各節(jié)點(diǎn)會(huì)生成類似directrouting一樣的 路由和iptables規(guī)則,用于實(shí)現(xiàn)二層轉(zhuǎn)發(fā)Pod網(wǎng)絡(luò)的通信報(bào)文,省去了隧道轉(zhuǎn)發(fā)模式的額外開銷。但是存在的問題點(diǎn)是,對(duì)于不在同一個(gè)二層網(wǎng)絡(luò)的報(bào)文轉(zhuǎn)發(fā),host-gw是無法實(shí)現(xiàn)的
[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該模式下,報(bào)文轉(zhuǎn)發(fā)的相關(guān)流程如下:
以上就是Flannel網(wǎng)絡(luò)模型的三種工作模式,但是flannel自身并不具備為Pod網(wǎng)絡(luò)實(shí)現(xiàn)網(wǎng)絡(luò)策略和網(wǎng)絡(luò)通信隔離的功能,為此只能借助于Calico聯(lián)合統(tǒng)一的項(xiàng)目Calnal項(xiàng)目進(jìn)行構(gòu)建網(wǎng)絡(luò)策略的功能。
網(wǎng)絡(luò)策略
網(wǎng)絡(luò)策略(Network Policy )是 Kubernetes 的一種資源。Network Policy 通過 Label 選擇 Pod,并指定其他 Pod 或外界如何與這些 Pod 通信。
Pod的網(wǎng)絡(luò)流量包含流入(Ingress)和流出(Egress)兩種方向。默認(rèn)情況下,所有 Pod 是非隔離的,即任何來源的網(wǎng)絡(luò)流量都能夠訪問 Pod,沒有任何限制。當(dāng)為 Pod 定義了 Network Policy,只有 Policy 允許的流量才能訪問 Pod。
Kubernetes的網(wǎng)絡(luò)策略功能也是由第三方的網(wǎng)絡(luò)插件實(shí)現(xiàn)的,因此,只有支持網(wǎng)絡(luò)策略功能的網(wǎng)絡(luò)插件才能進(jìn)行配置網(wǎng)絡(luò)策略,比如Calico、Canal、kube-router等等。
部署Canal提供網(wǎng)絡(luò)策略功能
Calico可以獨(dú)立地為Kubernetes提供網(wǎng)絡(luò)解決方案和網(wǎng)絡(luò)策略,也可以和flannel相結(jié)合,由flannel提供網(wǎng)絡(luò)解決方案,Calico僅用于提供網(wǎng)絡(luò)策略,此時(shí)將Calico稱為Canal。結(jié)合flannel工作時(shí),Calico提供的默認(rèn)配置清單式以flannel默認(rèn)使用的10.244.0.0/16為Pod網(wǎng)絡(luò),因此在集群中kube-controller-manager啟動(dòng)時(shí)就需要通過--cluster-cidr選項(xiàng)進(jìn)行設(shè)置使用該網(wǎng)絡(luò)地址,并且---allocate-node-cidrs的值應(yīng)設(shè)置為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#應(yīng)用 [root@master calico]# kubectl apply -f rbac.yaml [root@master calico]# kubectl apply -f canal.yamlCanal作為DaemonSet部署到每個(gè)節(jié)點(diǎn),屬于kube-system這個(gè)名稱空間。需要注意的是,Canal只是直接使用了Calico和flannel項(xiàng)目,代碼本身沒有修改,Canal只是一種部署的模式,用于安裝和配置項(xiàng)目。
[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系統(tǒng)中,報(bào)文的流入和流出的核心組件是Pod資源,它們也是網(wǎng)絡(luò)策略功能的主要應(yīng)用對(duì)象。NetworkPolicy對(duì)象通過podSelector選擇 一組Pod資源作為控制對(duì)象。NetworkPolicy是定義在一組Pod資源之上用于管理入站流量,或出站流量的一組規(guī)則,有可以是出入站規(guī)則一起生效,規(guī)則的生效模式通常由spec.policyTypes進(jìn)行 定義。如下圖:
默認(rèn)情況下,Pod對(duì)象的流量控制是為空的,報(bào)文可以自由出入。在附加網(wǎng)絡(luò)策略之后,Pod對(duì)象會(huì)因?yàn)镹etworkPolicy而被隔離,一旦名稱空間中有任何NetworkPolicy對(duì)象匹配了某特定的Pod對(duì)象,則該P(yáng)od將拒絕NetworkPolicy規(guī)則中不允許的所有連接請(qǐng)求,但是那些未被匹配到的Pod對(duì)象依舊可以接受所有流量。
對(duì)特定的Pod集合來說,入站和出站流量默認(rèn)是放行狀態(tài),除非有規(guī)則可以進(jìn)行匹配。還有一點(diǎn)需要注意的是,在spec.policyTypes中指定了生效的規(guī)則類型,但是在networkpolicy.spec字段中嵌套定義了沒有任何規(guī)則的Ingress或Egress時(shí),則表示拒絕入站或出站的一切流量。定義網(wǎng)絡(luò)策略的基本格式如下:
該網(wǎng)絡(luò)策略就是將default名稱空間中擁有標(biāo)簽"app=myapp"的Pod資源開放80/TCP端口給10.244.0.0/16網(wǎng)段,并排除10.244.3.0/24網(wǎng)段的訪問,并且也開放給標(biāo)簽為app=myapp的所有Pod資源進(jìn)行訪問。
Ingress管控
創(chuàng)建
[root@master manifests]# mkdir networkpolicy #創(chuàng)建名稱空間 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#定義規(guī)則 [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#執(zhí)行 指定在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運(yùn)行Pod
[root@master networkpolicy]# vim pod-a.yaml apiVersion: v1 kind: Pod metadata:name: pod1 spec:containers:- name: myappimage: 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>嘗試訪問剛剛創(chuàng)建的pod1
[root@master networkpolicy]# curl 10.244.2.3 curl: (7) Failed connect to 10.244.2.3:80; 連接超時(shí)嘗試訪問在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>總結(jié)
此時(shí)dev名稱空間使用了Ingress規(guī)則,但是為空,代表拒絕所有入站請(qǐng)求,而prod名稱空間沒有定義Ingress規(guī)則,允許所有訪問。
定義dev名稱空間可以對(duì)外被訪問
[root@master networkpolicy]# vim ingress-def.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata:name: deny-all-ingress spec:podSelector: {}ingress:- {}policyTypes:- Ingress#應(yīng)用后嘗試訪問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可以被訪問,因?yàn)樵黾恿薸ngress規(guī)則 {}表示映射所有。
放行特定的入棧流量:
可以使用標(biāo)簽選擇器來選擇指定的一組pod來定義規(guī)則
嘗試訪問dev中pod1的80和443端口,驗(yàn)證,(myapp沒有443端口,如果被阻擋應(yīng)提示連接超時(shí),如果可以訪問應(yīng)提示 拒絕連接)
[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; 連接超時(shí)Egress管控
通常,出站的流量默認(rèn)策略應(yīng)該是允許通過的,但是當(dāng)有精細(xì)化需求,僅放行那些有對(duì)外請(qǐng)求需要的Pod對(duì)象的出站流量,也可以先為名稱空間設(shè)置“禁止所有”的默認(rèn)策略,再細(xì)化制定準(zhǔn)許的策略。networkpolicy.spec中嵌套的Egress字段用來定義出站流量規(guī)則。
實(shí)踐中,需要進(jìn)行嚴(yán)格隔離的環(huán)境通常將默認(rèn)的策略設(shè)置為拒絕所有出站流量,再去細(xì)化配置允許到達(dá)的目標(biāo)端點(diǎn)的出站流量。
舉例,禁止prod名稱空間所有出站規(guī)則,入棧全部允許
#配置禁止出站規(guī)則 [root@master networkpolicy]# vim egress-def.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata:name: deny-all-egress spec:podSelector: {}policyTypes:- Egress#應(yīng)用并查看 [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內(nèi)創(chuàng)建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>#進(jìn)入pod1內(nèi)部,嘗試ping集群內(nèi)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通,現(xiàn)在修改配置清單允許所有出站流量 [root@master networkpolicy]# vim egress-def.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata:name: deny-all-egress spec:podSelector: {}egress:- {}policyTypes:- Egress#應(yīng)用后再次進(jìn)入pod1內(nèi)嘗試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隔離名稱空間
實(shí)踐中,通常需要彼此隔離所有的名稱空間,但是又需要允許它們可以和kube-system名稱空間中的Pod資源進(jìn)行流量交換,以實(shí)現(xiàn)監(jiān)控和名稱解析等各種管理功能。下面的配置清單示例在default名稱空間定義相關(guān)規(guī)則,在出站和入站都默認(rèn)均為拒絕的情況下,它用于放行名稱空間內(nèi)部的各Pod對(duì)象之間的通信,以及和kube-system名稱空間內(nèi)各Pod間的通信。
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata:name: namespace-deny-allnamespace: default spec:policyTypes: ["Ingress","Egress"]podSelector: {} --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata:name: namespace-allownamespace: default spec:policyTypes: ["Ingress","Egress"]podSelector: {}ingress:- from:- namespaceSelector:matchExpressions:- key: nameoperator: Invalues: ["default","kube-system"]egress:- to:- namespaceSelector:matchExpressions:- key: nameoperator: Invalues: ["default","kube-system"]有一些額外的系統(tǒng)附件可能會(huì)單獨(dú)部署到獨(dú)有的名稱空間中,比如將prometheus監(jiān)控系統(tǒng)部署到prom名稱空間等,這類具有管理功能的附件所在的名稱空間和每一個(gè)特定的名稱空間的出入流量也是需要被放行的。
參考資料
https://www.cnblogs.com/linuxk
馬永亮. Kubernetes進(jìn)階實(shí)戰(zhàn) (云計(jì)算與虛擬化技術(shù)叢書)
Kubernetes-handbook-jimmysong-20181218
轉(zhuǎn)載于:https://www.cnblogs.com/wlbl/p/10694374.html
總結(jié)
以上是生活随笔為你收集整理的Kubernetes之(十七)网络模型和网络策略的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CF1658F 题解
- 下一篇: 瀚高数据库解锁