架构互操作性与可重复性:微服务的工业界视角

摘要

微服务及其支持技术(如容器)已成为当今软件系统中普遍采用的架构方法,尤其是在企业环境中。它们代表了数十年来向基于服务和组件的软件架构演进的最新阶段。结合虚拟化技术,微服务实现了服务接口(消息传递)和服务集成(形式与适配)的松耦合。本文基于我们开发两个基于微服务系统的经验,探讨微服务对软件架构互操作性和可重复性的影响。我们的核心论点是:如果我们把软件架构视为一组主要设计决策,那么微服务方法能够更优雅地将这些决策与架构性、领域特定的决策分离开来,从而使这些决策在不同的问题领域中更具互操作性、可复用性和可重复性。因此,我们建议为软件工程和软件架构研究的全社区基础设施建立基于微服务的参考架构(RA)和参考实现(RI),并提出一系列详细的考虑因素。

索引术语 —微服务,软件架构,DevOps,云计算

引言

在软件行业,微服务作为一种新的架构与设计方法,其日益流行的趋势显而易见。近期的学术研究也证实了这一趋势[14],[23]。从许多方面来看,微服务可被视为朝着基于服务的和基于组件的架构风格发展的又一进化步骤,因为每个微服务都实现了一个小而明确的功能单元,并服务于单一的业务或任务目标[21]。然而,微服务被广泛且迅速采纳的背后,其原因远不止细粒度模块化。我们自身的经验表明,微服务方法的成功在于:它不仅通过松耦合的服务interfaces(例如通过RESTful API)实现了软件组件之间的松耦合,还能借助虚拟化容器等现代封装机制,解决组件的“形式与适配”问题。后者为组件提供了迫切需要的隔离性,无论是在静态地(开发和部署阶段)还是在动态地(运行时),而这正是传统面向服务架构(SOA)实现所未能达到的。

本文介绍了我们成功实施的两个基于微服务的真实案例研究系统,从我们的经验与教训中得出了若干观察结论。其中一个案例研究是针对内部使用的多语言建模与模拟平台,该平台在私有(本地)云中实现。另一个案例是为美国政府客户在亚马逊网络服务(AWS)公有云上交付的交通数据清算所,用于智能车辆近实时的数据交换。这两个系统均基于 Kubernetes 容器编排平台 [6]。在这两个项目中,我们亲身经历了微服务架构、去中心化的敏捷软件开发方法以及 DevOps 最佳实践之间的自然协同效应。从软件架构的角度来看,我们也开始意识到,微服务所涉及的内容远不止表面所见。具体而言,我们观察到:

  • 尽管微服务容易实现组件级复用,但基于微服务的架构可以被整体复用和重复使用wholesale,从而实现架构级复用;这在一定程度上是因为许多非功能性关注点(如安全性、可靠性和可用性)被解耦并外部化到微服务之外;
  • 基础设施即代码机制能够将基本的架构属性和约束进行编码和实施,达到传统模型驱动工程(MDE)方法难以实现的水平,进一步提升了架构层面的“整体”复用。

这些观察结果指出了微服务作为一种架构方法取得成功的更深层次原因——关注点分离。与在单体系统的范围内进行架构决策相比,微服务迫使系统从两个层面来对待其架构:用于服务内设计的“微观”层面,以及用于参与微服务的集体和涌现行为的“宏观”层面。

这些见解促使我们提出,应将微服务方法视为整个社区研究基础设施的关键要素。其主要优势包括增强的工具可重用性、互操作性、架构可重复性以及可检测性。

本文其余部分组织如下。第二节进一步介绍容器化微服务及相关研究工作。第三节和第四节分别展示了两个使用云原生微服务实现的案例研究。基于这些开发项目的观察结果,第六节提出了一些建议和考虑因素,用于构建支持软件架构研究的社区基础设施。

II. 背景

微服务是指“一种可以独立部署、独立扩展和独立测试且具有单一职责的小型应用程序”[24]。微服务最初起源于工业界现象,但已逐渐引起学术界的关注。当前的研究工作包括从单体应用迁移的策略[17];将现有架构方法应用于微服务,例如基于模型的工程[13];以及探索微服务与DevOps及持续集成/持续交付(CI/CD)之间的天然协同效应[14]。

