kubernetes的clusterip机制调研及macvlan网络下的clusterip坑解决方案

kubernetes的clusterip机制调研及macvlan网络下的clusterip坑解决方案

最近一直在设计借贷宝的底层k8s架构,大体方案已经明确,资源层Pod网络采用macvlan+hostnetwork的方式,服务层采用nginx(后期切入到ingress-nginx-controller)+dubbo(还有一部分基于zk的自研注册中心)的服务发现机制,域名发现采用外部DNS。从这个架构就可以看出来,我们不打算用k8s的clusterip机制,但是,自己的应用Pod不用,k8s的自身管理Pod是会用到clusterip的,例如coredns、dashboard、prometheus等内部管理Pod都会通过kubernetes service的clusterip来访问apiserver,还有prometheus内部之间,也会通过service clusterip来互访。这就带来了一个问题,如果这些Pod都是使用macvlan网络的话,他们是无法访问clusterip的,因为macvlan不是一个cluster network plugins(具体原因见后文),所以这怎么办?带来了三个问题需要解决:

1、clusterip的实现机制是什么,是否可以改造?

2、不能改造的话,如何能够沿用macvlan网络插件,但是让这些管理Pod可以正常运行?

3、如果管理Pod确实不能使用macvlan网络插件的话,如何让应用Pod继续采用macvlan,而单独让管理Pod使用其他网络插件?

本文就是对这三个问题的调研分析思考过程的一个整理和总结。

问题1:clusterip的实现机制是什么,是否可以改造?

clusterip是k8s service用来对内部网络Pod暴露服务的一种方式,可以认为就是service的内部负载均衡ip,一个vip,这个vip分布在每一个node节点上,可以理解成是一个分布式的内部负载均衡。它的配置使用,如何内部联动,也即管理平面的东西,我这里就不说了,可以参考官方文档:kubernetes.io/docs/conc,里面几个图都不错,可以好好看下。

这里重点讲clusterip的数据平面的实现(基于iptables/ipvs),也就是网络到底是怎么实现负载分摊,路由转发的,关于这块内容,本文重点参考《Kubernetes Service详解(概念、原理、流量分析、代码)》这个文章,这个文章对于实现机制的说明和验证,已经写的很好,尤其是那个表格,很不错,但是少了一种情况,所以这里记录补充下。

对于clusterip的访问情况,可以分为以下四种情况,但是不管哪种情况,其实都是通过DNAT+SNAT(可选)的方式来实现的,DNAT用来实现负载均衡,不管是用iptables还是ipvs,都是将目标地址clusterip:port转换成后端service的具体某个pod的ip:port。而SNAT是为了让内部Pod可以出访到外部网络,所以只有Pod访问内部网络Pod的service clusterip的时候,才不需要做SNAT,因为网络流量全部在内部,除此之外,都需要做SNAT,即使是Pod访问apiserver的clusterip也需要SNAT,因为apiserver使用hostnetwork网络,不属于内部网络,这一情况,上面那个文章里没有列出来,也没有说明SNAT的本质原因,所以本文补充一下。下面列出具体情况,以及示例图。

情况一:其他Pod(或node节点上的其他独立ip,例如非k8s纳管的docker容器)访问内部网络Pod的 service clusterip。只有这种情况不需要SNAT。

情况二:自身Pod访问内部网络Pod的service clusterip,且负载均衡到了自己。

情况三:其他Pod(或node节点上的其他独立ip)访问hostnetwork网络Pod的service clusterip。

情况四:自身Pod访问hostnetwork网络Pod的service clusterip,且负载均衡到了自己。

具体实现这个能力的iptables/ipvs规则,这里就不写了,可以参考上面提到那个文章。

了解了clusterip的实现机制,那macvlan能用吗?很遗憾,答案是不能。

原因是,macvlan网络属于外部网络,并且拥有独立的网络空间namespace,所以并不会经过node的网络空间的内核协议栈,进而造成并不会经过iptables/ipvs的配置,所以,不会被DNAT和SNAT,现象就是,macvlan的pod可以ping通clusterip,但并无法telnet端口,更无法使用后端的负载均衡。具体原因可以看下面这个图(来源《图解几个与Linux网络虚拟化相关的虚拟网卡-VETH/MACVLAN/MACVTAP/IPVLAN》,这个文章不错,可以细看下)。

可以看到,处于独立namespace的macvlan,是不会走node的协议栈的。那问题来了,对于k8s来讲,除了hostnetwork网络的Pod,所有基于cluster network网络插件的Pod都是独立的网络namespace,为什么它们就能够走node的协议栈呢?答案在下图,也出自上面这个文章。

因为对于cluster network来讲,它们都属于cluster的内部网络,在做三层路由的时候,是会通过类似图中Peer的方式,进入node的网络空间,从而被iptables/ipvs转发的。

可能你现在有点蒙,但其实整理一下你就理解了。像macvlan这样的网络,你可以认为它是一个与node的hostnetwork网络平齐的网络,它们都是需要外部进行三层路由转发的,这个时候,它们的结构大概是这样,如下图。

