编者按
新春伊始,七牛全新策划 “技术实践专题”,精选技术大牛最具诚意的干货分享,内容涵盖云平台核心技术、系统架构、编程语言、性能优化、大数据等领域,助力所有技术人在2016继续开拓。
本文内容来自由七牛云主办的ECUG Con,独家授权InfoQ整理完成
容器云面临的网络挑战
在传统的IDC的架构里面网络是很重要的事情,在虚拟机环境中网络的发展已经有很多成熟的解决方案,现在为什么还在研究新的方案呢?因为云计算。云计算在2013、2014年之前的时间段,其主要的发力点在虚拟机。但是从容器技术出现之后,虚拟机被更轻量的容器方式颠覆,从而大幅降低开发、运维、测试和部署、维护的成本。当然也带来了很多在虚拟机里没有面临过的问题。
在网络层面,容器的本质是几个被封装了的进程。这必然导致每个容器都会有自己完整的网络栈、有自己的IP。所以整个云的基础设施——集群里有大量的网络和IP要管理,并且进程的启动和结束是与容器伴生的,进程生灭的频繁程度超过了虚拟机几个量级。创建一个虚拟机会开启很多程序,程序之间彼此通信,但和虚拟机外部的通讯是少很多的。而容器的诞生,从网络管理角度来说,微服务架构呼之欲出。
用容器做迁移的时候,如果一台机器宕机了,对应用而言希望是无感知的。因此,如果容器所在的主机出故障,就带来了新的问题,即容器IP的任意漂移。在创建虚拟机的时候,IP地址的分配是一种静态方式;但是在容器上面,IP地址的分配通常是动态分配。一个容器启来的时候,通常我们不会知道它的IP是什么,只有起来之后才知道。
在容器云的场景下,我们倾向于把多个应用放在一个应用上运行,这导致应用和应用之间需要做安全隔离——我们不希望一个出问题的应用会影响其他的应用。所以说容器之间需要决定哪个容器可以通信,哪个不能通信。以前在虚拟机的时代,通过大量分层可以解决,但容器环境里这件事情的难度提高了。
所以我们选择网络解决方案的需求也发生了变化,最大的要求是容易管理。网络间通信量大了,不能够承受太大的损耗。而当发生容器间迁移的时候,需要一种机制,来保证迁移后的IP不会发生变化。同时,它的解决方案是需要细粒度的安全策略。
容器本身是一个单机软件,并没有为跨主机的网络考虑太多。跨主机容器互联的解决方案最典型的是两台主机之间通过一个交换节点实现连通。在主机内起十到几十个主机的规模,考虑到主机上有没有足够的CPU和内存,在这个主机上做静态IP规划几乎是不太可能。这时候我们就引入SDN技术——用软件的方式定义网络,软件数据发生变化时网络就相应地发生变化。
常见SDN方案
简单的谈一下DockerBridge,关于主机内部的网络管理工作。每个容器里面都有一个虚拟网卡,和主机上的虚拟网卡做配合,所有容器内的虚拟网卡都可以和主机通信。通过端口映射,我调用对方的容器服务的时候,不能使用该容器直接的地址,必须使用它主机的地址。因为有了这样一个转发,网络通讯的性能有所损耗。当然,还有一个问题更严重,访问你的容器先要搞清楚你的主机是什么,这样网络通讯会跳来跳去。不但麻烦,还会带来端口冲突。每创建一个容器会绑定一个端口,怎么管理好这些端口是一个很大的挑战。
第二个解决方案是Flannel,这个想法很好。每个主机负责一个网段,在这个网段里面分配一个IP地址。访问另外一台主机的时候,通过网桥到达主机上的IP地址,这边会有一个设备,程序会把你发的包读出来,去判断你的目标地址是什么,归哪台机器管。还会把你的IP包外面加一个UDP包,发到目标地址。收到之后,会把前面的包扔掉,留下来的就是目标容器地址。这个方法有几个问题,第一个是要做封包的操作。第二个问题是每个主机上的容器是固定的,容器的不同主机之前的迁移必然带来IP的变化。
再来说说Weave,他的思路是共享IP而非绑定。在传输层先找到目标地址,然后把包发到对端,节点之间互相通过协议共享信息。Flannel和Weave的特点是类似的,都用的是UDP或者是VxLAN的技术。事实上使用UDP性能是很差的,VxLAN和UDP差不多,它有一个网络切隔,而且在里面可以跑二层协议。还有一种是IPIP封包,直接把一个IP包封装在一个IP包里面。
以上不难看出,原生网络性能比较差,使用UDP的时候性能损失在50%以上,使用VxLAN也会有20%~30%的损耗。所以我要在内核上封包,使用硬件来解决这些问题。而且容器和容器之间出现通讯故障,调试的时候非常困难。
关于封包
回过头来想一下,我们为什么要封包?其实它是改包,主要解决的问题是同一个问题。即容器网络里,主机间的不知道对方的目标地址,没有办法把IP包投递到正确的地方。
传统的二层网络是用路由来互相访问,不需要封包。但是路由规则怎么维护?利用BGP(基于TCP之间两两连接)搞一个分布式的路由集群能否满足规模?
Metaswitch:容器内部配一个路由指向自己宿主机的地址,路由规则下发,这是一个纯三层的网络没有封包,接近原生网络性能。
在容器里面我们怎么做呢?我们发现了Calico项目。不同主机上的每个容器内部都配一个路由,指向自己所在的IP地址;每台服务器变成路由器,配置自己的路由规则,通过网卡直接到达目标容器,整个过程没有封包。
所以这是一个纯三层网络,没有引入一个DP,没有封包。在主机内部做另外一个容器,可以三条到达终端,你可以知道是谁出了问题,调试的时候很容易,很好管理。容器内的应用数据传出来,和二层完全隔离,对于我们绝大多数的应用来讲只需要三层就够了,很少有应用处理二层。
那么,路由交换是不是很难呢?用传统的BGP技术就可以实现。这个协议在大规模应用下是一个很好的场景。而且BGP有一个自治域的概念。在这个场景下会有一个问题,路由之间的信息交换实际上是基于TCP协议,每两个之间都有一个TCP连接,规模大了以后复杂度非常高。所以在这种场景下,大概是几百个主机直接使用BGP的方式来做;再大一点的话,选择分布式互联,这样可以把网络规模继续扩大,最后能够支持到几万台机器。如果不需要这些策略,不做商业逻辑和业务逻辑,只是用BGP来做路由互联的话,其实还是很简单和高效的。
金融业务的具体实现
宜信内部的做法是用powerstrip实现,还有一个是手工去做,启动容器直接用Docker,然后自己再去设置IP。如果容器之间都互通,创建容器的时候都通过配置环境变量,会创建一个Profile,配置好iptables 以及ipset可以保护我们的服务器。再稍微复杂一点的话,可以修改ACL规则,如果有很多个APP,每个APP都需要访问数据库,每增加一个IP都要修改配置这样显得太麻烦,我们可以通过给所有的APP打标签的方式来解决这个问题。
配置容器的三种方法:
用powerstrip来Hijack劫持Docker的API,这个思路很有意思。
calicoctr container命令。
libnetwork驱动(需Docker1.9以上)。
因为做的是金融业务,性能其实不是最吸引人的,ACL应用是宜信关注的重点。比如在宜信的场景下做云平台的时候,会有应用的概念。一个应用有多个容器,互相可访问;不同应用的容器互相不能访问;一个应用对另外一个应用提供服务,作为服务容器并且规定哪些可以访问。所有的应用容器能够访问集群,应用容器不可以访问自己所在的集群的任何一台服务器。总之,我们要实现应用可以访问主机上指定的服务,可以访问除本网段之外的其他网段。
Calico使用总结
Calico的应用场景主要是IDC内部,推荐部署在二层网络上,这样所有路由器之间是互通的。这种场景是大二层的解决方案,所有服务器都在里面。但大二层主要的问题是弹性伸缩的问题。频繁开关机的时候,容器启停虽然不影响交换机,但容易产生广播风暴。网络规模太大的时候,一旦出现了这样的问题,整个网络都会有故障,这个问题还没有解决的特别好。
Calico作为一个纯二层的应用问题不大。我们可以把集群分成了很多网段,对外是三层网络的结构。集群内部分成多个自制域,比如一个机柜是一个自制域,之间通过交换路由的方式实现在三层容器到容器的互通。瓶颈在于容器的路由信息容量。所以说本质上它不是一个应对互联网规模的解决方案,但对宜信场景是够用的。
我们实施的时候遇到一些坑,比如容器在启动时没有网络、操作ETCD的时候BGP配置频繁加载,等等,最后我们多一一解决了。我们也发现了driver方案功能方面的弱点,现在还继续使用劫持API的方法。如果要在云上使用,如果支持BGP,直接可以用;如果不支持BGP可以用IPIP。现在英特尔的网卡支持两个协议,一个是GRE、一个是VxLAN。如果真的把它放在云上,我们可以考虑一下把VxLAN引进来,性能上的差别不是很大。
本文根据洪强宁在ECUG Con的演讲整理而成,ECUG(Effective Cloud User Group,实效云计算用户组)是由Erlang爱好者、分布式领域的知名专家组成的民间技术团体。ECUG Con是该组织一年一度交流云计算产业前沿技术的大会。
本文转载自InfoQ网站。
作者介绍
洪强宁,宜信大数据创新中心首席架构师,资深 Python 开发者,曾为豆瓣首席架构师。在互联网平台架构方面有十年的工作经验。目前兴趣聚焦在以 Docker 为核心的技术架构和安全方面。
关于容器云网络的话题,可配合七牛架构师徐兆魁的分享《主流容器SDN技术与微服务架构实践》,作为延伸阅读。
七牛架构师实践日 第五期【容器核心技术与最佳实践】火热报名中,详情请“点击这里”查看。