购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.2 数据平面

上一节笔者介绍了 Istio 的总体架构,分“数据平面”与“控制平面”两部分。数据平面的职责是流量承载、服务路由及数据管控,是与链路对接的前端;而“控制平面”则是集中化地配置、遥测、监控及调配台面,“数据平面”在需要时将其承载的请求发送至中心,以进行管理员预配置的操作。

由此可见,“数据平面”最主要的就是形成这个面的节点,即之前提到的 Sidecar。在Kubernetes 生态里,Sidecar 会被自动注入 Pod (当然也可以手动注册),将其后面的服务纳入Service Mesh 生态。

第2章谈到对于 Sidecar 的具体实现有很多选择,比如早期的 Linkerd,后来的 Envoy 或者经典反向代理 Nginx 衍生出的 Nginmesh 等。然而 Istio 选择的却是 Envoy,笔者个人认为Nginx 作为之前服务架构中广泛使用的网关,却没有得到 Istio 的青睐,很大程度上是因为没有像 Envoy 那样优秀的配置扩展 API。虽然 Nginx 也支持 Lua 扩展脚本,但却是静态级别的,与 Envoy 的实时配置相差甚远,与 Istio 的透明配置设计相违背。

接下来看看 Envoy 的xDS接口是如何得到 Google 巨人青睐的吧。

2.2.1 xDS-API

在 Envoy 中,xDS-API 中的 xDS 其实是一类发现服务的统称,包括如下几类。

○ SDS/EDS(Service/Endpoint(v2) Discovery Service):节点发现服务,针对的就是提供服务的节点,让节点可以以聚合成服务的方式提供给调用方。在 Envoy v2 API 中,Service Discovery Service 已经更名为 Endpoint Discovery Service,因此这两个描述本质上是一个概念。

○ CDS(Cluster Discovery Service):集群发现服务,集群指 Envoy 接管的服务集群,例如三台机器组成了一个服务集群提供 reviews 服务;Istio 可以使用这个接口创建虚拟集群,例如一个应用可以划分出不同版本号的部署结构。

○ RDS(Route Discovery Service):路由规则发现服务,路由规则的作用是动态转发,例如上面 BookInfo 中的指定流量只流向 v3 版本的 reviews 服务,基于此可以实现比如请求漂移、蓝绿发布等功能。

○ LDS(Listener Discovery Service):监听器发现服务,监听器主要作用于Envoy 的链接状态,例如像 downstreamcxtotal____(连接总数)、downstreamcxactive(活动的连接总数)等。

可见 Envoy 的动态转发/代理功能是相当强大的,几乎可以动态配置一切路由、转发及监听规则。Istio 正是利用了这点,对所有注入的 Sidecar 进行全局管控,将系统管理集中配置的路由及服务信息分发给各节点的 Envoy。

这一点非常重要,结合之前对 Istio 架构的理解,Istio 想要做到的是业务透明、低维护成本与高可用性。假设 Istio 采用类似 Nginx 的静态脚本配置,那么每次在路由规则发生变化或者模块加强的情况下,都需要重启服务。这显然会对业务造成影响,达不到其设计初衷。

在上面的各类配置中,Istio 中使用得较多的主要是 CDS 与 RDS,其作用是对服务及路由信息进行配置,例如之前 BookInfo 示例项目中,要将 reivews 的流量都分配到 v3 版本中,则需配置如下规则:

这里的配置是针对 Istio 模块 Pilot 的配置。这些配置会被 Pilot 翻译成 Envoy 理解的格式,并通过之前提到的 xDS-API 分发给它们(就是 Sidecar)。在第57页“2.7 BookInfo 示例分析”中,笔者将演示如何通过 istioctl 命令查看分发至 Envoy 中的原生配置,对此有兴趣的读者可以去看一下。下发至 Envoy 中的配置都是按照官方的配置要求 来转换的。

2.2.2 服务负载及流量控制

使用 Istio 的核心优势在于完全将业务逻辑、分布式调用以及基础组件设施解耦。运维人员只需要简单地向 Pilot 中发送一些配置,就可以轻松掌控全局。在正常情况下,Envoy 会对下游流量做负载均衡,这里主要有“轮询(Round Robin)”与“最少连接(Least Connection)”两种。除此之外还可以做特殊的定制,例如可以向指定的下游分配 5% 的流量;或为链路添加链路跟踪功能;抑或针对某个版本做特定的转发规则。

