目录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