不是技术也能看懂云原生
云原生越来越火了,无论是企业内部,还是技术论坛,上到应用架构,中到数据库存储,下到基础设施,无不谈云原生。可是云原生到底是什么,容易让人感到概念混乱不清。其实这不怪大家,这个概念太新了,不但大家困惑,业内大牛也在不断的改变着定义,直到现在才稍稍有所统一。
一、混乱且不断变化的云原生定义
1.1.云原生定义之乱
我们来看看这些大牛们都如何定义云原生的:
2010年,WSO2技术总监PaulFremantle 首次提出Cloud Native,他一直想用一个词表达一个架构,这种架构能够描述应用程序和中间件能够在云环境中有良好的运行状态。云原生有以下特性 分布式、弹性、多租户,子服务,按需计量和计费,增量部署和测试。
2013年,Netflix云架构师,Adrian Cockcroft介绍了Netflix在AWS上基于Cloud Native的成功应用,Netflix在AWS上有上万个实例。
2015年,来自Pivotal的Matt Stine,他的电子书《迁移到云原生应用架构》,他认为单体架构在向云原生架构的演进过程中,需要流程、文化、技术共同变革,该书把Cloud Native描述为一组最佳实践,具体包含如下内容:十二因子,微服务,敏捷基础设施,基于API的协作,反脆弱性。
2017年,Matt Stine在接受媒体采访时又改了口风,将云原生架构归纳为模块化、可观察、可部署、可测试、可替换、可处理6特质;
而Pivotal最新官网对云原生概括为4个要点:DevOps+持续交付+微服务+容器。
2015年云原生计算基金会(CNCF)成立,最初把云原生定义为包括:容器化封装+自动化管理+面向微服务。
CNCF于2018年通过了对云原生重新定义的提案,V1.0的定义如下:云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式API。这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。
这里面涉及到太多的专业词汇,例如:
- 分布式,弹性,多租户,子服务,按需计量和计费,增量部署和测试;
- 十二因子,微服务,敏捷基础设施,基于API的协作,反脆弱性;
- 容器化封装+自动化管理+面向微服务;
- 容器、服务网格、微服务、不可变基础设施和声明式API。
要了解云原生一个概念,结果被抛出来这么多概念,这不越解释越糊涂么?
没关系,如果你看不懂这些概念,就先放一放,后面我们会抽丝剥茧的来讲解。
但是第一件让我们困惑的事情是,这么多大牛,在长达这么长的时间内,前赴后继的来研究和定义云原生,他们这是干啥呢?这玩意儿有那么重要么?
1.2. 云原生是发挥技术部门价值的学问
这么多大牛研究的东西当然重要了,究其本质是在互联网模式下,如何才能发挥技术部门价值的问题。
为什么这么说呢?
因为我们从各行各业越来越互联网化的趋势中能够发现,企业的技术人员比起原来可是越招聘越多,越招聘越贵了。原来只有简单的开发和运维部,甚至大多数要靠外包和采购商业软件解决问题,现在开始逐渐萌生出硬件采购部,基础设施部,系统部,运维部,中间件部,服务架构部,数据库部,大数据部,业务中台部,业务开发部,前端开发部等等N多部门。
这个时候,给公司赚钱的业务部门肯定在想,公司养这么多技术人员在干嘛,技术部门的老大也会想,我养了这么大的团队,如何才能展现自己的价值呢?
第一点价值为业务创新。在互联网大潮下,无论是互联网企业还是传统企业,都存在业务变化快的情况,传统的商业模式被飞速的打破,新的商业模式和玩法只有想不到,没有做不到,这个时候,业务部门最不想听的技术部门说的一句话就是,业务部门都想好了新的打法玩法,结果技术部门做不出来,这下技术部门丢人就丢大了,也就没有了价值。
第二点价值为系统稳定。也就是当前运行着的系统不要挂,不要业务员正依靠系统做活动或者办业务,结果系统老是刷不出来,肯定会被诟病。
第三点价值为成本优化。虽然IT投入会越来越多,但是商业竞争还是残酷的,如何提高资源的利用率,在尽量少的资源的情况下,保证业务的创新和稳定,是价值的体现。
第四点价值为技术创新。任何一家公司的技术部门都是希望能够构建技术影响力的,而且一家没有技术创新和技术影响力的公司,是无法吸引好的IT人才的,也就没有办法保证业务创新和稳定。
然而这四点价值其实是有矛盾的,比如创新就要快速开发,如何同时保证稳定呢?再比如系统稳定需要做多副本的冗余,那如何成本优化呢?成本优化就要做改造,一折腾,系统可能就不稳了。再比如技术创新引入新技术,成本就会高,新技术就会不稳,咋办?如果成本控制很严,三年五年不创新,IT人员都跑了,到时候想开发新系统,找不到人,咋办?
这里面的各种度是很难把握的,我就见过有的企业未来创新,大刀阔斧的系统重构,结果导致系统长时间不稳定,被业务部门投诉从而背锅的事情。
这也是这么多大牛,包括国内的CIO们不断讨论如何解决这个问题的原因。
为了解决业务变而系统稳这一对矛盾,大牛们不断摸索,从而构建了一套目前看来边界尚未清晰的体系,最终命名为云原生。
接下来我们沿着大牛们逐渐摸索的道路,看一下云原生到底是什么?
二、第一阶段:基于虚拟化的云原生探索期
2.1. Netflix的云原生分享
让我们把时光拉回2010年左右,那是一个传统Web类网站荣光已过,新一代互联网应用逐渐崛起的时代。传统Web类网站国外的代表是雅虎,国内的代表是新浪,搜狐,网易。而新一代互联网应用,包括电商,视频等都开始崛起。
在国外有一家公司Netflix,本来是做传统的DVD发行服务的,2007年变为在线视频公司,实现互联网化,在2008年正面临着业务快速发展而稳定性变差的压力,这家公司摸索这样一条道路,就是上云,当时云计算市场没有太多选择,Netflix选择了AWS,来解决稳定性问题,扩容问题,业务快速发展问题。
经过几年的实践,2013年,Netflix云架构师,Adrian Cockcroft介绍了Netflix在AWS上基于Cloud Native的成功应用,Netflix在AWS上有上万个实例。在这个演讲里面,Netflix分享了他们如何基于云解决问题的。
我贴了几张当时分享的PPT截图,如果您不是技术,可以不用关注其中的细节。
第一张图说明了Netflix使用了大量的AWS服务,并且和自己的数据中心做了打通。
第二张图看起来比较头晕,密密麻麻纷繁复杂的的服务之间的调用,不过你也不用管他。当时云原生和微服务还没有火,也谈不上追风口,Netflix是真的因为业务的演进事实性的架构变成这样的,完全处于业务的需要。
2.2. Netflix与Spring Cloud小插曲
这里有一个小插曲,现在开发大型分布式系统的主流语言之一是Java,而Java最火的框架之一是Spring,这个框架诞生于2002年,运营他的公司叫springsource,2009年被Vmware收购。
2013年,Vmware、EMC和通用资本合资成立了一家公司叫Privotal,记住这个名字,后面他还会出现。
在Spring基础上,2014年Privotal发布了Spring Boot的第一个release版本,你可以认为这是升级版。同年,以Spring Boot为核心的一套分布式应用开发模式Spring Cloud诞生。
模式只是抽象的模型,底层实现可以不同,而Netflix的Spring Cloud Netflix于2015年发布第一个版本,是最重要的实现之一。一度一提Spring Cloud,就是Spring Cloud Netflix,这正是上面那个网状的分布式架构的结晶。
2.3. Netflix的云原生动因
我们回到那次演讲的PPT,在下面一页,Netflix表明了他为什么要这样做。
虽然当时云原生的概念还处在初级阶段,但是Netflix在这里的表述已经从实践角度非常全面的描述了未来云原生的方方面面,以至于多少年后,当我们的业务真的发展到某个程度,回过头来再看这页《Outcomes》PPT,才理解其中真意。
如果读到现在你还不能完全理解,则目前仅需关注其中三点,后面回过头来再说。
第一点:微服务是为了关注点分离(seperation of concern),这是业务层敏捷的关键。
这一点其实很好理解,是源于人类思考问题的物理极限。就像按照组织行为学研究的那样,一个人经历再旺盛,最多直接领导10-20个人就了不起了,再多一定会效率低下,必须要分子团队。同理,人类关注问题也一样,把太多问题混在一起,所有人一起关注,一定不会清晰,一定管理混乱,一定效率低下,要拆分子问题。于是一个大的服务就不断拆分成子服务甚至微服务,最终每个服务聚焦解决一个子问题,被少量的人关注,则边界清晰,决策容易,开发速度快,业务敏捷就体现出来了。
当然微服务的实施也是有成本的,因为服务数目多,就一套机制将多个服务统一管理起来。
比如要有注册和发现机制,服务少的时候,哪些服务可以调用,IP是多少,域名是多少不需要怎么管理,但是一旦服务数目多到上面那张图,就需要维护一个登记簿管理系统内所有的服务地址。服务启动的时候会向登记簿注册自己的地址信息。当一个服务的域名或IP地址改变了之后可以及时通知依赖方,或者依赖方可以快速的发现服务提供方的地址变化。
再比如要有统一的配置中心,原来一个程序运行,配置放在本地也容易查询和管理,如果服务数目多了,配置要放在一个集中的地方,才能散而不乱。
我们称这些为服务管理机制。
第二点:开源实现代码共享,是开发敏捷的关键。
分布式微服务的开发所需要的新的模式,例如注册,发现,配置都需要额外的代码来适配这种模式。这时候问题来了,每当有一个新的需求要开发一个新的服务,原来都是拷贝一份代码,重新造个轮子,开发效率比较低下,还容易出错,代码无法形成标准,很难维护。利用现有的开源软件,形成开源标准,一方面新开一个工程要容易的多,也标准的多。另外当需求紧张的时候,公司的人员可以在组之间借调,如果部门墙很重,这个部门看不懂另外一个部门的代码和框架,那很难形成人力资源池,而开源也解决了这个问题。
第三点:用云实现资源的弹性,是资源层敏捷的关键。
服务数目的增多必然会对资源的灵活性造成一定的压力,如果不能够实现快速敏捷的部署,要运维上面那个网状的架构,运维得累死。
Netflix使用了一种基于虚拟机镜像的敏捷部署机制,如下图所示,对于要发布的一个新的应用,会用虚拟机镜像打包一个运行环境,无论复制多少份,无论是在开发,测试,生产部署,都能够保持环境的一致性,这样会大大增加发布的效率。
在《Outcomes》那一页提到了immunitability,也即不可改变基础设施,这里的意思是,所有对于部署环境的改变都要通过发布系统来改变,一旦发布就不再改变,如果要改变就重新走一次发布,而不能私自登陆到某台机器上去做改变。这是因为Netflix已经有上万台服务器了,如果允许私自登陆做改变,那这种改变是无法追溯的,一旦出现了问题也不知道是因为哪次改变导致的问题发生,而且又没办法登陆到机器上一台台的定位,所以就让发布系统追溯对于环境的改变。
其实这里的很多思想以及十分接近容器了,只不过那个时候还没有容器罢了。
这三点我画了下面一个图,和咱们的技术架构对应起来,就比较容易看明白了。
总而言之,在这个阶段,Netflix是通过充分利用云的弹性和敏捷,利用开源标准,促进业务微服务化和快速迭代,满足业务需求。
2.4. Netflix云原生实践的遗留问题
Netflix虽然很牛,在大规模落地云原生方面给全世界的人打了个样儿,但是对于整个行业来讲,大家看到的还只是这个PPT,感觉这个思路不错,至于如何落地仍然心里没有谱,大家仍处在各自为政的摸索阶段。
比如Netflix为了基础设施层的弹性和敏捷,坚定的选择了使用AWS上的服务,这在Netflix上云的那个时刻,其实云服务没有太多的选择,但是后来不一样了,云厂商越来越多了。很多企业意识到了云平台弹性和敏捷的重要性,但是不愿意像Netflix一样绑着一家云厂商玩到底,于是一度开始建设私有云平台,并试图形成行业的标准,当时发展的如火如荼的OpenStack社区就被给予厚望,各种平台争相遵从OpenStack的接口标准,从现在的角度看,这种统一IaaS的尝试并没有成功。
再如开发框架Netflix自己使用并且开源贡献了Spring Cloud Netflix,也只是在比较新的Java分布式应用开发方面形成了一定的业界影响力。但是很多互联网企业在发展过程中,根据不同的业务模式,不同的开发语言,不同的技术演进路线,也有了自己的分布式应用开发框架,也是各做各的,没有形成标准。
于是整个行业处于下图的状态,云已起航,但无法形成标准,其他方面,各做各的。
三、第二阶段:跨环境场景下的标准形成期
3.1. Pivotal与云原生的故事
这种无标准的状态不利于行业的飞速发展,不过这些Netflix都不在乎,他是做业务的,而非以开源软件为生的,只要能够满足自己的业务需求就可以了。2018年,Netflix 宣布 Spring Cloud Netflix 系列技术栈进入维护模式,也即不再添加新特性了。业内很多用了Spring Cloud Netfix的也不得不寻找替代方案。
从表中可以看出Netflix不再维护之后,官方有了替代的实现方案,很多互联网厂商也有了替代方案。
这个表里面最左面的一列又是很多的新概念,这里面的分布式配置和注册发现,我们前面讲过了,是一个分布式微服务系统所必备的。这里又出现了路由,熔断等其他的机制,这些是干什么的呢?我们还是回到《Outcomes》那一页,里面还有一个词叫anti-fragile,叫反脆弱,说白了是如何保持整个系统的稳定性的问题,能不能有一些机制,使得系统没有那么脆弱。其实微服务已经比原来的脆弱性好多了,原来一个应用,挂了就都挂了,现在服务数目多,挂了只挂一部分,但是这个时候挂的这部分到底对于整套系统的影响是什么呢?能不能影响尽量小呢?这就是反脆弱。常用的方式就是进行服务治理,说到治理,你有没有想到城市治理,交通治理等,意思是类似的,就是通过一些机制,减少意外造成的影响,比如一个地方出了车祸,会越来越堵,就需要治理了,路由就是已经出门的能不能改条道路走,熔断就是没出门的先别出门了,从而降低影响面,直到交通恢复正常。我们称这些问服务治理机制,图中Spring Cloud的相应的组件中就有是做服务治理的。
说到Spring Cloud官方,前面的小插曲要继续了。
Pivotal作为Vmware和EMC成立的子公司,明显就是To B的一家公司,云原生的标准问题Netflix不关心,Pivotal当然很关心,这对To B公司来讲是巨大的一个机会,一个在IaaS已经杀的昏天黑地后的PaaS层的机会。
而且在这个方面Pivotal有着得天独厚的优势。背靠Vmware,IaaS层有天然的合作优势。手握Spring Boot和Spring Cloud社区,开发框架层次也有话语权。在中间的跨云的运行标准,他还有另一个法宝Cloud Foundry。这个软件2009年被springsource收购,后来和Spring一起被Vmware收购,后来就归了Pivotal,他主要解决跨云服务的运行和生命周期管理的标准化问题。
有没有感觉Pivotal一下子凑够了七龙珠的感觉。于是我找了一张Pivotal的PPT,如下图所示。
这里面多云虽然难形成统一标准,但是有Vmware这个私有云第一品牌做靠山,加上很多企业喜欢建设公有云,则谁不得给点面子。跨云的运行标准和编排标准用Cloud Foundry覆盖,这里解释一下,任何一个程序的运行都需要一个环境,将这个环境标准化称为运行标准,对于复杂的业务会有多个程序运行,并且互相有一定的关系,维护多个程序运行的相互关系并且形成标准称为编排标准。开发标准Pivotal有Spring Boot,以及分布式应用开发标准Spring Cloud,同时也是服务治理的标准。
3.2. 云原生十二要素与Cloud Foundry
于是Pivotal定义了云原生十二要素,后来又补充了三个,从而开始有了标准的样子。
这里的要素其实覆盖了:
- 运行标准的问题,例如一份代码,多份部署,优雅启动和关闭,而且环境等价
- 编排标准的问题,例如依赖关系,明确端口,以服务的形式关联
- 弹性的前提,例如无状态,水平伸缩
- 集中管理的问题,例如配置中心,日志中心。
这样一个公司的业务是否符合云原生,就可以根据这十二个原则来衡量。
可是原则毕竟是虚的,在实现层面还需要有个标准,Pivotal给的方案就是Cloud Foundry,他的架构非常复杂,如下图所示,覆盖云原生的十二要素,并且是一个跨云的PaaS方案。
这个图是一个比较新的图,里面有了Docker容器,在早期的Cloud Foundry里面,是另一种容器叫Warden,他主要使用了两种技术namespace和cgroup。
容器技术,是一种将一台大的服务器切割为独立且隔离小箱子,从而每个小箱子里面可以运行独立且隔离的应用。容器主要使用了两种技术,一种是看起来是隔离的技术,称为 Namespace,也即每个 Namespace中的应用看到的是不同的 IP地址、用户空间、进程号等。另一种是用起来是隔离的技术,称为 Cgroups,也即明明整台机器有很多的 CPU、内存,而一个应用只能用其中的一部分。从这里可以看出Cloud Foundry对于容器的理念,已经和后面产生的Docker容器非常接近了,但是他却没有解决一个问题,就是跨云的平滑迁移问题。
在虚拟机时代,跨云是一个相当难的事情,因为云主机的镜像是不能在云之间随意的迁移的,如果一个业务想部署在多个云上,一般采取的方式是在不同的云上创建不同的云主机,每台云主机上安装一个agent,agent通过拉取各种安装包在不同的云上部署类似的程序。当时业内几乎都是这样做的,别无他法,Cloud Foundry也不例外,虽然他也有容器的概念,通过namespace和cgroup技术进行应用隔离,但是他仍然采取这种传统的方式,buildpack进行打包,DEA组件Droplet Execution Agent,用于管理应用实例的整个生命周期,也即下载,解压,运行应用程序。
这种模式的缺点,一是比较重,每个云创建的虚拟机都是空的,要全部重新安装应用。二是不标准,不同的云创建的虚拟机里面的环境多少都有差异,相同的脚本有时候运行的好,有时候不行。
3.3. Docker横空出世
后来就有了Docker,这家2010年就创建出来的软件一开始名不见经传。Cloud Foundry觉得Docker同样使用了类似的namespace和cgroup技术,并没啥新鲜的。
然而Docker除了上述两个技术,还有一个法宝,就是镜像,一个看起来没有那么有技术含量的东西,却产生了深远的影响。
所谓的镜像,就是将你焊好集装箱的那一刻,将集装箱的状态保存下来,就像孙悟空说:“定”,集装箱里面就定在了那一刻,然后将这一刻的状态保存成一系列文件。这些文件的格式是标准的,谁看到这些文件都能还原当时定住的那个时刻。将镜像还原成运行时的过程(就是读取镜像文件,还原那个时刻的过程)就是容器运行的过程。
虽然上面我们讲过Netflix基于AWS的虚拟机镜像也实现了类似的功能,但是虚拟机镜像是非标准的,不同的云不一样,而且非常大,动不动就几百G。
容器的镜像就小很多,在MB级别,而且重点在于标准,一旦有这个镜像,无论在哪个云,哪个环节部署都能得到相同的容器内的环境。这个特性使得Docker既能在开发,测试,生产环境之间进行标准化迁移,也能在多云间进行标准化迁移,真正实现云原生的运行标准。
3.4. Kubernetes统一编排标准
运行标准有了,接下来就该编排标准出现了。
2014年,Google推出Kubernetes,并且发展迅速,很快微软、RedHat、IBM、Docker都加入了社区。当然空白的编排市场岂能让Kubernetes一家独占,很多其他的编排系统也虎视眈眈。2009年产生了一款软件mesos,被引入了Twitter进行了大规模的落地,这是一款调度算法非常牛的软件,被用在spark这种大数据处理领域,需要高效的调度大量容器的场景,非常有效,到了云原生时代,mesos社区推出了marathon做在线业务程序的编排,成为Kubernetes强有力的竞争者。2016年,Docker在自己成为运行标准之后,开发发力编排标准,大力推广swarm进行容器编排,由于和Docker社区兼容性好,也成为kubernetes的强有力的竞争者。Kubernetes可谓前有拦截,后有追兵。
Kubernetes走的路和另外两家不一样,mesos强在有大规模落地案例,侧重于以调度为核心,引入很多其他框架形成云原生生态DCOS(数据中心操作系统),但是DCOS里面的组件各种语言都有,相比于mesos的成熟,这些组件繁杂且成熟度低,而且并没有在面向业务的编排标准上下功夫。swarm强在运维简便,上手容易,也没有在面向业务的编排标准上下功夫。唯有Kubernetes,既不着急稳定下来,也不着急简化运维,而是定义了一大套的概念,初看非常困惑,仔细看发现这才是面向云原生的标准定义的思路,Google和Redhat就是不一般,一流的公司定标准,此言不虚。
我还专门写了一篇文章,详细分析了Kubernetes才是真正的站在云原生业务的角度来编排容器。为什么 kubernetes 天然适合微服务
下图是Kubernetes定义的那些复杂的概念,和云原生十二要素的要求一对应,就让人恍然大悟了。
这里面包含了运行时的标准Docker。
编排用Deployment和Service,服务注册发现用Service。
例如相互依赖的四个服务,全部部署在容器里面,分别运行在不同的机器上面。服务之间的调用通过服务名称进行,而非固定IP进行,而服务名称Kubernetes会用Service管理起来。
当一台服务器宕机的时候,服务B和服务C会被自动调度到另外两台机器上,这时候服务A和服务D如何再找到服务B和服务C呢,这两个服务的物理主机变了,很可能IP也变了。其实是没有问题的,Kubernates会自动将服务名和对应的IP地址关联起来,服务之间只要配置的是服务名,而非IP地址,就依然能够相互访问。
弹性是通过Deployment,HPA实现的。通过改一个副本数,就能够实现程序的横向扩展,程序运行的环境全部标准的封装在Docker镜像里面了,外部依赖全部在Deployment的编排文本里面定义好了,只需要改数字就可以了。
在集中管理方面,Kubernetes自带配置中心,注册发现,对于日志中心,监控中心都非常容易集成。
可以说除了服务治理Kubernetes稍有欠缺外,其他能覆盖云原生的方方面面。
Kubernetes还有一个亮点,是他是基于声明式API的,这和传统的运维模式有所区别。传统的运维模式是面向动作的,比如说需求是启动三个应用,那面向动作的运维就是找三个地方,把三个应用启动起来,检查启动没有问题,然后完事儿。稍微自动化一点的,就是写个脚本做这个事情。这种模式的问题是一旦三个应用因为某种原因挂了一个,除非人工检查一遍,要不就没人知道这件事情。而声明式API是面向期望状态的,客户提交的期望状态,kubernetes会保存这种期望状态,并且会自动检查当前状态是否和期望状态一致,如果不一致,则自动进行修复。这在大规模微服务运维的时候更加友好,因为几万个程序,无论靠人还是靠脚本,都很难维护,必须有这么一个Kubernetes平台,才能解放运维,让运维只需要关心期望状态。
比如有个程序,状态一定要是三个副本,每个副本都能在1s内返回,共能承载每秒N笔的QPS,如果用传统的运维模式,极有可能出现看起来是三个副本,其实有两个已经不能响应请求了,或者能响应要10s才能返回,这样虽然表面看起来这个程序是处于高可用的状态,其实非常危险,剩余的那个节点一挂,可能就都挂了,或者突然来了客户流量,另外两个节点根本扛不住几个客户请求。如果用Kubernetes,配合里面的副本数,健康检查,服务发现功能,如果有两个不能响应或者响应过慢,自动就会销毁重新创建,直到达到标准,并且加入服务,共同承载流量。这样只要给Kubernetes的编排文件写的好,通过接口看到的状态就可以默认为是真实的状态,这样哪怕有一万个服务,运维起来也很简单。
设计如此好的一套云原生标准,很快在各大企业实践开来,形成如下的样子。
四、第三阶段:跨语言服务治理的标准形成期
前面讲Netflix的时候,我们说过业务层的拆分对于运维层的敏捷性带来了巨大的压力,从而Netflix基于AWS实现了基础设施的敏捷性。事务的作用是相互的,基础设施层因Kubernetes使得敏捷性,可迁移性,可运维性更加容易的时候,又会对业务层的拆分有进一步的促进作用。这就像咱们的电脑和手机,随着装的应用越来越多,需要的资源越来越多,会对电脑和手机的配置造成压力,逼着电脑和手机有更强的CPU,内存,硬盘。可是一旦电脑和手机的硬件升级了之后,我们又会装更多的应用,如此反复。
但是当服务数目多了,不仅仅对于基础设施层有压力,对于自己如何管理也有很大的压力,虽然一个大服务拆分成了微服务,不会整个挂掉,但是服务之间是相互关联,相互影响的,就如同Netflix那张图一样。虽然通过注册发现等服务管理机制,可以看到服务之间的关系,但是服务之间的影响却很难评估,因而为了《Outcomes》里面的反脆弱,需要防止以下的几件事情发生:
- 服务雪崩:一个服务挂了,拖累其他服务挂掉一片
- 请求堆积:一个服务慢,拖累整个链路慢一整条链
- 性能瓶颈:找不到整个服务的性能的瓶颈点
4.1. 服务治理反脆弱的五大场景
要解决这些问题,需要服务治理机制,就像我们前面打过的比方,就是交通治理。现在我们仔细解析一下如何治理。
我们把复杂的服务之间的关系简化成为一个三层调用的关系,如下图。每个进程会有三个副本,每个进程会多个线程,而业务逻辑就是在某个线程里面处理的。比如进程A里面的业务逻辑是下单,如果两个人同时进行下单,处理这两个人的请求可能会被分配到两个进程副本里面的两个线程,也可能会被分配到一个进程里面两个线程。A业务的请求处理,需要B,C,D三个上游的业务都完成A才能完成,如果A是下单,B可能是商品,C可能是库存,D可能是优惠券,这些落地的处理也会被分配到某个B,C,D的某个副本里面的某个线程。B也是有上游的,需要E和F都完成B才能完成,假设E是商品的价格,F是商品的规格。
接下来我们来看如果进行服务治理反脆弱。
第一种场景称为容错。如下图所示,这里面进程F的第2个副本里面的某个线程已经处理了1分钟没有返回了,这在服务调用已经是很长时间了,肯定是某个地方卡住了。但是这里也会带来连锁效应,F不返回,则B进程的某个线程会一直等着他,也会被卡住,同理A进程的某个线程也会被卡住。而且一个系统里面的总线程数是有限的,卡住一个就少一个,最后用户请求都进不来了。
这有点像咱们交通堵塞,一个路口出问题被堵住,如果不及时处理,那上一个路口的车都涌过来也会被堵住,从而一堵就很长的一条路。但是车道和路就这么几条,那最后谁都别走了。那怎么办呢?当然是及时处理,先通畅了再说。
这里采取的治理策略是快速失败(Failfast)及失败转移(Failover),也即每个服务的调用都设置超时时间,比如1s,超过了就不再等了。如图中F的某个副本不知道为什么卡住了,不管,超过一定时间就重试调用另一个副本,这样至少B里面的线程不会卡住,A里面的也不会卡住,这样故障点就只有进程F的某一个副本,脆弱性就控制在了很小的范围内了。
第二种场景是熔断和降级。情况比上面要更加严重一些,这次可不是一个F副本出问题了,而是所有的F都出问题了,比如所有的F都1分钟返回,甚至整个F都挂了,B肯定会收到一个调用F的错误,这可咋办?如果处理的不好,如果B错了就挂了,那接下来A也会挂掉,于是整个链条都挂了。
这里我们做另外一个比喻,比如B是一家小超市,超市的老板每天早上从菜市场E那里采购牛肉,从菜市场F那里采购牛杂,A是一家小牛肉面馆,每天早上从B这里采购牛肉牛杂,C这里采购面,D这里采购一些凉菜。如果有一天菜市场F里面所有卖牛杂的都起晚了,或者都倒闭了,超市应该关门吗?如果A发现有一天采购不到牛杂了,牛肉面馆应该关门吗?当然不应该了。
这里采取的治理策略是熔断和降级。所谓的熔断,就是发现所有的F都有问题了,那就先不调用F了,但是B也不要挂掉,而是在有缺陷的情况下运行,等待F被修复,这样故障点就被控制住了,只要F这一个服务被修复好了,整条链路就都好了,如果挂一条链路,那还得修复了F,再修B,修完了B再修A,那时间就长了,脆弱性就强了。
那这个时候B没有F的返回数据如何运行下去呢?这就是降级,常常采取的手段是返回一个预先设置好的值,或者从缓存里面拿一个不那么实时的值。比如想获取库存数目,真实的库存只有4件,但是发现库存服务不响应了,无法获取这个值,则可以返回一个“有库存”作为默认值,并且设置一个最大可以卖出去的值比如10,那最坏的情况就是多卖出去几件,如果商业模式允许,也没啥问题。比如想获得商品规格,可以定时刷新一部分到缓存里面,如果商品服务不响应了,那就去缓存里面拿,可能实时性有问题,但是如果刷新不频繁,也可以接受。
可能你会问,是不是所有的业务场景都能熔断和降级呢?当然不是了,如果要支付,但是无法获得准确的金额,那肯定不行。如果要下单,使用优惠券,发现获取不了优惠券的扣减信息,也是不行的。因而我们把服务之间的依赖分为强依赖和弱依赖,牛肉面馆没有牛肉和面,今天生意就做不下去,这是强依赖,不可以熔断和降级,但是如果牛肉面馆没有凉菜,生意还是可以做下去的,这是弱依赖。这就需要我们能够分析出强弱依赖,其实服务之间的依赖关系看起来纷繁复杂,但是真实一个业务中,完全强依赖的整条链路还是其中很少的一部分,这部分我们长成为核心交易链路,或者核心调用链路,这条链路是一个环节出问题整个业务就运行不下去的,是整个团队要重点关注的部分,好在这部分非常少,比如电商里面,只有下单,支付的链路是比较核心的,哪怕浏览商品这些,都可以降级。
第三种场景是限流。任何一个系统无论扩容到如何规模,能够承载的最大流量是有限度的,每一个模块也是一样的,这个时候如何再来更多的用户请求,很可能让整个系统全部挂掉,最后谁也用不成,因而经常使用的手段就是限流。其实就像故宫每天的游客限流类似,如果人太多,不但破坏文物,而且光看人了,谁也玩不好。
第四种场景是路由分流与灰度。
除了上面出现异常情况导致系统脆弱,其实变更才是最脆弱的。因而每个服务上线之前,都要经过灰度,如下图,比如某个功能需要A服务开发一个新功能,C服务开发一个新功能,会形成一个灰度环境,可是这里的新功能是否能够保证完全正确呢?当然不能,即便经过严格的测试也不能,因为测试环境无法模拟生产环境的所有情况,所以要有分流和灰度措施,比如切分1%的流量到灰度环境,如果有问题,也只影响1%的用户,而且可以瞬间切回来。
第五种场景是全链路性能监控。
一旦出现了性能问题,其实像交通治理一样,我们希望能够看到到底哪条链路出现了问题,以及在这条链路的哪个环节出现问题。
看这个服务之间的调用图,里面可以像看地图一样,看到红色的链路表示出问题了。这个图和Netflix那个图不一样,那个图是通过注册发现画出来了,仅仅表示关系,但是服务之间的调用性能没有健康,而全链路监控需要另外一个APM的系统获得。
全链路监控的思想最初来自于Google的论文提到的Dapper,他会将服务之间调用的性能数据通过tracing日志的形式集中保存起来分析,从而可以集中展现和定位分布式系统里面的问题和性能瓶颈。
例如对于某个慢的请求,我们可以通过全链路监控看到每层调用所花费的时间,如下图。
4.2. 当前服务治理的问题与Service Mesh的诞生
通过这五个场景,我们会发现,治理的场景还是很复杂的,而且目前业内也没有一定的标准。下图是目前比较主流的集中治理方式的选型,而且都是针对Java语言比较友好些。
当前的方案有几个问题:
- 和应用是绑在一起的,运行在一个进程里面,无法独立升级和维护
- 支持Java比较友好,跨语言比较复杂
怎么解决这些问题呢?一个普遍的思路是将服务治理功能独立成为一个进程来实现,每个应用都会有一个服务治理进程陪着他,拦截应用发出和接收的所有流量,经过治理策略后再转发给自己或者其他服务。还记得小时候看鬼子进村的时候开的这种摩托车么?主驾驶员旁边会带着一个人,这个位置叫边车sidecar。这个独立做服务治理的进程因为总是在主进程旁边拦截流量,也被称为sidecar。
除了sidecar之外,服务治理的策略也需要一个地方统一管理,也即需要一个控制面,两者加起来,称为Service Mesh。
2017年,Google,IBM,Lyft联手发布Istio,逐渐成为Service Mesh服务治理标准。
下图是istio的架构图。
在Sidecar里面,分流灰度,熔断降级,限流等都可以做,而且还可以对接统一监控Monitoring和全链路监控Tracing。
目前istio已经在很多企业落地,但是说已经成为通用标准,为时尚早。
4.3. 服务治理反脆弱阶段的工作模式
一旦服务数目多到需要治理的阶段,其实工作模式也需要相应的变化。前面讲到超时时间,可是每个服务都不一样,有的服务可能本来时间就会很长,有可能是集成了外部系统,连优化都没办法,这怎么办呢?比如熔断降级里面,哪些是强依赖,哪些是弱依赖,返回什么样的默认数据,如何缓存,这些都是和业务相关的,不同的服务也不一样。再比如限流里面,每个服务能够承载的吞吐量也是不一样的,有的服务比较边角,也没必要优化到性能吞吐量过于好。
这个时候你发现,和传统的应用中,一个技术总监或者运维总监掌控全局的时代过去了,已经没有一个人可以掌控系统的全部了,对于服务的治理需要从集权到民主,是不是理解Netflix的说法了,我们回到《Outcomes》那一页,要信任员工和团队。每个服务都会有一个小团队进行维护,一方面满足快速迭代,一方面保障质量,服务的Owner像牛肉面馆老板一样思考,从上游服务进货,为下游提供服务。每个层次的服务根据依赖的服务和中间件的约定来预估自己对于下游客户的SLA约定,包括高可用性,QPS(每秒查询数),TPS(每秒交易数),MRT(平均返回时间)。每个服务梳理强弱依赖关系,并采取相应的策略。
比如上游E服务约定的QPS和TPS的值应该默认认为他能达到,如果不满足,E服务会自己扩容,和你无关,但是如果你超过了约定的值,E服务是有权力自己进行限流的,你要根据你依赖的上游算一个自己的QPS和TPS,并且对于超过的部分也进行限流。
对于上游E服务约定的MRT,是你设定调用他超时时间的参考,如果真超时了,就说明E出问题了。如果E没有超过约定的MRT值,但是你仍然感觉太慢了,无法满足你对上游的MRT承诺,这个时候,你就需要通过异步调用或者消息队列的方式,让这个慢的部分异步的完成。
对于弱依赖,你要想好如果挂了怎么办,太慢了怎么办各种场景。对于强依赖,仍然要考虑后面挂了或者慢了,虽然不能正确返回了,但是如何不被弄挂。
当然对于核心调用链路上的每个服务,高可用,QPS, TPS, MRT都是会严格要求的,这才是技术总监或者总架构师应该重点关注的地方。
经过这个步骤,一个企业的架构如下图所示,业务部门只要关注业务本身就可以了。
五、第四阶段:大量业务使用云原生的规模落地期
在企业开始大规模落地云原生的时候,还是会遇到很多问题的,我专门写了一篇文章阐述这些问题:一篇文章搞定大规模容器平台生产落地十大实践
第一个问题是如何兼容原来的传统运维发布模式。大部分企业都有自己历史包袱,哪家公司也不可能上来就完全按照云原生的标准操作来,因为在公司内部,业务方是甲方而容器方是乙方,得根据业务的节奏来。于是业内涌现出很多的富容器的概念,就是经过容器层的修改满足固定IP,原地升级,可SSH登陆等功能。
支持Pod 绑定静态 IP ,基于K8s的自定义控制器—Enhanced Statefulset
第二个问题是容器的隔离性的强化。前面讲过容器是根据namespace和cgroup进行隔离的,但是对于习惯了虚拟机隔离的应用来讲,这两种隔离显然不够,需要加强。
第三个问题是当K8S节点数目超过一定数量后,性能会有瓶颈,这个时候如何进行优化呢?这个时候有两个选择,一个是直接修改K8S源代码支持超大集群,一个是采取多集群的K8S。双方各有各的优缺点,前者优点是可以一个集群容纳上万个节点,维护集群数目少,缺点是可能对于K8S的核心代码改动比较多,将来K8S版本升级复杂。后者的优点是可以在不改K8S核心代码的情况下做参数调优,升级容易,缺点是调优后规模5000节点,如果规模大,需要维护更多的集群。
如何用 Kubernetes 管理超过 2500 个节点的集群
[译]将 Kubernetes 扩展至7500个节点
PPT | 腾讯云多Kubernetes的多维度监控实践
第四个问题是K8S网络如何选型的问题。有以下几种方式,一是使用开源组件Calico等,二是对于性能要求比较高的会使用硬件SDN,三是如果自己有云网络VPC团队的,或者基于云上容器平台的,会使用虚拟网络,四是直接使用host网络,性能好,只是端口需要管理,需要比较好的应用层适配及注册发现机制,五是通过桥接方式使用扁平网络,这种方法相对比较简单。
第五个问题是有状态存储。大部分采取两种方式,一是如果有存储团队的,则会构建统一对象存储或者文件存储,二是使用本地存储,简单,就是管理麻烦一些,三是使用商业化存储,这在传统行业比较多一些。
第六个问题是镜像如何下发的问题。现在Harbor镜像仓库成为主流,互联网公司因为下载压力比较大,会使用另一种P2P的镜像下发模式。
腾讯WeMake工业互联网平台的边缘容器化实践:打造更高效的工业互联网
ImageApparate(幻影)镜像加速服务让镜像分发效率提升 5-10 倍
第七个问题是K8S的版本迭代比较快,如何平滑升级的问题。以及在运维的过程中,etcd如何备份恢复。
第八个问题是集群大了或者集群数目增多,如何构建多集群统一监控,巡检,审计,保证集群的稳定性。
如何用Prometheus监控十万container的Kubernetes集群
如何使用 K8s 两大利器"审计"和"事件"帮你摆脱运维困境?
第九个问题是Istio Service Mesh的性能优化问题。
OCTO 2.0:美团基于Service Mesh的服务治理系统详解
抖音春晚活动背后的 Service Mesh 流量治理技术
六、第五阶段:全面发挥云原生价值的成熟深耕期
别看现在Kubernetes已经在很多企业大规模落地了,其实还存在着非常多的问题,就像前面说的,容器落地的时候,为了适配原有应用的部署模式,不得不做一些让步,这就使得原来的落地表现出以下特点,从而不能发挥云原生降本增效的功能:
- 容器以虚拟机的模式被使用,申请规格大,利用率低
- 业务虽有波峰波谷,但是申请容器的人员往往按波峰申请资源,没有利用好弹性能力
- 较多使用本地盘,大内存的有状态应用,自愈,迁移,可调度能力比较差
前面主要讲使用容器之后,如何增效,也即加快业务的迭代速度,当一个平台进入成熟期后,企业开始慢慢关注成本了,如果容器平台深耕下去,也是有很多可以提供利用率的地方。
kubernetes 降本增效标准指南| 容器化计算资源利用率现象剖析
经过调研,我发现资源的利用率呈现以下模型
第一阶段:传统部署模型, 业务为应对不同时间段计算资源使用不同的情况,必须以最高使用资源的峰值加一定的 buff 进行基础设施的采购,平均利用率降低。
第二阶段:简单容器化改造后的业务,上云并容器化改造,利用了容器进行业务混合部署,一定程度提高了资源利用率。
第三阶段:业务进行微服务改造,业务可利用容器和云的弹性伸缩能力,结合 Kubernetes 的HPA、VPA、ClusterAutoScaling 等能力,高峰扩容、空闲缩容,极大提高资源利用率。
第四阶段:极致利用云和容器化后的弹性, 提高弹性伸缩灵敏度和精度, 有离线业务的进行在离线混布,极致提高平均资源利用率。
这里一方面需要业务层的配合,另一方面底层的能力要具备。
对于一些处于稳定状态的业务,成本的压力会更大一些,这些业务可以试图从资源利用率方面提出要求,从而降低成本。比如整个部门可以进行云原生成熟度的评分,如果使用比较大的规格,不是不允许,但是要减分,如果使用了弹性,弹型的约敏锐,则可以加分等待。
对于底层需要提供一些工具,例如增加弹性的敏感度和准确度的问题,以及通过更好的隔离手段,使得在离线业务可以混合部署,进一步增强利用率。
kubernetes 降本增效标准指南| 资源利用率提升工具大全
Kubernetes 降本增效标准指南 | 基于K8s 扩展机制构建云上成本控制系统
kubernetes 降本增效标准指南|ProphetPilot:容器智能成本管理引擎
今日Qcon热门分享|腾讯K8s大规模离在线混部与内核隔离实践
Kubernetes还提供了多云多地域的部署能力,可以大大提供可用性。这也需要业务层可以进行适配,进行Set化,也即将业务按用户或者地域进行拆分到不同的数据中心,并在数据中心之间进行数据同步,既能实现就近访问,也可以实现容灾,任何一个机房挂掉,都可以将用户切换到其他机房或者城市,这样才能很好的利用多地多中心的资源。
目前各家互联网公司都在深耕中......
欢迎关注个人公众号