LOADING

Follow me

【转载】基于Neutron的Kubernetes SDN实践经验之谈
四月 24, 2017|DockerPaaS

【转载】基于Neutron的Kubernetes SDN实践经验之谈

【转载】基于Neutron的Kubernetes SDN实践经验之谈

KubernetesGoogle多年大规模容器管理技术的开源版本越来越多的企业被迫面对互联网规模所带来的各类难题,而Kubernetes以其优秀的理念和设计正在逐步形成新的技术标准。

本次分享将给大家介绍Kubernetes的网络通信原理,并介绍几种典型的Kubernetes网络实现方案。最后分享下我们公司ECP容器管理平台的在Neutron基础上为Kubernetes实现的SDN方案(Skynet)实践经验和演进。

首先,向大家科普下Kubernetes所选择的CNI网络接口,简单介绍下网络实现的背景。

CNI即Container Network Interface,是一套容器网络的定义规范,包括方法规范、参数规范、响应规范等等。CNI只要求在容器创建时为容器分配网络资源、删除容器时释放网络资源。CNI与调用者之间的整个交互过程如下图所示:

基于Neutron的Kubernetes SDN实践经验之谈

CNI实现与外界的交互都通过进程参数和环境变量传递,也只要求输出结果符合CNI规范即可,与实现语言也没什么特殊要求。比如Calico早期版本就使用Python实现了CNI规范,为Kubernetes提供了网络实现。常见的环境变量设置如下:

  • CNI_COMMAND:调用指定CNI动作,ADD表示增加网卡,DEL表示释放网卡

  • CNI_CONTAINERID:容器ID

  • CNI_NETNS:容器网络命名空间文件位置

  • CNI_ARGS:额外传递的参数

  • CNI_IFNAME:设置的容器网卡名称,如eth0

正因如此,CNI规范实现起来非常容易扩展,除了CNI自带的Bridge、Macvlan等基本实现以外,还有大量的第三方实现可供选择,包括Calico、Romana、Flannel等常用实现。

同时CNI支持多种容器运行时,包括Docker、rkt、Mesos、Hyper等容器引擎都可以使用。这也是Kubernetes选择使用CNI的一大重要原因。

相对的,Docker提出的CNM(Cotainer Network Model)模型实现就比较复杂,但更为完善,比较接近传统的网络概念。如下图所示:

基于Neutron的Kubernetes SDN实践经验之谈

Sandbox就是容器的网络命名空间,Endpoint为容器连接到网络中的一张网卡,而网络则是一组相互通信的Endpoint的集合,比较接近Neutron中的网络定义。

在CNM中,docker engine通过HTTP REST API调用网络实现,为容器配置网络。这些API接口涵盖网络管理、容器管理、创建endpoint等十几个接口。

同时CNM模型还隐含在docker自身附带的service机制、dns机制等附加约束,因此可以在一定程度上说,CNM模型只是专为docker容器实现的,对别的容器运行时并不友好。

由于上面这些技术上的原因以及一些商业上的原因,Kubernetes最终选择了CNI作为自己的网络接口。

当然,Kubernetes也提供一些取巧的方法,将CNI接口转化为对CNM模型的调用,从而实现两种模型的通用。

例如to_docker,这个脚本就将Kubernetes对CNI的调用转换为Docker CNM网络的对应操作,从而实现CNI到CNM的转换。

接下来,给大家介绍下Kubernetes中网络概念和通信原理。

在Kubernetes的网络模型中,约定了三个基本约束:

  • 所有容器之间都可以无须SNAT即可相互直接以IP通信。

  • 所有主机与容器之间都可以无须SNAT即可相互直接以IP通信。

  • 容器看到的自身IP与其他容器看到的容器IP相同。

在满足约束的基础上,Kubernetes不关心具体的网络通信原理,只以三个约束为既定事实,在此基础上,根据Kubernetes自身逻辑处理网络通信,从而避免Kubernetes功能纠结在纷繁复杂的网络实现中。

而在网络概念上,Kubernetes中有两种核心IP:

  • POD IP:有CNI实现提供,Kubernetes不管这个IP是否可达,只负责使用这个IP实现配置iptables、做健康检查等功能。默认情况下,这个IP在Kubernetes集群范围内都是可达的,并且可以进行ping等操作。

  • cluster IP:即服务IP,这个IP在Kubernetes中只是用于实现服务交互通信,本质上只是iptables上的几条DNAT规则。默认情况下,这个IP上只能提供服务端口的访问,且不可ping。

