技术大牛讲述微架构与容器化的持续交付实践

本文主要讲述了两大时下较火的技术概念以及具体操作流程,一是微服务,二是Docker。关于什么是微服务,什么场景下适合采用微服务,以及用Docker来做持续集成有什么好处等问题,都可以在本文找到答案。(点击原文链接查看PPT)

1、什么是微服务

对于“微服务”这个现在特别热的技术名词,想必大家都或多或少了解过。所谓微服务,就是将一个时间跨度比较长,比较大型的应用,拆分为多个功能非常内聚的小型的服务架构的方式。本文主要基于Java生态环境来讲解一下微服务的构成。

20161013221842

在Java生态环境中,微服务一共分为三类:Container-less,Self-contained和In-container。下面根据每一个服务的种类介绍一下服务的构成。

第一种架构 Container-less,单一JAR部署。其主要是将所有的代码和代码依赖的第三方类库打包进一个单一的jar,然后启动这个jar包,进行服务的提供。

这种架构方式的优缺点:

「 优点 」是在一些业务变化或者应对一些快速事件的响应时可以非常轻松的去启动停止或者扩展服务,因为只需要启动或者停止一个jar包。

「 缺点 」就是当需要一些第三方依赖的时候,或者选择一个新的技术时,势必要考虑它的兼容性甚至需要自己动手做一些源代码的修改。

20161013221855

所以针对这种不足,出现了第二种架构Self-contained,嵌入式框架的架构,其实它跟上一种架构的模式是非常类似的,底层是硬件、操作系统和JVM。唯一的区别是把一些需要自己引入的一些第三方的框架和依赖,或者需要自己写的一些框架,变为了Spring Boot,Wildfly Swarm这样的一个嵌入式完整生态的框架,这个框架里面包含所有的安全持久化消息的各种技术。

「 优点 」是开发人员可以自由的从框架选择所需的一些技术。

「 缺点 」因为它是将一个嵌入式的框架融入系统,所以势必会增加一些配置的复杂性。可能又会启动一些不需要的配置来配置这个框架的一些协议,比如地址或源码级的一些东西。

20161013221902

第三种架构 In-container 是一个完整的Java EE容器。这种方式是指会将完整的Java EE的容器作为平台,之前的两种架构最底层的平台就是JVM,这种架构会把平台提上一个档次,就是会用Java EE的容器来做一个平台。举个简单的例子就是把tomcat等应用服务器,打包进一个Docker的镜像,在这个Docker的镜像里,只需要把jar包或war包拖进来,然后就可以启用这个镜像去构建服务。

「 优点 」:

一、是写jar包或war包的代码,不需要考虑Java EE底层的一些依赖,只需要聚焦于业务代码;

二、是不依赖于任何的应用服务器,因为只要符合Java EE的规范, jar包部署到任何一个服务器下面,都可以非常完美的运行起来。

「 缺点 」因为其中涉及一些容器、应用服务器的知识,所以对开发人员的要求会比较高。

2、Why 微服务

20161013221913

了解了微服务的基本构成,大家肯定想问为什么要使用微服务,微服务能够带来什么好处?

首先在传统项目中,功能可能包含多个菜单,多个子系统,所有的业务代码。这种架构其实在开发初期代码很简洁,功能也比较容易扩展。

但是这种架构有一个致命的缺点,就是随着迭代开发中每个迭代的推进,开发人员都必须向代码库里推送大量的代码,导致代码仓库变的臃肿,变的非常庞大,使得开发人员每天都在做一些需求或者是改一些bug,而且还可能影响到其他功能的不可用。甚至到最后这个项目组没有任何一个人可以理解代码完整的流程和业务完整的流程,最终使开发人员陷入一个深深的泥沼中。

还有一个非常可怕的缺点,就是经过几年单体的大型项目,往往启动时间和编译时间都是很长的。在开发完功能之后,肯定需要进行一些测试或修改,可能需要很长时间去启动或部署这个服务,这对人员成本、时间成本都是一种极大的浪费,甚至对于开发人员的的心理都是很大的一一种挑战。

