基于Docker的云处理服务平台

10月20日-22日,由InfoQ主办的QCon 2016全球软件开发大会上海站已成功举办。QCon内容源于实践并面向社区,演讲嘉宾依据热点话题,面向5年以上工作经验的技术团队负责人、架构师、工程总监、高级开发人员分享技术创新和最佳实践。

主题演讲由创客公司讲师分享各自经验,一起探讨整个微服务架构及与它相关的一些技术。作为云CDN行业的代表企业,又拍云受邀出席了本次大会。又拍云系统开发工程师叶靖在“微服务实践与架构演进之路”分论坛上,发表了“基于Docker的云处理服务:更新、扩容与自动容错”的主题演讲,叶靖分享的思路主要是下面三个方面:

整个架构演进是怎样;

微服务带给又拍云怎样的好处;

又拍云在实践当中有什么挑战。

经验在此,小编忍不住马上整理过来和大家分享啦!

以下就是叶靖的分享全文呦~

大家早上好,我叫叶靖,来自杭州又拍云科技有限公司。又拍云为客户提供场景化CDN、云存储、云处理等服务,比如针对直播,我们提供又拍直播云解决方案。我在又拍云主要负责云处理这方面的业务,所以今天的分享也和云处理有关,主要介绍又拍云基于Docker的云处理服务平台,我将会着重分析我们平台是怎么做服务的更新,以及我们在处理能力不够的时候是怎么扩容的;或者是当我们一台服务出现故障时候,我们是怎样快速切掉的。

总结起来,其实我们想要解决的就是三个问题,

  • 第一个问题:怎么做无宕机时间的更新——更新的时候,我的服务、我的请求不能出现任何的异常;
  • 第二个问题:怎样做自动扩容以及故障恢复,换一种说法就是怎样可以方便地提升我的处理能力,以及可以快速摘除故障的机器;
  • 第三个问题:如何方便的对请求进行改写,我们有一些需求是要对原始的请求进行一些请求的改写操作。

关键在路由

总结一下,要达到这三点,其实只要做一个东西就够了,那就是需要在云处理集群的最前面做路由。让这个路由支持无宕机时间的更新,让它支持方便进行扩容与故障摘除,也让它支持能够对请求进行改写。我今天主要是分享又拍云在做的一个东西。

首先从路由开始说起,说到服务路由,要涉及到三个概念:服务注册、服务发现和负载均衡,如下图:

20161024203433

服务路由

服务注册就是我的容器需要主动上报我提供的服务的名字,以及所在IP端口这些信息;

服务发现是把服务注册的信息集中起来;

因为我们提供的容器非常多,还需要在容器之间做负载均衡。

服务发现有很多开源的工具可以用,比如Consul、etcd和Apache Zookeeper。Consul功能多一些,所以我们选型的时候选择它作为我们服务发现的工具。

20161024203444

服务发现各种工具的优缺点

负载均衡也有很多开源的方案

负载均衡开源方案

负载均衡开源方案

在选型上,又拍云选了Nginx,架构简图如图所示:

基于 Docker 的云处理集群架构简图

基于 Docker 的云处理集群架构简图

最下面是Mesos,Mesos上面跑的是容器,服务都是以容器提供的,有做图的,音频的,各种各样的服务,有一些是特殊的服务,比如可以用Registrator做注册服务,上报到Consul,前面的Nginx主要是做负载均衡,通过upstream把请求代理到下面。在Consul中的服务地址改了以后,怎样快速反馈给Nginx呢?其实如果把这个解决了,这个图就可以非常好地运转起来。

这个方案要做得很简单,需要解决Consul的服务如何主动、方便更新到Nginx。我们调研了一下,发现有这么几种解决方案:

第一种解决方案是用Consul template,监听Consul中服务变动,当配置变动时,由模块重新生成配置,最后对Nginx进行Reload操作,让它生效。这里有一个例子,如图:

 

Consul Template更新Nginx Upstram过程

Consul Template更新Nginx Upstram过程

上图的框里面是一个配置的模块,中间两个括号括起来的都是一些需要被实例化的变量,当服务发生改变的时候,会生成真实的配置文件,然后用生成的真实的配置文件进行reload操作,Reload是一个平滑的过程,不会对当前的请求造成任何影响,所以可以达到更新Consul,并且把Consul的信息反馈给Nginx的目的,但是我们没有采用这个方案,主要是因为下面几个问题:

方案一缺点

方案一缺点

鉴于方案一的缺点,我们希望找到更好的办法,更优雅地改变upstream,所以我们又想到第二个方案,用DNS,这个方案非常好理解,用内部的DNS对IP进行解析,但是这个方案隐藏着更大的问题。

DNS方案的缺点

DNS方案的缺点

以上两个方案都不行,那我们想要什么方案?我们想让Nginx监听一个管理端口,可以通过对管理端口发指令,让它自己更新Upstream,有没有这样的方案呢,其实是有的,下面是一个开源的Nginx C模块,它做的事情就是通过 HTTP 指令更新、操作、删除upstream,如下图:

方案三功能图

方案三功能图

这里有个例子:

20161024203543

上图例子中,请求了8080端口,发现502了,因为这个时候,我们在host下面没有任何上游服务存在,最后给127的8081端口发送请求,要在Nginx动态加上两个server地址,他们对应的host就是dyhost,第三个指令和第一个指令是完全一样的,但是结果不一样,它返回了正确的输出,并没有返回502,说明这第二个指令中的两个地址已经生效了,这个已经完全达到了我们的目的,通过一个HTTP请求,改变Nginx的upstram。

又拍云用了第三个方案大概两个月之后,感觉还是有一些问题困扰着我们,最终我们选择重新造轮子。为什么没有继续走下去,原因如下:

方案三劣势图

方案三劣势图

又拍云经验——造轮子

为了解决面临的问题,我们开始造自己的轮子。经过讨论,我们决定用lua建造一个符合我们需求的轮子。

介绍这个轮子之前,我简单介绍一下Openresty项目,它的原理就是把Nginx、Nginx的lua模块以及常用的lua代码,集合起来打包成一个项目,由一整个项目提供高性能web服务器。

下图是我们的动态负载均衡,它目前是已经开源的,由四部分组成,

  • 第一部分就是Nginx,官方的Nginx,没有任何改动;
  • 第二部分是Nginx Lua 模块;
  • 第三个部分是lua resty checkups,它是把Nginx upstream模块常用的功能单独抽出来,用lua重新实现了一遍;

第四部分是luacocket是用于加载配置信息的。

Slardar组成图

Slardar组成图

我们造的这个轮子,有什么特点,有什么功能?

首先它是一个动态负载均衡,它的功能如下图:

动态负载均衡功能

动态负载均衡功能

第二个特点是我们以host区分服务,这里有一个例子,

host服务区分例子

host服务区分例子

如图我发了两个CUR请求,它的地址是一样的,都是1.155的3130端口,但是它的host不一样,最终这两个请求所到达的处理集群是不一样的,一个是拿图片的宽高信息,另一个是对图片进行缩略图等这些做图操作。

下图是我们请求的流程图:

动态负载均衡请求流程图

动态负载均衡请求流程图

从一开始接收请求到把HTTP里面的host解析出来,解析出来以后这里涉及到动态lua代码加载,当host解释出来以后,我们到upstream列表里面查后端有没有提供服务,如果有的话会进行转发。

动态upstream更新

动态upstream更新

动态upstream更新主动健康检查

动态upstream更新主动健康检查

在上图的状态里可以看到椭圆中那两个我们刚刚加进去的服务,因为有主动健康检查,如果加的是有问题的服务地址,会显示一个错误,比如我们第二个加进去的就是不存在的地址,然后这个服务就不会被Nginx选为可用的上游。

接下来介绍一下动态lua代码加载,为什么要做动态lua加载这个功能?

  • 首先因为我们是一家2B的公司,需要方便地对客户的请求做改写;
  • 需要执行简单的参数检查,节省带宽。

这些lua代码动态注入Nginx里,让Nginx执行。下图是动态lua加载的例子:

20161024203704

中间是lua的代码,它的意思是说我想要把host为admin.upyun.com的DELETE操作全部禁用,除此之外,任何你想象出来的操作都可以用lua代码实现,它的功能是非常强大的。我们lua代码也有状态页,当我们加入了lua代码,可以看到当前跑的lua代码的版本以及加载时间等信息。

