专栏名称: 石杉的架构笔记
专注原创、用心雕琢!十余年BAT一线大厂架构经验倾囊相授
目录
相关文章推荐
南城发布  ·  明确了!可放宽至70周岁 ·  17 小时前  
南城发布  ·  明确了!可放宽至70周岁 ·  17 小时前  
逆行的狗  ·  搭建属于你自己的AI知识库 ·  昨天  
逆行的狗  ·  搭建属于你自己的AI知识库 ·  昨天  
昆明警方发布  ·  “顶流”眼中的昆明公安 ·  2 天前  
中国社会科学网  ·  常青松 ... ·  2 天前  
51好读  ›  专栏  ›  石杉的架构笔记

不懂Istio 架构原理,我被同事Diss了。

石杉的架构笔记  · 公众号  ·  · 2021-01-18 09:30

正文


我的新课 《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 的配置文件中 按照红色数字描述如下
  1. kind中定义VirtualService的类型
  2. 定义要访问的入口服务的名称,这里 ServiceA作为入口服务,通过它访问后面的三个服务。
  3. hosts中定义主机的url地址作为路由的一部分,因为这里的地址访问按照“Test.com/ServiceB”的方式进行访问,因此定义为“Test.com”。
  4. 这里针对 http请求进行路由,因此在http下面的match(匹配)中的uri中定义prefix,显然如果要访问“Test.com/ServiceB”,这里的prefix需要定义为“/ServiceB”。实际上就是具体服务路由的地址。
  5. 最后,针对 uri地址制定对应的route,在destination(目标)的host中定义服务的名称:“ServiceB”。ServiceC、D的定义和B的基本一致,不再赘述。

代码段1


流量切分

上面描述了简单路由的规则设置,如果遇到需要更具访问内容进行流量切分的情况,或者需要按照比例切分流量的情况配置文件的内容就需要修改了。如图 6 所示, Service A 要访问 Service B 的三个不同版本 V 1 V 2 V 3 。当 URI ”Test.com/status” ”Test.com/ data 的时候会请求 Service B V 2 V 3 版本,导入的流量分别是 2 0% 8 0% (红线标注的部分)。其他 URI 路由到 Service B V 1 版本(绿线标注的部分)。

图6 流量切分

照旧看看配置文件的每个配置项的内容,按照红色数字描述如下:
  1. http - match 的部分用来匹配 URI ,这里有两个 prefix 分别是: data ”和“ status ”,当请求 URI 满足这个两个条件中的一个时进入下面的路由选择。
  2. 在路由选择 route destination 中对应了 ServiceB 服务的, subset V 2 也就是 V 2 版本。 W eight 设置是 2 0 ,意思是 2 0% 的流量,流入 ServiceB V 2 版本。
  3. 在路由选择 route destination 中对应了 ServiceB 服务的, subset V 3 也就是 V 3 版本。 W eight 设置是 80 ,意思是 80% 的流量,流入 ServiceB V 3 版本。
  4. 最后,如果在没有命中上述两个 prefix 的情况下,流量会流入 ServiceB V 1 版本。

代码段 2


负载均衡

负载均衡是服务治理中经常遇到的功能,来看看在 Istio 中是如何实现的。如图 7 所示, Service A 会访问 Service B V 2 版本的集群以及 Service B V 1 版本的集群。针对同一个服务的两个不同版本的集群,需要使用两种不同的负载均衡策略,分别是 ROUND_ROBIN RANDOM

7

负载均衡


在看完负载均衡的需求之后再来看看如何通过配置文件实现它,在介绍配置文件之前先来介绍一下 DestinationRule 的规则描述。

如果说 VirtualService 是一个虚拟 Service 其描述的 内容是 “从服务流出的请求 被哪个服务处理 ”, 那么 DestinationRule 描述的是 “流入的 请求到达 服务之后如何处理 ,从字面意思理解就是 目标规则, 如果落到负载均衡的这个例子上来说,也就是流量到达 Service B 的两个版本( V 1 V 2 )以后如何进行访问。

如代码段 3 所示,按照红色数字的顺序如下:
  1. 规则配置定义为 DestinationRule ,表示处理服务流入的请求。

  2. 这里请求流入的服务是 Service B ,其从在两个版本,每个版本都是以集群的方式存在的。

  3. 针对 Service B 版本 V 2 的情况,在 trafficPolicy (流量规则)的 loadBalancer (负载均衡)中使用了 ROUND _ROBIN







请到「今天看啥」查看全文