Kubernetes 为它管理的工作负载提供了工业级的韧性与弹性,也为每个处于运行状态的 Pod 维护其相互连通的虚拟化网络。不过,程序之间的通信不同于简单地在网络上拷贝数据,一个可连通的网络环境,仅仅是程序间能够可靠通信的必要但非充分的条件。
作为一名分布式程序员,你必定已经深谙路由、容错、限流、加密、认证、授权、跟踪、度量等问题在分布式系统中的必要性。
在之前远程服务调用相关学习中,始终存在着对“远程服务调用是否可能实现为透明通信”的争论。今天,服务网格的诞生在某种意义上,可以说是当年透明通信的重生,服务网格试图以容器、虚拟化网络、边车代理等技术所构筑的新一代通信基础设施为武器,重新对已经盖棺定论三十多年的程序间远程通信中,非透明的原则发起冲击。
首先需要理清楚的是,分布式服务的通信是如何逐步演化成我们要探讨的“服务网格”的?
将通信的非功能性需求视作业务需求的一部分,由程序员来保障通信的可靠性。
属于早期技术策略,这类系统原本所具有的通信能力不是作为系统功能的一部分被设计出来的,而是遇到问题后修补累积所形成的。
在刚开始时,系统往往只具备最基本的网络 API,比如集成 OKHTTP、gRPC 这些库来访问远程服务,如果远程访问接收到异常,就编写对应的重试或降级逻辑去应对处理。而在系统进入生产环境以后,遇到并解决的一个个通信问题,就逐渐在业务系统中留下了越来越多关于通信的代码逻辑。这些通信的逻辑由业务系统的开发人员直接编写,与业务逻辑直接共处在一个进程空间之中:
将代码中的通信功能抽离重构成公共组件库,通信的可靠性由专业的平台程序员来保障。
开发人员解耦依赖的一贯有效办法是抽取分离代码与封装重构组件。实际上,微服务的普及也离不开一系列封装了分布式通信能力的公共组件库,其代表性产品有 Twitter 的 Finagle、Spring Cloud 中的许多组件等。
这些公共的通信组件由熟悉分布式的专业开发人员编写和维护,不仅效率更高、质量更好,还都提供了经过良好设计的 API 接口,让业务代码既可以使用它们的能力,又无需把处理通信的逻辑散布于业务代码当中。分布式通信组件让普通程序员开发出靠谱的微服务系统成为可能。但普通程序员使用它们的成本依然很高,不仅要学习分布式的知识,还要学习这些公共组件的功能的使用规范,最麻烦的是,对于同一种问题往往还需学习多种不同的组件才能解决。
造成这些问题的主要原因是因为通信组件是一段由特定编程语言开发出来的程序,是与语言绑定的。目前,基于公共组件库开发微服务仍然是应用最为广泛的解决方案,但肯定不是一种完美的解决方案,这是微服务基础设施完全成熟之前必然会出现的应用形态,同时也一定是微服务进化过程中必然会被替代的过渡形态。
将负责通信的公共组件库分离到进程之外,程序间通过网络代理来交互,通信的可靠性由专门的网络代理提供商来保障。
为了能够让分布式通信组件与具体的编程语言脱钩,也为了避免程序员还要去专门学习这些组件的编程模型与 API 接口,这一阶段进化出了能专门负责可靠通信的网络代理。这些网络代理不再与业务逻辑部署于同一个进程空间,但仍然与业务系统处于同一个容器或者虚拟机当中,它们可以通过回环设备甚至是UDS(Unix Domain Socket)进行交互,可以说具备相当高的网络性能。
只要让网络代理接管掉程序七层或四层流量,就能够在代理上完成断路、容错等几乎所有的分布式通信功能,前面提到过的 Netflix Prana 就属于这类产品的典型代表。
在通过网络代理来提升通信质量的思路提出以后,其本身的使用范围其实并不算特别广泛,但它的方向是正确的。这种思路后来演化出了两种改进形态:
将网络代理以边车的形式注入到应用容器,自动劫持应用的网络流量,让通信的可靠性由专门的通信基础设施来保障。
与前一阶段的独立代理相比,以边车模式运作的网络代理拥有两个无可比拟的优势:
这意味着它对所有现存程序都具备开箱即用的适应性,无需修改旧程序就能直接享受到边车代理的服务,这样使得它的适用面就变得十分广泛。目前边车代理的代表性产品有 Linkerd、Envoy、MOSN 等。
边车代理能够透明且具有强制力地解决可靠通信的问题,但它本身也需要有足够的信息才能完成这项工作,比如获取可用服务的列表、得到每个服务名称对应的 IP 地址等等。这些信息需要由管理员主动去告知代理,或者代理主动从约定的好的位置获取的。可见,管理代理本身也会产生额外的通信需求。如果没有额外的支持,这些管理方面的通信都得由运维人员去埋单。为了管理与协调边车代理,程序间通信进化到了最后一个阶段:服务网格。
将边车代理统一管控起来实现安全、可控、可观测的通信,将数据平面与控制平面分离开来,实现通用、透明的通信,这项工作就由专门的服务网格框架来保障。
从总体架构看,服务网格包括两大块内容,分别是由一系列与微服务共同部署的边车代理,以及用于控制这些代理的管理器所构成。代理与代理之间需要通信,用以转发程序间通信的数据包;代理与管理器之间也需要通信,用以传递路由管理、服务发现、数据遥测等控制信息。
数据平面与控制平面并不是什么新鲜概念,它最初就是用在计算机网络之中的术语,通常是指网络层次的划分。在软件定义网络中,也把解耦数据平面与控制平面作为其最主要的特征之一。服务网格把计算机网络的经典概念引入到了程序通信之中,既可以说是对程序通信的一种变革创新,也可以说是对网络通信的一种发展传承。
分离数据平面与控制平面的实质是将“程序”与“网络”进行解耦,把网络可能出现的问题(比如中断后重试、降级),与可能需要的功能(比如实现追踪度量)的处理过程从程序中拿出来,放到由控制平面指导的数据平面通信中去处理,这样来制造出一种“这些问题在程序间通信中根本不存在”的假象,仿佛网络和远程服务都是完美可靠的。而这种完美的假象,就让应用之间可以非常简单地交互,而不必过多地考虑异常情况;而且也能够在不同的程序框架、不同的云服务提供商环境之间平稳地迁移。与此同时,还能让管理者能够不依赖程序支持就得到遥测所需的全部信息,能够根据角色、权限进行统一的访问控制,这些都是服务网格的价值所在。
此文章为3月Day13学习笔记,内容来源于极客时间《周志明的软件架构课》