作者 | 王夕宁 阿里巴巴高级技术专家
本文摘自于由阿里云高级技术专家王夕宁撰写的《Istio 服务网格技术解析与实践》一书,文章从基础概念入手,介绍了什么是服务网格及 Istio,针对 2020 服务网格的三大发展趋势,体系化、全方位地介绍了 Istio 服务网格的相关知识。
有外文指出,2020 年 Service Mesh 技术将有以下三大发展:
Gartner 2018 关于服务网格技术趋势分析报告,展示了一系列的服务网格技术,划分服务网格技术的依据是基于应用服务代码是否必须对其服务网格感知及其是否锁定,或锁定的程度。
基于编程框架的网格技术可以帮助开发人员构建一个架构体系良好的服务,但这会导致应用代码与框架和运行时环境的紧密耦合。而基于 Sidecar 代理的服务网格技术不会为开发人员设置这些障碍,并且使其管理和维护更加轻松,能够提供更灵活的方法来配置运行时策略。
在微服务环境中,可将单一应用程序分解为独立的多个组件,并作为分布式服务进行部署,这些服务通常是无状态的、短暂的、动态可扩展的,运行在容器编排系统(如 Kubernetes)中。
服务网格一般由控制平面和数据平面组成。具体来说,控制平面是一组在一个专用的命名空间中运行的服务。这些服务完成一些控制管理的功能,包括聚合遥测数据、提供面向用户的 API、向数据平面代理提供控制数据等。而数据平面则是由一系列运行在每个服务实例旁边的透明代理构成。这些代理自动处理进出服务的所有流量,因为它们是透明的,所以这些代理充当了一个进程外网络堆栈,向控制平面发送遥测数据并从控制平面接收控制信号。
服务实例可以根据需要进行启动、停止、销毁、重建或替换。因此,这些服务需要一个通信中间件来支持服务的动态发现和自我修复连接能力,从而使得这些服务之间能够以安全、动态和可靠的方式相互通信,这就是服务网格所支持的功能。
服务网格是一个专用的基础设施层,使服务到服务之间的通信更加安全、快速、可靠。如果你正在构建云原生应用程序,则需要服务网格。在过去的一年中,服务网格已成为云原生程序的关键组件,它通过包含现代云原生应用程序的复杂服务拓扑来可靠地传递请求。实际上,服务网格通常实现为轻量级网络代理的组合,这些代理与应用程序代码一起部署,不需要知道应用程序是什么。
服务网格作为单独层的概念与云原生应用程序的兴起有关。在云原生模型中,单个应用程序可能包含数百个服务,每个服务可能有数千个实例,并且每个实例可能处于不断变化的状态。这也是为什么像 Kubernetes 这样的协调器日益流行和必要的原因所在。这些服务之间的通信不仅变得越来越复杂,而且也是运行时环境中最为常见的一部分,因此管理这些服务之间的通信对于确保端到端的性能和可靠性至关重要。
服务网格是一种网络模型,位于 TCP/IP 之上的抽象层。它假定底层的三四层网络存在并且能够从一点到另一点传送字节。它还假设该网络与环境的其他方面一样不可靠,因此服务网络也必须能够处理网络故障。在某些方面,服务网格类似于 TCP/IP。正如 TCP 协议栈抽象了在网络端点之间可靠地传递字节的机制一样,服务网格抽象了在服务之间可靠地传递请求的机制。与 TCP 一样,服务网格不关心实际有效负载或其编码方式,只负责完成从服务 A 发送到服务B,并且在处理任何故障的同时实现这一目标。但是,与 TCP 不同的是,服务网格不仅仅具备“使其工作”的能力,还提供了一个统一的应用程序控制点,用于将可见性和控制引入应用程序运行时。服务网格的明确目标是将服务通信从不可见的基础设施领域移出,并转变为生态系统的一部分,可以对其进行监控、管理和控制。
在云原生应用程序中,保证请求具备完整的可靠性并非易事。服务网络通过各种强大的技术来管理这种复杂性,支持熔断、延迟感知的负载均衡、最终一致性的服务发现、重试与超时等机制来尽可能保证可靠性。这些功能必须全部协同工作,并且与其运行的复杂环境之间的相互作用也非常重要。
例如,当通过一个服务网格向服务发出请求时,其交互过程可以大致简化为如下步骤:
服务网格组件通过应用动态路由规则来确定请求者想要的服务。请求应该路由到生产还是预发布的服务?是路由到本地数据中心还是云中的服务?是需要灰度到正在测试的服务的最新版本,还是仍然路由到在生产中经过验证的旧版本?所有这些路由规则都是动态可配置的,并且可以全局应用,也可以应用于任意流量片段;
找到正确的目的地后,服务网格组件从相关的服务发现端点检索相应的实例池,可能有多个实例。如果这些信息与服务网格组件在实践中观察到的信息不同,那么它会决定要信任哪些信息来源;
服务网格组件根据各种因素选择最有可能返回快速响应的实例,包括观察到的最近请求的延迟数据;
服务网格组件尝试将请求发送到选择的实例,记录响应结果的延迟和响应类型;
如果实例已经由于各种原因宕机,或者请求根本没有响应,或者由于其他任何原因而无法处理请求,服务网格组件则会根据需要在另一个实例上重试该请求,前提是它知道请求是幂等的;
如果实例始终返回错误,服务网格组件会将其从负载均衡池中逐出,以便稍后定期重试。这种情况在互联网分布式应用中非常常见,公共网络中的实例非常有可能由于某些原因导致瞬间故障;
如果请求的超时点已过,服务网格组件则会主动使请求失败,而不是通过进一步重试来添加负载,以防雪崩发生。这一点对于互联网分布式应用至关重要,否则一个小故障极有可能会引起雪崩式灾难;
与此同时,服务网格组件以度量指标和分布式跟踪的形式捕获上述行为的各个方面,并将这些数据发送到集中式的度量系统或者链路跟踪系统。
值得注意的是,这些功能都是在为分布式应用提供逐点弹性和应用程序范围的弹性能力。大规模分布式系统(无论如何构建)都有一个明确的特征:任何小型本地化故障都有可能升级为系统范围的灾难性故障。服务网格必须设计成在基础系统接近其极限时通过减少负载和快速失败来防止这些故障升级。
服务网格本身并不是一个新功能,更像是功能所在位置的转变。Web 应用程序始终必须管理服务通信的复杂性。在过去的十五年中,服务网格模型的起源可以追溯到这些应用程序的演变过程。
在本世纪初,中型 Web 应用程序的典型架构常见的是三层应用程序架构,分为应用程序逻辑层、Web 服务逻辑层和存储逻辑层,都是单独的层。层之间的通信虽然复杂,但范围有限。这个时候的应用架构并没有网格,但是在每个层的代码处理逻辑之间存在通信逻辑。
当网络发展到非常高规模时,这种架构方法开始变得捉襟见肘。特别是一些大型互联网公司,都面临着巨大的流量需求,实现了有效的云原生方法的前身:应用层被分成许多服务,也就是现在通常所知的“微服务”,层之间形成拓扑的通信方式。在这些系统中,通常采用“胖客户端”库的形式,也就是前面讲述过的类似于 Netflix 的 OSS 库,Hystrix 的熔断能力就是很好的例证。这些代码库虽然与特定的环境相关,并且需要使用特定的语言和框架,但它们是用于管理服务之间通信的形式与能力,在当时的情况下是不错的选择,而且也在众多公司里被使用。
进入云原生时代之后,云原生模型有两个重要因素:
尽管这些代码库组件在一定程度上允许应用程序