谈谈微服务架构中的基础设施:Service Mesh与Istio
preview image

微服务架构的演进

作为一种架构模式,微服务将复杂系统切分为数十乃至上百个小服务,每个服务负责实现一个独立的业务逻辑。这些小服务易于被小型的软件工程师团队所理解和修改,并带来了语言和框架选择灵活性,缩短应用开发上线时间,可根据不同的工作负载和资源要求对服务进行独立缩扩容等优势。

另一方面,当应用被拆分为多个微服务进程后,进程内的方法调用变成了了进程间的远程调用。引入了对大量服务的连接、管理和监控的复杂性。

microservice

该变化带来了分布式系统的一系列问题,例如:

  • 如何找到服务的提供方?
  • 如何保证远程方法调用的可靠性?
  • 如何保证服务调用的安全性?
  • 如何降低服务调用的延迟?
  • 如何进行端到端的调试?

另外生产部署中的微服务实例也增加了运维的难度,例如:

  • 如何收集大量微服务的性能指标已进行分析?
  • 如何在不影响上线业务的情况下对微服务进行升级?
  • 如何测试一个微服务集群部署的容错和稳定性?

这些问题涉及到成百上千个服务的通信、管理、部署、版本、安全、故障转移、策略执行、遥测和监控等,要解决这些微服务架构引入的问题并非易事。

让我们来回顾一下微服务架构的发展过程。在出现服务网格之前,我们最开始在微服务应用程序内理服务之间的通讯逻辑,包括服务发现,熔断,重试,超时,加密,限流等逻辑。

1

在一个分布式系统中,这部分逻辑比较复杂,为了为微服务应用提供一个稳定、可靠的基础设施层,避免大家重复造轮子,并减少犯错的可能,一般会通过对这部分负责服务通讯的逻辑进行抽象和归纳,形成一个代码库供各个微服务应用程序使用,如下图所示:

2

公共的代码库减少了应用程序的开发和维护工作量,降低了由应用开发人员单独实现微服务通讯逻辑出现错误的机率,但还是存在下述问题:

  • 微服务通讯逻辑对应用开发人员并不透明,应用开发人员需要理解并正确使用代码 库,不能将其全部精力聚焦于业务逻辑。
  • 需要针对不同的语言/框架开发不同的代码库,反过来会影响微服务应用开发语言 和框架的选择,影响技术选择的灵活性。
  • 随着时间的变化,代码库会存在不同的版本,不同版本代码库的兼容性和大量运行 环境中微服务的升级将成为一个难题。

可以将微服务之间的通讯基础设施层和 TCP/IP 协议栈进行类比。TCP/IP 协议栈为操作系统中的所有应用提供基础通信服务,但 TCP/IP 协议栈和应用程序之间并没有紧密的耦合关系,应用只需要使用 TCP/IP 协议提供的底层通讯功能,并不关心 TCP/IP 协议的实现,如 IP 如何进行路由,TCP 如何创建链接等。

同样地,微服务应用也不应该需要关注服务发现,Load balancing,Retries,Circuit Breaker 等微服务之间通信的底层细节。如果将为微服务提供通信服务的这部分逻辑从应用程序进程中抽取出来,作为一个单独的进程进行部署,并将其作为服务间的通信代理,可以得到如下图所示的架构:

sidecar

因为通讯代理进程伴随应用进程一起部署,因此形象地把这种部署方式称为“sidecar”/边车(即三轮摩托的挎斗)。

应用间的所有流量都需要经过代理,由于代理以 sidecar 方式和应用部署在同一台主机上,应用和代理之间的通讯可以被认为是可靠的。由代理来负责找到目的服务并负责通讯的可靠性和安全等问题。

当服务大量部署时,随着服务部署的 sidecar 代理之间的连接形成了一个如下图所示的网格,该网格成为了微服务的通讯基础设施层,承载了微服务之间的所有流量,被称之为 Service Mesh(服务网格)。

mesh