为什么选择微服务?关于微服务的好处,已有诸多讨论,例如提升模块化[12]和可部署性[15],,从而支持并行团队独立开发,并能够进行增量式和独立发布。值得深入思考的是,这些优势究竟是如何实现的。从工业界的角度来看,我们认为答案的一个重要部分在于:微服务不仅是一种逻辑结构,更是一种物理结构——它是一个构建单元、部署单元和运行时单元,拥有自己专用的计算资源(虚拟或物理)。在当今的工业界格局中,微服务越来越多地采用容器化实现,这是一种轻量级虚拟化形式,使得每个服务或应用程序都能在自身隔离的环境中运行,即使它们可能仍在与其他应用程序共享同一个操作系统内核。

单体内核应用程序似乎有望使微服务的形态更加紧凑[18]。

基于容器的形态具有一些关键优势:

  • 静态地,容器充当一种打包机制(例如,Docker镜像),使得每个微服务成为一个可独立构建和部署的单元——真正意义上的一个组件 [16]。这大大简化了开发生命周期中的应用集成和依赖管理。与数月才能测试和发布一次的单体应用相比,微服务支持零星的、持续交付流程。
  • 动态地,容器隔离了每个微服务的运行时环境,包括配置、库依赖和硬件资源,从而大大降低了全局配置复杂性。正如最近的研究所示,组件之间的紧密耦合直接影响可维护性[20]。

尽管容器化并非实例化微服务的唯一方式,但本文将在这种普遍形式的背景下讨论微服务架构,并希望进行推演。

一些更深层次的主题有助于构建软件架构研究的全社区基础设施。

示意图0

III. 案例研究1:基于微服务的工程分析平台

A. 项目背景

作为一家政府资助的研发中心,我们公司在为公共部门执行航空航天工程和分析方面拥有悠久的历史。目前,由于种种原因,体现企业知识的模型和算法在不同项目之间难以发现、重用和集成。首先,我们传统的工具是单体式的且不具备互操作性——事实上,大多数工具只能在安装它们的桌面工作站上运行,在其他环境中无法使用。有用的功能被深埋在这些工具内部。其次,代码和算法使用不同的语言(C/C++、Python、Fortran 等)编写,并运行于不同的环境(如 Windows、Linux 或高性能计算集群),因此难以集成。也许最大的障碍在于,工程师接受的训练是解决各自工程领域的问题,而不是编写高质量的软件,更不用说具备软件架构前瞻性了。因此,大多数软件实现都是临时性的,不适合重复使用。回答新客户问题时,每个项目都需要花费大量时间编写新的实现,导致响应缓慢且人工成本高昂。

为了改变现状,我们开展了一项名为灵活语言无关分析与建模环境(FLAME)的试点计划。该计划旨在为航空航天工程师提供一个平台即服务(PaaS)环境,实现模型组件的互操作性与共享,可在任何通用计算平台上运行,并允许开发者继续使用他们最熟悉和习惯的编程语言进行开发。FLAME 还采用现代软件工程方法,支持敏捷和协作式开发、自动构建与测试,以及持续部署。

示意图1

B. 架构与设计

FLAME的架构愿景如图1所示。我们采用容器化微服务方法,将现有工具分解为独立的组件,每个组件通过 RESTful API和标准数据格式(如JSON)暴露。为了支持包括本地硬件和公共云服务提供商在内的异构基础设施,我们选择使用Kubernetes容器编排框架[6],该框架提供了容器负载均衡和弹性可扩展性等多种功能。

FLAME 还提供了一个 DevOps 工具链,可简化从构建、单元测试、容器化、容器镜像发布到在 Kubernetes 中部署的整个流程。为了进一步减轻领域软件工程师的负担,我们提供了一套代码生成工具,能够根据 RESTful API 规范(使用 Swagger OpenAPI 格式 [9])生成特定语言的骨架代码。开发人员只需将业务逻辑(例如 C++ 函数)插入到生成的微服务包装器中即可。

C. 实现与结果

