谈谈service mesh:微服务架构的基础设施服务
另一方面,当应用被拆分为多个微服务程序后,程序内的方法呼叫变成了了程序间的远端呼叫。引入了对大量服务的连线、管理和监控的复杂性。
该变化带来了分散式系统的一系列问题,例如:
如何找到服务的提供方?如何保证远端方法呼叫的可靠性?如何保证服务呼叫的安全性?如何降低服务呼叫的延迟?如何进行端到端的除错?另外生产部署中的微服务例项也增加了运维的难度,例如:
如何收集大量微服务的效能指标已进行分析?如何在不影响上线业务的情况下对微服务进行升级?如何测试一个微服务丛集部署的容错和稳定性?这些问题涉及到成百上千个服务的通讯、管理、部署、版本、安全、故障转移、策略执行、遥测和监控等,要解决这些微服务架构引入的问题并非易事。
让我们来回顾一下微服务架构的发展过程。在出现服务网格之前,我们最开始在微服务应用程序内理服务之间的通讯逻辑,包括服务发现、熔断、重试、超时、加密、限流等逻辑。
在一个分散式系统中,这部分逻辑比较复杂,为了为微服务应用提供一个稳定、可靠的基础设施层,避免大家重复造轮子,并减少犯错的可能,一般会通过对这部分负责服务通讯的逻辑进行抽象和归纳,形成一个程式码库供各个微服务应用程序使用,如下图所示:
公共的程式码库减少了应用程序的开发和维护工作量,降低了由应用开发人员单独实现微服务通讯逻辑出现错误的概率,但还是存在下述问题:
微服务通讯逻辑对应用开发人员并不透明,应用开发人员需要理解并正确使用程式码库,不能将其全部精力聚焦于业务逻辑。
需要针对不同的语言/框架开发不同的程式码库,反过来会影响微服务应用开发语言 和框架的选择,影响技术选择的灵活性。
随着时间的变化,程式码库会存在不同的版本,不同版本程式码库的相容性和大量执行环境中微服务的升级将成为一个难题。
可以将微服务之间的通讯基础设施层和tcp/ip协议栈进行类比。tcp/ip协议栈为操作系统中的所有应用提供基础通讯服务,但tcp/ip协议栈和应用程序之间并没有紧密的耦合关系,应用只需要使用tcp/ip协议提供的底层通讯功能,并不关心tcp/ip协议的实现,如ip如何进行路由,tcp如何建立连结等。
同样地,微服务应用也不应该需要关注服务发现,load balancing、retries、circuit breaker等微服务之间通讯的底层细节。如果将为微服务提供通讯服务的这部分逻辑从应用程序程序中抽取出来,作为一个单独的程序进行部署,并将其作为服务间的通讯代理,可以得到如下图所示的架构:
因为通讯代理程序伴随应用程序一起部署,因此形象地把这种部署方式称为“sidecar”/边车(即三轮摩托的挎斗)。
应用间的所有流量都需要经过代理,由于代理以sidecar方式和应用部署在同一台主机上,应用和代理之间的通讯可以被认为是可靠的。由代理来负责找到目的服务并负责通讯的可靠性和安全等问题。
当服务大量部署时,随着服务部署的sidecar代理之间的连线形成了一个如下图所示的网格,该网格成为了微服务的通讯基础设施层,承载了微服务之间的所有流量,被称之为service mesh(服务网格)。
服务网格是一个基础设施层,用于处理服务间通讯。云原生应用有着复杂的服务拓扑,服务网格保证请求可以在这些拓扑中可靠地穿梭。在实际应用当中,服务网格通常是由一系列轻量级的组成的,它们与应用程序部署在一起,但应用程序不需要知道它们的存在。
服务网格中有数量众多的sidecar代理,如果对每个代理分别进行设定,工作量将非常巨大。为了更方便地对服务网格中的代理进行统一集中控制,在服务网格上增加了控制面元件。
这里我们可以类比sdn的概念,控制面就类似于sdn网管中的,负责路由策略的指定和路由规则下发;资料面类似于sdn网络中交换机,负责资料包的转发。
由于微服务的所有通讯都由服务网格基础设施层提供,通过控制面板和资料面板的配合,可以对这些通讯进行监控、托管和控制,以实现微服务灰度释出,呼叫分散式追踪,故障注入模拟测试,动态路由规则,微服务闭环控制等管控功能。
istio是一个service mesh开源专案,是google继kubernetes之后的又一力作,主要参与的公司包括google,ibm和lyft。凭借kubernetes良好的架构设计及其强大的扩充套件性,google围绕kubernetes打造一个生态系统。kubernetes用于微服务的编排(编排是英文orchestration的直译,用大白话说就是描述一组微服务之间的关联关系,并负责微服务的部署、终止、升级、缩扩容等)。其向下用cni(容器网络界面),cri(容器执行时界面)标准界面可以对接不同的网络和容器执行时实现,提供微服务执行的基础设施。向上则用istio提供了微服务治理功能。
由下图可见,istio补充了kubernetes生态圈的重要一环,是google的微服务版图里一个里程碑式的扩张。
google借istio的力量推动微服务治理的事实标准,对google自身的产品google cloud有极其重大的意义。其他的云服务厂商,如redhat、pivotal、nginx、buoyant等看到大势所趋,也纷纷跟进,宣布自身产品和istio进行整合,以避免自己被落下,丢失其中的市场机会。
可以预见不久的将来,对于云原生应用而言,采用kubernetes进行服务部署和丛集管理,采用istio处理服务通讯和治理,将成为微服务应用的标准配置。
istio服务包括网格由资料面和控制面两部分。
资料面由一组智慧代理(envoy)组成,代理部署为边车,调解和控制微服务之间所有的网络通讯。
控制面负责管理和配置代理来路由流量,以及在执行时执行策略。
pilot
pilot维护了网格中的服务的标准模型,这个标准模型是独立于各种底层平台的。pilot通过界面卡和各底层平台对接,以填充此标准模型。
例如pilot中的kubernetes界面过kubernetes api服务器得到kubernetes中pod注册资讯的更改,入口资源以及储存流量管理规则等资讯,然后将该资料被翻译为标准模型提供给pilot使用。通过界面卡模式,pilot还可以从mesos、cloud foundry、consul中获取服务资讯,也可以开发界面卡将其他提供服务发现的元件整合到pilot中。
除此以外,pilo还定义了一套和资料面通讯的标准api,api提供的界面内容包括服务发现 、负载均衡池和路由表的动态更新。通过该标准api将控制面和资料面进行了解耦,简化了设计并提升了跨平台的可移植性。基于该标准api已经实现了多种sidecar代理和istio的整合,除istio目前整合的envoy外,还可以和linkerd、nginmesh等第三方通讯代理进行整合,也可以基于该api自己编写sidecar实现。
pilot还定义了一套dsl(domain specific language)语言,dsl语言提供了面向业务的高层抽象,可以被运维人员理解和使用。运维人员使用该dsl定义流量规则并下发到pilot,这些规则被pilot翻译成资料面的配置,再通过标准api分发到envoy例项,可以在执行期对微服务的流量进行控制和调整。
mixer
在微服务应用中,通常需要部署一些基础的后端公共服务以用于支撑业务功能。这些基础设施包括策略类如访问控制,配额管理;以及遥测报告如apm、日志等。微服务应用和这些后端支撑系统之间一般是直接整合的,这导致了应用和基础设定之间的紧密耦合,如果因为运维原因需要对基础设定进行升级或者改动,则需要修改各个微服务的应用程序码,反之亦然。
为了解决该问题,mixer在应用程序程式码和基础架构后端之间引入了一个通用中间层。该中间层解耦了应用和后端基础设施,应用程序程式码不再将应用程序程式码与特定后端整合在一起,而是与mixer进行相当简单的整合,然后mixer负责与后端系统连线。
mixer主要提供了三个核心功能:
前提条件检查。允许服务在响应来自服务消费者的传入请求之前验证一些前提条件。前提条件可以包括服务使用者是否被正确认证,是否在服务的白名单上,是否通过acl检查等等。
配额管理。 使服务能够在分配和释放多个维度上的配额,配额这一简单的资源管理工具可以在服务消费者对有限资源发生争用时,提供相对公平的(竞争手段)。rate limiting就是配额的一个例子。
遥测报告。使服务能够上报日志和监控。在未来,它还将启用针对服务运营商以及服务消费者的跟踪和计费流。
mixer的架构如图所示:
首先,sidecar会从每一次请求中收集相关资讯,如请求的路径,时间,源ip,目地服务,tracing头,日志等,并请这些属性上报给mixer。mixer和后端服务之间是通过界面卡进行连线的,mixer将sidecar上报的内容通过界面卡传送给后端服务。
由于sidecar只和mixer进行对接,和后端服务之间并没有耦合,因此使用mixer界面卡机制可以接入不同的后端服务,而不需要修改应用的程式码,例如通过不同的mixer界面卡,可以把metrics收集到prometheus或者influxdb,甚至可以在不停止应用服务的情况下动态切换后台服务。
其次,sidecar在进行每次请求处理时会通过mixer进行策略判断,并根据mixer返回的结果决定是否继续处理该次呼叫。通过该方式,mixer将策略决策移出应用层,使运维人员可以在执行期对策略进行配置,动态控制应用的行为,提高了策略控制的灵活性。例如可以配置每个微服务应用的访问白名单,不同客户端的rate limiting,等等。
逻辑上微服务之间的每一次请求呼叫都会经过两次mixer的处理:呼叫前进行策略判断,呼叫后进行遥测资料收集。istio采用了一些机制来避免mixer的处理影响envoy的转发效率。
从上图可以看到,istio在envoy中增加了一个mixer filter,该filter和控制面的mixer元件进行通讯,完成策略控制和遥测资料收集功能。mixer filter中储存有策略判断所需的资料快取,因此大部分策略判断在envoy中就处理了,不需要传送请求到mixer。另外envoy收集到的遥测资料会先储存在envoy的快取中,每隔一段时间再通过批量的方式上报到mixer。
auth
istio支援双向ssl认证(mutual ssl authentication)和基于角色的访问控制(rbac),以提供端到端的安全ku网页版登录的解决方案。
认证
istio提供了一个内部的ca(证书机构),该ca为每个服务颁发证书,提供服务间访问的双向ssl身份认证,并进行通讯加密,其架构如下图所示:
其工作机制如下:
部署时:
cakubernetes api server,为丛集中的每一个service account建立一对金钥和证书,并发送给kubernetes api server。注意这里不是为每个服务生成一个证书,而是为每个service account生成一个证书。service account和kubernetes中部署的服务可以是一对多的关系。service account被储存在证书的san(subject alternative name)字段中。
当pod建立时,kubernetes根据该pod关联的service account将金钥和证书以kubernetes secrets资源的方式载入为pod的volume,以供envoy使用。
pilot生成资料面的配置,包括envoy需使用的金钥和证书资讯,以及哪个service account可以允许执行哪些服务,下发到envoy。
备注:如果是虚机环境,则采用一个node agent生成金钥,向istio ca申请证书,然后将证书传递给envoy。
执行时:
服务客户端的出站请求被envoy接管。
客户端的envoy和服务端的envoy开始双向ssl握手。在握手阶段,客户端envoy会验证服务端envoy证书中的service account有没有许可权执行该请求的服务,如没有许可权,则认为服务端不可信,不能建立连结。
当加密tsl连结建立好后,请求资料被发送到服务端的envoy,然后被envoy通过一个本地的tcp连结传送到服务中。
鉴权
istio“基于角色的访问控制”(rbac)提供了名称空间、服务、方法三个不同大小粒度的服务访问许可权控制。其架构如下图所示:
管理人员可以定制访问控制的安全策略,这些安全策略储存在istio config store中。 istio rbac engine从config store中获取安全策略,根据安全策略对客户端发起的请求进行判断,并返回鉴权结果(允许或者禁止)。
istio rbac engine目前被实现为一个mixer adapter,因此其可以从mixer传递过来的上下文中获取到访问请求者的身份(subject)和操作请求(action),并通过mixer对访问请求进行策略控制,允许或者禁止某一次请求。
istio policy中包含两个基本概念:
servicerole,定义一个角色,并为该角色指定对网格中服务的访问许可权。指定角色访问许可权时可以在名称空间,服务,方法的不同粒度进行设定。
servicerolebinding,将角色系结到一个subject,可以是一个使用者,一组使用者或者一个服务。
istio资料面
istio资料面以“边车”(sidecar)的方式和微服务一起部署,为微服务提供安全、快速、可靠的服务间通讯。由于istio的控制面和资料面以标准界面进行互动,因此资料可以有多种实现,istio预设使用了envoy代理的扩充套件版本。
envoy是以c 开发的高效能代理,用于调解服务网格中所有服务的所有入站和出站流量。envoy的许多内建功能被istio发扬光大,例如动态服务发现、负载均衡、tls加密、http/2 & grpc代理、熔断器、路由规则、故障注入和遥测等。
istio资料面支援的特性如下:
备注:outbound特性是指服务请求侧的sidecar提供的功能特性,而inbound特性是指服务提供侧sidecar提供的功能特性。一些特性如遥测和分散式跟踪需要两侧的sidecar都提供支援;而另一些特性则只需要在一侧提供,例如鉴权只需要在服务提供侧提供,重试只需要在请求侧提供。
1.分散式呼叫追踪
在微服务架构中,业务的呼叫链非常复杂,一个来自使用者的请求可能涉及到几十个服务的协同处理。因此需要一个跟踪系统来记录和分析同一次请求在整个呼叫链上的相关事件,从而帮助研发和运维人员分析系统瓶颈,快速定位异常和优化呼叫链路。
istio通过在envoy代理上收集呼叫相关资料,实现了对应用无侵入的分散式呼叫跟踪分析。 istio实现分散式呼叫追踪的原理如下图所示:
envoy收集一个端到端呼叫中的各个分段的资料,并将这些呼叫追踪资讯传送给mixer,mixer adapter将追踪资讯传送给相应的服务后端进行处理。整个呼叫追踪资讯的生成流程不需要应用程序介入,因此不需要将分散式跟踪相关程式码注入到应用程序中。
注意:应用仍需要在进行出口呼叫时将收到的入口请求中tracing相关的header转发出去,传递给呼叫链中下一个边车进行处理。
2.度量收集
istio 实现度量收集的原理如下图所示:
envoy收集指标相关的原始资料,如请求的服务、http状态码、呼叫时延等,这些收集到的指标资料被送到mixer,通过mixer adapters将指标资讯转换后传送到后端的监控系统中。由于mixer使用了外挂机制,后端监控系统可以根据需要在执行期进行动态切换。
如果你们想提升自己的技术,想学习以上的技术要点,你们可以加群获取,在此我向大家推荐一个交流学习群:575745314 里面会分享一些资深架构师录制的视讯录影:有spring,mybatis,netty源代码分析,高并发、高效能、分散式、微服务架构的原理,jvm效能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多。
3.灰度释出
当应用上线以后,运维面临的一大挑战是如何能够在不影响已上线业务的情况下进行升级。无论进行了多么完善的测试,都无法保证线下测试时发现所有潜在故障。在无法百分百避免版本升级故障的情况下,需要通过一种方式进行可控的版本释出,把故障影响控制在可以接受的范围内,并可以快速回退。
可以通过灰度释出(又名金丝雀释出)来实现业务从老版本到新版本的平滑过渡,并避免升级过程中出现的问题对使用者造成的影响。
istio通过高度的抽象和良好的设计采用一致的方式实现了灰度释出。在释出新版本后,运维人员可以通过定制路由规则将特定的流量(如具有指定特征的测试使用者)汇入新版本服务中以进行测试。通过渐进受控地向新版本汇入生产流量,可以最小化升级中出现的故障对使用者的影响。
采用istio进行灰度释出的流程如下图所示:
首先,通过部署新版本的服务,并将通过路由规则将金丝雀使用者的流量汇入到新版本服务中。
测试稳定后,使用路由规则将生产流量逐渐汇入到新版本系统中,如按5%、10%、50%、80%逐渐汇入。
如果新版本工作正常,则最后将所有流量汇入到新版本服务中,并将老版本服务下线;如中间出现问题,则可以将流量重新导回老版本,在新版本中修复故障后采用该流程重新发布。
4.断路器
在微服务架构中,存在着许许多多的服务单元,若一个服务出现故障,就会因依赖关系形成故障蔓延,最终导致整个系统的瘫痪,这样的架构相较传统架构就更加的不稳定。为了解决这样的问题,因此产生了断路器模式。
断路器模式指,在某个服务发生故障时,断路器的故障监控向呼叫放返回一个及时的错误响应,而不是长时间的等待。这样就不会使得呼叫执行绪因呼叫故障被长时间占用,从而避免了故障在整个系统中的蔓延。
istio的断路器还支援配置最大连结数,最大待处理请求数,最大请求数,每连结最大请求数,重试次数等引数。当达到设定的最大请求数后,新发起的请求会被envoy直接拒绝。
5.故障注入
对于一个大型微服务应用而言,系统的健壮性非常重要。在微服务系统中存在大量的服务例项,当部分服务例项出现问题时,微服务应用需要具有较高的容错性,通过重试、断路、自愈等手段保证系统能够继续对外正常提供服务。因此在应用释出到生产系统强需要对系统进行充分的健壮性测试。
对微服务应用进行健壮性测试的一个最大的困难是如何对系统故障进行模拟。在一个部署了成百上千微服务的测试环境中,如果想通过对应用,主机或者交换机进行设定来模拟微服务之间的通讯故障是非常困难的。
istio通过服务网格承载了微服务之间的通讯流量,因此可以在网格中通过规则进行故障注入,模拟部分微服务出现故障的情况,对整个应用的健壮性进行测试。
服务网格为微服务提供了一个对应用程序透明的安全、可靠的通讯基础设施层。采用服务网格后,微服务应用开发人员可以专注于解决业务领域问题,将一些通用问题交给服务网格处理。采用服务网格后,避免了程式码库带来的依赖,可以充分发挥微服务的异构优势,开发团队可以根据业务需求和开发人员能力自由选择技术栈。istio具有良好的架构设计,提供了强大的二次开发扩充套件性和使用者定制能力。虽然istio目前还处于beta阶段,但已经获得众多知名公司和产品的支援,是一个非常具有前景的开源服务网格开源专案。
今天的分享就到这里,尽请关注,后续更精彩。