基于容器的微服务架构剖析


E9FACC1D-BEDC-4589-9B8C-578B7A2E6455




随着容器技术的成熟和大规模实践,基于容器的微服务架构凭借其对云服务的天然适应性,以及能够快速迭代和扩展应用的特点,成为互联网创业公司的技术首选。如何更好地利用计算资源?如何更方便地维护越来越复杂的应用程序?由七牛主办的开发者最佳实践日第15期,我们邀请到专业CaaS服务提供者灵雀科技的产品经理成然,京东服务平台资深架构师李鑫和对容器技术有深刻见解的七牛云技术总监肖勤,和大家一起分享微服务的架构实践、容器技术的应用,以及对微服务底层的技术框架支持。



“微服务+”从理念到行动



为了便于理解微服务的内涵,七牛云技术总监肖勤先抛出了一个简单的问题:线上服务器看到很多请求日志,有成功有失败,如何统计出成功或者失败的日志数量?对此,采用AWK这样强大的工具,或是采用CUT、SORT这样的小工具都可以解决问题,而二者在思路上则存在着明显的差别:AWK是非常强大的工具,在面对复杂的问题时,可以以很多分支和循环的方式去分析和应对;而像CUT、SORT、UNIQ这样的程序,各自能做的事情非常简单。比如说把每一行的前几项分隔开,用SORT做简单地排序,UNIQ把相同的剥离出来。通过把它们结合起来,一样可以处理复杂的事情。



将这个例子放大到应用和产品层面,就会发现,第二种思路是用一些功能比较明确、业务比较精练的服务去解决更大、更实际的问题,这就是微服务架构的本质。



微服务架构基础案例



下图是一个描述微服务的典型案例。具体场景类似于打车派单类的服务,左边是采用一个大而全的架构,来处理包括管理司机和乘客,订单分配和费用支付等所有问题,而右边是进行拆分,分别采用相对独立的服务对各方面进行管理,彼此之间使用统一的接口来进行交流。


1



从结构来看,右边的网状结构其实比左边星型的结构更复杂,联系更多。那么这样一个复杂架构的好处表现在哪里?




  • 复杂度可控:在将应用分解的同时,规避了原本复杂度无止境的积累。每一个微服务专注于单一功能,并通过定义良好的接口清晰表述服务边界。由于体积小、复杂度低,每个微服务可由一个小规模开发团队完全掌控,易于保持高可维护性和开发效率。



  • 独立部署:由于微服务具备独立的运行进程,所以每个微服务也可以独立部署。当某个微服务发生变更时无需编译、部署整个应用。由微服务组成的应用相当于具备一系列可并行的发布流程,使得发布更加高效,同时降低对生产环境所造成的风险,最终缩短应用交付周期。



  • 技术选型灵活:微服务架构下,技术选型是去中心化的。每个团队可以根据自身服务的需求和行业发展的现状,自由选择最适合的技术栈。由于每个微服务相对简单,当需要对技术栈进行升级时所面临的风险较低,甚至完全重构一个微服务也是可行的。



  • 容错:当某一组建发生故障时,在单一进程的传统架构下,故障很有可能在进程内扩散,形成应用全局性的不可用。在微服务架构下,故障会被隔离在单个服务中。若设计良好,其他服务可通过重试、平稳退化等机制实现应用层面的容错。



  • 扩展:单块架构应用也可以实现横向扩展,就是将整个应用完整的复制到不同的节点。当应用的不同组件在扩展需求上存在差异时,微服务架构便体现出其灵活性,因为每个服务可以根据实际需求独立进行扩展。



七牛图片处理微服务应用案例



以七牛云的图片处理(简称FOP)场景为例。七牛能够为用户提供格式转换、尺寸和加水印等图片处理服务,从而帮助他们节省计算资源和带宽资源。FOP服务早期的架构很简单,以它的每一个应用为后端。但由于原始图片一般都很大,随着用户越来越多,流量越来越高,负载均衡逐渐出现了带宽和流量的压力。而面对这种情况,加更多的前端只是权宜之计,因为图像处理对于CPU要求很高,意味着后端的数量会越来越多,负载均衡和后端流量方面始终是不平衡的,并且这也不是一个环保和节省资源的方式。



于是,FOP服务做了一个架构上的调整:把服务再做拆分。图像处理服务拆成两个部分,分别负责处理文件的传输和图像本身的处理。从负载均衡过来的请求不再是完整的文件,而是文件的地址。这样,我们可以以更灵活来处理,把负载均衡和流量优化,优化和部署也可以跟整个图像处理没有关系,可以做单独的部署。下面两幅图即是一个对微服务再进行细分的例子。


