为何Kubernetes中只是关心网络的联通,并不关心容器之间的隔离,是这样吗?Kubernetes真的不关心网络中的多租户的需求吗?
其实,在Kuberentes中,对于网络的隔离,是有一种专门的API对象来描述的,叫做NetworkPolicy
一个完整的NetworkPolicy对象示例如下
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy metadata: name: test-network-policy namespace: default spec: podSelector: matchLabels: role: db policyTypes: – Ingress – Egress ingress: – from: – ipBlock: cidr: 172.17.0.0/16 except: – 172.17.1.0/24 – namespaceSelector: matchLabels: project: myproject – podSelector: matchLabels: role: frontend ports: – protocol: TCP port: 6379 egress: – to: – ipBlock: cidr: 10.0.0.0/24 ports: – protocol: TCP port: 5978 |
在没有这个配置之前,Kubernetes中Pod都是默认允许所有的,Pod可以接受来自任何发送方的请求,或者,向任何接收方发送请求
而有了这个NetworkPolicy就可以来进行限制了,我们首先看到的是podSelector字段,就是定义这个NetworkPolicy的限制范围,当前Namespace中携带了role=db标签的Pod
这个sepc:podSelectr如果留空,那么就会作用于这个Namespace下的所有Pod
一旦Pod被NetworkPolicy选中,就会先进入拒绝所有的状态,既不能被外界访问,也不会访问外界
我们在上面的例子中,在policyTypes字段,定义了这个NetworkPolicy的类型是ingress和egress,会影响流入请求,影响流出请求
ingress字段,定义了from和ports,允许流入的白名单和端口,白名单中的匹配情况有 ipBlock namespaceSelector和PodSelector
在engress字段中,定义了to和ports,允许流出的白名单和端口,这里,可以流出的白名单定义方法和ingress类似,不过ipblock指定的却是目标的网段
综上所述,这个NetworkPolicy对象,指定的隔离规则如下
这个隔离规则只对default Namespace下的,携带了role=db的Pod有效,隔离的请求包含了ingress流入和egress流出
Kuberentes会拒绝所有访问被隔离Pod的请求,除非这个请求来自于如下的白名单的对象
default Namespace里的,role=fronted标签的Pod
携带了project=myproject标签的Namesapce的任何Pod
任何源于地址属于172.17.0.0/16,且不属于172.17.1.0/24网址的请求
也会屏蔽这个Pod发的所有请求,除非请求的目的地址属于10.0.0.0/24网段,并且访问的是这个网段地址的5978端口
在白名单中,有或和与的关系
我们对于或的定义,基本如下
…
ingress: – from: – namespaceSelector: matchLabels: user: alice – podSelector: matchLabels: role: client … |
我们上面的定义,就是或的关系,只要有一个满足的,就会触发这个NetworkPolicy
对于与的定义,基本如下
…
ingress: – from: – namespaceSelector: matchLabels: user: alice podSelector: matchLabels: role: client … |
只有两者都满足条件,这个NetworkPolicy才会生效
如果需要在K8S中支持NetworkPolicy的网络插件,需要由网络插件去维护一个NetworkPolicy Controller,通过一个大循环来进行控制对NetworkPolicy对象的增删盖茶
目前已经实现的网络插件包含Calico Weave Kube-router等多个项目,并不包含Flannel
如果需要在使用Flannel的时候还需要使用一个NetworkPolicy的话,就需要额外安装一个网络插件,比如Calico项目,进行执行NetworkPolicy
那么具体的NetworkPolicy对Pod进行隔离的手段是什么呢?
假如,我编写了一个简单的NetworkPolicy对象,如下所示
apiVersion: extensions/v1beta1
kind: NetworkPolicy metadata: name: test-network-policy namespace: default spec: podSelector: matchLabels: role: db ingress: – from: – namespaceSelector: matchLabels: project: myproject – podSelector: matchLabels: role: frontend ports: – protocol: tcp port: 6379 |
我们指定的ingress白名单,是任何Namespace中,携带了project=myproject标签的Pod,以及default Namespace中,携带了role=frontend标签的Pod,
这个时候,Kubernetes就使用这个NetworkPolicy的定义,为宿主机上生成iptables规则,这个过程,可以通过如下的一段Go语言风格的伪代码来进行描述
for dstIP := range 所有被networkpolicy.spec.podSelector选中的Pod的IP地址
for srcIP := range 所有被ingress.from.podSelector选中的Pod的IP地址 for port, protocol := range ingress.ports { iptables -A KUBE-NWPLCY-CHAIN -s $srcIP -d $dstIP -p $protocol -m $protocol –dport $port -j ACCEPT } } } |
这样,就设置,只有IP包源地址是srcIP,目的地址是dstIP,协议是protocol,目的端口是port,就可以被允许通过了
Kubernetes网络插件对Pod进行隔离,本质上是NetworkPolicy对应的iptables规则来实现的
这样,就会在宿主机上配置两组iptables规则来实现
第一组规则,负责拦截对隔离Pod的访问请求,生成一组规则的伪代码,如下所示
for pod := range 该Node上的所有Pod {
if pod是networkpolicy.spec.podSelector选中的 { iptables -A FORWARD -d $podIP -m physdev –physdev-is-bridged -j KUBE-POD-SPECIFIC-FW-CHAIN iptables -A FORWARD -d $podIP -j KUBE-POD-SPECIFIC-FW-CHAIN … } } |
这样,iptables规则使用了内置链 FORWARD,这个链,对应了iptables上多个节点上的路由去向,我们的包在经过一个路由节点上,会有两种去向,一种是继续在本机上处理,一种是转发到其他的地方
IP包的第一种去向,IP包在流动中,进入传输层之前,会先经过一个INPUT的检查点,IP包在此处检查,通过后进入传输层,交给用户进程处理,
用户进程处理完成后,进行返回,会进入流出路径,这时候会经过主机的路由表路由,路由后,就进入到一个名为OUTPUT的检查点,在OUTPUT之后,设置一个名为POSTROUTING的检查点
这个POSTROUTING,是IP包在传出过程中的汇聚点,毕竟在经过路由处理的时候,有些包是不会由本机处理的,而只是进行转发工作,转发的路径上,也会有一个检查点,名为FORWARD
上述检查点对应的操作界面是ebtables,在Linxu Netfilter的完整流动过程,如下所示
iptables表的主要作用,就是在不同的检查点,进行不同的检查操作
我们之前配置的NetworkPolicy对应的iptables就不难懂了
iptables -A FORWARD -d $podIP -m physdev –physdev-is-bridged -j KUBE-POD-SPECIFIC-FW-CHAIN
iptables -A FORWARD -d $podIP -j KUBE-POD-SPECIFIC-FW-CHAIN
…
第一条FORWARD链 拦截的一种特殊的情况,对应的宿主机容器经过CNI 网桥进行通信的流入数据包,-physdev-is-bridged的意思是,这个FORWARD链匹配的是,通过本机上的网桥设备,发往目的地址是PodIP的IP包
如果是Calico这样的非网桥模式的CNI插件,不存在这种情况
第二条FORWARN连接的是容器跨主通信,这个是通过路由转发FORWARD检查点进行的
这些规则都跳到了名为 KUBE-POD-SEPCIFIC-FW-CHAIN的规则上,正是网络插件为NetworkPolicy设置的第二组规则
这个规则,具体还是允许 或者 拒绝 的判定,可以简单的描述为下面的iptables规则
iptables -A KUBE-POD-SPECIFIC-FW-CHAIN -j KUBE-NWPLCY-CHAIN
iptables -A KUBE-POD-SPECIFIC-FW-CHAIN -j REJECT –reject-with icmp-port-unreachable
如果成功,就交给了KUBE-NWPLCY-CHAIN规则进行匹配,匹配成功,就可以进行通过
如果没匹配上,就到了第二条规则,就是一个REJECT规则,通过这个规则,不满足NETWOEK POLICY定义的请求就会被拒绝掉,从而实现了隔离
这样,就是CNI插件实现NetworkPolicy的基本方法了
那么,本片中,我们说了Kubernetes对于Pod隔离的手段,即为NetworkPolicy
NetworkPolicy上面是宿主机的一系列iptables规则,和传统的Iaas里面的安全组是类似的
所以Kubernetes的网络模型,不会去主动实现二层网络的互通或者二层网络的隔离,这和laas项目管理虚拟机的方式是不同的
在Kubernetges底层的设计和实现上,Kubernetes更加倾向于去实现一套管理物理设施的体系,而不是去维护多租户的概念
Kubernetes也不太可能将Namespace变成一个具有实质意义的隔离能力,映射为子网或者为租户的概念,这也是Kuberenetes将自己设置为基础设施和Paas中间的中间层的定位
但是,随着Kubernetes社区以及CNCF生态的不断开发,Kubernetes项目开始逐渐下探,吃掉了基础设施的很多蛋糕,这是容器生态继续发展的一个必然方向
我们编写一个NetworkPolicy,使得指定的Namespace中所有的Pod,都不能接收任何Ingress请求
apiVersion: extensions/v1beta1
kind: NetworkPolicy metadata: name: my-network-policy1 namespace: myspace spec: podSelector: {} policyTypes: – Ingress |
像这样全封闭的Pod,适用于计算密集型的吧,一般可以使用于去进行一些自我计算,计算完成就死亡的Pod