一切都是那么简单,如图2.4所示:

图2.4 “Envoy 服务负载逻辑示意图”

这样解耦的最大好处是,Istio 可以提供多种链路功能,例如 A/B 测试、多版本转发及链路压测功能,同时还支持故障注入(Fault Injection)等非常实用的功能。这些都是依赖分布在各端前的 Sidecar 来实现的。

Istio 中所有的服务关系都是维护在 Pilot 中的,但需要特别注意的是 Pilot 本身并不做服务发现,而是提供接口与标准让三方扩展来实现,其是完全平台独立的(这也是 Istio 的设计原则,不绑定平台,其计划支持 Kubernetes、Mesos、Cloud Foundry 等平台)。除了服务本身,前面提到的链路功能也是通过 Pilot 配置的。同样,这些配置都是平台无关的。虽然 Istio 目前使用的是 Envoy,但也不排除之后会使用其他数据平面实现。这对 Istio 都是很简单的事,其在架构层面上已经做到了完全接口化,如果真的需要变更数据平面,只需要将 Envoy 的配置翻译器替换成其他的即可,非常简单有效,如图2.5所示。

图2.5 “Istio 的服务发现并不依

除了流量负载,每个 Envoy(即 Sidecar)还向其挂载的服务发起周期性的健康检测。这样新服务在启动时便能自动接入网格:即在健康检测失败时及时排除无效的服务;而当检测成功达到一定的次数后,又可以将其加回服务列表中。有些时候服务需要主动下线(例如发布的时候),这个时候只需在健康检测时返回 503 (Service Unavailable),就不会多次确认,服务会立即下线。

当然,其实很多容器平台,例如 Kubernetes,已经提供了类似的服务,之所以还需要是因为 Istio 设计目的是提供一个通用解决方案,不能依赖平台的功能特性。

在服务网格中,Istio 默认使用的 RPC 调用协议是 HTTP。在第1章也提到过,HTTP 在具有非常明显的轻量优势,并且容易解码与转发,所以是首选;不过在实际生产环境,特别是国内环境,HTTP 被很多大厂,如蚂蚁金服、华为以及腾讯 等不看好,原因是其过于弱类型 ,在故障排查、性能调优以及链路分析上都有很多不足

2.2.3 入口与出口网关

Istio 提倡所有进入与离开服务网格的流量都经过一个特殊的网关(见图2.6),这样才能比较轻松地管理内部的网格,在安全、单元封闭、接口扩展层面上都有好处。

图2.6 “Istio 的流量入口与出口网关”

换一种角度来讲,网关除了对入口进行管控,还能够提供对外接口兼容以及流量统一跳转功能,这一点对于多单元部署特别有作用。

所谓的多单元部署就是将全局的应用以单元的形式部署,每个单元都是完全的整套服务,而调用链路是自封闭的,可以提供以下几个功能:

服务能力扩展 :当一个单元的服务能力不能满足要求时,可以快速甚至自动地扩展一个单元,由于所有单元的请求都是自封闭的,所以整个扩展只需要将单元的入口网关接入全局的CDN 即可,非常简单。

数据容灾能力 :当系统单元化后,多个单元间的数据是实时全量同步的,这样以后就将数据冗余到了多地,除非整个地球遭遇灾难,否则数据都是安全的。

就近访问能力 :单元化的具体落地是机房,由于其自身的完备性,工程师可以把用户定向到离其最近的单元,减少延时,以提供优质的服务访问质量。

由此可见,单元流量的调度都基于统一的入口,否则,例如当一个单元出现问题需要迁移至另一个的时候,还需要逐一地通知各业务方,显然这是不合适的。单元网关为整个服务网格确立了边界,犹如城墙与城门一样重要。

2.2.4 故障应急机制

Envoy 提供一系列容灾解决方案,主要由以下部分组成。

超时机制 :在服务请求总时间超过一个阈值时直接返回错误,避免卡死。

重试机制 :采用不确定重试时间间隔,并限制重试次数,最大程度保障服务调用的顺畅性。

