目录
背景
近年来,微服务架构已渐趋成熟,微服务架构使得应用程序更易于扩展,更快开发,从而加速创新并缩短新功能的上线时间,与单体应用相比,微服务能更好的满足现在业务快速变化的需要。但是与其他现存的架构和解决方案一样,微服务也不是银弹,尽管它解决了单体服务的很多问题,但也带来了负载均衡、服务治理、服务注册发现、如何拆分服务等问题。
Service Mesh (服务网格)是一个用于处理服务和服务之间通信的基础设施层,它最重要的变革,就是引入了数据面和控制面的概念:通过 sidecar 模式将原本在 SDK 中的代码独立出来,用控制面代替配置中心的部分功能,以透明代理的形式提供安全、快速、可靠的服务间通信,同时也能实现微服务所需的基本组件功能。
实际上,Service Mesh 需要的基础组件和传统的微服务并没有太大的差别,很多公司选择自研控制面的原因,很多就是出于兼容老的微服务的基础组件的考虑,也可以把 Service Mesh 看作是分布式的微服务代理。
任何架构都不是凭空而来,而是切实地解决了某些痛点和业务场景。
Service Mesh 这个架构之所以这么迅速地被各大公司实施落地,正是切中了传统微服务架构中诸如升级成本高、中间件演变困难、缺乏统一管控手段、治理功能不全的痛点,解决了实际的问题。下表是各大公司 Service Mesh 架构的落地情况。
公司 | 方案 | 落地情况 |
蚂蚁金服 | 数据面MSON+自研控制面 | 核心交易链路 |
阿里 | Istio+Envoy二次开发 | 闲鱼 |
字节跳动 | 自研控制面+Envoy二次开发 | 所有服务 |
网易 | Istio+Envoy | 网易严选 |
本系列学习结构图
微服务架构的演进
单体服务架构的缺点
单体服务架构所有的功能模块都在一个代码仓库,并且部署在同一个进程内,比如最常见的博客系统 WordPress。存在如下问题:
- 人员协作问题
所有人员都在一个代码仓库开发,当开发人数过多时,新成员改动的模块代码可能会涉及到其它人的代码,就会产生不必要的沟通成本
- 部署问题
当不同人员开发的不同模块,同时需要上线时,如果分别将分支代码合并到主干代码,再按照流程上线,就会产生先后顺序的等待时间;如果合并到一起上线,线上功能一旦出问题,就需要花费大量时间排查是哪个模块出现的问题。
- 架构演进问题
当应用中的某个模块,遇到了性能瓶颈,我们希望以另外一种语言重新编写此模块,这个时候单体应用完全无法演进。如果我们要替换语言,就得重写整个应用,花费的成本就会很大。
- 程序稳定性问题
在单体应用中,某个模块出现bug时,可能会导致整个网站不可用。
微服务优势
微服务架构可使应用程序更易于扩展、更快开发,从而加速新功能上线。与单体应用相比,微服务能够更好地满足互联网时代业务快速变化的需要。
- 敏捷开发
- 技术自由
微服务团队,可以选择自己喜欢的技术或者语言,并不需要局限在同一种语言上。
- 弹性扩缩容
微服务可以灵活地应对服务的扩缩容问题。在单体服务中,假设某个接口在高峰期有较大的访问量,需要比平时增加一些机器应对,但我们很难根据 CPU 的水位做出准确的判断,因为所有接口都在一个应用中,而在微服务中做出这种判断要容易得多。
- 组织匹配
康威定律:如果技术架构和组织架构不匹配,会造成严重的跨部门沟通效率问题。【架构演进重要原则】
微服务架构可以非常好地与组织架构相匹配,比如一个短视频业务,在业务发展到一定阶段,很自然地会拆分出用户增长部门和内容部门。而我们的微服务架构,也可以将不同的服务拆分到相应的部门中,降低维护和沟通的成本。
虽然微服务解决了单体服务很多问题,但同时也带来了单体服务中一些没有的问题,比如负载均衡、服务治理、服务注册发现、如何拆分服务等,当然其中的大部分问题,都可以通过技术手段解决,但也增加了系统的复杂性。
Service Mesh 的演进历程
sidecar 时代
在2013年,作为微服务架构的大规模应用方 Netflix, 就发现了微服务架构在跨语言上的问题。Netflix 大量使用 Java 技术栈,,但因为公司的业务发展,使用单一技术栈是不现实的。语言总是有特定的应用场景,这个时候 Netflix 发现开发多语言的 SDK 要耗费大量的人力,毕竟 Spring Cloud 里的组件可不是一般的多,为每个语言开发一套,显然得不偿失,所以就想到了 sidecar 模式,把 SDK 里的功能转移到 sidecar 中。
sidecar, 其实就是一个部署在本地的代理服务器,它既接管了入口流量,也接管了出口流量。其实这种模式要追溯到 Web Server 的时代,比如 Nginx + Php-fpm 这种模式,实际上 Nginx 也是充当了 sidecar 的角色,只是通信协议由比较常见的 HTTP ,变成了 FastCGI ,另外 Nginx 只是代理了入口流量,并未代理出口流量。
初代 Service Mesh
在 sidecar 模式中,是为了解决公司非主力语言的 SDK 开发问题,非主力语言通过 sidecar 连接,而主力语言还是通过 SDK 的方式。但是,随着技术的不断发展,人们意识到了统一流量处理模型的重要性。于是诞生了第一代 Service Mesh,比如:Linkerd 和 Envoy。已经不再依赖特定的基础设施,最重要的是它的出发点已经不是解决多语言的问题,而是从统一流量处理模型的角度,形成了一套统一的流量控制的解决方案。
但是有一个致命问题(也一直存在于微服务架构中),就是缺乏统一的管控手段,比如 sidecar 的服务治理相关配置文件的维护,可能需要运维手动维护、无法集中管理,因为这个原因,控制面诞生了。
新一代 Service Mesh
代表作品就是 Istio,引入了控制面的概念。Istio 使用 Envoy 作为数据面,控制面和 Kubernetes 深度绑定,早期版本将流量治理的功能放在 mixer 中,形成了一套完整的 Service Mesh 解决方案。控制面负责了资源管理、配置下发、证书管理等功能,解决了数据面配置难以管理的问题。
Service Mesh 的优势
- 语言无关
传统的微服务架构要为各种语言开发 SDK ,而 Service Mesh 将 SDK 的功能集成到 sidecar 中,实现了真正的语言无关性。
- 基础设施独立演进
Service Mesh 架构将框架中和业务无关的通用功能放在 sidecar 中,升级时只要升级 sidecar 就可以了,这样做到了基础设施的独立演进。写框架的时候不用再考虑太多的向后兼容性,降低了编写代码的心智负担。
- 可观测性
可观测性一般包含两个部分:监控报警和链路追踪。
监控报警可以通过数据面的 Metrics 集成,无感知地做到系统监控报警,减少了业务和框架的重复工作。
链路追踪,因为需要通过 header 将 traceid 传递下去,所以还是需要客户端的 SDK 将 traceid 通过 header 传递。不过这个做法也简化了 Trace SDK 的封装。
- 边缘网关统一
sidecar 本身就是一个网关/反向代理,自然可以将以前 Nginx/Kong 之类的系统网关迁移到 sidecar 上来,这样就可以维护一套统一的代码。
也可以进一步将以前边缘网关的工作,比如鉴权、 trace 初始化等工作下沉到 sidecar 上,进一步简化系统网关的功能。
Service Mesh 的基础组件及常见名词
数据面(Data Plane)
负责数据的转发,一般我们常见的通用网关、Web Server,比如 Nginx、Traefik 都可以认为是数据面的一种。在 Service Mesh 中,Envoy 可以说是最知名的数据面了。
另外数据面并非局限于网关类产品,实际上某些 RPC 框架也可以充当数据面,比如 gRPC 就已经支持完整的 xDS(数据面和控制面的交互协议),也可以当作数据面使用。一般我们把负责数据转发的数据面称为 sidecar(边车)。
控制面(Control Plane)
通过 xDS 协议对数据面进行配置下发,以控制数据面的行为,比如路由转发、负载均衡、服务治理等配置下发。
常见基础组件
服务注册中心:服务间通信的基础组件。服务通过注册自身节点,让调用方服务发现被调方服务节点,以达到服务间点对点通信的目的。
配置中心:用于服务的基础配置更新,以达到代码和配置分离的目的。减少服务的发布次数,配置发布可以更快更及时地变更服务。
API 网关:通过统一的网关层,收敛服务的统一鉴权层、链路 ID 生成等基础服务,并聚合后端服务为客户端提供 RESTful 接口。另外 API 网关也负责南北向流量(外网入口流量)的流量治理。
服务治理:通过限流、熔断等基础组件,杜绝微服务架构出现雪崩的隐患。
链路追踪:通过 trace 将整个微服务链路清晰地绘制出来,并进行精准的故障排查,极大地降低了故障排查的难度。
监控告警:通过 Prometheus 和 Grafana 这样的基础组件,绘制服务状态监控大盘,针对资源、服务、业务各项指标,做精准的监控报警。
常见的名词解释
Upstream: 上游服务,如果 A 服务调用 B 服务,在 A 服务的视角来看,B 服务就是上游服务,但是在中文的语境中,经常被叫作“下游服务”。所以为了避免语言上的歧义,会直接使用upstream,而不是中文翻译。在中文的语境中,可以称它为服务端或者被调用方。
Downstream: 下游服务,如果 A 服务调用 B 服务,在 B 服务的视角中,A 服务就是上游服务。在中文的语境中,可以称它为客户端或者调用方。
Endpoint:指的是服务节点,比如 A 服务有 192.168.2.11 和 192.168.2.12 两个服务节点。
Cluster:指的是服务集群,比如 A 服务有 192.168.2.11 和 92.168.2.12 两个服务节点,那么A服务就是 Cluster,也可以直接理解为 Service。
Node:在 kubernetes 语境中,指的是承载 pod 的服务器,但在微服务的语境中,更多的等同于Endpoint。
Route:指的是 Service Mesh 中的路由配置,比如 A 服务访问 B 服务,要匹配到一定的规则,比如 header 中要带有服务名(-H servicename:B),才能够拿到 B 服务的访问方式,通过服务发现或者静态列表访问到 B 服务的节点。
Listener:指的是 Service Mesh 的监听端口,通常我们访问 Service Mesh 的数据面,需要知道数据面的监听端口。