2


3



而细分之后,对于稍微复杂一些的请求,比如一个图片需要改变格式和尺寸,并且打上自定义水印。那么就用管道的方式把不同的服务串联起来最终实现。



容器在微服务中的应用



目前微服务架构的主流技术方案中都使用了Docker,七牛也是如此。将Docker理解为轻量级的虚拟机,它的一些特性如隔离、物理机制等可以说和微服务架构有天然的契合度。但是Docker并不能解决微服务的所有问题,它最初是一个单机的工具,虽然后来官方也推出了很多的工具链,要真正解决部署的问题,还有很长的路要走。



肖勤推荐了一个去年谷歌发布的开源容器系统Kubernetes。Kubernetes提供了管理多个容器的方式,能够令它们在真正的物理机或者虚拟机以一个合理的方式运用起来,供外界访问。Kubernetes存在的意义,是因为它解决了微服务另外一个部署的问题,拆分之后不同模块之间的联系和依赖会更加复杂,势必对于运营和部署有更高的要求,如果没有工具和系统能够提供这样的能力,或者没有更好的方法去做的话,微服务就是空中楼阁。而Kubernetes提供的能力是用户只需要去定义它的服务期望的状态,而不用去关心它的过程。整个调度的过程,整个提供服务的过程都由这个系统去帮你实现。



Kubernetes提出了很多概念。比如说Kubernetes Master,它是一个中心的服务而不是工具。首先由API接受容器的配置,提供一个调度器,把容器调度到合理的位置运行,再提供一定的控制技术,保证容器运行的状态。Minion可以理解为容器运行的真正节点,Pods是一个或者一组容器,在Kubernetes 中是可以调动的最小程序,ReplicationController保证了它运行的数据,如果它挂掉了,就不能再提供服务。现在也会有很多服务的副本,以保证Pods的数量和运行状态。另外一个标签是Labels,是容器运行时附加的。它的作用是容器在被使用的时候,能够有一个规则让前面的服务正确找到它。服务是一个虚拟的概念,是描述一组Pods对外提供的接口和对外提供的能力,通过代理的方式来实现。



4



上图是Kubernetes官方文档中的描述。值得注意的是,Kubernetes系统是按照微服务架构设计的。因为项目很多部分和逐渐,比如说调度器和复制器,甚至包括Docker在整个系统中都会被替换,如果有更好的容器,Docker有可能会被弃用,变成另外的技术。



如何评价Kubernetes系统呢?是不是整个微服务在实践上的一个未来呢?目前无法确定,因为从学习的角度上来看, Kubernetes也表现出了一定的局限性。比如说Pods的网络,虽然它提供了文档可以某些方式体现,但是1.0提供的方式还是和自己的云服务相关。这些局限性可能跟谷歌的策略有关,因为谷歌非常希望去推它的GCE平台。我们发现Kubernetes在设计当中,如果不适用他们的云服务,摘出来的话,这一部分还需要做很多事情,才可以把Kubernetes在自己的基础上做一个比较好的网络服务。



基于Docker的微服务架构云端实践



应用设计架构的演变



十年前想构建一个具备基本功能流程的电子商务网站,包括用户浏览产品信息、加入购物车、下单,在线支付和完成配送,在进行系统构建时,典型做法是把功能模块简单地做一些划分,后台会访问统一数据库,包括用户管理、商品信息、支付。后来有些程序员意识到可以把一些软件应用当中的最底层和数据库独立起来,紧接着出现了很多的工具做这些东西,如JDBC、ADO.Net等。



接下来就出现了比较传统的经典三层架构Web方式:最上面是展现层,主要是处理UI,中间层处理基本业务,下面是数据访问层,后面是作为它的存储方。如下图所示。


5



时至今日,这种多层次的应用构架仍然具备一些优势:层次划分清晰,层与层之间有比较清晰的接口,每层之间非常清晰。展现层只负责UI,逻辑层负责业务逻辑,每一层又可以进而被分解成逻辑的组建。特别是业务逻辑层,一般来说都会把它分为逻辑组建,包括服务于用户管理的,服务于产品信息的,服务于支付的,等等。而处于最下面的数据访问层,虽然能够被分成若干个模块和组建,但是从部署上来讲,仍然是单块的组建。所有的程序基本上运行在一个进程上,所有的代码都会被打包、编译和部署,我们把它做成一块的东西。