另外,由于现在持续集成技术和容器化技术发展的非常蓬勃,其实一个应用在每天向测试环境甚至是生产环境推送多次改动或者升级多次生产环境,都是很容易实现的。

但是这种架构方式的部署时间长,编译时间长,导致无法实现持续集成和持续交付的流程。所以在大概2000年初期,几家比较大的公司推出了一个SOA的架构,这一架构其实就是将单体应用架构中的一些业务拆分开来,分解成各个内聚性比较高,可以统一对外提供某一个单一功能的服务的群组。

它使开发变得更加敏捷、独立,各个服务之间只需要考虑自己需求的修改,而不需要担心一些服务提供者系统的改造带来的影响。

但是在架构的初期,所有的公司都采用了一种ESB业务总线的模式。这种模式导致各个服务之间被强绑定一个技术栈,比如说Java EE。如果针对技术栈形成强绑定,遗留的系统如果要对接SOA的架构是非常困难的。而且在SOA架构上线之后,业务稳定性收敛曲线是非常缓慢的,可能上线1到2年才可以非常平稳地运行。

事实上微服务的概念在很早以前就有了,近些年随着大数据、互联网的兴起,这一概念,又重新开始变得非常火热。微服务其实本质上跟SOA没有太大差别,微服务可以看成一种SOA的实践。就像极限编程和Scrum,其实就是敏捷的一种实践,他们之间的理论是相通的。

微服务也是将业务拆分,把一个整体的系统拆分成各个独立的子系统,但是这些系统之间会定义自己的API,会通过同步或异步的数据交换方式,去解除服务与服务之间的耦合,而不是依赖于一条ESB业务主线,这使得开发者可以去自由、快速的创建服务。

每一个微服务的体量都是非常小的,只专注于自己那一块业务,比如一个服务里只做订单管理的事,一个服务只做客户管理的事。这样服务体积变的很小,可以在进一步持续演进的路上走得更远,甚至可以让开发人员变的更加自由,可以针对业务特点和数据模型去选择最有针对性的技术或语言。

比如说我一些前后台的服务,可以选择用Java,Go,Pathon,PHP都是可以的,这也使得在单体结构中,无法做到技术的持续演进以及选择一些更适合框架的问题得以解决。

而且还有很重要的一点,每一个服务粒度都是很小的,启动还有停止的速度都是很快的,所以也使得持续集成变成了一个可能。而且通过服务之间非常轻量级的通信方式,使得各个服务都是非常内聚的,只对外提供固定的API,这样消除了服务之间的耦合,使服务之间变的更加独立,也增强了它的替换性。

比如说现在有一个服务是用Java来编写的,可能在生产实践的过程中发现随着业务量的增长和订单量的增长,发现效率已经无法满足当前的一个I/O的性能,我可能会使用Go语言把他换掉。但是这个服务是非常小的,所以用一个新的语言或者一个新的技术重写的难度,也比之前的单体应用会小很多。

20161013221923

这张图就是三种架构风格的一个比较形象的对比图,在90年代甚至更早期,就是单体服务的,它的架构非常杂乱,没有人可以理清整体的流程。在2000年初期诞生了SOA,解除了单体服务架构的耦合性,但是它们中间会依赖ESB的业务总线,这导致各个服务之间必须绑定某些技术,难以在技术上进行进一步的演化。

到了近些年,微服务的出现彻底把这些耦合性完全解开,每一个服务都是完全独立的,所以开发更加灵活,持续集成和持续交付成为了可能。

什么场景下适合用微服务?介绍了这么多关于微服务的优点,其实微服务也不是万能的,也有很多的缺点。比如把一个巨型的应用拆分成多个微服务组成的服务群,势必会增加服务与服务之间交互的复杂性。

还有因为服务之间要进行一些网络通讯,它比单体服务肯定是增加了一些网络的消耗,所以必须有比较成熟的网络方案来支撑这么多服务的网络通信。这也对开发人员和运维人员提出了比较高的要求。

20161013221934

说了这么多,那究竟在什么场景下适合用微服务?脱离了业务架构其实都是空谈。即便你设计出一个再完美,再有可行性的架构,在业务系统中无法落地,这个架构也是一文不值的。