为了展示FLAME平台的实用性,我们选择了一个基于真实客户需求的用例:天基成像性能评估。这是一个涉及传感器和辐射度量建模以及飞行器平台运动评估的多学科问题。该综合分析需要高保真的卫星平台运动模型,将地面点投影到成像传感器,并应用仪器物理模型。这一复杂的建模任务包含多个计算密集型步骤,此前从未以端到端集成方式进行过。借助现代云计算技术,FLAME平台使得能够将分析和建模能力模块化为微服务,并在即插即用架构中按需集成。我们成功完成了端到端仿真,生成了关于传感器所观测景象的几何精确图像。

扫描传感器将在轨收集数据。该演示通过一组用不同编程语言(包括C++、Java、Python和JavaScript)编写的微服务实现。高层架构及生成的图像如图2所示。

如架构图所示,左侧的传输层作为系统的外部接口,使用NGINX Web服务器处理用户请求。所有其他 FLAME组件均被打包为“容器化”的微服务,运行在 Kubernetes集群中。Kubernetes可根据需求实现计算资源的向上/向下扩展——事实上,每个微服务都已成为单独可扩展,使我们能够更有效地解决性能瓶颈问题。

FLAME环境还利用了一些企业提供的IT服务,例如 DNS、LDAP和DevOps工具,这些服务显示在架构图的右侧。

在实施过程中,我们的核心团队使用开源软件框架实现了一组后端组件,例如 WS02 API 网关、用于数据缓存的 Redis 等。同时,其他部门的软件工程师使用他们选择的编程语言开发了其他服务,例如使用 Python 开发的 San运维服务 和使用 C++ 开发的 星历服务。这种协作式且“多语言”的软件开发在单体系统环境中是难以想象的。

还需注意,在这种计算密集型问题场景中,图像中每个像素的生成可能涉及超过 10^4次微服务调用。借助弹性且容错的FLAME架构,即使出现零星的软件和硬件故障,我们也能执行长时间运行的模拟(有时持续数天),而无需从头开始重新启动。

IV. 案例研究2:基于微服务的智能车辆数据清算所

A. 项目背景

美国交通部(USDOT)一直在推动智能交通系统的发展,以实现智能车辆与智能基础设施的数字化融合。此类系统可支持多种重要用例,例如通过提供道路状况的实时态势感知来提升驾驶员安全。例如,联网车辆(CV)和自动驾驶车辆(AV)可以利用短程无线电信号相互通信,并与智能路侧单元(RSU)进行通信;汽车将接收到附近车辆不安全变道或交通违规等危险情况的通知。由于每年道路上发生超过30,000起死亡事故,美国交通部正将其重点从帮助人们在交通事故中生还,转向首先预防交通事故的发生[3]。一个具体的例子是,其中一个联网车辆试点项目涉及怀俄明州繁忙的I‐80公路,该地区冬季有吹雪、夏季有雾和强风等极端天气条件,给卡车司机带来危险的驾驶环境[11]。

随着车辆、组织、系统和人员之间互联互通的增强,前所未有的大量数据正在产生。美国交通部(USDOT)已认识到有必要从不同的联网车辆(CV)和自动驾驶车辆(AV)部署及来源收集、融合并重新打包运行数据,以实现全国范围内的分发。人们设想建立一个态势数据清算所(SDC)系统,以提供跨各种数据提供方、分发方和消费者的数据收集、融合、转换和分发能力。图3描述了该系统的运行环境。

示意图2

SDC 的关键要求包括:

  • 灵活、开放的架构和互操作性,以实现与各种第三方设备和应用程序的数据共享和集成;
  • 近实时性能,低延迟消息交换;
  • 按需可扩展,以支持区域和国家级别的运营;
  • 低成本、使用开源软件和敏捷开发方法的渐进式交付。

早期的SDC原型展示了其效用,但该系统作为专有系统实现,维护繁琐且难以集成。通过一次偶然的机会,项目团队了解到FLAME微服务平台,并认为其模块化、云原生架构可以作为现代化的态势数据清算所的基础。

B. 架构与设计

基于FLAME平台的领域无关组件构建的SDC架构如图4所示。新服务以红色字体突出显示。与图2中的 FLAME架构相比,注意以下变化:

  • 传输层现在提供了更多样化的双向数据交换机制。由于连接不稳定和带宽有限,车辆和路侧单元通常通过UDP协议上的专用短程通信(DSRC)消息发送数据包,而交通管理中心和其他企业系统则可能使用基于TCP的 RESTful API或WebSocket流以实现更高的吞吐量。