并行连接控制 :对下游的并行连接数量进行控制,以避免过载的情况。

健康检测 :对挂载服务进行健康检测,支持主动与被动(即平台提供的健康检测结果)两种方式,以及时剔除不健康的节点,当其恢复时又及时加入。

断路器支持 :同样,对服务过载进行保护。

Envoy之所以会结合主动与被动两种健康检测方式,是为了避免将不可用的服务纳入网格。当与平台提供的健康检测结果相结合时,应用能够最大限度地保证故障节点及时被移除,最大限度地减少因为检测延时而导致的错误调用。

以上功能都是可以通过 Istio 的配置规则进行动态设置的。这些功能的结合,为 Istio 中的服务提供了健康保障,能有效避免因过载导致的各类链路故障。

2.2.5 故障注入

有时候为了测试链路的健壮性,需要模拟一些故障。通常来说,故障模拟都是通过杀掉服务进程或者发送一个错误的 TCP 包来模拟的,显然这种方式非常低效。Istio 从架构设计上就考虑到了这一因素,其支持协议层面上的故障注入,对应用来说结果都一样。相对于之前的方案,这种方式不仅益于问题观察,还能根据请求匹配定向注入问题,最为关键的是对自动化友好,可以随时进行回归测试。

Istio 支持两种形式的错误注入:延时(delays)与丢包(aborts)。延时,顾名思义就是增加网络的传输延时以模拟服务过载时出现的问题;丢包则是模拟上行服务故障,这种故障一般是利用 HTTP 错误码来实现的。

故障注入是现在大型企业保障业务稳定性的重要手段之一。一般来说,大的故障不常见,一见便是灾难;所以时常对自己的系统进行容灾演练是非常好的预防手段,而故障注入便是这其中很好的工具。 Nal2/c7FolG60CeBqaLAhzISTGPZ1lPFzKqlLz5fDYqK+TgdKDZNIUuin2qI0ERF



2.3 控制平面

上一节向大家介绍了数据平面,它是对流量的实际承载,而承载的具体的操作方式则是由控制平面统一决定的。这样的设计为系统整体运维带来了极大便利,可能以往需要3~5人的运维团队,现在只需 1~2 个人就可以了。这些都得益于 Istio 平台化、透明接入的设计思想,将链路层面的基础服务与业务逻辑层明确界定开,并实施统一配置及管理,Istio 设计的优秀之处便在于此。

控制平台的核心由三大组件组成:Pilot、Mixer 及 Citadel,三个组件分别负责配置维护、策略执行及安全相关功能。它们都拥有非常强大的扩展性,特别是 Mixer,通过其扩展接口接入众多三方链路扩展。

接下来,就逐一向读者介绍。

2.3.1 Pilot 结构及功能

在 Istio 架构中,Pilot 是对各容器平台(也可以是VM)的逻辑抽象,通过适配器模式,形成统一的对接接口,官方版本可以支持 Kubernetes、Cloud Foundry 及 Apache Mesos。由于Istio 开源,要想支持更多的平台也是非常容易的事。

Pilot 的基本工作就是为数据平台的 Sidecar 提供服务发现能力,让服务之间可以相互发现,并根据配置的策略进行调用。不过 Pilot 本身并不做服务注册,它只是提供一个接口,对接已有的服务注册系统,例如 Eureka 、Etcd 等。除此之外,还负责为动态路由(如灰度发布、测试环境分离)提供总控配置,并将这些配置分发给数据平面代理(即 Envoy)。Pilot 对配置格式做了抽象,与具体代理支持的配置格式无关,并且能够实时推送给代理。这就是为什么 Istio能够支持多种平台的关键所在。

Pilot 的整体结构模块如图2.7所示。

图2.7 “官方文档中的 Pilot 构架图”

2.3.2 Mixer 结构及功能

在 Istio 架构中,Mixer 负责在服务调用之间实施控制策略,同时在调用间收集请求的遥测数据(例如调用时长、异常统计等)。Mixer 拥有非常强大的扩展接口,可以根据自己的需要添加各种链路功能,仅在官方文档中其支持的就达十几种。从逻辑抽象角度上来说,Mixer 提供两个方面的服务。

