简介
kube-router是一套开源网络方案,基于bgp协议构建路由,从而构建整个容器集群网络,它使用ipvs实现了k8s的service网络,并使用ipset和iptables工具实现了networkpolicy。
我们知道calico对于networkpolicy的实现也是ipset+iptables这一套,本文将结合源码和实例,介绍kube-router是如何实现networkpolicy的,并分析它与calico在设计上的异同。
简要概括
kube-router在节点上启动一个agent同步pod、networkpolicy对象的信息, 并构建iptables规则和ipset集合。主要的程序入口在func (npc *NetworkPolicyController) fullPolicySync()
。读者可以自己阅读源码,挺清晰的。
规则处理分为四步:
- 顶层规则集:
npc.ensureTopLevelChains()
- networkpolicy粒度的规则集:
networkPoliciesInfo, err = npc.buildNetworkPoliciesInfo() ; activePolicyChains, activePolicyIPSets, err := npc.syncNetworkPolicyChains(networkPoliciesInfo, syncVersion)
- pod粒度规则集:
activePodFwChains, err := npc.syncPodFirewallChains(networkPoliciesInfo, syncVersion)
- 清理stale规则
err = cleanupStaleRules(activePolicyChains, activePodFwChains, activePolicyIPSets)
顶层规则集
与calico一样,在INPUT/FORWARD/OUTPUT三个地方做hook。分别hook到KUBE-ROUTER-INPUT、KUBE-ROUTER-FORWARD、KUBE-ROUTER-OUTPUT。
KUBE-ROUTER-FORWARD、KUBE-ROUTER-OUTPUT两个链中没有什么特殊的规则,直接就进入到了各个pod粒度的规则集中。
KUBE-ROUTER-INPUT链中做了一些特殊目的地址的egress白名单,如下:
- 允许访问service cidr
-d $SERVICE_RANGE -j RETURN
- 允许访问nodeport port range
-p tcp/udp -m addrtype --dst-type LOCAL -m multiport --dports $NODEPORT_RANGE -j RETURN
- 允许访问service external ip
-d $SERVICE_EXTERNAL_RANGE -j RETURN
- 各个pod的规则
networkpolicy粒度规则集
networkpolicy粒度的规则以KUBE-NWPLCY
为前缀,一个规则一个链。
来个例子:
Chain KUBE-NWPLCY-4UOMHLJABMXP4VW2 (1 references)
target prot opt source destination
MARK all -- anywhere anywhere /* rule to ACCEPT traffic from source pods to dest pods selected by policy
name access-pod namespace default */ match-set KUBE-SRC-IMXDMRGLZFP7QJCX src match-set KUBE-DST-5CXCQ5O6AOA47RC6 dst /* rule to mark traf
fic matching a network policy */ MARK or 0x10000
RETURN all -- anywhere anywhere /* rule to ACCEPT traffic from source pods to dest pods selected by policy
name access-pod namespace default */ match-set KUBE-SRC-IMXDMRGLZFP7QJCX src match-set KUBE-DST-5CXCQ5O6AOA47RC6 dst /* rule to RETURN tr
affic matching a network policy */ mark match 0x10000/0x10000
MARK all -- anywhere anywhere /* rule to ACCEPT traffic from source pods to specified ipBlocks selected b
y policy name: access-pod namespace default */ match-set KUBE-SRC-5CXCQ5O6AOA47RC6 src match-set KUBE-DST-7VKW2UTKN2UOEU4I dst /* rule to
mark traffic matching a network policy */ MARK or 0x10000
RETURN all -- anywhere anywhere /* rule to ACCEPT traffic from source pods to specified ipBlocks selected b
y policy name: access-pod namespace default */ match-set KUBE-SRC-5CXCQ5O6AOA47RC6 src match-set KUBE-DST-7VKW2UTKN2UOEU4I dst /* rule to
RETURN traffic matching a network policy */ mark match 0x10000/0x10000
一个KUBE-NWPLCY-***
chain里描述了一个networkpolicy对象, 对于该对象的每一个rule(ingress or egress),我们都会有如下的操作:
- 判断源地址+端口/目的地址+端口是否符合ingress/egress rule。如果符合就打mark:
MARK or 0x10000
。 这里的地址匹配,是一个ipset - 判断mark是否匹配
mark match 0x10000/0x10000
, 如果匹配,return ,如果不匹配, 再检查下一个rule的规则。
pod粒度规则集
从顶层规则集中,kube-router基于包的源/目的 IP跳转到相应的pod粒度规则集。
如何进入pod粒度集
因为kube-router对接的网络插件是官方的bridge插件, node上容器host-veth都绑在网桥上,pod彼此之间属于二层互联(包直接桥接转发)
所以同一个node上的pod-to-pod 的包会直接在bridge进行二层转发,这类包必须要经由如下的iptables规则处理:
-t filter KUBE-ROUTER-FORWARD -m physdev --physdev-is-bridged -m comment --comment "******" -d $POD_IP -j KUBE-POD-FW-XXXXX (匹配某个pod的ingress policy) 例:同node的pod要访问本pod,node对pod做健康检查
-t filter KUBE-ROUTER-FORWARD -m physdev --physdev-is-bridged -m comment --comment "******" -s $POD_IP -j KUBE-POD-FW-XXXXX (匹配某个pod的egress policy) 例:本pod要访问同node其他pod
注意:
XXXXX
是pod的ns+name的hash,但格式与calico不一样- 同一个node上的pod-to-pod走二层转发,这个过程想要被netfilter hook,需要加载br_netfilter模块,并且开启
/proc/sys/net/bridge/bridge-nf-call-iptables
,/proc/sys/net/bridge/bridge-nf-call-ip6tables
为1
除此之外的包,都是通过对方向、src或dst IP的匹配,来进入特定pod的规则链。例如:
-t filter KUBE-ROUTER-FORWARD -d $POD_IP -j KUBE-POD-FW-XXXXX (匹配某个pod的ingress policy) 例:其他节点(及上的pod)访问pod
-t filter KUBE-ROUTER-OUTPUT -d $POD_IP -j KUBE-POD-FW-XXXXX (匹配某个pod的ingress policy)
-t filter KUBE-ROUTER-INPUT -s $POD_IP -j KUBE-POD-FW-XXXXX (匹配某个pod的egress policy) 例:pod访问其他任何地址
-t filter KUBE-ROUTER-FORWARD -s $POD_IP -j KUBE-POD-FW-XXXXX (匹配某个pod的egress policy)
-t filter KUBE-ROUTER-OUTPUT -s $POD_IP -j KUBE-POD-FW-XXXXX (匹配某个pod的egress policy)
pod粒度规则集里有什么?
ingress policy
在KUBE-POD-FW-XXXXX链中,添加了若干规则:
一、 已经建立的连接,直接接受
-t filter KUBE-POD-FW-XXXXX -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
二、 所有从node访问过来的包,都予以接受
-t filter KUBE-POD-FW-XXXXX -m addrtype --src-type LOCAL -d $POD_IP -j ACCEPT
三、 其他情况下,需要基于匹配该pod的每个ingress policy,都添加一条jump规则:
-t filter KUBE-POD-FW-XXXXX -j XXXXXXXXX (XXXXXXXXX表示作用于该pod的第一个networkpolicy的ns+name的hash)
-t filter KUBE-POD-FW-XXXXX -j XXXXXXXXY (XXXXXXXXY表示作用于该pod的第二个networkpolicy的ns+name的hash)
...
egress policy
一、 已经建立的连接,直接接受
-t filter KUBE-POD-FW-XXXXX -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
二、 其他情况下,需要基于匹配该pod的每个egress policy,都添加一条jump规则:
-t filter KUBE-POD-FW-XXXXX -j XXXXXXXXX (XXXXXXXXX表示作用于该pod的第一个networkpolicy的ns+name的hash)
-t filter KUBE-POD-FW-XXXXX -j XXXXXXXXY (XXXXXXXXY表示作用于该pod的第二个networkpolicy的ns+name的hash)
...
示例
Chain KUBE-POD-FW-VFY4MIYKQH6U2ZBK (7 references)
target prot opt source destination
ACCEPT all -- anywhere anywhere /* rule for stateful firewall for pod */ ctstate RELATED,ESTABLISHED
ACCEPT all -- anywhere 10.0.0.24 /* rule to permit the traffic traffic to pods when source is the pod's loca
l node */ ADDRTYPE match src-type LOCAL
KUBE-NWPLCY-4UOMHLJABMXP4VW2 all -- anywhere anywhere /* run through nw policy access-pod */
KUBE-NWPLCY-TOUTUXPCBONZJXQA all -- anywhere anywhere /* run through nw policy access-pod-and-node */
NFLOG all -- anywhere anywhere /* rule to log dropped traffic POD name:hyserver-d4f9cf4df-55ggn namespace:
default */ mark match ! 0x10000/0x10000 limit: avg 10/min burst 10 nflog-group 100
REJECT all -- anywhere anywhere /* rule to REJECT traffic destined for POD name:hyserver-d4f9cf4df-55ggn na
mespace: default */ mark match ! 0x10000/0x10000 reject-with icmp-port-unreachable
MARK all -- anywhere anywhere MARK and 0xfffeffff
我们注意到,先是Accept了已成立的连接和来自宿主机的IP, 然后就会遍历所有的networkpolicy,全部走一遍后,判断mark,如果mark不匹配,打印日志,并且reject,如果匹配,我们将用于标记的那一位再置0:MARK and 0xfffeffff
整体上的链路如下图:
总结
根据对kube-router的实现判断,可以看出它和calico-felix在实现上的一些区别。kube-router的规则脉络更清晰,更简单,而且有充分的comment进行判断和跟踪。最重要的是,它是基于pod IP进行匹配的; felix在规则上更复杂,但是做了一些优化,所以从设计上看,性能应该会比kube-router更好。体现在:
- felix基于网卡做前缀判断pod流量,相比于根据pod IP(记录于etcd)更准确靠谱一些
- felix基于网卡前缀做分表,可以节省遍历时间
- felix在任何一个networkpolicy规则匹配后直接return,不需要遍历所有networkpolicy。
kube-router比较人性化的地方在于:
- 它默认放行了node对pod的主动访问(兼容了健康检查)
- 默认放行了pod对各种service的访问(避免用户定义了过于严格的egress规则导致pod无法访问dns等关键service)
所以使用上心智负担会更少。
附录
calico中networkpolicy的实现
可以参考[网易数帆k8s集群对networkpolicy的实践——felix]
iptables中physdev选项的说明
--physdev-in [name|prefix+|!name] 匹配从name端口进入网桥的数据包。(作用于INPUT、FORWARD和PREROUTING链),可以通过'+'对端口名进行前缀匹配。'!name'用于反向匹配。
--physdev-out [name|prefix+|!name] 匹配从name端口发出的数据包。(作用于FORWARD、OUTPUT和POSTROUTING链)
--physdev-is-in 匹配进入网桥数据包
--physdev-is-out 匹配离开网桥数据包
--physdev-is-bridged 匹配被桥接不是被路由的数据包。(作用于FORWARD和POSTROUTING链)