我的新课《C2C 电商系统微服务架构120天实战训练营》在公众号儒猿技术窝上线了,感兴趣的同学,可以长按扫描下方二维码了解课程详情:
课程大纲请参见文末
开篇
在云环境下,技术栈可谓是多种多样,通过不同的技术生成不同的应用。如何能将这些异构的服务或者应用有机地串联起来,成为了服务治理的重大课题,在这样的大背景下Istio架构为这样应用场景提供了服务治理的功能,Istio提供的流量治理、策略、遥测、访问安全等功能至今都被人津津乐道。
今天就围绕Istio架构的实现原理为大家介绍如下内容:
为什么选择Istio
什么是Istio
Istio架构原理
Istio服务治理功能介绍
为什么选择Istio
随着业务的复杂度提高,为了应对高并发、大流量,系统架构对服务/应用进行拆分。拆分以后的服务/应用可以进行分布式部署,来应对高并发带来的系统压力以及处理复杂的业务逻辑。
这样的做法会造成系统中存在大量独立的服务或者应用,它们分布在不同的进程、主机上面,在它们之间互相调用的时候就存在服务治理的问题。微服务就是一个典型的例子,微服务的开发和运维对程序员来说是一个挑战。
分而治之的思想使得业务本身的规模和复杂度不降反增。
在分布式系统中,网络可靠性、通信安全、网络时延、网络拓扑变化等都成了关注的焦点,同时服务注册、服务发现、负载均衡、服务间通讯、分布式调用链追踪都是要解决的问题。
为了解决这个问题,把服务治理部分抽象成公共库,让所有微服务都使用这个公共库。如图1所示,在Node 1 和 Node 2 上分别用Service 1 和Service 2两个服务,它们分别针对自己的业务逻辑都有对应的服务治理的SDK,通过这个SDK完成服务治理的服务发现、服务注册等功能。
图1
将服务治理的逻辑抽象成公共库
如果将图1中的SDK包含到开发框架中(例如:Spring Cloud),当运用这种开发框架后就拥有服务治理的能力了。SDK的模式虽然解耦了业务逻辑和服务治理,由于在一个开发框架中,因此业务逻辑需要和 服务治理的SDK一起编译,发布以后业务逻辑和服务治理的代码在一个进程中运行。这会导致业务代码和 SDK 基于同一种语言,无法兼容其他语言开发的服务。同时,在服务治理升级时,需要升级整个服务,即使业务逻辑没有改变。如果说图1的模式,SDK和业务代码在同一进程,因此需要对其进行解耦,把服务治理从业务代码中剥离出来。如图2所示,红色的部分代替了原来的SDK,其使用了Sidecar模式。在这种形态下,业务逻辑和服务治理在独立的进程下运行。
图2
Sidecar解耦业务逻辑和服务治理
Sidecar的模式使两者代码和运行无耦合。如图3所示,业务逻辑就好像绿色的方块,再其右边的蓝色方块就是Istio提供的Sidecar(边车),也就是通过这个Sidecar与网络中其他服务的Sidecar进行链接,从而实现服务之间的通信。
这样业务逻辑可以使用不同的语言进行开发,升级也相互独立,而其他的服务治理的工作,例如:服务注册、服务发现、负载均衡、通讯等都由Sidecar来完成。
图3
Istio的Sidecar模式
这里通过业务逻辑与服务治理的角度,将使用Istio之前和之后的微服务做区分,从以下三个维度进行对比。
业务逻辑与服务治理 |
||
使用Istio之前 |
使用Istio之后 |
|
运行进程 |
两者在同一进程内 |
两者在不同的进程 |
技术栈 |
两者使用相同技术栈 |
两者使用不同技术栈 |
服务升级 |
两者同时升级 |
两者分别升级 |
因此在使用Istio架构以后,会将业务逻辑与服务治理在运行进程、技术栈和服务升级三个方面进行完全解耦,通过Sidecar模式打造分布式系统的最佳实践,接下来就来看看Istio包括哪些内容。
什么是Istio
众所周知Istio是一个Service Mesh形态的,用于服务治理的开放平台。这里的服务 “治理”不仅限于“微服务”,可以推广到任何服务。只要存在服务或者应用,在它们之间存在访问,也存在对服务与应用的管理,都可以使用到 Istio。
如图4所示,在 Istio 官方介绍中,其功能包括:连接(Connect)、安全(Secure)、控制(Control)和观察(Observe)
图4
Istio 官方功能介绍
将上面四项功能总结如下:
连接:通过流量规则控制服务间的流量和调用,实现负载均衡、熔断、故障注入、重试、重定向等功能。
安全:提供认证机制、通道加密、服务访问授权等安全能力,增强访问的安全性。
控制:通过可动态插拔、可扩展的策略实现访问控制、速率限制、配额管理、服务计费等能力。
观察:获取服务运行数据和输出,提供调用链监控和日志收集能力。
在微服务时代,Kubernetes提供了服务的部署、升级、扩容等运行管理能力,但在服务治理方面,如服务的熔断、限流、动态路由、调用链追踪显得能力不足。
Istio作为服务治理的架构刚好在这一点上弥补了Kubernetes的不足,成为了Kubernetes的好搭档。
既然把Istio吹上了天,就来看看Istio 在服务访问的过程中有哪些建树吧。如图5所示,有两个Pod容器,分别存放两个不同的服务,Service A和Service B。其中Service A 由Java进行开发,而Service B由Python进行开发。
Service A通过服务发现获取Service B服务实例列表,如果Service B存在多个水平扩展(Service B集群),还需要根据负载均衡策略选择一个具体的Service B实例。
为了保证安全性,服务之间的请求和响应需要启用双向认证和通道加密。
在一段时间内,Service A在访问Service B不断出现错误,需要进行熔断处理,停止对Service B的请求动作。
针对Service B的处理能力,设置最大连接的请求数、访问超时等参数,从而对其进行服务保护。
如果有需要可以将Service A对Service B发起的请求重定向到其他服务上。
如果Service B 有新、老两个版本,在执行灰度发布的时候,将Service A请求的部分流量(20%)导入到Service B的新版本中,其他的流量(80%)导入到Service B的老版本上。随着Service B 新版本的逐步稳定,再将剩下的80% 流量导入到新版本上。
对Service A调用 Service B 的调用链进行追踪,为提升服务之间的调用效率提供数据依据。
图5
Istio 针对服务治理的功能
Istio架构原理
在上一节中介绍了什么是Istio,是针对其功能进行的描述,看上去比较抽象,这里从Istio的工作机制和架构进一步进行描述。如图4所示,Istio整个架构可以分为控制面和数据面两部分,控制面主要包括Pilot、Mixer、Galley、Citadel等组件;数据面由伴随服务部署的代理Envoy组成,Envoy针对服务完成服务治理的逻辑。这里我们按照Istio的运行机制将每个步骤标上序号,逐个介绍。序号并不表示执行的顺序,只是为了方便标注,为的是讲解功能。在数据面中的交互通过带箭头的实线表示,数据面和控制面的交互通过虚线标注。其资源是通过Kubernetes进行部署的,在Node 1 和Node 2 通过Pod容器部署了服务A和服务B,其中服务B有两个版本V1 和V2,分别部署在Node 2 的两个Pod中。通过描述服务A调用服务B不同的版本,以及外部请求访问服务A的过程给大家讲述Istio各个组件的工作流程。
图4
Istio的控制面和数据面
1.自动注入
由于Istio使用了Sidecar代理的模式,将业务逻辑和服务治理进行了解耦。因此在 Kubernetes场景下创建 Pod时,同时创建Sidecar容器。实际上是注入并创建了istio-proxy和istio-init两个容器。其中istio-proxy包含了Pilot-agent和Envoy两个进程。Envoy作为处理服务之间请求流量的进程在服务调用中起到重要的作用,因此在图4中特别标注出来。
2.服务发现
在注入Envoy以后,假设服务A调用服务B,因此需要通过Envoy向服务B发起请求。服务A如何得知服务B的访问地址能,就需要通过服务发现的方式完成。此时,Envoy 需要调用管理面组件 Pilot 的服务发现接口,获取服务B的实例列表。Pilot 直接从运行平台提取数据并将其转换成 Istio 的服务发现模型,这种服务发现的方式还支持Kubernetes、Consul等平台。
3.流量拦截
当服务A得知服务B的地址以后,就会通过服务A向Envoy发送请求流量。发出的流量成为Outbound,在服务B端会接收到这个流量成为Inbound。图4中从服务A流出的流量(Outbound)会被服务A侧的 Envoy拦截,而当流量作为流入的流量(Inbound)到达服务B时,会被服务B侧的Envoy拦截。这里拦截的目的是对流量进行控制,特别是在高并发的情况下会对某些服务进行流量的限制。
4.负载均衡
服务A作为请求的发起方,Envoy根据配置的负载均衡策略选择服务实例,并连接对应的实例地址。这些负载均衡的策略是通过Pilot以配置文件的形式下发到Envoy上实现的,具体策略如RANDOM和ROUND_ROBIN。图4中访问服务B,V2版本的时候,发现该版本有多个服务B,此时就需要使用负载均衡策略访问其中某个服务B了。
5. 流量治理
Envoy 从 Pilot 中获取配置的流量规则,在拦截到 Inbound 流量和Outbound 流量时执行治理逻辑。和流量拦截不同的是,其目的是为了访问同一服务的不同版本。服务A通过Envoy获取规则,通过规则判断将流量分发到服务B的V1或V2版本。
6. 访问安全
在服务A和服务B之间建立双向认证和通道加密,并基于服务的身份进行授权管理。同样由Pilot下发安全配置,在服务A和服务B对应的Envoy上加载证书和密钥来实现双向认证,证书和密钥由管理面的Citadel组件维护。
7. 服务遥测
在服务间通信时,通信双方的Envoy会连接管理面的Mixer组件上报访问数据。例如:监控指标、日志和调用链都可以通过这种方式进行收集。
7. 外部访问
在左下角有个“外部请求访问”,其作为这个网格之外的请求访问网格内的服务A。因此在入口处有一个Envoy扮演入口网关的角色。外部服务通过Gateway访问服务A。如果需要负载均衡以及流量治理的策略,都在这个Gateway的Envoy进行设置。
9. 管理配置
最后是管理面的galley组件。它不面向数据面提供服务,而是在控制面上向其他组件提供支持。主要负责验证控制面的配置信息格式和内容的正确性,并将配置信息提供给 Pilot和 Mixer组件使用。
Istoio服务治理功能介绍
通过上面Istio架构原理的介绍,把控制面和数据面的组件给大家过了一遍。由于篇幅问题不能在这里逐个展开介绍,由于Istio的主要功能是服务治理,这里选取几个服务治理中经常使用的功能给大家介绍,也算是窥豹一斑吧。
服务路由
服务路由在实际场景中比较常见,如图5所示Service A根据不同的路由:Test.com/ServiceB, Test.com/ServiceC, Test.com/ServiceD,分别访问Service B、C和D。
图5
服务路由
正如在“Istio架构原理”章节中提到的,Istio配置规则从Pilot发起传送到Envoy上执行。其配置文件格式基本与Kubernetes相似。具体到图5的配置文件会使用到VirtualService类型的配置。
VirtualService定义了对特定目标服务的流量规则。它在表示一个虚拟服务,功能是将满足条件的流量转发到对应的服务(一个或者多个)。
如代码段1所示,在Istio的配置文件中,按照红色数字描述如下:
在kind中定义VirtualService的类型
定义要访问的入口服务的名称,这里ServiceA作为入口服务,通过它访问后面的三个服务。
在hosts中定义主机的url地址作为路由的一部分,因为这里的地址访问按照“Test.com/ServiceB”的方式进行访问,因此定义为“Test.com”。
这里针对http请求进行路由,因此在http下面的match(匹配)中的uri中定义prefix,显然如果要访问“Test.com/ServiceB”,这里的prefix需要定义为“/ServiceB”。实际上就是具体服务路由的地址。
最后,针对uri地址制定对应的route,在destination(目标)的host中定义服务的名称:“ServiceB”。ServiceC、D的定义和B的基本一致,不再赘述。
代码段1
流量切分
上面描述了简单路由的规则设置,如果遇到需要更具访问内容进行流量切分的情况,或者需要按照比例切分流量的情况配置文件的内容就需要修改了。如图6 所示,Service A要访问Service B的三个不同版本 V1、V2、V3。当URI为”Test.com/status”和”Test.com/data”的时候会请求Service B的V2 和V3版本,导入的流量分别是20%和80%(红线标注的部分)。其他URI路由到Service B的V1 版本(绿线标注的部分)。
图6 流量切分
照旧看看配置文件的每个配置项的内容,按照红色数字描述如下:
在http-match的部分用来匹配URI,这里有两个prefix 分别是:“data”和“status”,当请求URI满足这个两个条件中的一个时进入下面的路由选择。
在路由选择route的destination中对应了ServiceB服务的,subset:V2 也就是V2版本。Weight设置是20,意思是20%的流量,流入ServiceB的V2版本。
在路由选择route的destination中对应了ServiceB服务的,subset:V3 也就是V3版本。Weight设置是80,意思是80%的流量,流入ServiceB的V3版本。
最后,如果在没有命中上述两个prefix的情况下,流量会流入ServiceB的V1版本。
代码段2
负载均衡
负载均衡是服务治理中经常遇到的功能,来看看在Istio中是如何实现的。如图7 所示,Service A会访问Service B V2版本的集群以及Service B V1版本的集群。针对同一个服务的两个不同版本的集群,需要使用两种不同的负载均衡策略,分别是ROUND_ROBIN和RANDOM。
图7
负载均衡
在看完负载均衡的需求之后再来看看如何通过配置文件实现它,在介绍配置文件之前先来介绍一下DestinationRule 的规则描述。
如果说VirtualService是一个虚拟Service,其描述的内容是“从服务流出的请求被哪个服务处理”,那么 DestinationRule 描述的是“流入的请求到达服务之后如何处理”,从字面意思理解就是目标规则,如果落到负载均衡的这个例子上来说,也就是流量到达Service B的两个版本(V1、V2)以后如何进行访问。
如代码段3所示,按照红色数字的顺序如下:
规则配置定义为DestinationRule,表示处理服务流入的请求。
这里请求流入的服务是Service B,其从在两个版本,每个版本都是以集群的方式存在的。
针对Service B 版本V2 的情况,在trafficPolicy(流量规则)的loadBalancer(负载均衡)中使用了ROUND_ROBIN 的策略。
同样,针对Service B 版本V1 的情况,在trafficPolicy(流量规则)的loadBalancer(负载均衡)中使用了RANDOM 的策略。
代码段3
上面Istio服务治理的功能介绍,主要围绕服务之间的关系展开的,通过配置文件中节点数据的调整定义服务之间的关系,获取这种方式对于开发者理解其工作原理有些抽象,为了方便理清服务之间的关系并且对其进行有效管理,Istio提供了可视化的服务网格工具-Kiali,针对服务拓扑图、全链路跟踪、指标遥测、配置校验、健康检查等功能提供可视化的界面。
这里着重介绍服务拓扑图的功能,由于篇幅的关系这里不介绍Kiali的安装,有兴趣的同学可以去Istio的官网查看。如图8 所示,登陆Kiali以后可以看到其Overview界面,其中包括网格里面所有命名空间的服务。
图8
Kiali overview 界面
如果要查看对应的服务,例如:Bookinfo,可以点击 Bookinfo 命名空间卡片,显示如图9所示的内容。这里展示了该命名空间下服务的调用情况。注意看上方红色框出的部分:Graph Type,这里可以选择服务显示的类型,也就是说通过不同形式展示服务之间的关系。目前有四种可以选择:App、Versioned App、Workload 以及 Service
图9
Bookinfo命名空间下的服务关系图
选择 App 类型会将同一应用的所有版本聚合到单点上,如图10所示,网格外部的请求通过istio-ingressgateway调用productpage服务,productpage分别会调用details服务和reviews服务。其中reviews会依次调用ratings服务和mongedb。App类型的服务关系展示,通过简洁的方式描述了服务之间的依赖(调用)关系,没有涉及到服务的具体版本。
图10
App 类型调用
Versioned App 类型会在App类型的基础上,将每个服务的版本显示出来。如图11 所示,可以看出productpage服务只有一个版本v1,但是reviews服务有v1、v2、v3三个版本,ratings有两个版本。这种方式的现实让服务调用的版本更加清晰。
图11
Versioned App 类型
Workload 类型又在Versioned App 类型的基础上,针对每个服务的workload进行了显示上的转换。如图12 所示,将每个服务的版本作为一个workload,通过圆形的方式显示,实际上每个圆形的workload在实际调用中也是一个实体。
图12
Workload 类型
最后是Service 类型,如图13所示,它为网格中的每个服务生成一个节点,但是会排除所有的应用和工作负载。
图13
Service 类型
总结
本文从服务治理作为切入点,描述了在分布式、微服务的环境下为什么需要Istio提供服务治理的功能。Istio能够将业务逻辑与服务治理的SDK进行解耦,能够支持不同技术开发的分布式服务/应用的服务治理。同时指出Istio是包含的连接(Connect)、安全(Secure)、控制(Control)和观察(Observe)等功能。以及在这些功能的支撑下,Istio的基本架构是如何工作的。在架构原理中通过九个步骤,将控制面的Pilot、Mixer、Galley和Citadel,以及数据面的Envoy的工作流程给大家梳理了一遍。最后针对使用频度较高的服务治理功能:服务路由、流量切分、负载均衡进行了展开描述。
END
征 稿
有酬投稿
愿意技术分享的朋友,欢迎投稿,每篇文章提供 800 ~ 1000 元的稿酬,投稿请扫描下方二维码,添加微信:jeversoncui
转载:https://blog.csdn.net/qq_42046105/article/details/112791356