透过90年代网络架构看可扩展性——云网络应有特点
本问讨论了Calico项目的目的,以及探讨了用从上世纪90年代以来发展起来的网络架构,三层网络模型,L2与L3层转发模式,论证了如何基于现有基础,通过采用新兴的“奶牛模式”,来实现云网络架构的可扩展性和高性能。
最近,有很多关于Calico项目的讨论,但是大多数讨论只是围绕着通过把现有互联网基础架构应用到数据中心来实现扩展性和简化网络架构来进行的。
然而,当审视这个项目时,还是有一部分人会用我称之为“经典企业镜头”的方式来进行,这种方式将会对Calico项目带来误解,或者将传统网络模型应用到Calico上。其原因在于Calico是从一个非常不同的视角发展而来的,因此用传统模式来分析它不一定能奏效。
这些误解集中在三点,下面我会逐点展开。在此之前,我还是要指出,在可扩展式架构方法和经典企业架构方法之间还是有哲学基本概念上的差别。
提起经典企业架构方法,一些特质包括:当企业数据中心建设时,系统将会长期运行而且很好地自包含(self-contained);每个应用都会有各自的需求,因此会有非常客制化的架构设计,或者说架构模板会非常多;必须要在增长的复杂度和架构灵活度之间寻找最佳点;因为需求经常是静态的,因此,最佳点基本上需要合理的妥协。
随着可扩展或者云中心架构时代的来临,我们今天采用的架构知识表明:真实的基础架构需求更多时候是统一的(同样的协议,相似的存储模型,相似的计算模式),但也是更加动态和更加扩展化的。这就引出了我们常说的“宠物和奶牛模式”。不幸的是,网络架构还是被当做宠物模式,“被接受”的模式还是试图将正在使用的,脆弱的,静态的设计模式一直到新的,可扩展式的,更少客制化的环境。明显的,向一个脆弱的,静态网络模式注入可扩展性必将带来新的“挑战”。在Calio项目中,我们将在网络上采用更多的“奶牛”模式,实际上,从云中其它模块(可扩展式,非常动态的,单一设计模式)中借鉴驱动 (drivers),从而整合成扩展性很好,可以用于公有云的网络模块。我们相信这是一条通向云、可扩展环境的可选道路,它抛弃了不必要的复杂性,从而带来了动态和扩展性。
真正为大家所知的是Calico是可扩展架构,因此,比overlay方式更有效。但是更重要的是,对于一般的使用场景非常有效,只对必须的场景增加相应的复杂度。100%的纯粹架构功能设计相对简单,只要你并不在意复杂度带来的操作上的困难。目前的办法是为不同子网采用不同的架构,在尽量不碰到麻烦的情况下带来最佳的效果。我们相信Calico项目正式朝着这个方向努力的。
关于Calico,最主要的三点建议是:
L3 vs. L2 and overlays vs. native
最近几个项目,我们听到很多关于“Calico为了可扩展性回归了L2”,或者“Calico使用microsegmentation模式(一种从L3路由架构看起来更像L2层的模式) ”。这两种说法,混淆了两个概念:
- 网络上转发量子(quata)是什么?或者说转发系统基于底层包的哪个比特?
- 网(fabric)中是什么把转发节点整合成一个大网络
让我们分开讨论。
网络中什么是转发量子?
今天在可扩展或者云架构中,大部分应用产生的数据包都是IP包。如果发现非IP包(或者IP相关,像 ICMP),那我就可能要拍砖了。。。IPX, NetBEUI, EtherTalk, Banyan Vines, ATM, and DECNet 等系统已经过时很久了 (我猜DECNet 有可能在一些暗黑角落仍然会生存一段时间). 当人们提到“我需要L2网络”时候,更多时候意味着“我需要一个私有IP网络”或者“我不想为了这个应用改变从上世纪90年代就使用的架构”。(好吧, 上世纪90年代令人窒息的, 三层结婚蛋糕似的网络回归。)
因为IP是我们目前使用网络的原子(quata),因此使用它作为转发模式才有意义。IETF选择L2OL3打包模式(PWE3,L2VPN等等),其原因就是:L3是现实中的标准,为什么不使用现实标准,在此之上打包呢(传统的L2)。但是他们忘记了,这种想问题的方法会走向死胡同,就如同将基于Ethernet的IP包包装在VXLAN上似的,有意义吗?效率可以接受吗?容易排错吗?如果我们真的认为Ethernet是正确的转发原子(quata),我们直接建一个L2网络不可以吗?---哦,看起来我们曾经尝试过,但是并不成功,对吗?
使用IP作为转发原子(quata)带来一个问题,L2层分段的概念会丢失很多信息。在一个以太网络,两个节点之间要么在一个段内是相连的,要么是不相连的。如果是相连的,他们之间可以转发,如果不相连则不能转发。 IP并没有一个段的概念(甚至是子段,将L2层段概念对应于IP地址段)。而在一个纯粹的IP网段,路由器转发“longest prefix match(最长前缀匹配)” 一般来说IP地址可以被一组比特前缀分组, (例如: 192.0.2.0/24, 198.51.100.16/30, and 2001:db8:://128),这些分组跟底层物理拓扑没有任何关系,或者,可以说所有符合以上分组特征的地址从路由器角度来看共享一个路由 。因此,如果一个路由器有一条路由 192.0.2.0/24 是通过 interface 1, 而192.0.2.26/32 通过 interface 2, 那么所有的通过192.0.2.0/24的流量都会通过 interface 1, 除非目的地址是 192.0.2.26, 会通过 interface 2. 这种方式允许一些在Ethernet无效的方式也可以被采用,使得“microsegmentation”的概念在IP网络中真正有意义。
Calico方法有一些前提。所有(或者几乎所有)流量都是可以被转发的IP,IP地址不是写死的,而是采用某种自发现服务(比如,DNS)。这两种模式在业界至少有15年历史,也许我们可以让这种上世纪90年代的网络技术用的更久。基于这种模式,Calico团队相信我们并不需要把IP流量打包进L2层,就如同将IP流量打包进其他网络层(例如VXLAN,NVGRE等等),最后把他们又打包成另外一个IP包,这是完全没有必要的。我们和大家在谈论Calico项目,或者在大会,交流会上期望表达这一观点,因此,我们不希望也不会使用打包的方式作为Calico项目主要的传输机制。
网内如何连接节点(网:internal fabri)
一些不太理解路由网络工作原理的人可能会说:“Calico回归Ethernet是因为可扩展的原因”。这和Calico网站讨论物理、网络拓扑的文档有关,这些可以从 here 和 here获得。
在互联网架构编年史中,你会发现如何让背板操作和路由器更好的结合是一个长期的“宗教式的”战争。某些人使用switching方式(首先是 ATM,然后是Ethernet或者MPLS),其他人使用routing(比如PPP over SDH),其原因并不是某些人更聪明些,而是不同的操作有不同的需求和限制。IP网络的优美之处在于几乎有无穷多的选择可以跟路由器相连,可以直连其它路由器(edge-router,或者core-router概念),从数据中心角度来看更像一个L3网络;你可以使用switching,例如 Ethernet或者MPLS,前者最有代表的是L2层,后者则是以大型服务提供商为代表。实际上,你甚至可以采用 carrier pigeons (如果某个人可以为Calico实现IP over Avian Carrier,Calico团队会有大奖给你)。
使用IP转发设计,然后将计算节点、服务器、从节点都连接到路由器上,我们允许基础架构来决定这些服务器如何相连,并把这一决定对应用层隔离,或者简单说,我们可以将架构和应用解耦。
与做任何工程设计类似,要在很多因素中间做折中,基础架构设计必须要权衡这些因素,最终决定怎样在Calico路由器间互连。 (今天很多大规模部署Calico的客户使用Trident II based ToRs 作为L3互联的方式。不带路由聚合会产生128K IPV4的负载。对于没有很多路由聚合的长前缀匹配场景,除了仍然具有IP的匹配性外,仍然具有高扩展性)。
我们在网站文档部分发布了一些关于不同互联方式的想法,但是因为存在L2和L3不同连接Calico节点的方式,不同的选择将会影响到路由的扩展性,但是这并不意味着Calico将会“回归Ethernet”。任何互联Calico节点的架构, 最终都会转发IP包。
“私有”网问题
另外一个我们经常听到的问题是:从上世纪90年代的网络并未消失,一些关键应用写死了IP地址;或者某些公司之间收购,大家都使用10.1.1.1作为关键应用的IP地址,而且无法更改。我们承认这个时间并不太平,这类事情很有可能发生,那么Calico怎么处理这类问题呢?让咱们首先看看其他人怎么做?
全部打包(Encapsulate everything)
网络供应商最喜欢打包模式……,网络操作人员却恰恰相反……然而即使 overlay操作,或者打包网络很困难,对这类问题的回答基本都是“全部打包”。如果你假设大部分流量并不是基于IPV4节点的,那么这个方案影响不大。但是如果不是这样的话,你的基础架构可能就很大的问题(叫做1:1 NAT和DNS ALG,NAT链,和更多的痛点)。如果大部分流量负载工作机制很好,为什么非要为少部分“不法之徒(miscreants)”而去改变呢?
NAT更简单?
另外一个问题来自NAT。如果我对全部负载都打包,我需要在服务末端提供NAT功能,但是NAT有一些问题:一个就是 NAT太面向状态。所有NAT实现中,必须为每一个链接或者流经NAT设备的流维护一个状态表。如果设备宕机,所有链接就会断掉。如果无法接受这种情况,就需要在多个NAT节点之间实时复制状态。在一个大型网络中,这回成为一个严重的问题。另外,如果使用NAT,有可能需要欺骗DNS(比如在NAT两端要对同一个域名解析出不同的IP地址)。这就需要维护一个多向映射表。要想对这种环境进行排错,就需要理解在源端和目的端应该开到什么,然后确认确实看到这些内容。这也是为什么在一个NAT/DNS-ALG云端部署一个应用第一次(有可能第二次,第三次)一般都不会成功的原因,NAT一点都不简单。
一个工具 (464-XLAT)
因此在Calico中,我们采用了不同的方式。我们采用IETF-464-XLAT标准,IPV6标准信息传递机制。是的,也就是意味着底层必须要是IPv6,但是近十年之内的设备都支持IPv6。另外,因为官方宣布IPv4地址近乎衰竭,真的需要在可扩展性云中激活IPv6功能(北美ARIN最近就拒绝了一次IPv4的申请,因为没有可用的IPv4地址了)。
现在深呼一口气,我们一起看看使用IPv6能够做什么。IPv6地址空间比起IPv4大很多,使得我们可以把整个IPv4地址空间(大约2百万地址)映射到预留的IPv6空间。在Calico中,我们给每一个实例一块重复的多租户空间,每个都是2百万地址。每个实例有一个不同的IPV6前缀,来假装IPv4的源和目的地址。然后将IPv4转换成IPv6包,转发出去,在接收端,过程刚好反过来,应用感知不到这个IPv6与IPv4地址的映射。
因为可以把所有IPv4地址都转换成唯一的IPv6地址,因此我们可以做一些算法上的优化,而不是只记录每个包的状态。重复一遍,如果愿意,Calico可以无状态的支持所有重复的IPv4,映射工作可能失败,当包弹回来,流量可以继续转发。无状态,无状态复制高可用,只是静态的地址传送。另外一个好处是,如果一旦知道“实例”如何包装成IPv6地址,不用查看NAT状态表,不用查看隧道映射表,只看IPv6地址,就可以查看任何数据包,精确告诉此包的IPv4源地址和目的地址,属于哪个实例。
因此,当(因为)Calico并不把重叠的地址看成固有的负载,因此更容易操作,维护,相对于“全部打包”模式和NAT模式,更容易排错。
接下来可能会问:“如果需要跟一个不在实例地址空间的服务通讯,怎么办?”,那么解答如下。
如果此服务被网(fabric)提供,强烈建议这些服务都采用IPv6。然后,可以使用DC-SIIT模式,这样IPv4节点并不知道在跟IPv6节点通讯。如果不可行,那么有状态的464-XLAT可以被用于从一个重叠的IPv4实例访问外部IPV4目的地址。
将策略和连接性解耦
最后一个迷惑也来自于90年代的,在那个网络暗黑年代,唯一一个给大型网络部署策略的方法就是通过防火墙,这也是三层模式的缘由。每一层(前端,应用,数据库)通过物理防火墙端口互相隔离,同一层内所有节点都连接到同一个防火墙端口。这使得监控和管理变的容易。至少,当我们讨论物理节点是很容易。更进一步,这种模型中,把策略和连通性合并了。连通性通过策略控制点提供。然而,本身他们都是不同的概念。一个是“我怎么能到那里?”,另外一个是“我能去哪儿吗?本来不可以被合并的,然而不行的是,在这种模型中,他们被合并了。
很多人仍然认为他们的想法是业界最佳实践,有时被称为“隔离”或者“服务接入”。然而我们并不在讨论物理设备。。。。
集中化 SPOFs ( firewalls) 更容易些吗?
如果我们吧这种模式引入到可扩展年代,我们会看到很有趣的模式。如果我们使用三层模式,那么我们会有很多应用服务器分布在数据中心的所有容器中,可以作为可移动负载飘移。我们也会有很多web前端具有相似的配置。实际上,某些 web前端可能跟一些应用在同一个物理节点上。然而,我们也有虚拟防火墙容器,很可能,在大网里(或者其他一些节点上)。那么,一个应用服务如何跟web 前端通讯呢?必须经历 IP→Ethernet→VXLAN→IP打包过程,最后通过数据中心网络发送到防火墙,在这里逐层解包,最终剩下最里面的IP包信息。防火墙检查此包,决定是否允许发送,然后重新打包,发送到web前端服务器,在这里数据包被再次解包,最内部IP信息被web服务器收取。看起来效率很低。。
下一个问题是“防火墙”。是的,这是一个单节点提供的服务,因此,是一个单点故障。可以有多个防火墙,但是每个防火墙保持同步。防火墙高可用是一个有趣的问题,会带来很多麻烦。。。。
因此,这样一个大规模可扩展的,弹性的三层应用模式,所有内部流量负载都记过一个单点故障或者脆弱HA能力的防火墙。我觉得没必要再说下去了,而且在如今大型数据中心中也会发现这种架构。
三层架构可能会慢慢被抛弃。当应用之间各自独立,不需要互相合作(互相只对应用本身而不是对方的数据感兴趣)。今天应用栈更面向互连(更多东西向而不是南北向)。这种模式下,三层模式会对开发带来灾难。也许到了抛弃这种模式的时候。
Scale out应该是一种横向的,平面化的模式
好了,现在我们清楚了基于单点故障的三层模式的问题,这也是Calico产生的原因。三层结构得以推广很重要一点是因为把安全分布到多个服务点会很痛苦。对于一小部分公司而言,自动化网络配置会同构邮件发送到维护人员。这样,防火墙提供商就可以独立出来,提供一个独自的地方配置安全策略。
从那开始,可扩展式环境出现了很多自动化的配置的方式。如果没有的话,我们也就不会有这个讨论,因为整个云模式就会不稳定。我们需要一种自动部署 “奶牛”的方式,现在我们已经有了(比如Ansible,Puppet,Chef,etcd/confd,以及其他)。通过他们可以一次性将配置推送到上千台服务点。在Calico,我们会使用每台服务器上的代理对路由和策略编程,使用BGP和etcd将数据推送到所有其他Calico节点。这种机制保证 Calico策略的同步。
我们在Calico网络第一跳和最后一跳安装和管理策略,相对于中心化的防火墙模式这种模式引起很多关于安全的争论。
我们可以简化这个规则为:每个Calico节点只跟连接到这个节点上的特殊服务点合作。但是我们没有SPOF问题,因为这个规则只对本节点起作用。一个节点失效只影响它容纳的服务点。
最后,我们解耦可连接性和策略。我们使用BGP来分发网络拓扑,告诉每个节点如何获得每个服务点以便两个服务点进行通讯。我们使用策略来决定这两个节点是否应该通讯,如果需要,怎么做?如果策略改变了,两个以前不需要通讯的服务点现在需要通讯,我们需要做的只是更新策略,而链接信息不需要改变。如果以后,他们需要拒绝通讯,可以再次更新策略,而连接性仍然不需要修改。应用变化和适应新世界,这种模式带来的灵活性是附带的福利。
关于性能的建议
我们也经常听说linux世界中转发性能问题,我们也需要仔细想想。假设你有一个服务器,连接到10GE网络上,当然希望获得线速(或者至少接近线速),结果会是什么样呢?如你所愿。那些采用linux系统,只能获得1-2Gbps的吞吐量的日子已经成为过去了(如果不是的话,为什么人们现在会把10GE网卡作为标配呢?)。实际上,最近我听一个朋友说他们在两个用100GE连接的节点间获得了97Gbps的带宽。知道吗?所有这些流量都是直接经过linux核心发送的。可以占满10GE网口的服务器一般都是运行在Linux Kernel级别上发送数据。有些同事卡在一个方向上linux性能不好,而另外一个方向上却很好,我感到很困惑,因为在一般情况下,任何从linux服务器出来的数据包都走的与Calico一样的转发路径。人们在服务器内部愿意买多块10GE卡,希望达到25GE,40GE和50GE的带宽,不仅仅是人们愿意花钱,更多是因为这样做确实可以达到目标。
最后的想法 (恭喜,你读完了很长的博客)