无论使用何种外部接口,情境数据均采用工业界标准的 SAE J2735 消息格式进行编码。消息还可进行数字签名和加密,以确保其完整性和机密性。

SDC的后端架构部署在Kubernetes集群中,基于前一节所述的FLAME服务,但仿真管理服务除外,因其在此问题上下文中不适用。新的领域相关微服务包括基于 Apache NIFI实现的数据注入服务,以及用于车辆数据的附加消息处理服务。我们还能够将旧版SDC原型系统中的软件模块封装为微服务,且几乎无需修改。与 FLAME项目一样,这些微服务也通过RESTful接口单独访问、独立部署和扩展,并通过DevOps流水线持续交付。

与最初在私有云中实现的FLAME不同,SDC系统部署在AWS公有云上,因此可以利用其他AWS服务,例如 S3对象存储和CloudWatch监控,如图4右侧所示。然而,AWS服务对于Kubernetes集群内的微服务是透明的——它们在大多数情况下对底层基础设施无感知。

示意图3

C. 实现与结果

初始版本,也称为最小可行产品(MVP),旨在为上述怀俄明州CV试点实现端到-end的数据流——来自怀俄明州交通部系统的出行者通告消息被摄入SDC,经过解码、验证、打包后分发至SiriusXMT M [8],,后者再通过卫星通信将消息传递给车辆。由于有意重用FLAME平台,MVP交付生产仅用了6周,远超预定计划,并且到目前为止保持了超过99%的正常运行时间。

V. 观察与讨论

看到新兴软件技术能够快速响应客户需求并产生实际影响,总是令人兴奋的。同时,我们的团队也从软件架构的角度获得了关于微服务方法的宝贵见解。在此分享一些关键观察结果。

A. 持续架构

首先,在这两个项目的过程中,软件架构从来不是生命周期早期的前期活动,而是随着系统不断演进而持续发展的过程——服务频繁地被添加和升级,工作流程不断调整,部署拓扑也持续优化。DevOps 实践和工具加快了这一进程。这似乎已成为基于微服务架构的常态。

我们看到了对建模工具的实际需求,这些工具能够使架构定义保持最新并与运行中的系统同步,目前我们正在实施一种架构重构工具,该工具能够准确描述 Kubernetes集群内的服务交互。

B. 从组件复用到架构可重复性

凭借明确的功能范围和容器化的“形态”,微服务很容易实现组件级重用。与需要代码级集成和兼容性(例如 JDK版本、类路径、数据结构等)的Java库重用相比,微服务的重用只需从注册表下载并部署一个容器镜像,并通过RESTful API进行交互即可。我们在FLAME项目中的一个设计目标就是让所有微服务都可发现且可访问,以便其他工程师可以在不了解其内部工作原理的情况下使用星历计算服务——实际上,他们甚至不需要知道该服务是用什么语言编写的。

重用的故事并未止步于此。FLAME平台的整体架构最终被证明在很大程度上是领域无关的,使其能够应用于完全不同的问题领域。项目启动后,我们很快意识到,其中多个组件根本不限于模拟图像评估这一用例,例如:

  • 基于 Kafka 的消息服务,用于分布式和异步的跨服务通信。
  • 参数管理服务,用于管理微服务实例级别的配置。该服务可用于管理微服务的内部状态(例如航天器轨道、载荷传感器姿态、成像芯片特性等),从而使每个微服务实例可以遵循十二要素设计原则实现无状态化 [10]
  • 使用Elasticsearch的监控与指标服务,用于服务状态和健康状况。
  • 使用Redis的内存数据库服务,用于缓存中间计算结果。

这些服务(以及其他实用工具和模板)构成了我们为智能车辆SDC系统制定的技术战略的基础,使开发团队节省了约50%的工作量和时间——尽管这些服务最初是为完全不同的行业开发的。我们的SDC经验表明,采用基于服务的架构,组件复用可以达到一个新的水平,即从临时性的、偶然的复用转变为“批量”复用和架构可重复性,如图5所示。

示意图4