以集群的DNS服务为例,相关的核心iptables如下图所示:

基于Neutron的Kubernetes SDN实践经验之谈

这些iptables都是由kube-proxy生成的,而且kube-proxy并不实际负责进行转发,因此即使kube-proxy服务异常,已经产生的iptables依然可以使流量能够正确地在服务IP和POD IP之间流转。其网络流量路径可以参考下图:

基于Neutron的Kubernetes SDN实践经验之谈

当访问DNS服务的端口10.254.0.3时,kube-proxy生成的iptables DNAT规则,将流量转发到后端POD IP及对应端口上,将流量按后端POD的IP个数实行随机均等分配。

而kube-proxy可以从kube-apiserver获取服务和POD的状态更新,随时根据其状态更新iptables,从而实现服务的高可用与动态扩展。

在基础的IP通信机制上,Kubernetes还通过Network

Policy和Ingress提高网络安全性和响应性能。

Network Policy提供了网络隔离能力,它基于SIG-Network group演进而来,Kubernetes只提供内置的labelSelector和label以及Network Policy API定义,本身并不负责实现如何隔离。

在Kubernetes使用的CNI网络实现中,目前只有Calico、Romana、Contiv等少数几个实现了Network Policy集成。一个典型的Network Policy定义如下所示:

基于Neutron的Kubernetes SDN实践经验之谈

它指定约束,具有role:db标签的POD只能被具有role:frontend标签的POD访问,除此之外拒绝所有流量。从功能上来讲,Network Policy可以等价于Neutron的安全组。

Ingress是负责对外提供服务的,通过Nginx对外提供一个单独接口,实现集群中的所有服务的对外提供,从而取代使用NodePort暴露每个服务的现有实现。

目前,Kubernetes的Ingress提供了Nginx和GCE两种实现,感兴趣的同学可以直接参考官文档:

https://github.com/kubernetes/ … llers。

Kubernetes社区中,比较常见的几种网络实现主要是以下两种:

基于Overlay网络:以Flannel、Weave为代表。Flannel是CoreOS为Kubernetes专门定制实现的Overlay网络方案,也是Kubernetes默认的网络实现。

它基于VXLAN或者UDP整个集群的Overlay网络,从而实现容器在集群上的通信,满足Kubernetes网络模型的三大基本约束。

由于在通信过程中存在数据包的封包解包等额外损耗,性能较差,但已经基本满足使用。

以L3路由为基础实现网络:以Calico、Romana为代表。其中,Calico是广泛流传的性能最好的Kubernetes网络实现,基于纯三层的路由实现网络通信,结合iptables实现的安全控制,可以满足大多数云的性能需求。

但是由于它要求主机上必须打开BGP形成路由拓扑,在一些数据中心上可能不会被允许。同时,Calico还比较早地支持了Network Policy,并且可以将Calico自身的数据直接托管在Kubernetes中,从而实现与Kubernetes的深度集成。

从上面这些网络实现来看,目前Kubernetes的网络实现都还谈不上是比较成熟的SDN,因此我们公司在考察Kubernetes后,决定基于Neutron,为Kubernetes提供一个可用的SDN实现,这就是Skynet项目的由来。

下面我来跟大家分享下,Skynet在实践过程中的一些经验。

在实践中,首先要解决的就是Kubernetes中的网络概念,怎么翻译到Neutron中,才能比较合适地实现功能。

在第一个版本中,Kubernetes网络中概念翻译对应如下表所示:

  • POD —-> 虚拟机

  • Service ——-> loadbalancer

  • Endpoints ——-> pool

  • Service后端POD —-> member

但是,由于Kubernetes中支持同一服务上设置多个服务端口,而Neutron的每个Load Balancer仅支持一个对外端口。好在,去年OpenStack的Mitaka版本后,Neutron LBaaS V2正式发布,因此有了第二个版本的概念翻译。

  • POD —-> 虚拟机

  • Service —–> lbaasv2 loadbalancer

  • Service port —–>lbaasv2 listener

  • Endpoints —–> lbaasv2 pool

  • Service后端POD ——>lbaasv2 member

  • POD livenessProbe —–>health monitor

LBaaS V2的基本术语图解如下所示:

基于Neutron的Kubernetes SDN实践经验之谈

  • Load Balancer:负载均衡器,对应一个HAProxy进程,占据一个子网IP。可以逻辑上映射为Kubernetes中的Service。

  • Listener:监听器,表示负载均衡器本身提供的一个前端监听端口。对应service定义中的ports中port。

  • pool:监听器后端的成员集合记录。

  • member:监听器后端的成员。对应service使用的Endpoints的addresses列表,每个地址可以对应service声明中的targetPort的映射。

  • health monitor:pool中的成员健康检查器,类似Kubernetes中的livenessProbe,目前不映射。

就资源数量的映射来说:

  • Kubernetes的一个service,对应一个Load Balancer。

  • service中的每个port对应监听这个Load Balancer的一个Listener。

  • 每个Listener后端都对接一个pool包含其后端资源。而Kubernetes中的每个Service都有一个对应的Endpoints来包含其后端POD。

  • Endpoints中的每个IP+Service声明port的targetPort就对应pool中的一个member。

初步完成了概念的映射后,我们简单介绍下开发中的思路。

在整体结构上,Skynet居于Kubernetes和Neutron之间,实现了CNI规范,基于Neutron为容器配置网络。

service-watcher负责监听Kubernetes的资源,对服务等概念翻译为Neutron实现,从而实现完整的网络功能。如下所示:

基于Neutron的Kubernetes SDN实践经验之谈

kubelet是创建POD的直接操作者,在为POD设置网络时,通过CNI接口规范,调用Skynet实现。Skynet通过调用Neutron为容器分配IP,并通过在POD容器网络命令空间中操作,实现IP、路由等通信规则的设置。

而Neutron原生的DHCP、LBaaS v2等机制可以基本保持不变。从而实现完整的集成,可以使Kubernetes集群获得完整的Neutron SDN功能。

而当容器内需要进行DNS时,则可以通过Neutron自带的DHCP Agent负责实现解析,在集群网络中正常工作。

如前文所述,Skynet实现了CNI规范,kubelet与Skynet之间的交互过程如下所示:

基于Neutron的Kubernetes SDN实践经验之谈

简要介绍下每个步骤:

kubelet通过CNI机制调用skynet,主要传递的参数如下:

  • CNI_COMMAND:调用指定CNI动作,ADD表示增加网卡,DEL表示释放网卡

  • CNI_CONTAINERID:容器ID

  • CNI_NETNS:容器网络命名空间文件位置

  • CNI_ARGS:额外传递的参数

  • CNI_IFNAME:设置的容器网卡名称,如eth0

执行ADD操作时,Skynet根据传入的参数和POD的配置,通过neutron-server为POD创建port。

执行ADD操作时,Skynet根据port和网络配置,为容器创建网络设备,并挂载到容器命名空间中。

neutron-linuxbridge-agent,根据容器的网络和安全组规则生成iptables。从而利用Neutron原生的安全组功能,同时也可以直接利用Neutron的一整套SDN实现,包括vRouter、FWaaS、VPNaaS等服务。

service-watcher将Kubernetes服务映射为Neutron LBaaS v2实现后,以VLAN网络为例,POD与服务之间的流量通信过程如下图:

基于Neutron的Kubernetes SDN实践经验之谈

当集群内容器访问服务时,Kubernetes默认都是通过服务名称访问,服务名通过Neutron的DHCP机制,可以由每个网络的Dnsmasq进程负责解析,获得service对应负载均衡的IP地址后,即可用于网络通信,由物理交换机负责流量的中转。

在实际实现中,以Kubernetes中一个服务的定义映射到Neutron的loadbalancer为例演示下。

例如对下面的service实现:

基于Neutron的Kubernetes SDN实践经验之谈

基于Neutron的Kubernetes SDN实践经验之谈

POD和Service通过特定注解来指定使用的Neutron网络、IP等配置,与Kubernetes尽量解耦。

上面的Service映射成Load Balancer后,其定义如下所示:

基于Neutron的Kubernetes SDN实践经验之谈

基于Neutron的Kubernetes SDN实践经验之谈

基于Neutron的Kubernetes SDN实践经验之谈

基于Neutron的Kubernetes SDN实践经验之谈

其对应的HAProxy进程配置如下所示:

基于Neutron的Kubernetes SDN实践经验之谈