_服务网格是一个基础设施层,用于处理服务间通信。云原生应用有着复杂的服务拓扑,服务网格保证请求可以在这些拓扑中可靠地穿梭。在实际应用当中,服务网格通常是由一系列轻量级的网络代理组成的,它们与应用程序部署在一起,但应用程序不需要知道它们的存在。

_William Morgan _ WHAT’S A SERVICE MESH? AND WHY DO I NEED ONE? _

服务网格中有数量众多的 Sidecar 代理,如果对每个代理分别进行设置,工作量将非常巨大。为了更方便地对服务网格中的代理进行统一集中控制,在服务网格上增加了控制面组件。

controlplane

这里我们可以类比 SDN 的概念,控制面就类似于 SDN 网管中的控制器,负责路由策略的指定和路由规则下发;数据面类似于 SDN 网络中交换机,负责数据包的转发。

由于微服务的所有通讯都由服务网格基础设施层提供,通过控制面板和数据面板的配合,可以对这些通讯进行监控、托管和控制,以实现微服务灰度发布,调用分布式追踪,故障注入模拟测试,动态路由规则,微服务闭环控制等管控功能。

Istio 服务网格

Istio 是一个 Service Mesh 开源项目,是 Google 继 Kubernetes 之后的又一力作,主要参与的公司包括 Google,IBM 和 Lyft。

凭借 kubernetes 良好的架构设计及其强大的扩展性,Google 围绕 kubernetes 打造一个生态系统。Kubernetes 用于微服务的编排(编排是英文 Orchestration 的直译,用大白话说就是描述一组微服务之间的关联关系,并负责微服务的部署、终止、升级、缩扩容等)。其向下用 CNI(容器网络接口),CRI(容器运行时接口)标准接口可以对接不同的网络和容器运行时实现,提供微服务运行的基础设施。向上则用 Istio 提供了微服务治理功能。

由下图可见,Istio 补充了 Kubernetes 生态圈的重要一环,是 Google 的微服务版图里一个里程碑式的扩张。

Kubernetes 生态圈

Google 借 Istio 的力量推动微服务治理的事实标准,对 Google 自身的产品 Google Cloud 有极其重大的意义。其他的云服务厂商,如 Redhat,Pivotal,Nginx,Buoyant 等看到大势所趋,也纷纷跟进,宣布自身产品和 Istio 进行集成,以避免自己被落下,丢失其中的市场机会。

可以预见不久的将来,对于云原生应用而言,采用 kubernetes 进行服务部署和集群管理,采用 Istio 处理服务通讯和治理,将成为微服务应用的标准配置。

Istio 服务包括网格由数据面和控制面两部分。

  • 数据面由一组智能代理(Envoy)组成,代理部署为边车,调解和控制微服务之间所有的网络通信。
  • 控制面负责管理和配置代理来路由流量,以及在运行时执行策略。
istio-architecture

Istio 控制面

Istio 控制面板包括 3 个组件:Pilot, Mixer 和 Istio-Auth。

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 实例,可以在运行期对微服务的流量进行控制和调整。

pilot

Mixer

在微服务应用中,通常需要部署一些基础的后端公共服务以用于支撑业务功能。这些基础设施包括策略类如访问控制,配额管理;以及遥测报告如 APM,日志等。微服务应用和这些后端支撑系统之间一般是直接集成的,这导致了应用和基础设置之间的紧密耦合,如果因为运维原因需要对基础设置进行升级或者改动,则需要修改各个微服务的应用代码,反之亦然。

为了解决该问题,Mixer 在应用程序代码和基础架构后端之间引入了一个通用中间层。该中间层解耦了应用和后端基础设施,应用程序代码不再将应用程序代码与特定后端集成在一起,而是与 Mixer 进行相当简单的集成,然后 Mixer 负责与后端系统连接。

