独立开发一个云(PaaS)的核心要素, Go, Go, Go!!!

时间:2021-10-17 15:32:45

最近一年的工作,有很大的比重在做云平台的事情,简单来说,就是为公司内用户提供一个PaaS,用户可以在我们的云平台上方便的将单机服务程序扩展为多实例程序,以平台服务化的方式对外提供。在这里简单分享一下。

首先简单说一下我们解决了用户的什么需求,或者说痛点。

基础算法直接以库的形式提供给应用方?

用户提供了一个基础算法,这个算法可能以一个动态库的形式提供,那么使应用方需要关注编译依赖,需要关注词典等模型文件,开发成本比较高。尤其是如果这个基础算法需要升级,或者模型文件需要升级的时候,还需要通知应用方进行修改。而且,如果基础算法不稳定,有出core的行为,那么势必影响了应用方的服务质量。还有就是,如果基础算法非常耗资源,比如CPU,内存占得太多,可能会影响应用方程序对于资源的使用。

基础算法的服务化

为了避免基础算法以动态库提供给应用方的种种弊端,基础算法可以以网络服务的形式提供,这就是基础算法的服务化。就是说,基础算法是一个网络服务,用户直接访问这个网络服务即可。这样,基础算法的服务接口只要不变,不论它如何升级,都不需要应用方做任何修改。开源的Thrift,等等RPC的方案,都很好的满足了基础算法服务化的需求。

服务化的质量如何衡量

那么一个服务化的基础算法,如何保证上面所说的服务质量和延时上的要求呢。尤其是服务质量,或者说可靠性。如果我们假设机器永远无故障,网络设备也永远运行正常,服务也足够稳定,不会有core,也不会升级等等,那么直接找一些机器拉起来就万事大吉了。但是,现实却是残酷了,首先服务可能会有故障,它应该会不断升级(如果有一个不用升级的程序,要么是太完美了,要么就是没有人用了,大家写程序都知道,完美的程序你永远写不出来,少写点烂代码才是正常追求),升级的过程不能影响服务质量,还有服务不可能独占机器的,很有可能会和其他服务混布,那么如何选择合适的机器部署,也是一个问题。还有,很多时候一个服务实例是满足不了需求的,可能要部署多个服务实例,这里边的问题就更多了,请求发给哪个实例,负载均衡,等等等。

服务平台化需要面对的问题

我们下面将服务化的基础算法,以承诺的服务质量对外提供的过程,称为服务的平台化。就是说,服务化的基础算法,经过一个平台对外提供,就能获得相应的服务质量。那么平台化的挑战有哪些呢?

如果一个基础算法自己想实现平台化,闭门造车,需要做很多工作。这也是PaaS存在的意义,将大家服务化的工作都通用化,让策略开发者解放出来。那么,平台化要解决的哪些问题,或者基础算法服务化的痛点在哪里呢?

服务的平台化,并不是简单的在几台机器上把一个服务部署起来,对外提供服务,尤其是对应用方承诺了服务质量的时候。而且,为了提升机器的资源利用率,一般几个服务会混布在一起,因此如何合理部署服务也是一个问题。除此之外,服务的平台化还需要关注很多事情,用户一般需要关注以下几点:

1.    如何定义服务接口,使得用户根据这个服务接口就可以自助完成服务访问

2.    服务寻址:应用方通过何种方式访问访问后台的策略服务,如何做请求的负载均衡,后台服务地址变更时,如何通知应用方

3.    服务上线:既要保证上线新的服务不能影响老的服务,又要满足当前服务的资源需求

4.    服务扩容:随着请求的增加,如何扩展实例数来满足应用方需要

5.    机器故障:机器出现故障时,如何保证服务质量不受影响,如何将故障机器上的服务迁移到其他的节点

6.    服务升级:如何实现在不影响服务质量的前提下完成服务的升级

7.    访问统计:如何统计访问量,服务延时,能够区分不同用户访问的不同服务等,类似于Hadoop Counter,实现一个比较简单通用的Counter