综上所述,通过基于Neutron的Skynet,我们为Kubernetes初步实现了SDN的功能,同时提供了如下网络功能增强:

  • POD的IP、Mac、主机名等网络配置的保持;

  • 基于Neutron安全组,实现了POD之间的网络隔离功能,更加通用;

  • 支持通过HAProxy直接对外提供服务,性能上会比原生的iptables好很多。

当然,目前有一些Kubernetes特性在Skynet网络方案中还不支持,需要在后面进行增强或实现:

  • Headless services这一类没有集群IP的Service无法处理。

  • 由于neutron-server与neutron-plugin之间的消息都是通过RabbitMQ进行,不是特别适合容器环境下网络快速变更的现状,会是整个方案的一大瓶颈。


Q&A

Q:请问Skynet用到的neutron-linux-agent需要部署到所有kubelet节点上吗?另外有没有开源的demo版本学习下呢?

A:需要部署,这个Agent不只是安全组,对VXLAN类型的网络,它还可以编写FDB规则实现VXLAN网络。代码开源正在筹划中,再测试一波,就会发布。

Q:感谢分享,请问你们有没有在在一个虚机内的pod需要挂到不同网络的场景?如果有的话,pod之间跨二层网络的互通你们是怎么做到的?

A:目前来看,单个POD挂接到不同网络里的场景还比较少,但逻辑上来说,跟设置单网络的方式区别不大,只是默认路由走哪张网卡的问题值得商榷。

POD之间跨二层网络互通可以通过物理交换机配置两个VLAN互通或者直接通过Neutron的vRouter做网关。

Q:还有个问题,当一个Docker物理机down了,上面的容器会自动迁移到另一个节点还是Kubernetes自动启动相关的容器!能否实现特定容器在不同的Kubernetes节点上自动迁移而且保持ip不变!

A:除非POD指定的调度需求无法满足,Kubernetes会自动尝试迁移POD到其他主机上启动。POD IP的保持还支持通过注解的方式设置POD的IP,当然需要限制RC的副本数只能是1或者运行的是单POD,否则会出现IP冲突。

Q:容器的port支持Neutron的一些QoS特性吗?例如限速。

A:据我对OpenStack的调研,Neutron的网络限速使用的是tc规则定义的,在使用Linux bridge+VLAN网络的情况下,Skynet可以保证POD的网络配置与VM的配置一致,从而使QoS规则能够正常运作,实现限速。其他特性暂时还没有研究过,后期会继续调研。

Q:Skynet目前支持Overlay的网络吗?如何解决内网和外部网络相互访问呢?

A:目前Skynet支持使用Neutron的VXLAN网络实现Overlay,结合Neutron vRouter的外网网关功能,通过设置路由为vRouter的外网网关,使内外网能够互通。

Q:请问Veth设备另一端连接的是什么设备?

A:Veth pair是Linux网络的基础技术,Veth总是成对出现的,所以Veth的另一端还是一个Veth,可以连接到bridge上或者直接暴露在主机上。

Q:目前OpenStack社区也有一个类似的项目Kuryr,你能简单介绍下你们的方案和Kuryr的差异吗?

A:Kuryr是社区项目,考虑的不像我们Skynet这么简单,可以短平快地以实现功能为首要目标,而Kuryr需要考虑的就比较多,需要同时支持Docker的CNM和Kubernetes的CNI。

我们的Skynet从去年11月份开始调研实现,当时Kuryr的Kubernetes支持还只是个空壳子。现在,根据Kuryr在的BP,我们的实现思路差别不大,只是Kuryr更多的考虑了租户对接等等问题。

Q:pod的IP保持是怎么做到的能详细介绍一下吗?如果pod名字发生变化了,可以保持吗?

A:如前面两个类似问题,POD的IP保持通过两种方式:如果POD名称不变(比如StatefulSet中的POD),则对应的port就不会变,因此根据port设置的POD IP、MAC、主机名都不会变。

如果POD名称变化,在一定的约束下,通过注解保留POD的IP地址信息,强制使POD保持IP不变。

以上内容根据2017年04月18日晚微信群分享内容整理。分享人冯建建,天云软件ECP开发工程师,主要负责ECP容器管理平台的网络、存储等方面的功能实现。熟悉CloudStack、OpenStack等开源IaaS平台软件。

最近专注于容器集群管理平台的技术实践,包括Kubernetes、Swarm,对它们的网络实现、存储实现都有比较深入的了解。

DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesz,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

文章来源:

http://dockone.io/article/2261?utm_source=tuicool&utm_medium=referral

no comments
Share

发表评论