Mixer 主要提供了三个核心功能:

  • 前提条件检查。允许服务在响应来自服务消费者的传入请求之前验证一些前提条件。前提条件可以包括服务使用者是否被正确认证,是否在服务的白名单上,是否通过 ACL 检查等等。
  • 配额管理。 使服务能够在分配和释放多个维度上的配额,配额这一简单的资源管理工具可以在服务消费者对有限资源发生争用时,提供相对公平的(竞争手段)。Rate Limiting 就是配额的一个例子。
  • 遥测报告。使服务能够上报日志和监控。在未来,它还将启用针对服务运营商以及服务消费者的跟踪和计费流。

Mixer 的架构如图所示:

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),以提供端到端的安全解决方案。

认证

Istio 提供了一个内部的 CA(证书机构),该 CA 为每个服务颁发证书,提供服务间访问的双向 SSL 身份认证,并进行通信加密,其架构如下图所示:

auth

其工作机制如下: 部署时:

  • CA 监听 Kubernetes 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)提供了命名空间,服务,方法三个不同大小粒度的服务访问权限控制。其架构如下图所示:

authorization

管理人员可以定制访问控制的安全策略,这些安全策略保存在 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 特性Inbound 特性
Service authentication(服务认证)Service authentication(服务认证)
Load Balancing(负载均衡)Authorization(鉴权)
Retry and circuit breaker(重试和断路器)Rate limits(请求限流)
Fine-grained routing(细粒度的路由)Load shedding(负载控制)
Telemetry(遥测)Telemetry(遥测)
Request Tracing(分布式追踪)Request Tracing(分布式追踪)
Fault Injection(故障注入)Fault Injection(故障注入)

备注:Outbound 特性是指服务请求侧的 Sidecar 提供的功能特性,而 Inbound 特性是指服务提供侧 Sidecar 提供的功能特性。一些特性如遥测和分布式跟踪需要两侧的 Sidecar 都提供支持;而另一些特性则只需要在一侧提供,例如鉴权只需要在服务提供侧提供,重试只需要在请求侧提供。

典型应用场景

Istio 服务管控包括下列的典型应用场景:

分布式调用追踪

在微服务架构中,业务的调用链非常复杂,一个来自用户的请求可能涉及到几十个服务的协同处理。因此需要一个跟踪系统来记录和分析同一次请求在整个调用链上的相关事件,从而帮助研发和运维人员分析系统瓶颈,快速定位异常和优化调用链路。

Istio 通过在 Envoy 代理上收集调用相关数据,实现了对应用无侵入的分布式调用跟踪分析。 Istio 实现分布式调用追踪的原理如下图所示:

Istio 实现分布式调用追踪的原理

Envoy 收集一个端到端调用中的各个分段的数据,并将这些调用追踪信息发送给 Mixer,Mixer Adapter 将追踪信息发送给相应的服务后端进行处理。整个调用追踪信息的生成流程不需要应用程序介入,因此不需要将分布式跟踪相关代码注入到应用程序中。

注意:应用仍需要在进行出口调用时将收到的入口请求中 tracing 相关的 header 转发出去,传递给调用链中下一个边车进行处理。

度量收集

Istio 实现度量收集的原理如下图所示:

Istio 实现度量收集的原理

Envoy 收集指标相关的原始数据,如请求的服务,HTTP 状态码,调用时延等,这些收集到的指标数据被送到 Mixer,通过 Mixer Adapters 将指标信息转换后发送到后端的监控系统中。由于 Mixer 使用了插件机制,后端监控系统可以根据需要在运行期进行动态切换。

灰度发布

当应用上线以后,运维面临的一大挑战是如何能够在不影响已上线业务的情况下进行升级。无论进行了多么完善的测试,都无法保证线下测试时发现所有潜在故障。在无法百分百避免版本升级故障的情况下,需要通过一种方式进行可控的版本发布,把故障影响控制在可以接受的范围内,并可以快速回退。

可以通过灰度发布(又名金丝雀发布)来实现业务从老版本到新版本的平滑过渡,并避免升级过程中出现的问题对用户造成的影响。