这对于最初的团队来说是一个相当意外(但令人愉快)的结果。我们目前正在将相同的架构应用于启动美国国家航空航天局和美国国家海洋和大气管理局的其他多个客户项目。许多促进因素使其成为可能,包括使用经过良好设计的容器编排例如Kubernetes这样的框架和现代DevOps工具,但我们认为最关键的因素是容器化微服务建立了一种自然边界,将系统划分为宏观和微观层面的关注点,这引出了我们的下一个观察。

C. 架构关注点的外化

在FLAME和SDC工作的过程中,软件架构师的注意力几乎完全集中在服务的集成与交互上,而来自各个部门的领域专用软件工程师则专注于服务本身的实现。为什么?

因为这些微服务主要关注于实现功能需求。在其静态和动态隔离的设计空间中,设计决策(无论好坏)对整个系统的影响非常有限。另一方面,典型的架构质量属性——如可用性、可扩展性、安全性等——在很大程度上已成为外部框架的责任,而外部框架能够更统一且高效地处理这些问题。这自然成为软件架构师关注的中心。换句话说,我们发现微服务有效地外化了架构关注点。

为了进一步观察,我们采用来自[22]的面向服务的架构质量属性,并检查它们在FLAME环境中的实现方式。如表一所示,这些属性是通过底层PaaS框架和支持流程来满足的,而不是在微服务代码中实现的。

属性 FLAME 方法
互操作性 用于微服务的RESTful API被定义并由Swagger管理;FLAME进一步提供用于服务封装器的代码生成工具
性能,可用性 弹性可扩展性、故障转移和负载均衡可扩展性和由基于声明式配置的 Kubernetes 提供策略;也使用了AWS自动扩展
安全性 Kubernetes中的内置网络安全;WSO2 API网关提供细粒度访问控制
可修改性 独立微服务部署;CI/CD 进程;零停机滚动升级
可测试性 由Jenkins CI工具支持的自动化测试;端到端可测试性仍然是一个挑战
可用性
服务级别协议,生命周期管理
通过仪表板管理和指标服务用户体验和指标服务
由API网关管理的服务级别协议;DevOps工具链自动化微服务生命周期管理。

回想一下,在传统的面向服务的架构中,服务开发者通常需要注意这些质量属性,例如通过WS‐事务实现消息传递的可靠性,或通过应用服务器集群实现高可用性等。而采用微服务风格的组件化并结合强大的容器管理框架后,这些属性大多在服务之外(因此对服务透明)得到解决。工业界将此称为“控制反转”原则——即服务不再试图成为控制整个系统的“主”程序;相反,它现在是基于这样的假设编写的:包含的框架或平台负责其实例化和使用方式。我们认为,能否成功实现关注点分离和控制反转原则,是传统面向服务的架构与微服务风格之间的一个显著区别,并部分解释了后者取得巨大成功的原因。

通过将架构关注点进行外部化,我们进一步假设微服务方法能够在宏观和微观层面实现软件架构二分法,其中前者关注与领域无关的非功能性特征,而后者则负责功能性和领域特定的实现。图6描述了这一双层结构。

示意图5

在宏观层面,架构关注的是满足系统的质量属性或非功能性需求,例如表一所列出的那些需求。根据我们的第一手经验,在宏观层面采用的策略、模式和战术通常是与领域无关的。例如,微服务架构本质上是分布式的;所有系统都需要应对一致性、延迟、部署拓扑等共同挑战。无论处于哪个行业或问题领域,这些问题都必须得到解决。事实上,在行业展会上,我们经常看到来自不同行业的微服务架构具有惊人的相似性。宏观架构的领域中立性带来了两个显著优势:首先,最佳实践和模式可以被规范化并自动地在其他地方重复应用,从而实现框架和平台的系统性、整体性复用;其次,解决通用架构问题的方案具有广泛的影响和巨大的市场潜力,为创新提供了强有力的激励。

新兴的服务网格技术(如Istio [5]和Envoy [4])就是一个很好的例子。

在微观层面,软件架构应关注高效实现功能性和领域相关需求的策略与方法,并在由宏观架构规定的资源限制和设计约束内完成。我们的经验表明,统一的设计约束并且实现的多样性可以共存——例如,在满足全球强制执行的设计规则(如 RESTful API、无状态设计和标准日志级别)的同时,每个微服务的开发人员可以选择自己偏好的操作系统版本、编程语言或第三方库,而不会影响其他服务。在宏观层面,架构质量是核心关注点,而微服务的开发人员则专注于代码质量,他们可以方便地使用现有的工具,如静态代码分析器和性能分析工具。