但是恰恰业务变化和用户需求有着非常大的不可预测性和可变性,所以这也导致了现在微服务能快速响应客户变化,快速响应开发变化的架构方式得以实现。

20161013221958

所以如果架构满足不了上图中提到的三点,项目可以尽量先不采用微服务的架构,或者是从系统中抽取很小的一个模块来试验一下微服务,看自己的系统到底适合不适合,而不是说什么东西流行,什么东西火就去用它。

第一点,用户、客户对系统的速度要求非常高,需求变化非常频繁,要求快速的响应,或者产品的订单量、数据量都在处于井喷状态,就是快速发展的状态,就需要采用敏捷开发或微服务、持续交付的方式去满足客户的一些需求。

第二点,对功能的隔离性和快速部署上线的要求,要比它模块之间内聚的性能要求高的时候,就可以去采用微服务。

第三点 ,也是最重要的一点,就是系统业务功能,必须能拆分成多个独立的组件。最不好的方式就是为了实现微服务,而硬去把一个系统拆分成多个细小的微服务,这会导致你的系统最后还会变成像单体架构一样臃肿巨大,而且内部服务的复杂性和网络之间的通信量比单体的架构更大。

20161013222012

这张图就是简单的描述微服务典型架构的样例图。首先微服务内部的各个服务都是非常独立的,它们唯一的联系可能就是通过一些HTTP、消息队列去对外暴露一个服务、一个接口。微服务的群组,会对外提供一个整体的API网关,向外面的用户进行服务的提供。

用户可以是一些手持设备或者有一些PC设备,甚至是一些大型设备,而且每一个服务都会有自己针对的技术和技术框架,以及他们特定的一些数据存储的方案,比如一些关系型数据库,还有一些内存型数据库,甚至一些全文检索的数据库,这都是根据业务的特征可以自由选择的,这也大大提高了项目的灵活性和项目的演进能力。

4、尝试

20161013222020

1. 具体的尝试包括首先将业务横向拆分。 业务必须能拆分成多个独立的子系统。所以我们会与客户和设计人员一同探讨,如何将业务拆分成多个独立的系统。

2. 会构建一个自治型、专注型的服务应用,就是这个服务只专注在一个业务模块上,而不会对其他系统之间有任何耦合的服务。

3. 是服务之间灵活稳定的交互方式,刚才提到的同步异步的方式。

4. 就是保证持续部署以及运行能力。因为现在微服务都是已经拆分为独立的小的服务了,所以每一个服务的启动停止以及构建是不与其他服务相耦合的,也就是可以非常快速的构建出一个服务,推送到测试环境甚至生产环境,保证了微服务的一个部署和运行能力。

5. 是实践可行的服务注册、发现、负载均衡方案。在这个方案上也是做了比较多的尝试,因为微服务之间通信是很重要的一块,开始是采用的淘宝之前开源的一个框架来做服务注册与发现的。但是在实践过程中发现这个框架还是有一些不足之处的,所以现在也在逐渐考虑向OSS框架进行转型。

6. 是必须要有一个完善的日志监控和性能度量的策略。在微服务出现之后,做日志监控和性能度量的东西已经变的非常重要了。因为可能从外部进来的一个请求会流转几个,甚至十几个微服务的系统,需要在各个系统之间都有一个完善的性能监控,包括一些错误路径的一些追踪,包括一些应用服务的一些指标。目前是通过一些开源的主流的技术栈去实现的,并且在上面做一些二次开发或封装来适应现在的业务需求。

20161013222028

这张图是其中一个项目的整体架构图,首先对于上游系统,也就是客户,比如猫眼、百度、淘宝,会为他们提供API的网关,实现的功能就是一些负载均衡和API的应用注册,会把底层所有服务的一些API进行有效的监控和管理,并且内部也附加了一些监控的策略。

然后内部就是一些微服务,微服务之间可能会通过MQ进行通信,或者是通过一个简单的REST API进行HTTP的通信。在这两个服务中,因为需求比较特殊,甚至用到了Redis去做任务队列来进行通信的模式。

而且每一个服务存储的选型也都是不同的,比如一些事务相关的服务,选型是MySQL,还有一些订单类、营销类的,选型是Redis+MySQL,甚至是Redis+Oracle。