而其他的cluster network,它们的网络结构是这样的,如下图:

因为cluster network是内部网络,在从Node节点出去的时候,必然要经过一次Node的网络namespace,所以iptables/ipvs对它们自然生效,我们可以认为,每台物理机上有一个分布式的路由和网关,在为这些内部网络服务。

所以,既然macvlan不能像cluster network一样,使用clusterip,那我们可以改造一下吗?答案是,可以改,但是太麻烦,不划算。其实从上面两个图就可以看出来了,本质上就是macvlan的命名空间里,没有iptables/ipvs的规则而已,而这些规则,其实都是可以通过配置,放入到macvlan网络的pod所在的namespace的,一旦放入,从macvlan的Pod中访问clusterip,就跟具有hostnetwork网络的Pod访问clusterip的效果一致,上文提到了,macvlan网络的效果,其实跟hostnetwork的对外暴露效果等价。关于配置思路,网上有一个文章,可以看下《Run IPVS in a separate network namespace》,总之是可行的。如果这样改造,意味着每个macvlan的Pod和Node节点本身,都在保存一份iptables/ipvs的规则,说实话,是有点麻烦,其实就是因为少了一层公共的内核协议栈(深入思考,这个应该给linux内核提个需求的,为什么就没有共享namespace,这么一个东西呢?有的话,这个问题是不是就完美解决了),所以这个事情可以做,但是太麻烦了,想想,一旦规则变更,需要更新这么多namespace的iptables/ipvs规则,也是够心累的。所以这事,我相信k8s不会干,我们也不打算自己干。其实共享namespace这件事,倒是可以看看,能否加入到linux里,等有时间再思考下可行性吧。

问题2:不能改造的话,如何能够沿用macvlan网络插件,但是让这些管理Pod可以正常运行?

既然不能改造,还是实际点,workaround一下吧,问题2的答案,是部分可以的。大部分的管理Pod都可以通过hostnetwork=true的方式运行,访问clusterip就一点问题没有了,原因可以见上文。那就剩下一部分管理Pod,就是没办法使用hostnetwork怎么办呢??带入问题3。

问题3:如果管理Pod确实不能使用macvlan网络插件的话,如何让应用Pod继续采用macvlan,而单独让管理Pod使用其他网络插件?

可以有两种方式:

第一种方式,部分Node标记master,采用cluster network,例如flannel/calico/weave,貌似最近weave比较火,可以借机熟悉一下。然后部署管理Pod的时候,指定部署到master上去。

第二种方式,基于multus-cni插件做双网卡,然后配置默认路由走macvlan的网卡,内部网络走cluster network那块网卡。

两种方式都可以,我个人倾向于第一种,因为首先是不想双网卡管理,另外就是第二种方式的multus-cni插件,必须有一个网络是cluster network,这点就不符合我们的需求了,因为我们并不想把cluster网络作为每个Pod都有的网络,而恰恰相反,我们大部分的网络都是macvlan网络。话说,这个插件可以做的更自由一点,网络类型完全由用户自己来选择,只用macvlan也可以,只用cluster网络也可以,一起用也可以,这样多好,个人认为,这才是k8s现在最欠缺的一个网络配置方式(正常来讲,Pod是什么网络,就应该由Pod来决定,但现在其实是Node来决定的,不觉得k8s这个设计有点怪怪的吗?这点,其实就没有docker做的合理了,docker是就是创建容器的时候,指定网络类型的。),本来这个multus-cni插件,可以在一定程度上,解决上面这个问题,但是并没有做的很彻底,感觉有一些遗憾。

OK,最后总结一下,经过上面的分析,3个问题基本解决,完全基于macvlan的网络,对于基于Baremetal方式部署的k8s,完全是可行的。而且可以很好的实现资源层和服务层的管理,对于这个方案,我们借贷宝的云团队很有信心可以把它做成,预计未来一两个月,会启动迁移计划,到时候,再把一些情况分享到这里吧,也希望知乎的朋友们,能够多提意见,共同进步。

参考:

IPVS-Based In-Cluster Load Balancing Deep Dive

DESIGN: Services v2 #1107

Kubernetes Service详解(概念、原理、流量分析、代码)

图解几个与Linux网络虚拟化相关的虚拟网卡-VETH/MACVLAN/MACVTAP/IPVLAN

Run IPVS in a separate network namespace

kubernetes系列之五:IPVS概览

kubernetes的Kube-proxy的转发规则分析

[转]IPTABLES中SNAT和MASQUERADE的区别

iptables 命令介绍

Iptables规则执行顺序详解

超级详细Tcpdump 的用法

Linux 虚拟网络设备 veth-pair 详解,看这一篇就够了

网卡也能虚拟化?网卡虚拟化技术 macvlan 详解

Linux ip netns 命令

Get started with Macvlan network driver

编辑于 2019-06-17

文章被以下专栏收录