后端逻辑抽象 :链路除了正常的请求,还拥有很多扩展逻辑(如分布式请求日志),Mixer 将后端这些功能实现细节都屏蔽,只留下直观的接口,方便调用与扩展。

中心化的控制 :Mixer 能对每个请求进行细致的控制,让任何通信都处于管控范围内。

为了实现上述目的,每个请求在到达 Sidecar 的时候都需要向 Mixer 发起一次逻辑请求,以进行前置检查;而在每次请求结束之后,还需要再次向 Mixer 做一次汇报,如图2.8所示。

图2.8 “官方文档中的 Mixer 架构”

当处理请求到达 Mixer 时,其就会依次执行通过适配器注册的各类处理器,例如日志处理器、服务遥测处理器、鉴权处理器等。Mixer 的这种设计将基础层的逻辑从业务代码中完全抽取了出来,业务工程师只需要关心自己的业务逻辑,至于基础框架层面上的功能开发,只需要交给框架工程师即可。逻辑写好后,以适配器的形式注册到 Mixer 中就可以了,完全不需要业务工程师的介入。而且,这一切都是透明的。例如,如果想对请求的参数进行遥测,只需要对Mixer 添加如下配置即可:

这样配置好后,每次请求经过数据平面的代理网关 Envoy,其都会向 Mixer 发送相关数据信息,Mixer 中对应的模块会进行汇总处理,以报表的形式展现给运维与开发人员,这里 Istio默认使用的是 Grafana

相信有调优经验的读者很快就能发现,这里存在一个很明显的性能瓶颈,即每次请求都需要经过中心节点 Mixer 才能进行下去。这就意味着,整体链路的请求被放大了 2 倍,原来只需 1 次的通信请求,变成了 3 次。而且针对 Mixer 的请求存在流量汇聚的情况,例如不管请求从何处来、去向何处,都需要请求 Mixer。

笔者在了解 Istio 之初也非常不理解 Istio 的这种设计,无论怎么考虑,这种设计都不适合大规模高并发的服务架构,每次对 Mixer 的请求都是没有必要的资源开销。要解决这个问题,有两种方式:一种是把 Mixer 的部分请求处理逻辑放置在数据平面的网关 Envoy 上;另一种方式则是将 Mixer 的数据二级缓存到 Envoy 上。

第一种请求逻辑下沉方案,即将 Mixer 的部分逻辑,例如限流、鉴权等链路基础逻辑放置到 Sidecar(Envoy)中,这些逻辑必须是与链路密不可分的;而其他如分布式日志、链路追踪等插拔的功能则还是放置在 Mixer 中。由于实现链路基础逻辑是非常高频的功能,这样已经能够大幅度缓解请求压力的问题了。

第二种缓存方案,即在 Sidecar 中放置缓存,以让绝大多数的检查规则都通过缓存走掉,而不是都请求中心的 Mixer,否则,不但流量压力非常大,而且整体链路的通信效率都会被拖累;而对于请求汇报,Sidecar 也会做一次缓存,批量上报,以避免频繁的请求。

官方在发现这个性能问题后,采用的也是第二种解决方案,如图2.9所示。为什么采用后一种而不是将功能放置在 Sidecar 上?经过仔细推敲后,笔者看来主要是从容灾能力与功能维护性两个层面上来考虑的。因为二级缓存在一定程度上就是 Mixer 的一个副本,就算 Mixer 采用心全面宕机,大部分请求也会通过走本地缓存而通过;另一方面,由于 Mixer 是中心维护,那么功能升级就变得非常简单,相比于第一种方案将功能分布式地放置在 Envoy,只需要升级中心的服务,本地网关就会在缓存失效后自动更新,整个升级显得更加平滑。第一种方案显然具备更高的性能,但是从维护工作上来讲,就不那么轻松了。

图2.9 “Mixer 解决请求集中问题

除此之外,Mixer 提供丰富的接口,为众多组件提供了强大的扩展支持,让其以插件的形式运作于 Istio 平台之上——如链路跟踪 Zipkin、分布式日志 Fluentd、监控报警 Promethus、性能遥测 StatsD 等

可以说,Mixer 就是整体大系统的链路数据及分析中心。

2.3.3 稳定性与容灾能力