右侧和左侧这两个服务是刚才提到的,之前用的是Dubbo的注册中心加上Zookeeper加上Dubbo的监控中心,去做服务的注册以及服务交互的一种方式。现在也在逐渐转型到Netflix OSS,这个生态体系里包含很多框架。

在这套微服务架构的外部,采用了三种监控的策略,刚才也提到了,包括一些业务的监控和性能指标的度量,现在采用ELK的技术栈去做业务日志的监控,包括一些订单量,或者是成功失败的一些量,或者是业务的一个走势。

通过Zabbix去做服务器性能的监控,包括CPU负载的量,网络出入的一些流量,还有内存的一些指标,包括整个服务器的监控。第三个是Metrics,这个是结合SpringBoot在用。也就是会监控这个应用性能的指标,就是这个应用有多少变化请求,内存怎么样,有多少个线程在跑,跑了多少时间,现在日志的级别,包括一些预警信息都是可以在这三个技术栈里去实现的。这就是整体的一个项目的架构。

5、Docker

20161013222038

遇到的一个挑战:

1. 是怎么样能够快速的实现一个高效的高可用的RPC调用;

2. 因为已经把一个大的服务拆分成多个细小、微小服务,就势必需要一个非常好的管理去管控各个服务之间的健康的检查,数据的流程。所以怎么有效或者高效的去部署和管理各个应用,也是非常重要的;

3. 因为微服务中间存在着很多网络的通信,所以需要一套非常完善的网络通道的通信管理策略。

在考虑到这几个问题的时候,正好看到了Docker,然后思考Docker到底能不能解决三个问题其中的一个、两个甚至是三个问题。

经过一番思考觉得需要Docker解决的,包括以下这三个问题:

20161013222046

1. 需要容器化技术实现一种可配置化的,并且可以快速迭代重复的构建、启停、弹性伸缩服务的整体方案;

2. 因为现在大家在开发代码中也都用到了一些代码托管工具。思考能否把服务也变成这样,可托管的方式在上面打上版本,通过托管式运营维护的工作,去实现服务之间版本的替换,服务之间上线下线交付的操作;

3. 必须要有一个成熟的网络方案跟集群的管理策略,否则把微服务拆分出来运行上线,无法管控住,也就无法给客户,或者给用户带来一个满意的效率。

6、Why Docker?

20161013222054

为什么采用Docker?

1. 它可以与Jenkins、GitHub、Gitlab有一个非常好的集成,可以从之前使用Gitlab到Jenkins然后启动jar包、war包的流程之中,把Docker很好的融入进来,使得有一套完整的持续交付持续部署的流程。

2. 就是Docker提供了非常完善的容器的管理策略,而且是托管式发布。在测试环境或者一个云服务系统中,将一个应用打包,构建成镜像,可以把它很容易的推送到包括像DockerHub、时速云镜像中心的这些服务中来更好的管理,更好的获取、启动、停止这些服务。

3. Docker最重要的一个特点,就是它的标准化。为什么会使用Docker,因为Docker无需处理环境的不一致性。在本地开发一个应用,启动一个容器,这个镜像可以拿到任何一个环境下面,拿到所有Linux发行版,包括Cent OS、Ubuntu、红帽,甚至能拿到Windows、MacOS上都可以启动与在本地启动一样的服务。

在生产环境、测试环境中遇到的任何一个问题,只需要把生产环境现在运行的镜像,拉到本地启动起来,就可以得到生产环境完全一致的环境,而不需要考虑它的一些服务器的全局变量,服务器内核的一些特性,甚至jdk一些版本。这些都是不需要考虑的。

4. Docker的生态体系提供了很多非常优秀,非常成熟的网络管理和集群管理的方案现在Docker的生态环境发展的极其迅猛,如果留意过Docker的话,你会发现他的版本迭代是非常快的,是以月甚至以周为单位快速迭代,而且每一个版本的改动量之大也是让人咂舌的。

而且现在Docker已经完全形成了一个CaaS的平台,也就是容器即服务的平台,包括像以时速云为代表的一些做容器服务的厂商,可以提供一套完整的,交付很灵活、敏捷的,并且是可以把控,可以监控的应用。