Istio 通过高度的抽象和良好的设计采用一致的方式实现了灰度发布。在发布新版本后,运维人员可以通过定制路由规则将特定的流量(如具有指定特征的测试用户)导入新版本服务中以进行测试。通过渐进受控地向新版本导入生产流量,可以最小化升级中出现的故障对用户的影响。

采用 Istio 进行灰度发布的流程如下图所示:

首先,通过部署新版本的服务,并将通过路由规则将金丝雀用户的流量导入到新版本服务中

canary

测试稳定后,使用路由规则将生产流量逐渐导入到新版本系统中,如按 5%,10%,50%,80%逐渐导入。

canary-2

如果新版本工作正常,则最后将所有流量导入到新版本服务中,并将老版本服务下线;如中间出现问题,则可以将流量重新导回老版本,在新版本中修复故障后采用该流程重新发布。

canary-3

断路器

在微服务架构中,存在着许许多多的服务单元,若一个服务出现故障,就会因依赖关系形成故障蔓延,最终导致整个系统的瘫痪,这样的架构相较传统架构就更加的不稳定。为了解决这样的问题,因此产生了断路器模式。

断路器模式指,在某个服务发生故障时,断路器的故障监控向调用放返回一个及时的错误响应,而不是长时间的等待。这样就不会使得调用线程因调用故障被长时间占用,从而避免了故障在整个系统中的蔓延。

Istio 实现断路器的原理如下图所示:

Istio 实现断路器的原理

管理员通过 destination policy 设置断路触发条件,断路时间等参数。例如设置服务 B 发生 10 次 5XX 错误后断路 15 分钟。则当服务 B 的某一实例满足断路条件后,就会被从 LB 池中移除 15 分钟。在这段时间内,Envoy 将不再把客户端的请求转发到该服务实例。

Istio 的断路器还支持配置最大链接数,最大待处理请求数,最大请求数,每链接最大请求数,重试次数等参数。当达到设置的最大请求数后,新发起的请求会被 Envoy 直接拒绝。

circuitbreaker-parameters

故障注入

对于一个大型微服务应用而言,系统的健壮性非常重要。在微服务系统中存在大量的服务实例,当部分服务实例出现问题时,微服务应用需要具有较高的容错性,通过重试,断路,自愈等手段保证系统能够继续对外正常提供服务。因此在应用发布到生产系统强需要对系统进行充分的健壮性测试。

对微服务应用进行健壮性测试的一个最大的困难是如何对系统故障进行模拟。在一个部署了成百上千微服务的测试环境中,如果想通过对应用,主机或者交换机进行设置来模拟微服务之间的通信故障是非常困难的。

Istio 通过服务网格承载了微服务之间的通信流量,因此可以在网格中通过规则进行故障注入,模拟部分微服务出现故障的情况,对整个应用的健壮性进行测试。

故障注入的原理如下图所示:

fault-injection

测试人员通过 Pilot 向 Envoy 注入了一个规则,为发向服务 MS-B 的请求加入了指定时间的延迟。当客户端请求发向 MSB-B 时,Envoy 会根据该规则为该请求加入时延,引起客户的请求超时。通过设置规则注入故障的方式,测试人员可以很方便地模拟微服务之间的各种通信故障,对微服务应用的健壮性进行较为完整的模拟测试。

总结

服务网格为微服务提供了一个对应用程序透明的安全、可靠的通信基础设施层。采用服务网格后,微服务应用开发人员可以专注于解决业务领域问题,将一些通用问题交给服务网格处理。采用服务网格后,避免了代码库带来的依赖,可以充分发挥微服务的异构优势,开发团队可以根据业务需求和开发人员能力自由选择技术栈。

Istio 具有良好的架构设计,提供了强大的二次开发扩展性和用户定制能力。虽然 Istio 目前还处于 beta 阶段,但已经获得众多知名公司和产品的支持,是一个非常具有前景的开源服务网格开源项目。

参考


TAGS
On this page