8.    监控与报警:服务出现问题时,如何实现报警机制,以便第一时间发现并解决问题

9.    访问权限与流控:服务属于保密性质,只能让指定用户使用;应用方的访问频度需要按照约定来进行限制

10. 服务实例的动态扩容与缩容:为了节省资源,增加资源利用率,需要有动态扩容与缩容的机制:即服务实例负载高的时候,能够扩容以满足计算需求,而在负载低的时候,缩减服务实例以将机器资源给其他服务使用。

独立开发一个云(PaaS)的核心要素, Go, Go, Go!!!

架构当然要去解决基础算法平台化的时候遇到的上述问题。但是除了这些问题之外,还需要关注以下问题:

1.    资源管理和调度:服务要分配哪些机器

2.    服务的部署:分配了机器后,服务实例的程序包如何下载,下载后如何拉起服务

3.    服务地址的汇报和更新:服务实例拉起后,如何汇报服务地址,让应用方使用这些地址,还有地址更新后,如何通知到应用方

4.    资源隔离:只能给服务以承诺的配额,比如多少CPU,多少内存;避免有问题的服务影响其他的服务

5.    不能影响服务的性能:这就要求不能用有性能损耗的虚拟化技术来做隔离等,我们要实现用户直接使用物理机相同的性能效果

6.    计费系统,按照不同的资源使用情况来计费。对于公司内部平台来说,如果机器是平台的,用户只是使用,那么可以没有这个系统;但是如果需要服务的提供方提供机器,来换取相应的计算资源,那么还是需要的。

由于以上问题,RD不能专注于核心策略的实现,不得不做很多平台化的事情。当然了,部分问题可以通过人工运维的来完成的,但是这又会造成服务的维护成本过高。而且,人工运维总有一定的滞后性,可能在某些场景下会出现服务质量下降的问题。还有机器的资源利用率的问题,也是一个复杂问题。

如何解决上述问题

那么如何解决上述问题,这里我们给出一些可能的解决方案:

1.    如何定义服务接口:使用一些通用的RPC解决方案,比如Thrift。像我们使用的是sofa-pbrpc,https://github.com/baidu/sofa-pbrpc。这个是百度内部应用比较广泛的一个RPC,有针对于公司内部应用场景的丰富组件。

2.    服务寻址:简单来说定义一个接口通知应用方后台服务的地址。当然sofa-pbrpc有实现的一个寻址组件,应用方只需要指定一个名字就可以完成寻址,这个组件也完成了负载均衡的策略,能够将请求打散平均发送到后台的服务商。当然可以以ZK来实现寻址。比如服务实例拉起后将自己的地址添加到ZK的一个节点下,退出时由于与ZK的连接断开而自动删除自己。这样应用方只要监控这个节点的数据变化即可。负载均衡的话最简单的就是将请求按照顺序发送到后台。但是有一个问题就是机器可能处理能力是不相同的,就是集群的机器都是异构的,因此可以记录一下每个节点的处理能力,当一个服务节点pending的请求比较多时,那么就优先发给处理请求比较快的节点上。

3.    服务上线:这个实际上需要集群资源管理与调度的功能,就是用户提交了服务上线的请求时,按照用户的资源请求,比如每个服务实例需要多少CPU Core, Memory以及网络等的情况,还有实例的个数,为这个服务分配计算资源。集群会选择符合这个服务的机器节点,分配给服务。这个分配算法业界也有很多研究,核心就是提供资源利用率,而又要保证服务的质量。对这个有兴趣的可以看一下dominant resource fairness算法。

分配了计算资源后,就需要在节点上把这个服务部署好,部署完成后把服务拉起。

4.    服务扩容:当服务的负载变高时,需要扩容服务实例数。这个其实和服务上线差不多,需要分配计算资源,部署,服务拉起等。

5.    机器故障:任何云平台都要考虑这个情况,尤其是机器数量多的时候,机器故障几乎成为一个必然会发生的事情。这个需要下线这个机器,即资源调度模块标记该机器未不可用。其次还需要将该机器上运行的服务实例迁移到其他节点。