在项目初期,多层架构能够使程序员更容易开发、测试和部署。但随着应用程序逐渐增长,业务复杂度会变的越来越高。这种情况下三层构架并不是特别适合业务的继续发展,可维护性和敏捷程度都会变差,并且团队扩大后成员之间的沟通也会影响产品的交付。从而最终影响到对市场需求新的跟进。而微服务,就是解决这些问题的一个很好的方案。



什么是微服务


6



上图即为通过微服务架构把原来的应用拆分成一系列的微服务,它们之间可以用比较轻量级的协议互相沟通,因为每个服务是运行在自己独立的进程上,完全可以进行非常独立的部署和维护。另外一点微服务提倡的是技术选型去中心化,在不同的服务中可以选择最适合这部分的持久化方案。



灵雀云对已Docker化的微服务的支撑



灵雀云认为,Docker是运行微服务的最佳解决方案。Docker实际上是一个应用容器的引擎,可以让开发者非常方便地把自己的应用以及这个应用所需要的所有依赖都打进容器镜像当中,且具有可移植性,能够部署到任何服务器上。灵雀云是基于Docker构建的,如果把封装的微服务比喻成集装箱的话,灵雀云则提供了一个大轮船,装载了所有集装箱,为微服务运行提供一个稳定的运行环境,用户也可以在此基础上进行管理。这里就可以享受到很多云端服务的优势。



创建:灵雀云的镜像构建和持续集成服务帮助用户将独立、可复用的微服务打包,转化为随时可以部署的容器镜像。


集成:灵雀云不仅在平台的镜像仓库中汇集了大量来自社区的优质镜像,也支持平台以外的任意镜像源。用户可以自由组合、复用数以万计的容器化微服务,像搭积木一样轻松集成应用。


部署:微服务由于组件数量众多,云端部署成为实践上的一个难点。灵雀云以容器为应用发布的载体,用户不必指定传统部署方式中繁琐的步骤,只需提供容器镜像和简单的容器配置,平台会将整个部署流程自动化。灵雀云与docker-compose兼容,实现对于由多个微服务容器组成的完整应用的一键部署。


运维:微服务由于独立进程众多,部署后的运维、管理成为实践上的另一个难点。灵雀云完全屏蔽底层云主机和基础架构运维,让用户专注于应用。同时,灵雀云通过容器编排、自动修复、自动扩展、监控日志等高级应用生命周期服务实现容器化微服务的智能托管,进一步帮助用户降低运维成本和难度。


网络:微服务架构下各组件之间的沟通、协调对网络有较高要求,尤其在云端实践中,各个微服务组件的物理位置是动态的,且不受应用控制。灵雀云提供完整的容器网络解决方案,支持负载均衡、服务发现、跨主机关联,以及应用安全内网来确保微服务对内、对外网络的可用性及安全性。


存储:微服务提倡多元化持久性(Polyglot Persistence),应用内的每个微服务可根据实际需求选择最合适的数据服务。灵雀云将持久性云存储抽象成数据卷,可以直接挂载在容器上,并在容器重启、迁移中自动重新挂载。可支持任意容器化数据服务,供微服务应用集成。



微服务架构的诞生和容器技术的流行,几乎是同时发生的,这并不是偶然。这是互联网时代倒逼传统技术和架构而产生的变革,最前线的开发者和他们所在的互联网企业最先感受到了这场变革。灵雀云希望与开发者一起共同引领这场变革,帮助互联网企业真正专注于自身的核心业务,并在技术和架构上保持领先。



京东对于微服务底层的技术支持实践



京东资深架构师李鑫主要负责京东的服务框架, 他所分享的内容是对微服务底层的技术框架支持。



为何要微服务化?



原因有以下几点。



  • 系统规模随着业务的发展⽽而增长,原有系统架构模式中逻辑过于耦合,不再适应;

  • 拆分后的⼦系统逻辑内聚,易于局部扩展;

  • ⼦系统之间通过接⼝口来进⾏交互,接⼝契约不变的情况下可独⽴变化。


7



上图中,左边是几年前京东的架构,很多服务都是访问同样一个DB。这种架构的问题在于:流量来了以后全部压力都在DB上。而且在之前京东的架构里比较强调快速开发,很多逻辑比如说仓储配送服务都不存在,全都依靠BD来进行。这样可扩展性相当差,性能也不太可控。后来,我们根据业务模块和特性进行水平拆分,应用和应用之间都要通过接口进行交互,有同步和异步接口。



京东服务平台演变历程



下图是京东服务平台的基本功能构成。


8


在2012年初,京东开始做第一代服务框架,用的是zookeeper集群作为注册中心,baseon开源的服务体系。如下图所示。


9


第一代架构在运营过程中,暴露出了很多问题。



