近年来,微服务架构已渐趋成熟,微服务架构使得应用程序更易于扩展,更快开发,从而加速创新并缩短新功能的上线时间,与单体应用相比,微服务能更好的满足现在业务快速变化的需要。但是与其他现存的架构和解决方案一样,微服务也不是银弹,尽管它解决了单体服务的很多问题,但也带来了负载均衡、服务治理、服务注册发现、如何拆分服务等问题。
Service Mesh (服务网格)是一个用于处理服务和服务之间通信的基础设施层,它最重要的变革,就是引入了数据面和控制面的概念:通过 sidecar 模式将原本在 SDK 中的代码独立出来,用控制面代替配置中心的部分功能,以透明代理的形式提供安全、快速、可靠的服务间通信,同时也能实现微服务所需的基本组件功能。
实际上,Service Mesh 需要的基础组件和传统的微服务并没有太大的差别,很多公司选择自研控制面的原因,很多就是出于兼容老的微服务的基础组件的考虑,也可以把 Service Mesh 看作是分布式的微服务代理。
任何架构都不是凭空而来,而是切实地解决了某些痛点和业务场景。
Service Mesh 这个架构之所以这么迅速地被各大公司实施落地,正是切中了传统微服务架构中诸如升级成本高、中间件演变困难、缺乏统一管控手段、治理功能不全的痛点,解决了实际的问题。下表是各大公司 Service Mesh 架构的落地情况。
公司 | 方案 | 落地情况 |
蚂蚁金服 | 数据面MSON+自研控制面 | 核心交易链路 |
阿里 | Istio+Envoy二次开发 | 闲鱼 |
字节跳动 | 自研控制面+Envoy二次开发 | 所有服务 |
网易 | Istio+Envoy | 网易严选 |
本系列学习结构图
单体服务架构所有的功能模块都在一个代码仓库,并且部署在同一个进程内,比如最常见的博客系统 WordPress。存在如下问题:
所有人员都在一个代码仓库开发,当开发人数过多时,新成员改动的模块代码可能会涉及到其它人的代码,就会产生不必要的沟通成本
当不同人员开发的不同模块,同时需要上线时,如果分别将分支代码合并到主干代码,再按照流程上线,就会产生先后顺序的等待时间;如果合并到一起上线,线上功能一旦出问题,就需要花费大量时间排查是哪个模块出现的问题。
当应用中的某个模块,遇到了性能瓶颈,我们希望以另外一种语言重新编写此模块,这个时候单体应用完全无法演进。如果我们要替换语言,就得重写整个应用,花费的成本就会很大。
在单体应用中,某个模块出现bug时,可能会导致整个网站不可用。
微服务架构可使应用程序更易于扩展、更快开发,从而加速新功能上线。与单体应用相比,微服务能够更好地满足互联网时代业务快速变化的需要。
微服务团队,可以选择自己喜欢的技术或者语言,并不需要局限在同一种语言上。
微服务可以灵活地应对服务的扩缩容问题。在单体服务中,假设某个接口在高峰期有较大的访问量,需要比平时增加一些机器应对,但我们很难根据 CPU 的水位做出准确的判断,因为所有接口都在一个应用中,而在微服务中做出这种判断要容易得多。
康威定律:如果技术架构和组织架构不匹配,会造成严重的跨部门沟通效率问题。【架构演进重要原则】
微服务架构可以非常好地与组织架构相匹配,比如一个短视频业务,在业务发展到一定阶段,很自然地会拆分出用户增长部门和内容部门。而我们的微服务架构,也可以将不同的服务拆分到相应的部门中,降低维护和沟通的成本。
虽然微服务解决了单体服务很多问题,但同时也带来了单体服务中一些没有的问题,比如负载均衡、服务治理、服务注册发现、如何拆分服务等,当然其中的大部分问题,都可以通过技术手段解决,但也增加了系统的复杂性。
在2013年,作为微服务架构的大规模应用方 Netflix, 就发现了微服务架构在跨语言上的问题。Netflix 大量使用 Java 技术栈,,但因为公司的业务发展,使用单一技术栈是不现实的。语言总是有特定的应用场景,这个时候 Netflix 发现开发多语言的 SDK 要耗费大量的人力,毕竟 Spring Cloud 里的组件可不是一般的多,为每个语言开发一套,显然得不偿失,所以就想到了 sidecar 模式,把 SDK 里的功能转移到 sidecar 中。
sidecar, 其实就是一个部署在本地的代理服务器,它既接管了入口流量,也接管了出口流量。其实这种模式要追溯到 Web Server 的时代,比如 Nginx + Php-fpm 这种模式,实际上 Nginx 也是充当了 sidecar 的角色,只是通信协议由比较常见的 HTTP ,变成了 FastCGI ,另外 Nginx 只是代理了入口流量,并未代理出口流量。
在 sidecar 模式中,是为了解决公司非主力语言的 SDK 开发问题,非主力语言通过 sidecar 连接,而主力语言还是通过 SDK 的方式。但是,随着技术的不断发展,人们意识到了统一流量处理模型的重要性。于是诞生了第一代 Service Mesh,比如:Linkerd 和 Envoy。已经不再依赖特定的基础设施,最重要的是它的出发点已经不是解决多语言的问题,而是从统一流量处理模型的角度,形成了一套统一的流量控制的解决方案。
但是有一个致命问题(也一直存在于微服务架构中),就是缺乏统一的管控手段,比如 sidecar 的服务治理相关配置文件的维护,可能需要运维手动维护、无法集中管理,因为这个原因,控制面诞生了。
代表作品就是 Istio,引入了控制面的概念。Istio 使用 Envoy 作为数据面,控制面和 Kubernetes 深度绑定,早期版本将流量治理的功能放在 mixer 中,形成了一套完整的 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 上,进一步简化系统网关的功能。
负责数据的转发,一般我们常见的通用网关、Web Server,比如 Nginx、Traefik 都可以认为是数据面的一种。在 Service Mesh 中,Envoy 可以说是最知名的数据面了。
另外数据面并非局限于网关类产品,实际上某些 RPC 框架也可以充当数据面,比如 gRPC 就已经支持完整的 xDS(数据面和控制面的交互协议),也可以当作数据面使用。一般我们把负责数据转发的数据面称为 sidecar(边车)。
通过 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 的数据面,需要知道数据面的监听端口。
因篇幅问题不能全部显示,请点此查看更多更全内容