6.    服务升级:这个过程可以不需要资源调度模块的参与:在各自的服务实例所在的节点上就地升级,比如直接下载程序包,下载完成准备好环境后直接停止原来的进程,启动新部署的即可。当然这个进程重启势必会影响服务质量。如果服务要求不需要是100%的,那么可能这个最简单的方法也是可以接受的。当然也可以在客户端加重试的逻辑,如果延时能够忍受的话。

另外一个方法就是重新分配一些节点,然后启动新的服务实例后,将老的机器先从应用方的寻址中删除,保证不会再有新请求达到老的服务实例。在服务实例的负载都为0时,老的服务实例下线。这样做稍微复杂,但是不会影响服务质量。但是如果服务实例数百上千,那么这个代价会比较高,甚至没有足够的节点来完成这个升级。当然可以分批次升级,这样无疑有复杂了些。

第一个方法有一个优点,由于都是就地升级的,因此可以快速回滚的老的版本,因为不需要下包了,回滚的时间开销就是进程重启的时间。

7.    访问统计:互联网公司都会有这个模块,就是分布式日志的收集,汇聚和展现。以便展示服务的调用量,延时,等等的报表。这个开源社区也有很多的实现。一般来说就是每个服务实例打印访问统计的日志,这些目标日志会被部署到每个节点的agent收集,发送到比如一个MQ里,然后由一些工作线程去处理这些日志。

8.    监控与报警:平台资源的整体使用量,集群节点的资源使用情况,比如CPU IDLE,Memory的监控,服务实例状态的监控等。在检测到异常触发阈值时报警给用户,或者集群管理员等。

9.    访问权限与流控:这个可以和公司的账号体系打通,这样就可以追踪到非常细粒度的访问权限控制,当然简单的做法也可以直接使用访问者IP来做限制,实行IP白名单的制度。流控对于离线服务比较重要,离线服务注重吞吐,但是有时候一个Hadoop任务起来,可能发到后台的压力非常大,因此流控可以保证后台服务不被压垮,或者保证每个Task不会与其他的Task有资源的竞争。对于在线服务,流控有的时候也会有,比如拒绝一部分请求,总比大家都一起慢下来,但是谁都用不了好。说道这里,又想起一个集群的慢节点的问题,就是一个集群有dead的节点不害怕,就怕有比较挫的节点,运行超慢但是半死不活,特殊情况下可能会拖累整个平台。可以看一下这个文章:http://danluu.com/limplock/

10. 服务实例的动态扩容与缩容:有的同学会问服务实例如果没有计算,就空跑在那里呗,但是至少它会占用内存,而且,一般集群为一个服务分配计算资源时,一般会以CPU,内存为度量单位,因此如果一个服务占用了CPU,内存,那么就会保证它至少能用到这些,对于在线服务来说,“虚拟”一些资源出来在某些场景下会影响服务质量的。

上面这个问题,任何一个问题都可以衍生出好多问题。因此,本文是我想给大家分享的自己构建一个云平台的时候,需要注意的事情和需要关注的点。完全是抛砖引玉。

我们在过去的一年中,使用Go实现了这么一个云平台。在这里也推荐一下Go。标题中的Go,实际上就是Go的意思。Go,云时代的系统编程语言,就像过去十年服务器的编程语言是C一样,学了你就知道这句话什么含义了。

最后做一个广告吧,百度的同学如果看到这篇文章,可以搜索内网搜索SofaCloud,一个彻底解决策略开发者的平台化之痛的云平台。除此之外,我们通过架构优化,还可以节省85%的业务方机器!!我们当前日均数百亿的计算量,数千个计算实例,服务数十个产品线!!!想什么呢,还不赶紧迁移到我们这上边来!!!

独立开发一个云(PaaS)的核心要素, Go, Go, Go!!!

用了SofaCloud后,明明的头发都长出来了有没有!!!气色变好了,有没有!!!

Cloud,Go!