感谢各位已经关注我的微信公众号(VIPDOCKER)的同学,正是你们的鼓励让我能继续坚持写下来,如果还没有关注,欢迎你的加入!新近公众被赋予了原创公众号权限,大家可以直接留言了,欢迎任何建议,谢谢!
容器网络背景
如果只是在一台机器上使用Docker,或许你基本不用关心网络配置,因为Docker Daemon默认模式还是比较友好的,直接帮你们生一个Bridge命名为Bridge0,这样你可以通过端口映射的模式或是主机模式就可以让容器与外部互联互通了,而不用使用任何网络工具(brctl, iptables)手工创建。但是如果将容器放到一个集群环境中(如使用Mesos或者K8S编排工具),这个网络问题就比较痛苦了。因为需要保证如何暴露容器给外部访问,同时又没有过多的额外性能开销,网络方案又是比较可控稳定,这成为了一个基于容器的PaaS平台的成功的关键因素。
我们的需求:独立IP
除了网络性能和网络稳定性的问题需要考量外,我们内部有个很重要的要求就是一定要确保每个容器获得一个独立的IP,并且确保每个容器间可以相互访问。因为我们有自己的基于RPC的服务化体系(有兴趣可以搜索我的分享《唯品会:分布式服务框架实践与容器化演进》),需要一个平面的通讯网络,并且关联系统如APM系统,都是依赖这个独立IP做分析的
网络方案选型
第一次做容器化的架构(半年前),选择网络方案的要点是简单和成熟的方案。网络选型主要有两个大方向,一个是物理机器方案即通过交换机的方式做方案如VLAN模式,VxLan模式等。另一个是软件方案SDN,如Overlay网络等。因为是我们第一次做容器方案,为了保险起见,我们更信任物理设备的方式,所以我们选择了VLAN的模式作为第一次选择,而VxLan对于物理设备有要求,先暂时放弃。不过如果有硬件基础的,则可以有限考虑VxLan方式
第一版的VLAN网络方案
对于使用VLAN的模式,一开始我们都没有找到合适的资料,都是自己摸索出来的,后来新版本的docker支持docker network命令后,这些就简单一些。而我们是无需用docker create network的。主要方案如下:
"vlan mode"
详细内容,参见我之前的文章《Docker的VLAN网络模式配置》,可以在我的公众号的Docker系列文章中找到。
第一版方案的缺陷
从第一版的方案上看,虽然简单而且也比较稳定,我们在生产上跑了4个多月了,没有出现过问题。但是从管理监控的角度看,这个方案还是有明显的缺陷,只是在容器规模还没有达到一定的量的时候,这个缺陷是可以忍受的。但是对于这些缺陷,我们在做下一版的平台的时候就需要寻找合适的解决方案了。
这些缺陷主要是:
一台主机上使用的IP数量被固定了,因为我们是将IP段分配给了docker daemon,这样会存在一个风险如果机器资源还有剩余,mesos+marathon就会在该主机上启动容器,但是有可能这个时候没有ip了,这样marathon就会不停的启动容器销毁容器。
在容器数量比较多的时候,二级平面网络的容器部署,在每个容器的新创建都会广播ARP包,这样整个网络的风暴比较厉害。当然在容器数量规模还未起来的时候,这个可以暂时忽略。特别是对于动态伸缩容器的场景比较小的时候,这个问题并不大。不过通过和京东的永成大神聊过后,有个workaround的方式绕开这个问题,后面会讲。
主要问题在于核心交换机的mac-ip的这个表的容量,因为容器打破了原来的物理机体系,让核心交换机来处理容器的mac-ip的关系,这个对于传统的交换机并不合适。不过本人对于网络设备只是略知一二,所以,这个问题只是听说的程度。如果有不对,请指出。
第二版的VLAN网络方案
第二版网络需求
动态IP管理方案
对于要使容器有独立的IP,又要使每个主机的IP的管理动态化,那么就需要将IP分配放在docker daemon/mesos+marathon的外部管理,通过插件plugin的方式集成。目前可选的方式是docker daemon的IPAM接口,或者是mesos slave的IPAM接口。虽然都叫IPAM,但是他们基于的机制不同。
Docker的IPAM(IP address management)是在Docker daemon的CNM(Container Network Model)机制下的IP管理驱动。CNM的设计模型,可以参考CNM Design。 Docker IPAM可以参考Docker IPAM
而对于Mesos则是遵循CNI的规范。CNI规范可以参看CNI Spec。
不过对于只需要动态管理IP地址的话,基于任何一种IPAM的接口都是可以的。所以我们经过老肖(@熟人云)介绍,参考了Talking Data公司的Shrike来做动态的IP管理。Shrike是基于Docker deamon的IPAM的方案, 所以只能用于基于Docker容器的方案,如果是用mesos unified container,则需要自己去改造成对接CNI的IPAM的接口。
虽然Shrike比较简单,但是目前Shrike有些局限,所以我们需要在上面做一些改造,以支持后续的扩展。主要包括:
Shrike
这个进程如果意外退出,IP的释放消息(Release address
)未被处理,就会造成IP被永久占用。目前我们的思路是,在shrike
启动的时候主动去扫描本机的docker container,然后和etcd上的数据比对,如果有IP没有被释放,则主动清理。
对于上面这个约束,我们需要改造Etcd中数据结构,以支持将主机IP存入etcd中,便于后续的主动清理。同时改造etcd数据结构也是为了限流使用。
网络限流方案
对于网络限流,一开始的思路是想利用mesos+marathon方案的自定义资源管理的方式,但是因为marathon迟迟不支持自定义资源调度,所以这个方式暂时放下了。希望用这样的方式管理的原因是想把网卡变成类似cpu/mem的资源同等对待,只要分配了带宽给容器的综合超过一定数就不再这个主机启动容器了。Mesos的自定义资源参考mesos attr resource
在这里还是要吐槽一下Marathon
,开发的进度和Mesos
太不一致了,希望Mesosphere的同学好好push一下。
这条路暂时走不通,那么在用Shrike
做动态IP分配的时候,我想能不能在IP动态分配的时候就同时做了这个容器的带宽限制呢?所以就有了以下方案
"shrike"
主要的流程如下:
在Shrike中增加一个Throttling的模块,Shrike动态分配IP后发送一条消息给这个Throttlilng模块,包含容器ip信息(可惜没有容器的id)
Throttling模块收到消息后,调用docker daemon接口查询这个ip对应的容器在主机的网络名称(这里可以用unix socket接口或者http接口,因为是本机调用)
查询这个容器的环境变量如net_in, net_out的值(这个目前是启动容器的时候配置上去的)
通过调用linux下的wondershape工具来对容器进行限制带宽
这个方案还有后续几个事宜我们还没有处理的:
我们还在构思,如图所示提供一个Console的管理控制台,实时展示容器的网络配置以及使用情况,因为net_id和net_out多会写入到Etcd中,所以完全可以在界面直接调整这两个值,然后Throttling的模块watch到变化就直接执行wondershape对容器进行更改就可。
减少ARP广播包的方案
ARP广播的目的是确定IP所在的主机的MAC地址,用于二层网络的寻址。但是容器的创建的时候,通常MAC地址是通过一定的规则生成的,所以每次有容器启动,都会广播这个MAC地址和它的IP的对应关系。但是如果MAC-IP的对应关系建立后没有关变,则ARP是无需再次广播的。这是通俗的说法。具体可以看RFC826规范。
既然是这样,我们是否可以找到一种手段来控制ARP包的量呢?有的,就是预先设置好IP-MAC的关系,也就是只要IP相同,它的MAC地址就是相同的。所以对于容器方案,可以预先初始化好规划的容量的所有IP和MAC的对应关系。然后每个容器启动时主动的去拉取这个表,并通过arp命令设置到容器中。(这里有个细节没有去实验,就是如果有几万个容器,arp命令执行这个表的时间是多少,是否影响容器启动的时间?)
这样容器启动后就有所有容器的IP-MAC对应关系,就无需发送过多的ARP包了。 不过这里还有另外一个问题,那就是无法使用IPAM接口,因为IPAM接口没有提供对应的容器的MAC的指定手段。(看CNI的源码历史,MAC设置原来是可以在IPAM中设置的,但是后来被挪到外层了)
所以如果需要在容器启动后直接更新一个大的固定的IP-MAC列表,就只能通过CNM/CNI的接口方式设置IP和MAC地址了
核心交换机的MAC地址的承载能力方案
这个只能是找硬件厂商解决了。据说京东下一代的方案是直接定制核心交换机以支持容器方案。具体细节不太清楚。如果有更好的方案,请记得告诉我
小结
到此为止,基本就涵盖我们第二版的网络改进方案的大体思路了。或许你会问,怎么不考虑用现在热门的几个三层网络方案呢?其实理由很简单,就是还是不能完全掌握的了,特别是如果问题定位,如何调试都是比较困难的。但是依赖与硬件交换机和Linux的特性,这个还是比较有信心的。所以建议如果不是技术有足够的掌控力,不要轻易尝试SDN的方案,除非你买专业的技术服务。
希望这个能对你有所启发,同时希望你能继续支持我(这是目前为止我最长的文章了)