上面内容讲的都是一些特性和功能,接下去主要介绍我们是怎么实现这些功能的,主要涉及到三个功能:动态的upstream管理怎么做、负载均衡和动态lua加载。

动态upstream是三个步骤

  • 启动的时候通过luasocket从consul加载配置文件;
  • 第二步,加载完配置之后,Slardar会启动一个管理端口,监听指令,
  • 接着会启动一个定时器,进行worker间同步。

下图是一个upstream管理的流程图,最开始的时候,从Consul加载,一个是接收端口更新指令的,然后另外一个到定时器,这个定时器不断超时查看共享内存,,如果发现共享内存里的版本与当前进程不一致,就更新到进程内部。

动态upstream管理的流程图

动态upstream管理的流程图

在了解了怎样管理upstream、怎样做upstream的同步后,我们还要在upstream之间做负载均衡,这个主要通过lua的指令实现,叫balance by lua。

Nginx 的负载均衡可以分成两步:

  • 第一步通过upstream的C模块选一个可用的后端服务的地址;
  • 第二步,会把请求代理到上一步选的后端地址。

这个指令,它的作用就是作用在upstream选择这步的,比如我们写在upstream里面,就不会再进原来C模块的代码,而会到我们指令后面的lua代码里面来,这样可以在lua代码里面控制这些upstream的选择,因为我们有lua版的checkups,所以这些操作可以很好的结合起来。

主要的代码就两步,如下图

 动态lua加载

动态lua加载

第一步就是用我们checkups选一个可以用的peer,根据什么选,就是根据当前请求的host选;第二步是从Consul或HTTP请求body加载代码。

这样的方案有什么优势,如下图:

lua版方案的优势

lua版方案的优势

目前在又拍云内部很多项目都已经用上了这套方案,我们有这么多优势,对于性能怎样,我们做了非常简单的对比,如图所示:

性能对比图

性能对比图

上图的对比有一个地方不一样,就是圈出来的地方,左边是用lua做upstream选择的,相应的upstream配置会有一些变化,右边是原生Nginx的配置。对这样的配置进行压测,压测的结果在QPS峰值上没有明显差距,所以用lua版方案和C方案在性能上的损耗其实可以忽略的,可以放心用它。

压测结果对比图

压测结果对比图

最近微服务非常火,我们也做了改造,如果我们把这样的架构改成微服务,Slardar能做什么?我想我们服务肯定是多很多,IP端口也多很多,那Slardar能做什么,举一个例子。

中间未加slardar的作图服务拆分

中间未加slardar的作图服务拆分

上图是作图服务拆分的示意图,如果我们不进行微服务改造的话就只有中间这个作图服务,它做了所有的工作、所有的缩略图操作。改造之后,这个大的作图,会被分解成一些小的作图模块,这些小的模块,各自提供服务,当然都是以容器的形式,比如做缩略图需要硬件加速,就可以把它抽出来了,可以做很多实例,水印的可能还是按原来老路子走,就是CPU做,也不需要太多实例,切开以后,发现中间大的服务,虽然是不需要任何功能的,因为它只要做调度,但是它需要从Consul里面拿到所有微服务的地址,并且还要去做一些负载均衡,因为地址有好多个,还有做容错,还得去做定时的更新,因为这个服务的地址,可能是会更新的,更新了以后要重新拉内部的地址,这样显得中间这个做图服务就会做了很多不该属于它做的工作。为了解决这样的问题,我们在中间又加了一层slardar。

中间加slardar之后的作图服务拆分

中间加slardar之后的作图服务拆分

中间加了slardar后,上面的作图就不需要微服务的端口,直接给我们内网的slardar,带上你需要访问的微服务的名字,这个名字带上以后,由内网的slardar做路由选择,里面会维护所有微服务的地址,它会做重试、做服务的更新,从这可以看出来,我们其实是把Consul里面一部分服务发现的工作,给它下移到我们slardar里面,Consul可能会变成一个持久化存储的角色,可能是把服务发现的角色弱化掉了,这样的话,使内网服务的层级,看起来更加的清晰。

我的分享就到此结束了,谢谢大家。

K8S中文社区微信公众号
分享到:更多 ()