作为全局的策略及统计中心,Mixer 的稳定性显然非常重要,一旦出现问题,请求策略均得不到有效的执行,影响面相当广。为此,Istio 的工程师们做了如下的努力。

无状态 :Mixer 本身是完全无状态的,其自身不管理、不存储任何数据。

99.999%可用性 :Mixer 通过架构上的优化与努力(例如使用多机存储、全量同步方案),确保实施可用高达 99.999% 的稳定系统。

缓存与缓冲 :Sidecar 会将 Mixer 的缓存策略做一次缓存(Caching),再对需要汇报的数据做一次缓冲(Buffering),以最大限度地减少对 Mixer 的直接依赖。

在第二点的架构上,Mixer 与 Sidecar 面临的情况并不同。Sidecar 由于是部署在服务资源中的,因此对内存及其他资源的占用上都有相当严格的要求,但 Mixer 则不同,它是独立占用资源的,因此可以利用分布式策略来横向以集群的形式扩展。笔者将在第4章从代码级别为大家深入分析 Mixer 在架构上的容灾考虑。

2.3.4 请求属性(Attribute)

请求属性是控制平面的数据元,每个 Sidecar 在处理数据流动的时候均会向 Mixer提供一种叫“请求属性(Attribute)”的键值,它的作用是描述请求体或其对应的环境情况,以让控制平面掌握请求的每项指标。具体来说,请求属性其实就是简单的 key-value对。以下是一些例子:

例如链路跟踪服务,就需要在整个请求栈中携带一个跟踪ID,才能对每次调用进行跟踪并最终汇总,来向运维或者开发人员展示详细调用路径,让大家轻松掌握一次用户请求在相关系统中的跳转情况。请求属性实质上就是为整个链路加上了类似消息头的功能,让元数据能够在其中穿梭,将流量与服务之间的关系打通。总之,基于请求属性,很多链路管理与策略相关的功能才得以实现。

按照请求属性的思路,Mixer 实际上就是一个处理请求属性的状态机(如图2.10所示),运维人员通过模板(Template)对每个属性做出相应的规则配置,然后请求在执行前后均会向Mixer 汇报。Mixer会依据之前的配置对属性进行处理,再将它们移交给后端的扩展逻辑——这些逻辑是由各类插件来支持的,官方或非官方的。

图2.10 'Mixer与Attributes组成请求处理状态机”

需要注意的是,Istio 里的请求属性数量及种类都是固定的,主要受 Sidecar 及采集体系影响。当然这个数量也不是完全不能扩展,只是没那么简单,而且对于一般情况来说,默认的请求属性已经够用了。

完整的列表可以参看第7章7.3节。

2.3.5 操作配置(Operator Config)

操作配置是控制平面的数据配置概念,是 Mixer 的规则定义,包含三方面子配置:数据实例(Instance)、处理器(Handler)以及规则(Rule)配置。

○数据实例(Instance)配置用于告诉 Mixer 需要根据请求属性生成什么样的数据实例——因为每次请求携带的属性不同,生成的消息体内容不同,所以称为实例。

○处理器(Handler)是实质处理数据实例的逻辑体,这其中有很多三方扩展,例如日志收集、监控数据分析、链路跟踪等。

○规则(Rule)配置则是连接以上两个配置的连接配置。

例如,一个典型的数据实例配置如下:

从上可以看出,数据实例配置的核心便是,规定 Mixer 从请求属性中采集什么数据以及在一些条件下应该采取什么样的行为,即如何来生成一个数据实例。针对上述数据实例配置的一个处理器与规则的配置可以如下:

在这种设计下,工程师可以将一个模板生成的数据源连接到不同的处理器,在需求变化时只需要更换部分配置即可,非常灵活,这是 Istio 设计巧妙体现之一。

这样就知道了,对于 Mixer 来说,三个基础的配置是数据实例(Instance)、规则(Rule)与处理器(Handler)。这三个配置将请求属性与后端的扩展插件结合起来,组成请求策略执行的数据链路。 Nal2/c7FolG60CeBqaLAhzISTGPZ1lPFzKqlLz5fDYqK+TgdKDZNIUuin2qI0ERF

点击中间区域
呼出菜单
上一章
目录
下一章
×