一、服务网格产生背景¶
随着业务的发展,传统单体应用的问题越来越严重:
- 单体应用代码库庞大,不易于理解和修改
- 持续部署困难,由于单体应用各组件间依赖性强,只要其中任何一个组件发生更改,将重新部署整个应用,而频繁的部署将增加服务宕机的风险,因此频繁地进行部署几乎不可能
- 扩展应用困难,单体应用只能从一个维度进行扩展,即很容易通过增加实例副本提供处理能力。另一方面,单体应用各个组件对资源的使用情况需求不同,一些是CPU密集型,另一些是内存密集型,但是不能独立地扩展单个组件
- 阻碍快速开发,随着公司业务的发展,单体服务框架变得更加庞大,更多的部门将会参与系统的开发,但是各个部门又不能独立开发、维护相应的模块,即使其中一个部门完成相应的更新,仍然不能上线,因此需要花费更多时间在部门间协调和统一。还有,需要增加新的功能时,单体应用最初的设计限制开发人员灵活选择开发语言、工具等,导致新功能上线慢
- 迫使开发人员长期专注于一种技术栈,由于单体应用本身设计的原因,后期引入新的技术栈需要遵循最开始的设计,因此存在非常大的局限性、挑战性,否则可能需要重写整个框架
针对上面问题的出现,微服务架构应运而生,将单体应用拆分后由多个微服务构建的复杂系统,系统中各个微服务之间彼此通过网络进行通信,很好地解决了上述问题。而微服务中最大的挑战便是如何以标准化的方式管理微服务以及如何保证复杂网络环境中微服务间的可靠通信,确保整个系统的最大可用性,提供尽可能高的SLA。
当单体应用拆分为微服务后,新的通信模型如下:

每个微服务由两部分构成:
- 业务逻辑:定义如何处理应用业务逻辑
- 网络功能:网络功能部分主要负责服务间的通信,包括上述列出的构建分布式高可用的技术实现,如超时、重试、服务发现、负载均衡等,由于它基于下层网络协议栈实现,因此被称作网络功能
此时相对于传统的单体应用,网络功能部分可以通过一个中心化的组件来统一实现或者直接嵌入到业务逻辑中,但是在微服务架构中,服务的粒度变得更小,为了实现它们之间的可靠通信,开发人员为每个微服务实现网络功能比实现业务逻辑花费的时间和精力可能更多。

从软件设计的角度,存在以下缺点:
- 耦合性很高,每个应用都需封装负载均衡、服务发现、安全通信以及分布式追踪等功能
- 灵活性差,复用率低下,不同的应用需要重复实现
- 管理复杂,当其中一项如负载均衡逻辑发生变化,需要更新所有服务
- 可运维性低,所有组件均封装在业务逻辑代码中,不能作为一个独立运维对象
- 对开发人员能力要求很高
随着不断发展,考虑将应用处理服务发现、负载均衡、分布式追踪、安全通信等设计为一个公用库,从而使得应用与这些功能具有更低的耦合性,而且更加灵活、提高利用率及运维性。