20161013222103

在实践了一段时间用Docker来做持续集成之后,发现它确实带来了很多好处。

从三个方面来说:

1. 第一个方面开发。开发不需要关心自己平台和语言的依赖性,也就是说这个团队可以采用新的技术和语言,而不是被公司规定的一个技术栈,或者是以前遗留系统的技术栈绑定住,这不但提高了开发人员的个人能力,也可以把更加适合的技术集成到应用之上,加强了服务和技术逻辑,极大的提高了开发效率。

2. 就是在交付阶段。其实开发人员和测试人员,还有运维人员可以一同制定一整套从设计到开发到测试到发布的一整套流程,使得开发、测试、运维人员的区分并不是那么明显,他们的合作也更加紧密。

3. 对于已经在线的运行状态来说,由于Docker还有Docker的厂商提供了很有利的关于弹性收缩、网络安全、高可用性高可靠性的支持,也使得在线上运维的人力和物力的成本都得到了极大的降低。

7、持续集成(CI)

谈到了Docker,那究竟用Docker来做持续集成是如何实现的?这张图描述了现在大概的做持续交付的逻辑,首先开发人员会在本地进行开发,包括编写业务代码,编写单元测试,以及符合一些代码检测的规范,然后本地测试通过之后,会向GitHub/Gitlab这样的代码托管的平台推送一个合并的请求。这个时候代码还没有被合并到主干版本的分支里,只是有一个请求等待代码review或者代码的合并。20161013222121

在这个同时,GitHub/Gitlab Hook会触发代码请求的函数,去自动触发Jenkins的构建。这个流程都是自动完成的,Jenkins会根据当前正在稳定运行的系统,加上这次提交的一个增量代码,去构建出一个增量可用的Docker镜像,并把这个镜像运行在阿里云或者本地的物理测试环境上,会交付测试人员。在测试通过之后,他会向团队通知,团队会对这个代码进行代码review。

Review通过之后,就可以将代码合并到主干分支。合并到主干分支之后,每日会集成出来一个可用的环境,这个环境对应我们服务的镜像,会把它推送到时速云的镜像中心。这样就完成了一个基本的测试的持续集成的流程。

20161013222128

这张大图就是现在正在实践或者试运行的一套持续集成/持续交付的流程。首先因为是微服务,所以前后台是分离的。

后台的服务也是有多个的,前台提供一个SPA的单页面的系统做前台的工作,前后台的人员都是独立进行开发的,这个虽然夹杂了一些单元测试,接口协议的强制性,但是这种包括单元测试、代码规范、代码覆盖率,这都是对于项目或者产品成熟度以及最后的稳定性有很大的好处。

所以这几点,也是要求开发必须要做的。在开发完成之后,会将代码提交到Gitlab,Gitlab会触发Jenkins的构建。Jenkins可以通过调用时速云提供的一个开发者API的接口,去触发镜像的构建。最终这个镜像会被推送到时速云的镜像中心。最终镜像运行在阿里云,AWS或者是本地的物理机上。

运行的状态是一个Kubernetes的集群,因为现在时速云已经基于Kubernetes来做了一套微服务的集群。这个集群是在测试环境甚至是生产环境都可以运行起来的。而且他们之间环境的不一致性是没有的。最终经过测试人员的测试,以及用户的测试,最终达到上线的版本。

而在整个流程里,只有开发人员本地开发是需要自己来做的。剩下其他所有的东西都是这一套CI/CD流程来自动完成的。开发人员只需要编写业务代码,编写单元测试用例,通过代码检测,通过代码review,工作就可以完成。

测试获取来一个测试地址,针对这一个bug,或者一个需求的改动去进行测试。在每日的集成环境中,我们会通过引入一些自动化测试的工具,来构建这些自动化的测试用例,最终在每一日的环境中,跑全量的自动化测试脚本,保证系统的可用性,以及一些性能的指标,最终达到持续交付或者持续部署的能力,可以由之前的按月、按周持续的部署交付,达到按日甚至按小时就可以进行多次测试环境生产环境部署的能力。

分享到:更多 ()