一文读懂微服务与服务网格WHAT-WHY-and-HOW-TO-DO

42次阅读

共计 7094 个字符,预计需要花费 18 分钟才能阅读完成。

作者注:
联系方式 leontian1024@gmail.com || github.com/XinyaoTian
新人入行,非常期待能与各位大牛们讨论,感谢各位的阅读,希望对您有所帮助。

一切都要从云计算和容器技术的出现说起 …

相信每一位老道的开发者和软件工程师们都会有过,曾经 (也许现在仍然) 被庞大且复杂的软件系统所支配的恐惧。随着软件版本的迭代和开发团队人员规模的扩大,曾经那个小巧别致、设计精良的软件或应用一去不返,如今已经变得遍地狼藉,惨不忍睹——混乱的接口、不规范的调用、像贴狗皮膏药一般贴上去的新功能组件……曾经那个赏心悦目的应用,如今看了就反胃。这一切都预示着一个问题:开发软件的方式需要改变。

纵使有那么多种软件开发模式,但如果不能从底层技术上实现对设计的约束,问题将会随着时间的推移,最终暴露出来。譬如“高内聚,低耦合”的设计理念,如果不能从底层就实现模块间的隔离,而依靠开发时的技巧和经验,那么最终这个软件依旧会变得一团糟(因为团队会有新人的加入、即使经验老道的开发者也会有头脑发昏的时候……)。因此,“不这么写就无法运行”这样的硬性要求就很有必要了。

云计算和容器技术的出现,非常及时地帮助我们解决了这一难题。云计算的高弹性和按需分配、容器技术的快速启停和隔离,都很好的帮助了我们减少运维开销,且对于不同模块实现操作系统级别的隔离。在这两种关键技术出现前后,软件架构的差别如下图所示:

(上图: 单体应用 与 微服务应用)

以 Web 应用为例,按照之前的开发方式,我们往往会选用一个功能齐全但相当复杂的开发框架(比如 JAVA 开发常用的 SpringBoot),然后在这个框架的基础上,根据开发经验和框架的功能将整个应用分层(比如最常用的表示层、业务逻辑层和数据资源层的分层方法),之后根据需求分析得出的各个功能,通过不同目录、文件和函数的方式分别开发,最终一个完整的 Web 应用被开发出来。其过程如下图所示。

(上图: 基于框架的软件开发架构网格)

通过使用框架,我们把软件在层次上分为三层,而之后的每个功能,就相当于纵向地添加一个新列。如果以这种方式看待一个应用,那么我们就可以将任何基于这种开发方法的应用看作一个 3 行 n 列的表格或矩阵了。

然而,这种非常普及的开发方式仍然有一些问题 …

虽然这种开发方式已经非常普及,非常成熟,但其仍有许多有待改进的地方。比如:

依赖和库过于庞杂。理想情况下,我们希望针对每一个模块,单独管理其相应的依赖和库,而不是以整个应用作为单位来管理。

无法改变层次结构。某种层次结构对于某些业务需求来说很棒,但对于另外一些也许就显得不那么合适了。使用这种方式开发,几乎所有功能都要遵从这种既定的层次开发(比如写 UI、写业务逻辑、写数据库层)。而对于目前日渐快速的迭代和敏捷的开发思想,我们需要一种更加灵活轻便的方式进行开发。

无法实现跨语言开发。我们知道,几乎每种编程语言都有自己最擅长的领域。也许某些功能选用其他编程语言的开发效率更高,运行效果更好。然而,使用这种方式我们往往只能使用选定的框架所支持的语言。

模块的独立性不足。单纯通过函数和文件来进行隔离,隔离性仍然不够。一个疏忽或是加急上线就会让之前良好的高内聚低耦合的良好软件结构灰飞烟灭。因此,我们需要更加底层的机制来硬性约束我们实现“高内聚低耦合”,把“应该这么做”变为“必须这么做”。

而伴随着云计算和容器技术的发展,微服务的出现,恰巧将这些问题迎刃而解。

(上图: 容器技术的基本层次结构)

先来说说容器技术的代表 —— Docker

Docker 可以看作是轻量级的虚拟机——它可以通过“容器镜像”快速启动和停止预先配置好的相应运行环境。每一个容器都可以被看作一个独立的操作系统,相互隔离,互不干扰。因此,借助 docker 我们就可以针对一个应用中不同的功能,为其独立定制运行环境,独立装载依赖和工具库,独立运行独立停止,独立升级,每个功能可以使用其最适合的编程语言进行开发,也将整个应用拘泥于一种框架了。利用 docker 将每个模块在操作系统层面进行隔离,对于每个模块都可以独立管理其生命周期了,这就是“微服务”中“微”字的具体含义。

