基于容器的分布式系统的设计模式

1. 介绍

在20世纪80年代末和20世纪90年代初,面向对象编程完全改革了软件开发方法,普及了将应用程序做为模块化组件的建立方法。随着基于容器化软件组件的微服务架构的逐渐普及,如今在分布式系统开发中也发生着相似的革命。容器之间相互隔离的优势使得容器成为了分布式系统中合适的基本单元。随着这种架构类型的成熟,咱们能够看到设计模型的出现,从容器的角度来思考问题能够将低层次代码细节抽象化。java

这篇论文描述了咱们观察到的三种基于容器分布式系统的设计模型:单个容器模式,密切合做模式容器的单节点模式,以及分布式算法的多节点模式。跟面向对象编程的设计模式相似,这些模式包含了最佳实践,简化了开发,使系统更加可靠。git

2. 分布式系统设计模式

在面向对象编程被使用了几年以后,设计模式出现并被编成文档。这些模式被编纂好,而且被规整成解决特别常见的编程问题的方法。这个编纂进一步提升了编程最前沿的技术,由于它可让经验不那么丰富的人也写出很好的代码,而且让重复使用的库文件的蓬勃发展,这些库文件可让开发代码更加可靠、快速。算法

前分布式系统工程的技术水准,比起面向对象开发,更像是20世纪80年代的编程的水平。显然,MapReduce模式将“大数据”编程的力量带入普遍的领域和更多的开发者是一个巨大的成功,将正确的模式放在合适的位置能够很大程度上提升分布式系统编程的质量、速度和可达性。虽然MapReduce的成功受限于单个编程语言,在Apache Hadoop生态系统范围内,只对一种编程语言(java)产生了影响。为分布式系统开发一款全面的一套模式须要一个很是通用,与语言无关的交流工具来呈现系统的精髓。编程