而这种方案也存在不足之处:
- 如果将类似Finagle、 Proxygen或者Zipkin的库集成现有的系统中,仍然需要花费大量的时间、人力将其集成到现有生态圈,甚至需要调整现有应用的代码。
- 缺乏多语言支持,由于这些库只针对某种语言或者少数几种语言,这使得在一个多技术栈的公司中需要限制开发语言和工具的选择
- 虽然公共库作为一个独立的整体,但在管理复杂性和运维性这些方面仍然有更大的提升空间
- 公共库并不能完全使得开发人员只关注业务代码逻辑,仍然需要对公共库有很深的认识
针对上面面临的问题,发现了更好的解决方案,OSI定义了开放系统的层次结构、层次之间的相互关系以及各层所包括的可能的任务,上层并不需要对底层具体功能有详细的了解,只需按照定义的准则协调工作即可。因此,我们也可参照OSI七层模型将公用库设计为位于网络栈和应用业务逻辑之间的独立层,即透明网络代理,新的独立层完全从业务逻辑中抽离,作为独立的运行单元,与业务不再直接紧密关联。通过在独立层的透明网络代理上实现负载均衡、服务发现、熔断、运行时动态路由等功能,该透明代理在业界有一个非常新颖时髦的名字:Service Mesh。率先使用这个Buzzword的产品恐怕非Buoyant的Linkerd(https://linkerd.io/)莫属了,随后Lyft也发布了他们的Service Mesh实现Envoy(https://github.com/envoyproxy/envoy),之后Istio(https://istio.io/)也迎面赶上,成为Service Mesh领域非常热门的一个项目。

在这种方案中,Service Mesh作为独立运行层,它很好地解决了上述所面临的挑战,使应用具备处理网络弹性逻辑和提供可靠交互请求的能力。它使得耦合性更低、灵活性更强,跟现有环境的集成时间和人力代价更小,也提供多语言支持、多协议支持,运维和管理成本更低。最主要的是开发人员只需关注业务代码逻辑,而不需要关注业务代码以外的其他功能,即Service Mesh对开发人员是透明的。
二、服务网格基本概念¶
Service Mesh的发起人、先驱者,Buoyant公司的CEO William Morgan,他对Service Mesh的定义如下:
- 专用基础设施层:独立的运行单元
- 包括数据平面和控制平面:数据平面负责交付应用请求,控制平面控制服务如何通信
- 轻量级透明代理:实现形式为轻量级网络代理
- 处理服务间通信:主要目的是实现复杂网络中服务间通信
- 可靠地交付服务请求:提供网络弹性机制,确保可靠交付请求
- 与服务部署一起,但服务感知不到:尽管跟应用部署在一起,但对应用是透明的
Service Mesh架构如下,告诉Service Mesh控制层和数据层在微服务架构中所处位置、服务间通信模式以及提供的各种功能

三、服务网格功能¶
服务网格作为透明代理,可以运行于任何基础设施环境,而且和应用非常靠近,服务网格的功能大致如下:
- 负载均衡:运行环境中微服务实例通常处于动态变化状态,而且经常可能出现个别实例不能正常提供服务、处理能力减弱、卡顿等现象。但由于所有请求对Service Mesh来说是可见的,因此可以通过提供高级负载均衡算法来实现更加智能、高效的流量分发,降低延时,提高可靠性
- 服务发现:以微服务模式运行的应用变更非常频繁,应用实例的频繁增加或减少带来的问题就是如何精确地发现新增实例以及避免将请求发送给已不存在的实例变得更加复杂。Service Mesh可以提供简单、统一、平台无关的多种服务发现机制,如基于DNS、K/V键值对存储的服务发现机制
- 熔断:动态的环境中服务实例中断或者不健康导致服务中断可能会经常发生,这就要求应用或其他工具具有快速监测并从负载均衡池中移除不提供服务实例的能力,这种能力也称熔断,以此使得应用无需消耗更多不必要的资源不断地尝试,而是快速失败或者降级,甚至这样可避免一些潜在的关联性错误。而Service Mesh可以很容易实现基于请求和连接级别的熔断机制
- 动态路由:随着服务提供商以提供高稳定性、高可用性及高SLA服务为主要目标,出现的各种应用部署策略都尽可能达到无服务中断部署,以此避免变更而导致服务的中断和稳定性降低,例如Blue/Green部署、Canary部署,但是实现这些高级部署策略通常非常困难。关于应用部署策略可参考https://thenewstack.io/deployment-strategies/的内容,其对各种部署策略做了详细的比较。如果运维人员想要轻松地将应用流量从staging环境切换到生产环境,从一个版本到另一个版本,或者从一个数据中心到另一个数据中心,甚至可以通过一个中心控制层控制多少比例的流量被切换。服务网格提供的动态路由机制和特定的部署策略(如Blue/Green部署)结合起来,实现上述目标将会变得更加容易。
- 安全通信:无论何时,安全在整个公司、业务系统中都占据着举足轻重的位置,也是非常难以实现和控制的部分。而在微服务环境中,不同的服务实例间的通信变得更加复杂,那么如何保证这些通信是在安全、授权的情况下进行的就非常重要。通过将安全机制(如TLS加解密和授权)实现在服务网格上,不仅可以避免在不同应用上的重复实现,而且很容易在整个基础设施层更新安全机制,甚至无须对应用做任何操作。
- 多语言支持:由于Service Mesh作为独立运行的透明代理,很容易支持多语言
- 多协议支持:同多语言支持一样,实现多协议支持也非常容易
- 指标和分布式追踪:Service Mesh对整个基础设施层的可见性使得它不仅可以暴露单个服务的运行指标,而且可以暴露整个集群的运行指标
- 重试和最后期限:Service Mesh的重试功能避免将其嵌入到业务代码,同时最后期限使得应用允许一个请求的最长生命周期,而不是无休止的重试
对这些功能,概括起来,即Service Mesh使得微服务具有下列性能
- 可见性(visiblity):运行时指标、分布式跟踪
- 可管理性(manageablity):服务发现、负载均衡、运行时动态路由
- 健壮性(resilience):超时重试、请求最后期限、熔断机制
- 安全性(security):服务间访问控制、TLS加密通信
四、服务网格产品¶
常见的服务网格产品有Linkerd、Envoy、Istio、Conduit
4.1 Linkerd¶
Linkerd是Buoyant公司2016年率先开源的高性能网络代理程序,是业界的第一款Service Mesh产品,甚至可以说Linkerd的诞生即Service Mesh时代的开始,其引领后来Service Mesh的快速发展。其主要用于解决分布式环境中服务之间通信面临的一些问题,比如网络不可靠、不安全、延迟丢包等问题。Linkerd使用Scala语言编写,运行于JVM,底层基于Twitter的Finagle库,并对其做相应的扩展。最主要的是Linkerd具有快速、轻量级、高性能等特点,每秒以最小的时延及负载处理万级请求,易于水平扩展,经过产线测试及验证,可运行任何平台的产线级Service Mesh工具。Linkerd除了具有上述所阐述的Service Mesh的功能外,还具有下列功能:
- 支持多平台,可运行于多种平台,比如Kubernetes、DC/OS、Docker甚至虚拟机或者物理机
- 无缝集成多种服务发现工具
- 支持多协议,如gRPC、HTTP/2、HTTP/1.x,甚至可通过linkerd-tcp支持TCP协议
- 支持与第三方分布式追踪系统Zipkin
- 灵活性、扩展性高,可通过其提供的接口开发自定义插件
Service Mesh由数据平面和控制平面构成,事实上,Linkerd本身是数据平面,负责将数据路由到目标服务,同时保证数据在分布式环境中传输是安全、可靠、快速的。另外,Linkerd还包括控制平面组件Namerd,通过控制平面Namerd实现中心化管理和存储路由规则、中心化管理服务发现配置、支持运行时动态路由以及暴露Namerd API管理接口。
4.2 Envoy¶
同Linkerd一样,Envoy也是一款高性能的网络代理程序,于2016年10月份由Lyft公司开源,为云原生应用而设计,可作为边界入口,处理外部流量,当然,也作为内部服务间通信代理,实现服务间可靠通信。Envoy的实现借鉴现有产线级代理及负载均衡器,如Nginx、HAProxy、硬件负载均衡器及云负载均衡器的实践经验,同时基于C++编写及Lyft公司产线实践证明,Envoy性能非常优秀、稳定。Envoy既可用作独立代理层运行,也可作为Service Mesh架构中数据平面层,因此通常Envoy跟服务运行在一起,将应用的网络功能抽象化,Envoy提供通用网络功能,实现平台及语言无关。作为Service Mesh工具,Envoy除了支持上述Service Mesh的功能外,还有下列功能:
-
大规模负载下,Envoy保证P99延时非常低
-
优先支持HTTP/2和gRPC,同时支持Websocket和TCP代理
- API驱动的配置管理方式,支持动态管理、更新配置以及无连接和请求丢失的热重启功能
- L3/L4层过滤器形成Envoy核心的连接管理功能
- 通过与多种指标收集工具及分布式追踪系统集成,实现运行时指标收集,分布式追踪,提供整个系统及服务的运行时可见性
- 内存资源使用率低,sidecar是Envoy最常用的部署模式
4.3 Istio¶
Istio为一款开源的为微服务提供服务间连接、管理以及安全保障的平台软件,支持运行在Kubernetes、Mesos等容器管理工具,但不限于Kubernetes、Mesos,其底层依赖于Envoy。Istio提供一种简单的方法实现服务间的负载均衡、服务间认证、监控等功能,而且无需应用层代码调整。其控制平面由Mixer、Pilot及Citadel组成,数据平面由Envoy实现,通常情况下,数据平面代理Envoy以sidecar模式部署,使得所有服务间的网络通信均由Envoy实现,而Istio的控制平面则负责服务间流量管理、安全通信策略等功能。由于其底层是Envoy,Envoy支持的各种功能以及Service Mesh要求的功能Istio均支持,除此之外还有以下功能:
- 完善的流量管理机制,如故障注入
- 增强服务间安全保障,如服务身份认证,密钥管理和基于RBAC的访问控制策略
- 支持多平台部署
4.4 Conduit¶
Conduit(https://conduit.io/)于2017年12月发布,作为由Buoyant继Linkerd后赞助的另一个开源项目。Conduit旨在彻底简化用户在Kubernetes使用服务网格的复杂度,提高用户体验,而不是像Linkerd一样针对各种平台进行优化。Conduit的主要目标是轻量级、高性能、安全并且非常容易理解和使用。同Linkerd和Istio, Conduit也包含数据平面和控制平面,其中数据平面由Rust开发,使得Conduit使用极少的内存资源,而控制平面由Go开发。Conduit依然支持Service Mesh要求的功能,而且还包括以下功能:
- 超级轻量级及极快的性能,亚毫秒级P99延迟
- 专注于支持Kubernetes平台,提高运行在Kubernetes平台上服务的可靠性、可见性及安全性
- 支持gRPC、HTTP/2和HTTP/1.x请求及所有TCP流量
Conduit以极简主义架构,以零配置理念为中心,旨在减少用户与Conduit的交互,实现开箱即用。作为Buoyant公司的第二款Service Mesh软件,其设计依据Linkerd在产线的实际使用经验而设计,其设计目标即专为解决用户管理产线环境运行的分布式应用程序所面临的挑战,并以最小复杂性作为设计基础。