1.客户端



  • 许多逻辑放到客户端,客户端逻辑太多,一旦需要修改,就面临升级问题。



2.注册中⼼



  • 将ZooKeeper作为注册中⼼,功能定制扩展受限。



3.服务治理



  • 缺乏流控手段,大流量打爆线程池;

  • 更改配置需要重启,对于运营不够友好;

  • 缺乏调用监控,没有调用分析图表。



为了解决以上问题,团队在2014年推出了新服务平台JSF,其框架示意图如下。


10


可以看出,服务注册和寻址没有太大变化,主要变化在于注册中心。采用团队自己写的服务,并提供了index服务数据库。相对来讲,询问注册中心地址,会进行一个服务的调用。因为这一块有自己内部的逻辑,没有办法实现,所以定期会发送性能统计数据。把RPC转化,生成负载均衡管理重试策略,然后经过序列化以后发送到server并解码,再进行实际的业务调用,然后进行一些过滤的逻辑。



京东服务平台核心技术



1.协议



  • 采⽤异步事件通讯框架Netty来实现⺴络协议栈;

  • 同⼀端⼝同时支持Http、TCP协议访问,根据数据包情况挂载不同解码器;

  • TCP⻓链接下使⽤用⾃定义二进制协议;

  • HTTP⺴关来应对跨语⾔言访问。



2.RPC-callback



  • TCP⻓长链接是双⼯工的,服务⽅方可以主动推送消息到调⽤用⽅方;

  • 调⽤用端检测到参数列表中有Callback类型,登记相应的callback对象;服务端收到调⽤用时,⽣生成相应的反向调⽤用代理;

  • 服务端持有此代理,并在需要时调⽤用此代理来推送消息。



3.负载均衡


一个服务至少部署两个以上实例,挂了一个以后,另外一个可以正常服务。服务消费者这一块,有一个负载均衡的算法选择服务提供者,可以设置权重。有一个可用列表,还有一个重连列,系统会定时连接。还有一个是非健康列表,虽然可以长链接建立起来,这个时候给出的反馈是不正常的,维护的时候会把它挪动可用列表里。



4.性能优化


首先有一个批量处理,请求先写入RingBuffer为,打成序列化与反序列化这种耗时的操作从Netty的I/O线程中挪到用户线程池中。启用压缩以应对大数据量的请求,默认snappy压缩算法。最后是定制msgpack序列化,序列化模板,同时还支持fast json、hessian等多种序列化协议。



5.注册中心


京东有很多机房,对于跨机房来说是耗用更多资源,也占用了专线资源。这里实现了优先访问本机房的注册中心,不可用的话才会去连起来机房。对于注册中心来说,后面会连一个数据库。但是这一块连数据库失败的话,也会有一些单点的问题。对于这种情况,实现了一个LDS,在写数据库的同时会写到本地的存储,在后台进行数据的同步。如果说DB服务不可用的话,还是可以取得相应的注册。



6.配置


配置这一块是服务提供者列表维护,动态推送。可以查看当前服务生效的配置和状态,可以看出调用的平均耗时和失败的次数。对服务动态分组无须启动,而且机房也有备份,出现问题直接调用就可以了。



7.限流


每一个服务调用者都有可能成为潜在的DDOS攻击者,这里会给服务的所有调用者带上标示,在系统环境变量中带上APPID。此外,开发了计数器服务,限定单位时间内最大调用次数。如果说你是一个推送服务,限定每分钟四百次,其实调用已经超限了。为了保护服务端,系统同时限定了并发数,服务端执行时检查请求的状态,如等待时间大于超时时间,直接丢弃。



8.降级



  • 每个服务接⼝口的每个⽅方法都有灾备降级开关;

  • 配置mock逻辑,返回的结果⽤用json格式预先设好;

  • 降级开关打开时将在consumer端短路RPC调⽤用,直接返回JSON结 果。



9.弹性云部署


在京东内部,CAP负责资源调度,JDOS负责资源的虚拟和部署。CAP会定时调度JSF监控平台的方法,如果说告诉它调用的次数已经超了,CAP会调用JDOS,分配具体的服务资源,把一些新服务自动部署。最终,到注册中心进行注册。



下一步研发方向



首先会做的是服务治理,根据应用ID的一系列管理增强。增强接口文档管理,建立接口文档中心,帮助用户使用接口。最后是增强跨语言支持,对于一些比较小规模的,直接通过网站来更新。



以上是牛小七对于本次开发者最佳实践日内容的概括性介绍,如需获取详细的演讲信息和往期内容回顾,可以访问活动专题。了解更多关于七牛开发者实践日,请点击这里