宏观与微观架构的划分并非绝对。即使是宏观架构,也可能需要考虑特定领域或工业界的需求——例如,航空航天工业界通常更重视可靠性与可用性,而非可修改性与可用性。同样,微服务仍需关注非功能性需求,例如遵循良好的编码实践和设计模式,避免命令注入、缓冲区溢出等安全漏洞。

然而,宏观与微观分离的优势在于,从整体架构质量的角度来看,微观层面的架构基本上可以被视为黑盒。即使微观层面存在一些设计缺陷,也可能在宏观层面得到补偿。例如,在早期的 FLAME 测试运行中,一个存在内存泄漏的微服务随着时间推移变得越来越不响应。我们作为 Kubernetes 服务代理一部分配置的存活探针检测到了这一异常,开始终止迟缓的实例并启动新的实例,从而使得整体模拟运行得以继续,尽管性能有所下降。

D. 架构即代码

最后一个观察结果与之前的几点有一定关联。当我们准备将FLAME从私有云迁移到AWS时,一位团队成员额外创建了CloudFormation [2]模板,以使该过程可重复。仔细查看该模板,软件架构师可能会发现它与某种形式的架构描述语言惊人地相似——例如,逻辑组件、它们之间的关系以及到物理组件的映射。尽管它对人类而言远非用户友好的ADL,但无疑是机器可读且机器可执行的。

CloudFormation只是工业界涌现出的所谓“基础设施即代码”工具家族中的一种代表;其他流行的同类工具包括 Puppet、Chef和Ansible。

通过将架构信息和制品以机器可读格式进行代码化,我们可以进一步增强架构的互操作性和可重复性。人们可以直接执行脚本或“配方”来复用或重复系统的部署过程,而无需手动遵循其文档。

一个潜在的研究机会是探索如何弥合工业界与学术界之间的差距,将学术界在模型驱动工程和可执行架构方面的努力与工业界在云资源供应和管理方面的强大工具相结合。

VI. 面向软件工程研究的社区级基础设施的考虑

微服务代表了数十年来向基于服务的软件架构演进的最新一步。结合虚拟化技术,微服务实现了服务接口(消息传递)和服务集成(形式与适配)的松耦合。可以说,我们比以往任何时候都更接近实现真正基于组件的架构。

本文试图从实践者的角度探讨微服务对软件架构理论与实践的影响。在开发两个基于微服务的系统过程中,我们认识到,如果将软件架构视为一组主要设计决策[19],,那么微服务方法能够更优雅地将这些决策与非架构性的、领域特定的决策分离开来,从而使这些决策在不同的问题领域中更具互操作性、可复用性和可重复性。

因此,我们建议为软件工程/软件架构研究的社区级基础设施创建基于微服务的参考架构(RA)([7]即为一个示例)和参考实现(RI),或者至少将其视为可行的方法之一。

具体而言,RA/RI 可能提供以下好处:

  • 工具可重用性 — 社区的研究工具将被打包为可发现的和可通过API访问的微服务,从而提升其可重用性;
  • ToolInteroperability — 容器化微服务允许不同的研究人员使用他们选择的编程语言,同时能够在共同的社区运营环境中以意想不到的方式将其组件集成在一起;
  • 架构即代码 ——规范性的架构决策和制品,例如结构关系、质量属性的实现或架构模式,可以使用现代自动化工具通过机器可读且可执行的脚本来进行编码,如第 V‐D节所述,从而使其具有可复用性和可重复性。这是将架构描述从以人为核心演进为机器可理解制品的重要一步[25]
  • 可检测性 — 现代云计算基础设施是微服务繁荣发展的环境,提供了通用的开放机制,以实现组件级可观测性和监控与测量。从Logstash和Fluentd等日志工具,到与主服务一同部署的容器“边车”(例如Istio [5]和Envoy [4]这类工具),架构关注点可以对目标微服务透明地进行观测和管理,且几乎不会产生性能影响。这不仅有助于基于架构的软件工程方法的开放且可重复的实现(例如一个开放、互操作的MAPE‐K框架),并且还支持在规范性架构与描述性架构之间进行简便的架构恢复和比较。

更多推荐