因此,很幸运可以看到过去两年内,愈来愈多的人选择使用容器技术。容器和容器镜像正是分布式系统模式开发所须要的抽象。至今为止,容器和容器镜像已经在很大程度上受到了欢迎,由于它是一种更好,更可靠的经过产品来交付软件的方法。由于容器是密闭的,包含了依赖关系,而且有一个原子的部署信号("succeeded”/“failed”),他们很大程度上提高了以前在数据中心或云端部署软件的技术。可是容器不只仅只是一个很好的部署工具,咱们认为容器最终会成为面向对象软件系统中的对象同样,而且所以驱使了分布式系统设计模式的发展。在接下来的部分,咱们会解释为何咱们相信会是这样,而且描述出现的一些在将来几年中可使分布式系统规整化的设计模式。json

3. 单个容器管理模式

容器为定义接口提供了一个天然的边界,相似于对象边界。容器不只可以暴露专用应用功能,还可以经过这个接口跟管理系统挂钩。后端

传统的容器管理接口是极其有限的。对容器的有效操做为:run,pause和stop。虽然这个接口十分有用,可是更丰富的接口能够给系统开发者和操做者提供更多的实用功能。考虑到目前几乎全部现代编程语言对HTTP服务器的开发以及对json这种数据格式的广泛支持,很容易让容器提供一个基于HTTP管理的API。设计模式

“upward”方向,容器能够暴露一套丰富的应用程序信息,包括应用专用监控参数(QPS,应用程序健康等等),用户感兴趣的信息(threads,stack,lock contention,network,message statistics等等),组件配置信息和组件日志。一个具体例子就是,Kubernetes,Aurora,Marathon和其它容器管理系统容许用户经过特定的HTTP端点(好比,”/health”)来定义健康检查。对于以前说过的“upword”API的其它元素的标准化支持就更少了。服务器

“downward”方向,容器接口提供一个地方来定义生命周期,使得写管理系统控制的软件组件更加容易。好比,集群管理系统一般会将“priorities”归到任务中,即便集群订阅超额,高优先级的任务也会保证运行。这个保证经过杀死已经在运行的低优先级的任务来完成,低优先级的任务要等到资源可用的时候才会再继续完成。驱逐只要经过简单的杀死低优先级的任务就能够实施,可是这就给开发人员带来了不少没必要要的负担,他们须要在代码中处理任务被杀掉的状况。相反,若是在应用系统和管理系统中定义了正式的生命周期,应用程序组件就会变得更加可管理,由于开发者能够依照正式的生命周期来开发。好比,Kubernetes使用Docker的“优雅终止”功能,经过SIGTERM信号来警告容器,它在一个自定义的时长以后会接受到SIGKILL信号,并被终止。这就容许应用程序经过完成当前任务,把状态写入磁盘等等操做以后再终止。你能够想象扩展这种机制用来支持序列化和恢复,使得有状态的分布式系统状态管理更加容易。网络

对于更加复杂的生命周期的例子,考虑到Android的Activity模型,这个模型包含了一系列的回调函数(好比onCreate(),onStart(),onStop(),…)和一个状态机来定义什么时候触发这些回调函数。没有了正式的生命周期,健壮,开发可靠的安卓应用程就更加难了。在基于容器的系统中,这就对应了自定义的一些钩子,这些钩子会在容器建立时,容器开始运行时,容器结束运行前等状况下被调用。架构

4. 单节点,多容器应用程序模式

除了单个容器的接口,咱们也看到了一些跨容器的设计模式。咱们以前就已经确认了几个这样的模式。这种单节点的模式包括了同时运行在单个主机上的共生容器。容器管理系统支持同时运行多个容器做为一个总体单元,因此抽象(Kubernetes叫它“pods”,Nomad叫它“task groups”)是一个用来启动咱们以前描述过的模式的必需功能。

1. sidcar模式

clipboard.png

对于多个容器配置来讲,第一个,也是最广泛的状况就是sidcar模式。Sidecar扩展而且提升了大多数的容器。好比,主容器多是一个网页服务器,它可能跟“logsaver” sidecar容器配对,而后saidecar容器从本地磁盘收集网页服务器的日志,而且将他们stream到集群存储系统。图1展现的就是sidcar模式的一个例子。另外一个广泛的例子就是从本地磁盘内容服务的网页服务器,这个sidecar会按期跟git库进行内容管理系统或者其它数据源的存储进行同步。这两个例子在谷歌是十分广泛的。由于在同一个机器上的容器能够共享一个本地磁盘数据卷,因此sidecar是可能作的。

虽然建立sidecar容器的功能到主容器里永远可行,可是使用分开的容器有如下几点好处。首先,容器是资源帐户和分配的单元,那么好比一个网页服务器容器的cgroup能够被配置,那样的话,它就会提供持续的低延迟反应到问题,虽然logsaver容器在网页服务器不忙的时候被配置来清除空闲CPU周期。第二,容器是打包的单元,因此将服务和日志保存分到不一样的容器可让两个独立的编程团队之间的可靠性分开,而且容许他们独立测试,跟一块儿测试的时候是同样的。第三,容器是重复使用的单元,因此sidecar容器能够跟不少不一样的主容器(好比logsaver容器能够被任意产生日志的组件使用)。第四,容器控制边界错误服务,使得整个系统可以正确推出(好比,网页服务器即便在日志保存运行失败的状态下也可以继续服务)。最后一点,容器是配置的单元,每一个功能均可以更新,而且必要的时候能独立回滚。(可是要注意的是,最后一点好处也有很差的地方——整体系统的测试模型必需要考虑到在生产过程当中全部的容器版本组合,这些版本可能会很大,由于容器整体上来讲一般不能自动升级。固然,单一的应用程序没有这个问题,组件化的系统在某种程度上更容易测试,由于他们是在更小的能够独立测试的单元的基础上测试的)。注意,这五点优势应用于咱们接下来在论文中描述的容器模式。

2. Ambassador模式

clipboard.png

接下来要说的是咱们观察到的ambassador模式。Ambassador容器代理服务会跟主容器进行交流。好比,开发者可能匹配一个正在跟twenproxy ambassador进行交流的memcache。应用程序相信这只是跟单个在本地主机上的memcache交流,可是现实中,twenproxy正在跟多个memcache集群中的其余节点的分布式安装进行共享。这个容器模式以三种方式简化了编程:他们只须要思考编程,依据他们链接到本地主机的单个服务器,他们能够经过运行真正的memcache实例来单独测试他们的应用程序,并且他们也能够利用其余的应用程序(可能会用不一样语言编写)从新使用twenproxy ambassador。Ambassadors也是可能的由于在同一个机器上分享同一个本地主机网络接口。这个模式的例子如图片2所示的。

3. 适配器模式

clipboard.png

最后一个要说的是咱们观察到的适配器模式。相比于用外部的简化来呈现应用程序的ambassador模式,适配器用的事简化的,均质的应用程序来呈现外部世界。他们是经过将多容器间输出和接口标准化才作到这样的。适配器模式具体的例子就是,适配器确保全部在一个系统内的容器都有相同的监控接口。如今应用程序使用不少种方法来输出他们的参数(好比,JMX,statsd等等)。可是对于单个监控工具来讲,收集,集合,以及从异构应用程序呈现数据就很容易,若是全部应用程序呈现一致的监控界面的话。在谷歌,咱们经过编码规范来完成,可是只有在你从scratch建立本身的软件的时候才有可能。适配器模式让异构的旧世界和开源应用程序呈现统一的接口,不须要修改原始程序。主容器可以跟适配器经过本地主机或者共享的本地数据卷交流。详情请查看图3。注意,一些已经存在的监控方法可以跟多种类型的后端,他们在监控系统中使用专用应用程序代码,提供了一个不那么清洁的关注点的隔离。

5. 多节点应用程序模式

在单个机器上移动合做的容器,让建立合做的多节点分布式应用程序更加容易。以后咱们接下来会具体描述一下这些分布式系统模式中的三种。好比以前章节提过的那些模式,这些也要求为Pod抽象提供系统支持。

1. leader选举模式

分布式系统中常见问题之一就是leader选举。副本被广泛使用在一个组件的多个相同的实例之间共享负载,副本的另外一个更加复杂的做用就是在应用程序须要区分副本跟设置来做为“leader”。其它的副本对于快速取代leader的位置是十分快速的,若是以前的副本失败了的话。一个系统甚至能够平行运行多个leader选举,好比,要定义多个碎片中每个的leader。运行leader选举有不少库。用起来又复杂又难理解,要正确使用真的很困难,另外,他们的限制之处在于,只能用特定的编程语言来写。把leader选举库链接到应用程序的另外一种方法就是使用leader选举容器。leader选举容器,每个都跟须要leader选举的应用程序的实例同步,并且可以在他们本身之间进行选举,同时他们也能够在本地主机上呈现一个简化的HTTP API给每个须要leader选举的应用程序容器(好比,becomeLeader,renewLeadership等等)。这些leader选举容器只能被建立一次,随后简化的接口能够由应用程序开发人员从新使用,无论他们选择什么语言来实现。在软件工程领域,这是最好的抽象和封装的表明。

2. work quene模式

clipboard.png

虽然work queen跟leader选举同样,是一种很好研究的项目,由于有不少框架能够来实现他们,他们同时也是分布式系统模式例子,这个模式能够从面向容器的架构中受益。在以前的系统中,框架限制于单个语言环境编程(好比,Celery for Python),或者work和二进制的分布练习留给了实现它的人(好比,Condor)。实现run()和mount()接口的容器可用性使实现一个通用的work queen框架十分简单,这个框架能够处理任意的进程中打包为容器的代码,而且建立一个完整的work queen系统。开发者只能选择去建立一个能够在文件系统处理输入数据文件的容器,而且将之转化为一个输出文件;这个容器将会变成work queen的一个阶段。用户的代码整合到这个共享的work queen框架的方式图4中已经阐述了。

3. Scatter/gather模式

clipboard.png

最后一个要强调的分布式系统模式就是Scatter/gather模式。在这样一个系统中,一个外部客户发送一个初始请求到“root”或者“parent”节点。这个root将请求分散到不少不少服务器上来执行平行计算。每一个碎片返回部分数据,root将这个数据收集起来归成单个的回应到原始请求。这个模式在搜索引擎中是十分广泛的。开发这样一个分布式系统牵扯到不少样板文件代码:分散请求,收集回应,与客户端交互等等。代码有不少是泛化的,再次,就如同在面向对象编程中,代码能够用这种方法被重构,单个实现能够被提供的方法,这个方法也能够被用在任意容器中,只要他们实施一个特殊的接口就能够。特别是,为了实施一个Scatter/gather系统,用户被要求提供两个容器。第一,容器实施了树结构端节点;这个容器会执行部分求值,而且返回对应结果。第二个容器就是合并容器;这个容器带走了全部树结构端节点的总生产额,而且将它们归到单个回应的组。

这样的系统如图5所示。

6. 相关工做

面相服务的架构体系(SOA)更新地比原来早,和基于容器的分布式系统共享不少特征。好比,都强调可重用的定义好的经过网络进行通讯的接口的组件。另外一方面,SOA系统中的组件趋向于大粒度,相比于咱们以前描述过的多容器模式,有更多的松耦合。此外,SOA中的组件实施商务活动,咱们在这里重点关注的组件相似于比较容易建立分布式系统的通用库。“微服务”这个词语最近出来,描述的是咱们在这篇论文中讨论过的组件的类型。

标准化管理接口的概念要至少要追溯到SNMP。SNMP主要关注管理硬件组件,并且如今尚未出现用来管理微服务/基于容器的系统。这仍是没能阻止许多容器管理系统的开发,包括Aurora,ECS,Docker Swarm,Kubernetes,Marathon和Nomad。

在第5节中提到的分布式算法都有段很长的历史。大家能够在Github上找到不少leader选举实施,虽然他们结构上做为库而不是独立的组件。仍是有不少受欢迎的work quene实现了的,包括Celery和Amazon SQS。Scatter/gather已经成为一个企业的继承模式。

若是须要转载,请联系咱们哦,尊重知识产权人人有责;)
原文连接

相关文章
相关标签/搜索