微服务开发的重点

基于这种开发方式,每个功能模块可以被独立开发,独立部署,独立运行,独立进行版本控制,等等。而对于规模比较庞大的系统来说,这种利用微服务架构所开发的应用,其天然的优势就更能体现出来了——即每个模块可以独立的团队由单独负责。因此,微服务开发中的第一个重点,就是要有非常明确的需求,以及一个经验丰富的架构师,在设计之初就对各个功能模块进行合理的规划和拆分。

在整个应用设计之初由总设计师或架构师设计好各个功能模块后,第二个重点就来了:设计微服务中各个模块间的调用接口——通常由 Rest API 或者 gRPC 组成——来负责模块之间的交互,这就是微服务的第二个重点。良好的接口设计将会使你的应用结构清晰,开发起来事半功倍。而且每个独立团队在开发时都能感受到明显的模块边界,且可以放心利用模拟数据和测试数据进行开发(只要符合接口规则的数据就能用,不用操心其他模块是如何实现的),从而真正实现每个团队富有效率的并行开发。

利用微服务架构开发除了上述好处之外,在运维方面的优势也非常直观——我们可以清晰地观测到整个系统的资源瓶颈在何处(哪个容器的资源开销最大),从而实现有针对性的“定向扩缩容”。利用微服务架构前后的扩缩容机制如下图所示意。

(上图: 单体应用和微服务应用最直观的差别:定向扩缩容 示意图)

将庞大的单体应用逐步改造成微服务应用

在看完上面的介绍后,相信饱受单体应用折磨的各位读者已经对微服务开发已经跃跃欲试了。但是,对于一个正在运行并使用的应用来说,完完全全从零开始开发并不现实。对于一个已经成熟并正在使用的单体应用系统来说,我们可以通过自己的努力,将一个单体应用在几次迭代过程中,逐渐改变为微服务应用。如何办到呢?下面放一张图片来帮助您激发灵感:

(上图: 热带雨林中的参天大树与附着在其上的藤蔓)

没错,就像您所想到的那样:在这幅图中,庞大的树木代表着我们原有的单体项目,而树外所覆盖着的藤蔓就象征着微服务组件。在将您的单体应用微服务化时,也可以采用这种方式,即:新的功能使用微服务架构来开发,通过对原有的单体应用暴露 IP 和端口号的方式供其进行调用和使用。

利用这种方式,您之后新开发的功能所对应的软件实体就都是基于微服务架构的了。这样随着时间的推移和版本的逐渐迭代,采用微服务架构所开发的部分所占比例越来越大,最后原来的单体应用也逐渐变为了整个应用中的一个独立服务,您的软件架构就彻底地完成了微服务化。

