微服务概念起来之后,很多大型互联网公司需要把资源(比如说几千台机器、几万台机器)抽象工作,让更多人来使用。之前大家的做法比较粗暴,一个部门分几百台机器,一个项目分几百台机器,然后把程序裸跑在一些硬件或者虚拟机上,但这个过程中资源的利用率不是很高。
另一方面,有些增加资源的场景中,增加一些机器非常缓慢,安装、做机器的provision等等都很困难。因此一些聪明的工程师就萌生了一个想法:能不能把这些资源都抽象,需要的时候分配给不同的人来使用?这个背景诞生了Mesos,即资源调度的工具。资源调度器Mesos不是创新,它源自于Google Omega的论文,由Twitter的公司最早推出来。
最近一段时间使用Mesos的API以及看代码时间比较多,接下和大家分享一下我对Mesos内部的理解。
首先,Mesos是一个分布式的架构,它分Mesos master和Mesos slave,slave和master分别有不同的职责。从Mesos的源代码可以看出Mesos实现得比较优雅——它是一个C++代码。代码中有大量的关键词叫process,它不是传统意义上的进程,而是一种抽象的libprocess,libprocess是它最核心的库。如果大家以前使用过Erlang的语言就知道libprocess实际是对Erlang的IO和通讯模型的一个抽象。
libprocess,在Erlang里面也叫进程,实际上是一个虚拟进程,在运行Erlang的VM上。它最优的特点是消息在不同的process之间传递,它抽象了process,消息传递其实是一个事件的库。向process里发一个消息,这个消息不是直接打到process,而是中间有一个buffer的过程。
这样做的优点是特别适合分布式的系统,以前最常用的做法是监听网络端口,有包来了,有一个模块专门负责解这个包,解开一个协议后把这个协议发到后面一个处理进程,这个处理的进程可能是IO操作,可能去做其它事情,然后里面有很多IO上的Block,最后构造出一个response,通过一个socket传给客户端。这是最常用的一个写网络程序的办法,但是这里有一个大的IO上Block的地方——后面处理的逻辑依赖于解包的逻辑。如果处理逻辑很快,但解包逻辑很慢,后面会拖慢,都在等解包。
后来人们想到一种IO处理的方式,让任何一个东西都是分离的,比如从某一个端口收到一个消息,有一个单独的进程,一个线程或者其它的东西去处理这个唯一的请求。这个线程很重,后来大家又发明了一些其它的东西,比如golang里面去搞一个channel,Erlang里面去搞一个process。libprocess实际上做了IO方面的事情, Mesos大量使用这个模型。
Mesos底层实际上依赖于Zookeeper,为了保证分布式存储最终一致性。在Mesos运行过程中产生了一些数据,最终都会落在Zookeeper。因为Mesos是多个master,为了达到HA的需求,只要一个master活的,那么整个服务就能得到保证。
Mesos内部在通信里面选择了protobuf协议。好处是比较流行,各个语言的库都比较多,结构化的语义也比较强,所以Mesos内部选择了protobuf。
至此,简单介绍了Mesos内部的一些动作。接下来介绍Mesos提出的一些概念。
Mesos是一个分布式的系统,分master和slave, master的部分主要协调任务的分发、一些资源的调度。slave是负责执行的部分,比如一个任务最终是slave去执行的。当然,可以配在master执行。Mesos是一个双层调度,slave处于下层,也就是说可以动态的增加或者减少一些slave而不影响整个的任务池资源的变化,不影响上一层的任务。
Executor是真正的执行任务的逻辑。Mesos平台不太区分需要执行什么任务,所以它给用户一些灵活性,可以写不同的Executor。比如最常用的Mesos运行容器的部分,就是一个Executor。还有Map Reduce的大型任务,一个Map、一个Reduce, Executor都可以执行。它的表现形式可以是一个二进制,Mesos在运行时,slave会把Executor从远程一个URL上拉下来,然后开始执行Executor。
Scheduler的意思是调度, Mesos master和slave把资源收上来之后,再把这些任务交给Scheduler,由Scheduler决定应该运行什么Task。
Framework是双层调度的上一层,也就是由Framework来决定到底该执行什么任务,然后执行多少这样的任务。
Offer是Mesos资源的抽象,比如说有多少CPU、多少memory,disc是多少,都放在Offer里,打包给一个Framework,然后Framework来决定到底怎么用这个Offer。
Task实际上是运行的小任务。有两大类Task,一大类我们叫Long Running Task,比如Docker的一个进程或者其它的进程。另外一类是Batch类型任务,这类应用非常广泛,比如说Map Reduce这么一个小任务或者是定时任务。
这里分别介绍一下刚才提到的这些名词,说一说它们都是如何工作的。
Mesos master是整个集群的核心,master为了保证高可用其实是可以跑多个master,分布式系统为了保证尽量的高可用,其实是可以有多个master在那儿运行的。比如倒了几个master,不会影响整个服务。Mesos master主要内部的工作有:Framework注册或者Framework出了什么问题,它来保证Framework的生命周期;slave的添加、slave的异常、slave的任务分配,我把它叫做slave的Lifecycle;Task Management,比如说Mesos master可能记录了哪些Task运行在哪些slave上;Resource Allocation&Offer,就是说slave有什么资源汇报给master,然后由master把这些任务交给注册在这个master上的一些Framework。
slave可以动态添加和减少,它lost不会影响整个服务,只是把这个事件(比如说一个slave掉了)由master去通知Framework。在Mesos slave代码里有大量执行器,即Executor的逻辑,因为所有Executor都是在slave上执行的,包括把Executor从远程拉下来、开始执行Executor、开始执行launch task,维护task的生命周期,task fail了如何去做等等。
Mesos的定位是一些资源的调度,它把任务的调度交给了Framework来做,Mesos只关心资源以及把资源给了谁, Framework来决定哪些资源怎么去使用。Mesos鼓励Framework在上面共生。想象一下,作为一个大型公司,有很多的资源,有核心的一组人来维护Mesos的集群,不断的往Mesos上添加资源和减少资源,而把Framework执行的能力交给其它的组、需要资源的那些组,各个组就可以写自己的Framework,丢到整个大的Mesos集群上来执行了。Mesos框架上和执行上各种各样的Framework,而Mesos本身也不了解为什么Framework工作,它只是知道把Offer给Framework,然后Framework告诉它来执行什么样的Executor。
Marathon Framework,它的任务偏Long Running,核心是application。因为Mesos只关注task本身,task偏向于小任务,不会产生什么巨大的效应,而在企业里面尤其是弹性应用,更多是一个应用,它有很多的实例来执行,这就是Marathon来做的。
Chornous是一个偏Job、定时任务类型,如果把定时任务以Docker形式发出来,这个Chornous是非常适合的。传统的的Cron Job也是解决这类问题, Cron Job其实有很大的痛点,因为Cron Job是跑在主机上的,主机有limit的限制,如何把Cron Job放在多机上,需要有一个很好的哈希算法。到底如何把一堆单台机器很难执行的多个job水平分布在很多机器上,很麻烦。但是有了这个Chornous的Framework,事情就变得简单多了。
它们俩区分度不是太大,有一些区分。Scheduler是做任务分配的,它从master上得到一个Offer的事件,拿到Offer后,决定到底接受这个Offer还是拒绝,接受这个Offer之后,把什么样的Task放在这个Offer上, Framework也开始占用这个Offer,这是Scheduler做的事情。Scheduler Driver其实是偏消息通信的那一部分,而Scheduler可定制化特别强,在代码里看到Scheduler其实是一个java的abstract glass,相当于一个interface,Framework自己去实现这么一个东西。如果想写一个Framework,其实大部分时间在如何写好一个Scheduler实现这一部分。
如果Executor和Scheduler是对应的, Executor就是执行的这一部分。Mesos container这个Executor是Mesos自己提供的,不用写Executor就可以launch一个Docker的任务。但如果有一些自己的需求,就需要去实现一个Executor,比如七牛和乐视实现了一些Executor。
举例来说,处理图片、处理文件的Executor,偏向于这种小任务,写一个Executor来实现这个interface,最终打成一个二进制,放在一个URL里面。在Framework,slave就可以把这个Executor拉下来,然后执行这个Tasks。Executor是一个独立的二进制,它和master、和slave之间的通信是要支持RBC的。
刚才已经提到了Marathon基本的功能,Marathon作为一个Long Running上一层的调度机制,为用户做了很多有意义的事情。单纯一个Mesos的话做不了什么,因为Task的力度特别小,Mesos的功能更偏重于资源的管理、资源的调度,Marathon更偏向于一些任务。
Marathon提出了几个概念,最核心的概念叫application,还有Group的application,是一组的application。一个application是什么呢?比如一个Rails的任务、NodeJS任务或者其它的一些任务,在部署的时候并不是部署一个Task,而是部署非常多的Task,application就是一组Task。这个Task在Marathon里面叫instance,可以选择scale down或者scale up这些instance,这些任务最终交给Mesos,Mesos调度到不同的slave上。
围绕着这个application,Marathon就提供了一些概念叫Group,Group是一组application。举例来说,在内部可能有很多的微服务,这些微服务达到同一个目标,比如说服务之间还有调用、还有依赖,这时去发一个Group application就比较好用。但实际工作中,因为生产级别的服务对稳定性的要求很高, Group之间其实假设服务和服务之间是有一定的依赖。
Marathon提供了一个有意思的feature—— rolling update。假如有APP的新版本上来,它可以通过一定的机制去发多个版本,而且可以多个版本共存。如果那个新版本没有问题的话,可以继续preceeding deployment。如果有问题的话,可以Rollback。这时有两个概念Deployment和Version,可以选定哪一个Version,想Rollback哪个Version。Deployment是每次update,每次更新、每次重新的Deployment、每次scale,都会在内部生成一个Deployment,和应用一对多有关。有趣的是Deployment之间是不可以重叠的,Deployment是一种部署排队的机制,Deployment不可以多个同时进行,既想update又想scale,会让Marathon崩溃。
Marathon scale和rolling update的功能都非常有用,比如新浪微博现在有一个大的事件,需要更多的Task顶上来,立刻 scale up,只要资源足够就可以无限多的Task生成。Rollback,如果有一个版本有问题,可以瞬间Rollback到以前一个健康的版本。
虽然Mesos对下面的资源做了一些抽象,但是有时候有一些倾向性,比如希望CPU使用率比较高的一些任务调度到CPU比较好的机器上,需要一种在调度上的倾向性来满足刚才的场景。很多调度器都有类似的功能,叫Constraints,比如一台主机的label,要把Task打到一组主机这样的Label上或者是Host name like,这是Marathon做的,Mesos不用做这类的事情。
Marathon的接口非常友好,都是HTTP的接口。Mesos的接口by design不是面向最终用户,所以它的接口并不是那么友好。马拉松的UI也非常漂亮,尤其是新版。
最后介绍一下Swan(https://github.com/Dataman-Cl... )这个项目,Swan是最近数人云做的一个Mesos的Framework,定位是做一个General Purpose Long Running Task的Framework。数人云使用马拉松较长时间,发现不太满足需求,比如很难做一些定制化,控制不住它的发展趋势。我们希望研发一款工具既有马拉松的功能又自主可控、添加一些想要的featur,具体的feature会在下文中逐一介绍。
我们希望这个通用性的Framework在任何情况下,服务不会受到影响。因为Mesos HA这方面已经做得很好,所以Framework不会是单点,首先Swan要支持HA,因为受到Swarmkit启发比较大, Swarmkit天生就是Raft协议,在一堆manager中只要有一个活的,就能健康的对外服务。
Marathon没怎么做服务发现,无非是把端口暴露出来,哪个任务在哪些IP和端口上,通过API的形式告诉给外面。Swan里内置服务发现,会有一个列表告诉外面哪些服务跑在哪些端口、哪些APP上。
DNS是服务发现的另一种。主流的一些Framework都把DNS这个功能放在了比较核心的位置,比如K8s里面的SkyNet,Mesos曾经以前有Mesos DNS,以及Swarmkit,Swarm的服务发现分为DNS Round Robin和IPVS两种,把DNS放在Swan这个模块里更加的可控。它不单是一个DNS,同时是一个DNS的代理。这样最终能实现的效果,在企业内部通过我们的DNS和用户的DNS来混搭,来达到一种Mesos内部和外部互相调用,在浏览器上既可以访问Mesos内部的东西又可以访问外面的东西,达到比较完美的效果。
Proxy,并不单是Proxy,还有负载均衡。最常见的Proxy的工具有HAProxy或者nginx。 HAProxy和nginx虽然很优秀、性能很好,缺点是可控性太差,很难控制它。HA可编程的能力很差,Nginx可编程的能力不错,新浪微博有一个项目叫upsync-module,非常优秀。之前评估过这个项目,发现集成这两个的难度很大。
我的分享就到这里,谢谢大家。
原文 https://segmentfault.com/a/1190000007723430