这种改造方式来源于 Chris Richardson 的系列博文中的一篇,如果您对这方面内容感兴趣的话,欢迎您移步其博客一探究竟(博客的中文翻译链接: https://www.jianshu.com/p/29f…)。由于内容过多,在此不再展开讨论一一赘述。

Happy Ending,十全十美了?

显然不是,技术的发展从来没有止境,也不存在止镜。微服务的出现,虽然解决了传统软件开发结构庞大、模块复杂等诸多难点,但是解决了原有的问题后,新的问题又浮出了水面:微服务应用的每个基本单元之间调用关系复杂、网络位置处于动态变化、且每个组件的生命周期都各自独立,因此难以实现统一的管理。

这里说的可能有些抽象,那么就为大家举一个小例子:请大家设想一个简单的场景:每一个微服务组件都有一个自己独有的网络位置(在因特网中就是我们最常用的 IP 和端口号),以此来唯一确定一个服务;其他服务若想调用本服务,就需要知道该服务的 IP 和端口号,以此对其发送网络请求。

细心的朋友可能已经察觉到问题了:对于一个微服务架构的应用场景,微服务的每一个组件都是不断处于动态扩缩容的状态中的,可能上一秒这个 IP 和端口号还对应着相应的组件,下一秒这个组件就由于当前访问量的下降而被节约成本,自动地缩容释放掉了。如果这时其他服务再来访问这个 IP 和端口号,那一定会出现找不到服务等各种故障。

(上图: 多变的网络位置是微服务管理中的一大难题)

架构改变所带来的诸如此类的问题还有许多,在此就不一一列举了。准备将自己的软件架构进行微服务化的朋友们需要三思:改变一种软件架构可能会解决许多曾经的架构所存在的问题,但同时也会带来很多原有架构不会出现的新问题。

不过幸好微服务架构已经有不少国内外的大公司和优秀团队作为先驱,率先摸着石头过了一次河,并且告诉了我们许多过河的宝贵经验,甚至已经有许多工具和开源项目被开发出来,帮助我们解决刚才分析到的种种问题了。

在目前种种微服务场景的解决方案中,有两种是使用最为普遍、同时也广受好评的。它们分别是较早出现的 Spring Cloud 框架,以及近几年微服务领域最为流行的 Service Mesh 微服务管理框架。由于 Service Mesh 所带来的“业务代码零侵入”、“直接与容器管理框架 (如 K8s) 集成”等诸多优点,因此基于 Service Mesh 的微服务开发和蔚云方式正在逐渐成为主流,特别是 Google 宣布了其 Istio 项目可以与 K8s 完美集成后,国内外社区的开发者对 Service Mesh 的发展更是翘首以盼。下面就对 Service Mesh 进行一下简单的介绍。

何为 “Service Mesh”

“A service mesh is a dedicated infrastructure layer for handling service-to-service communication.“—— William Morgan(Founder of Service Mesh)

Service Mesh 是一个专注于处理服务间通信的基础设施层。
云原生应用有着复杂的服务拓扑,而 Service Mesh 保证请求可以在这些拓扑中可靠地穿梭。在实际应用当中,Service Mesh 通常是由一系列轻量级的网络代理组成的,它们与应用程序部署在一起,但应用程序不需要知道它们的存在。

(上图: Service Mesh 示意图——帮助您管理错误复杂的微服务应用)

技术起源与出现背景

Service Mesh 的概念最早由前 twitter 的基础设施工程师 William Morgan 于 2017 年 4 月 25 日提出。虽然在此之前,微服务领域也有类似的概念被提出或用于开发项目,但在业界始终没有一个统一的名称。William Morgan 在自己的博文 “What’s a service mesh? And why do I need one?” 中正式给 Service Mesh 做出了权威的定义。至此,”Service Mesh” 这个名词正式出现在各大公司以及技术社区的视野中。

随着云计算的普及,越来越多的开发者和企业开始使用“微服务”的开发模式。这种开发模式拥有诸如耦合度低、跨语言开发、更小粒度扩容等许多优势,但同样也面临着许多挑战,就如我们上文所分析的那样。

Service Mesh 的发展

Service Mesh 从出现至今总共经历了三个阶段:微服务初期、Sidecar 时期和 Service Mesh 时期。

微服务初期

在微服务初期 (2015 年前) 开发微服务应用的过程中,我们需要重复性地处理一系列基础工作,比如:服务注册、服务发现、得到服务实例后的负载均衡、熔断机制等。这些工作在 Service Mesh 出现之前统统都要开发人员在项目中用代码解决并实现,导致应用程序中加入了大量的非功能性代码。即使使用类似 Netflix OSS 的库和 Spring Cloud 的框架,开发人员依然面临着需掌握内容多、技术门槛高等诸多困难。

Sidecar 的出现

(上图: Sidecar,中文意思为摩托车的跨斗,不由赞叹命名的非常生动)

“Sidecar” 这个词,本人使用 Google 搜索引擎同时检索 sidecar 和 microservices 这两个关键字并按时间顺序排序,最早出现的检索结果是在 2014 年 5 月 14 日的这篇演讲中。根据本人查阅的资料显示,”Sidecar” 这个名词最早由 Netflix 提出并被用于 Eureka 项目。由于这个项目的广泛应用,故在此之后,凡是微服务中“用于端对端通信的、被单独分离出来的“组件,就都被称为 “Sidecar” 了。

仔细分析上述一系列的重复性工作,我们可以发现,这些工作几乎全部集中在处理各个服务间的通信问题。那么,为何我们不把这些工作从业务逻辑中抽离出来,使其专注于服务间通信,并形成单独的组件呢?
Sidecar 模式,即在微服务中将关于服务通讯的功能抽离出来,并作为一个单独的组件运行在微服务中。这种在微服务中独立负责端对端通信的组件,我们称之为 Sidecar。这种在微服务中将业务逻辑与服务通信解藕,并分离为两个独立运行组件的做法,正是 Service Mesh 概念的雏形。
但在这个阶段,每个微服务中的 Sidecar 还无法通用,即这个微服务的 Sidecar 没有办法拆出来给另一个微服务使用。

Service Mesh 的提出

Service Mesh 在 Sidecar 模式的基础上更进一步。Service Mesh 的定义——一个专注于处理服务间通信的基础设施层——站在开发者的角度来讲,就是在每一个微服务中将用于通信的部分从业务中彻底解藕,应用程序甚至不需要知道它们的存在。在 Service Mesh 中,每个微服务至少含有两个组件:一个用于处理业务功能的“应用程序”和一个专职处理服务间通信的“Sidecar”(类似网络代理)。

Service Mesh 的愿景是希望开发者再也不需要将精力花费在服务通信上。服务通信由每个微服务的 Sidecar 负责,而 Sidecar 由专门的项目来接管。目前,许多被熟知的项目都可以被我们当作 Sidecar 来运用,比如 Envoy、HAProxy 和 Nginx。

使用了 Service Mesh 之后,开发团队和运维团队就可以更加明确的划清自己的职责范围——开发团队专注于业务的开发,而运维团队只需关注微服务中的 Sidecar 就可以明确地了解到每个微服务的健康情况和各种指标。

(上图:“Service Mesh”一词成为技术术语,首次在公众场合亮相)

Service Mesh 的设计理念和作用

随着云原生应用的崛起,Service Mesh 逐渐成为一个独立的基础设施层。在云原生模型里,一个应用可以由数百个服务组成,每个服务可能有数千个实例,而每个实例可能会持续地发生变化。服务间通信不仅异常复杂,而且也是运行时行为的基础。管理好服务间通信对于保证端到端的性能和可靠性来说是非常重要的。

Service Mesh 实际上就是处于 TCP/IP 之上的一个抽象层,它假设底层的 L3/L4 网络能够点对点地传输字节(当然,它也假设网络环境是不可靠的,所以 Service Mesh 也必须具备处理网络故障的能力)。

从某种程度上说,Service Mesh 有点类似 TCP/IP。TCP 对网络端点间传输字节的机制进行了抽象,而 Service Mesh 则是对服务节点间请求的路由机制进行了抽象。Service Mesh 不关心消息体是什么,也不关心它们是如何编码的。应用程序的目标是“将某些东西从 A 传送到 B”,而 Service Mesh 所要做的就是实现这个目标,并处理传送过程中可能出现的任何故障。

与 TCP 不同的是,Service Mesh 有着更高的目标:为应用运行时提供统一的、应用层面的可见性和可控性。通过每个微服务中的 Sidecar,Service Mesh 得以将服务间通信从底层的基础设施中分离出来,让它成为整个生态系统的一等公民——它不再是单纯的基础设施,更可以被监控、托管和控制。

Service Mesh 的未来

尽管 Service Mesh 在云原生系统方面的应用已经有了快速的增长,但仍然存在巨大的提升空间。服务发现和访问策略在云原生环境中仍显初级,而 Service Mesh 毫无疑问将成为这方面不可或缺的基础。就像 TCP/IP 作为互联网的基础一样,Service Mesh 将在微服务的底层基础设施这条路上更进一步。

总结及展望

本文主要介绍了两个关键点:当下最为流行的一种软件开发架构——微服务架构、以及解决微服务架构带来的种种问题的微服务管理模型——Service Mesh(服务网格)模型。

在使用微服务架构开发应用的初期,大家一定会沉醉于其清晰的模块边界和独立运行所带来的种种便利。然而,这并不是说微服务应用就已经十全十美了。随着一个庞大的单体应用被拆分为细碎的各个微小模块,如果对它们进行有效的管理就成为了新的挑战。毕竟,一个中等体量的应用被微服务化后,几百个小模块同时运行是常有的事儿。那么,如何控制服务之间的调用?如何定位这些服务(你知道,在这种场景下手动修改配置文件指定其他服务的 IP 和 port 已经不现实了)?如何将出现错误的应用及时熔断?这都是亟待我们解决的事情,也是目前微服务架构所发展的主要方向。

在熟练运用 Docker 后,我们可以继续去学习 Kubernetes,一款由 Google 和 IBM 共同开发的开源“容器集群管理框架”,类似于控制容器的“操作系统”。

而微服务所面临的上述种种问题,目前我们可以借助同样由 Google 开发的 Istio(服务网格的一种) 来解决,诸如服务注册、服务发现、服务治理、流量管理和容错机制等等。其基本层次关系如下图所示。

(上图: 自己整理的“目前微服务架构的技术栈”,希望各位读者有所帮助)

希望本篇文章能够成为您开展微服务相关工作的参考,为您了解为服务理念、转型微服务架构提供帮助。文章中如果存在遗漏或错误,也非常欢迎您指出,非常期待与您的交流与讨论。

正文完
 0