微服务的机器学习-全-

微服务的机器学习(全)

原文:annas-archive.org/md5/3f14a795cd1d01bbb6c6e570b8a9f4b2

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

机器学习ML)以之前被认为不可能的方式革命了技术行业和我们的日常生活。通过将 ML 算法与微服务架构MSA)相结合,组织能够创建智能、健壮、灵活和可扩展的企业系统,这些系统能够适应不断变化的企业需求并提高整体系统性能。

这本书是一本全面的指南,涵盖了构建智能 MSA 系统的不同方法以及解决系统设计和运营中面临的常见实际挑战。

书的第一部分提供了对 MSA 及其应用的全面介绍。你将了解常见的企业系统架构、MSA 的概念和价值,以及它与传统企业系统的不同之处。在这一部分,你将了解 MSA 的设计、部署和运营,包括 DevOps 流程的基础。

书的第二部分深入探讨了 ML 及其在 MSA 系统中的应用。你将了解关键的 ML 算法及其在 MSA 中的应用,包括回归模型、多类分类、文本分析和深度学习DL)。这一部分提供了如何开发 ML 模型、组件和子组件的全面指南,以及如何在 MSA 系统中应用它们。

书的最后一部分将前几部分涵盖的内容汇总在一起。它提供了一个逐步指南,用于设计和开发智能系统,包括实际案例和可导入实际用例的实际代码。你还将了解 DevOps 在企业 MSA 系统中的应用,包括组织结构对齐、质量保证测试和变更管理。

在阅读完这本书之后,你将对 MSA 及其益处有一个扎实的理解,并具备构建自己的智能 MSA 系统以及迈向实现更好的业务成果、运营绩效和业务连续性的第一步所需的技术和知识。无论你是初学者还是有经验的开发者,这本书都是理解并应用 MSA 和 ML 在企业系统中的完美指南。

这本书面向谁

这本书非常适合 ML 解决方案架构师、系统和 ML 开发者以及系统和解决方案集成人员。这些人将从这本书中获得最大收益,因为它涵盖了 ML 中的关键概念和最佳实践。这本书的编写是为了给这些专业人士提供实施智能 MSA 解决方案所需的知识和技能。

要充分利用本书,您应具备基本的系统架构和操作知识。此外,对 Python 编程语言的熟练掌握非常受欢迎。这是因为本书中的示例和案例研究主要是用 Python 实现的。然而,本书涵盖的概念和最佳实践也可以应用于其他编程语言和技术。本书旨在提供 ML 的坚实基础,同时帮助您深化现有的知识和技能。

本书涵盖的内容

第一章MSA 和机器学习在企业系统中的重要性,介绍了 MSA 及其在提供具有竞争力和可靠性的企业系统中的作用。本章将比较 MSA 与传统单体企业系统,并讨论部署和运营 MSA 系统的益处和挑战。它还将涵盖 MSA 的关键概念,包括服务驱动和事件驱动架构以及拥抱 DevOps 在构建 MSA 系统中的重要性。

第二章重构单体系统,专注于从单体架构到 MSA 的过渡过程。它强调如何重构单体系统以构建灵活且可靠 MSA 系统。本章将探讨过渡到 MSA 所需的步骤,包括识别微服务、分解业务需求和分解功能和数据。本章将提供通过采用 MSA 来现代化组织企业系统的见解。

第三章解决常见的 MSA 企业系统挑战,讨论了应对保持可靠、耐用和顺畅运行的 MSA 系统挑战的方法。本章涵盖了使用反腐败层ACL)进行 MSA 系统隔离、API 网关、服务目录和编排器、微服务聚合器和微服务断路器等主题;网关、编排器和聚合器之间的区别;以及其他 MSA 系统增强措施。

第四章关键机器学习算法和概念,提供了对基本 AI、ML 和 DL 概念的全面理解,以使您具备在 MSA 系统中构建和部署 AI 模型所需的知识。它涵盖了这些领域之间的区别,并概述了在 Python 中使用的常见 ML 包和库。然后,章节深入探讨了 ML 的各种应用,包括构建回归模型、多类分类、文本情感分析和主题建模、模式分析和预测,以及使用 DL 构建增强模型。

第五章, 《机器学习系统设计》,全面介绍了构建 ML 管道的设计考虑因素和涉及组件,并为您提供构建和部署强大且高效的 ML 系统所需的知识。本章涵盖了拟合和转换接口、训练和提供接口以及编排的主要概念。

第六章, 《稳定机器学习系统》,使您对数据集偏移现象及其在 ML 系统中如何解决有全面的理解,以确保稳定和准确的结果。本章讨论了在保持其功能目标的同时,可以应用于解决数据集偏移的优化方法。本章涵盖了 ML 参数化概念、数据集偏移的原因、识别数据集偏移的方法以及处理和稳定数据集偏移的技术。

第七章, 《机器学习和深度学习如何帮助 MSA 企业系统》,通过讨论您可以在智能企业 MSA 系统中应用 ML 和 DL 的不同用例来总结前几章。您将了解一些可能的用例,例如使用监督线性回归模型进行模式分析和使用深度学习进行自我修复。

第八章, 《在构建智能 MSA 系统中 DevOps 的作用》,向您介绍如何在构建和运行 MSA 系统中应用 DevOps 的概念。本章涵盖了 DevOps 与组织结构的对齐、企业 MSA 系统操作中的 DevOps 流程,以及从开始到运维和应用的 DevOps 应用。

第九章, 《使用 Docker 容器构建 MSA》,介绍了容器及其在 Docker 平台上的应用,Docker 是该领域广泛使用的平台。本章涵盖了容器及其用途概述、Docker 的安装、创建我们示例项目的容器,以及 MSA 项目中的微服务之间的交互通信。目标是为您提供对容器及其在 MSA 中如何利用的全面理解。

第十章构建智能 MSA 系统,将 MSA 和人工智能AI)的概念结合起来,构建一个演示智能-MSA 系统。该系统将使用各种 AI 算法来增强之前在书中创建的原生 MSA 演示系统的性能和操作。智能-MSA 将能够检测交通模式中的潜在问题,并自我纠正或自我调整以防止问题发生。本章涵盖了使用机器学习的优势、构建第一个 AI 微服务、演示智能-MSA 系统的实际操作以及分析 AI 服务的操作。目标是让您全面了解如何将 AI 集成到 MSA 系统中以增强其性能和操作。

第十一章管理新系统的部署——绿地与棕地对比,介绍了在绿地和棕地部署中部署智能-MSA 系统。它提供了在保持整体系统稳定性和业务连续性的同时平稳部署新系统的方法。本章涵盖了部署策略、绿地与棕地部署之间的差异以及克服部署挑战的方法,特别是在棕地部署中,现有系统已经投入生产。

第十二章部署、测试和运行智能 MSA 系统,是最后一章,它整合了书中涵盖的所有概念,提供了部署智能 MSA 系统的实际操作和实用示例。它教您如何将书中学到的概念应用到您的部署需求和标准中。本章假设存在一个现有的单体架构系统,并涵盖了克服部署依赖性、部署 MSA 系统、测试和调整系统以及进行部署后审查。

为了最大限度地利用这本书

为了最大化您的学习体验,您应该对系统架构、软件开发概念、DevOps 和数据库系统有基本的了解。虽然不需要有 MySQL 和 Python 的先前经验,但它将有助于更有效地理解代码示例中的概念。

本书涵盖的软件/硬件 操作系统要求
Docker Ubuntu Linux 或 macOS
Python Linux、Windows 或 macOS
MySQL Ubuntu Linux 或 macOS
VirtualBox Windows 或 macOS

我们建议您安装一个 Python IDE,例如 PyCharm,以便能够跟随 Python 示例。PyCharm 可以从www.jetbrains.com/lp/pycharm-anaconda/下载。

使用 VirtualBox 构建演示环境和创建测试虚拟机。您可以从 www.virtualbox.org/wiki/Downloads 下载 VirtualBox。

Ubuntu Linux 是我们在书中用来安装 Docker 和其他工具的操作系统。要下载最新的 Ubuntu 版本,请使用以下链接:ubuntu.com/desktop

如果您使用的是本书的数字版,我们建议您亲自输入代码或从书的 GitHub 仓库(下一节中提供链接)获取代码。这样做将帮助您避免与代码的复制和粘贴相关的任何潜在错误。

下载示例代码文件

您可以从 GitHub 下载本书的示例代码文件 github.com/PacktPublishing/Machine-Learning-in-Microservices。如果代码有更新,它将在 GitHub 仓库中更新。

我们还有其他来自我们丰富图书和视频目录的代码包,可在 github.com/PacktPublishing/ 获取。查看它们吧!

使用的约定

本书使用了多种文本约定。

文本中的代码:表示文本中的代码词汇、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 账号。以下是一个示例:“完成 Dockerfile 的编写后,您需要将其保存为 Dockerfile,以便能够使用它创建 Docker 镜像。”

代码块设置如下:

import torch
model = torch.nn.Sequential( # create a single layer Neural Network
    torch.nn.Linear(3, 1),
    torch.nn.Flatten(0, 1)
)
loss = torch.nn.MSELoss(reduction='sum')

当我们希望您注意代码块中的特定部分时,相关的行或项目将以粗体显示:

import numpy as np
from scipy import linalg
a = np.array([[1,4,2], [3,9,7], [8,5,6]])
print(linalg.det(a)) # calculate the matrix determinate
57.0

任何命令行输入或输出都按照以下方式编写:

$ docker --version
Docker version 20.10.18, build b40c2f6

粗体:表示新术语、重要词汇或屏幕上看到的词汇。例如,菜单或对话框中的文字会以粗体显示。以下是一个示例:“从管理面板中选择系统信息。”

提示或重要注意事项

看起来是这样的。

联系我们

我们始终欢迎读者的反馈。

一般反馈:如果您对本书的任何方面有疑问,请通过 customercare@packtpub.com 发送电子邮件,并在邮件主题中提及书名。

勘误:尽管我们已经尽最大努力确保内容的准确性,但错误仍然可能发生。如果您在这本书中发现了错误,我们非常感谢您能向我们报告。请访问 www.packtpub.com/support/errata 并填写表格。

盗版:如果您在互联网上发现我们作品的任何非法副本,我们非常感谢您能提供位置地址或网站名称。请通过 copyright@packt.com 联系我们,并提供材料的链接。

如果您有兴趣成为作者:如果您在某个领域有专业知识,并且您有兴趣撰写或为书籍做出贡献,请访问authors.packtpub.com.

分享您的想法

一旦您阅读了《微服务中的机器学习》,我们很乐意听听您的想法!请点击此处直接进入亚马逊评论页面并分享您的反馈。

您的评论对我们和科技社区非常重要,并将帮助我们确保我们提供高质量的内容。

下载此书的免费 PDF 副本

感谢您购买此书!

您喜欢在路上阅读,但无法携带您的印刷书籍到处走?您的电子书购买是否与您选择的设备不兼容?

请放心,现在每购买一本 Packt 图书,您都可以免费获得该书的 DRM 免费 PDF 版本。

在任何地方、任何设备上阅读。直接从您喜欢的技术书籍中搜索、复制和粘贴代码到您的应用程序中。

优惠不止于此,您还可以获得独家折扣、时事通讯和每日免费内容的每日电子邮件访问权限

按照以下简单步骤获取福利:

  1. 扫描二维码或访问以下链接

二维码

packt.link/free-ebook/9781804617748

  1. 提交您的购买证明

  2. 这就是全部!我们将直接将您的免费 PDF 和其他福利发送到您的电子邮件中

第一部分:微服务设计和架构概述

我们将从提供对微服务架构MSA)及其在企业系统中的应用的全面介绍开始第一部分。我们将了解常见的企业系统架构、MSA 的概念和价值,以及它与传统企业系统的不同之处。在整个本部分中,我们将了解 MSA 的设计、部署和运营,包括 DevOps 流程的基础。

我们将更深入地了解每个企业架构模型最佳使用案例的使用情况。第一部分还将探讨如何设计一个基本的模块化、灵活、可扩展和健壮的 MSA,包括如何将业务需求转化为微服务。我们将获得关于过渡到 MSA 的不同方法及其优缺点的深入了解。

本部分讨论了设计真实 MSA 的挑战,并提供了解决这些挑战的工具和技术。我们将了解用于优化系统模块化、可测试性、可部署性和运营的企业系统组件。

第一部分旨在为读者提供对 MSA、其优势和如何在企业系统中实施 MSA 的坚实基础。

本部分包括以下章节:

  • 第一章企业系统中 MSA 和机器学习的重要性

  • 第二章, 重构单体架构

  • 第三章, 解决常见 MSA 企业系统挑战

第一章:MSA 和机器学习在企业系统中的重要性

在当今的市场中,竞争从未如此激烈,用户对 IT 系统的需求不断增长。为了能够跟上客户需求和市场需求,IT 系统需要更短的上市时间TTM)的需求从未如此重要,所有这些都推动了敏捷部署和简化开发流程的需求,以及尽可能多地利用代码重用的需求。

微服务架构MSA)解决了这些担忧,并试图在保持高效、稳定的系统运行的同时,提供更具竞争力、更可靠、更快速部署和更新的交付。

在本章中,我们将更深入地了解微服务如何帮助构建现代、灵活、可扩展和具有弹性的企业系统。本章将介绍 MSA 中的关键概念,并讨论常见的企业系统架构,每种架构如何与 MSA 不同,为什么它们不同,以及当你选择一种或多种架构而不是其他架构时,你将获得或失去什么。

在我们介绍本章时,我们将涵盖以下领域:

  • MSA 是什么以及为什么

  • MSA 与单体企业系统对比

  • 服务驱动架构、事件驱动架构EDA),以及如何在 MSA 中融入这些架构

  • 部署和运营 MSA 企业系统面临的挑战

  • 为什么在构建 MSA 时采用DevOps很重要

为什么是微服务?优势和劣势

微服务通常与 MSA 相提并论。MSA 指的是从一组较小的应用程序中构建复杂系统的方式,其中每个应用程序都是为了特定的有限范围的功能而设计的。这些小型应用程序(或服务,或微服务)是独立开发的,并且可以独立部署。

每个微服务都有一个 API 接口,用于与其他系统中的微服务通信。所有这些独立的微服务组织在一起,形成了更大的系统功能。

为了理解微服务的价值以及设计 MSA 时面临的挑战,了解微服务之间如何通信和交互至关重要。

微服务可以以线性或非线性的方式相互通信。在线性微服务管道中,每个微服务与其他微服务通信,以顺序方式在整个系统中处理数据。输入始终传递给第一个微服务,输出始终由系统中的最后一个微服务生成:

图 1.1:线性微服务管道

图 1.1:线性微服务管道

实际上,然而,大多数现有系统都是使用非线性微服务管道构建的。在非线性微服务管道中,数据分布在系统中的不同功能中。你可以将输入传递给系统中的任何功能,输出也可以由系统中的任何功能生成。因此,你可以有多个具有多个输入的管道,服务于多个功能并产生多个输出:

图 1.2:非线性微服务管道

图 1.2:非线性微服务管道

考虑以下典型电子商务系统中简化的订单履行流程图。下单过程中的每个功能都代表一个微服务。一旦客户下单,就会触发一个 API 调用到添加/更新客户信息微服务,以保存该客户的信息或在需要时更新它。这个微服务的唯一职责就是:根据从 API 调用者接收到的数据输入来管理客户信息。

同时还会向流程中的验证付款部分发出另一个 API 调用。调用将根据 API 调用的付款类型被引导到处理 PayPal 付款处理信用卡付款微服务。注意这里如何将付款验证过程分解为两个不同的微服务——每个都是针对特定的付款功能专门设计和开发的。这使得这些微服务具有灵活性和可移植性,可以在系统或另一个系统中的其他部分使用,如果需要的话。

在处理完付款后,会同时触发系统中的其他微服务的 API 调用以履行订单:

图 1.3:非线性微服务管道示例——客户订单

图 1.3:非线性微服务管道示例——客户订单

订单下单示例展示了如何模块化和灵活地设计 MSA 企业系统。我们经常使用这个例子来展示在设计、部署和运营 MSA 企业系统时可能面临的某些优势和挑战。

我们必须探讨使用 MSA 构建企业系统的优缺点,以帮助决定 MSA 是否是您组织更好的选择。

注意,以下列出的某些优势在其他情况下也可能被视为劣势(反之亦然)。

微服务的优势

实施 MSA(微服务架构)具有一定的显著价值。以下是我们认为适用于当今市场的部分优势。

自主性

微服务最大的优点之一是它们的自主性——这是 MSA(Microservices Architecture,微服务架构)其他许多优点的基石。由于它们的自主性,微服务拥有自己的技术栈,这意味着每个系统服务都可以使用与任何其他系统服务完全不同的工具、库、框架或编程语言进行开发,但它们可以顺利地相互集成。

微服务可以在系统中的任何其他应用程序之外独立开发和测试,这使得每个微服务都可以拥有自己的生命周期,包括质量保证QA)、变更管理、升级、更新等,这反过来又极大地减少了应用程序的依赖性。

可移植性

微服务的自主性使得它们可以在不同的平台、操作系统和系统中移植,所有这些都与这些服务编写的编程语言无关。

重用

当重用微服务时,您不需要重新发明轮子。由于它们的自主性,微服务可以在不添加额外编码、更改或测试的情况下重用。每个服务可以根据需要重用,这极大地增加了系统的灵活性和可扩展性,显著减少了开发时间、成本和部署时间,并减少了系统的 TTM。

松散耦合、高度模块化、灵活和可扩展

微服务是 MSA 企业系统的主要构建块。每个块都与系统中的其他块松散耦合。就像乐高积木一样,这些块组织在一起的方式可以形成一个复杂的、针对特定业务解决方案的企业 MSA 系统。

以下图示展示了我们可以如何使用多个微服务构建三个不同的系统示例。

该图显示了九个服务,其中七个服务以这种方式组织,以便重用和构建三个不同的系统——系统A、系统B和系统C。这展示了松散耦合如何使 MSA(Microservices Architecture,微服务架构)具有灵活性,以至于你可以重用每个服务来构建不同的系统功能。

您可以在现有的微服务基础上,通过添加最少量的开发工作来构建系统,这些微服务可以是第三方购买的,也可以是之前内部开发的。这极大地促进了系统的快速开发、新功能的发布、极短的 TTM(Time To Market,上市时间),以及可靠、灵活且更加稳定的即时更新和升级。所有这些都有助于业务连续性BC)并使企业系统具有更高的可扩展性:

图 1.4:微服务的灵活性和模块化

图 1.4:微服务的灵活性和模块化

短暂的发布周期和 TTM

由于我们之前提到的单个和独立的服务特性,微服务的部署变得更容易、更快。自动化可以在减少服务测试和部署时间方面发挥重要作用,正如我们将在本章后面讨论的那样。

故障容错和故障隔离

每个微服务都有自己的独立故障域。一个微服务的故障将被限制在该微服务内部,因此更容易进行故障排除,修复速度更快,并能更快地将系统恢复到完全运行状态。

考虑我们之前提到的订单履行示例;即使消息/电子邮件客户微服务(例如)出现任何故障,系统仍然可以正常工作。由于故障的性质和小的故障域,将很容易确定故障的位置以及如何修复它。因此,平均修复时间MTTR)显著降低,BC 得到极大提升。

架构师有时能够构建具有高度内嵌容错能力的系统,以防止这些故障的发生,或者在主微服务检测到故障时,有其他备用微服务可以接管。正如我们稍后将会看到的,本书的一个主要目标就是能够设计出足够智能的系统,以提供所需的高弹性。

然而,软件架构师必须牢记的是,在 MSA 中系统组件过多时,可能出现的问题也越多。因此,架构师和开发者必须具备坚实的回退和错误处理能力,以管理系统的弹性。

不同微服务之间的通信,例如,可能因为任何原因简单地超时;这可能是一个网络问题、服务器问题,或者在接收微服务或系统中开发的事件处理机制中 API 调用过多,从而压倒这个系统组件并导致故障或响应延迟。

系统中有许多数据流和数据处理的点都需要同步。如果系统没有妥善处理单个故障,可能会导致系统级联故障,并相应地可能导致整个系统的故障。

故障容错的设计方式将大大影响系统性能和可靠性。

可靠性和单一责任原则(SRP)

如果你来自编程世界,你可能对面向对象编程OOP)中的 SRP 很熟悉:一个类应该只有一个,且只有一个,改变的理由。系统中的每个对象、类或函数都应该只负责系统的那个功能,因此一旦该类开发完成,它应该只因为最初创建它的原因而改变。这个原则是提高 MSA 中系统可靠性和 BC 的主要驱动力之一。

在开发 MSA 企业系统的初期阶段,以及从头开始开发新微服务的阶段,MSA 企业系统可能尚未完全测试或成熟,可靠性仍在建立中。当系统成熟时,对单个微服务的更改最小——如果有的话——因此微服务的代码可靠性更高,操作更稳定,故障域被限制,容错性高,因此系统的可靠性比具有单体架构的类似系统要高得多。可靠性高度依赖于系统的设计、开发和部署效果。

降低系统开发和运营成本

重新使用微服务在很大程度上减少了将系统投入运行所需的开发努力和时间。您可以重新使用的微服务越多,开发时间和成本就越低。

微服务不需要从头开始开发;您可以购买已经开发好的微服务,并将其集成到您的 MSA 企业系统中,从而显著缩短开发时间。

当这些微服务稳定成熟时,可靠性更高,平均修复时间(MTTR)更短,因此系统故障更低,业务连续性(BC)更高。所有这些因素都可以在减少开发成本、运营成本和总拥有成本TCO)方面发挥重要作用。

自动化和运营编排非常适合微服务;这有助于敏捷开发,也可以显著降低运营成本。

微服务的缺点

在考虑在您的组织中采用 MSA 之前,需要考虑微服务带来的一系列挑战。好消息是,许多这些挑战——如果不是全部——都可以有效地解决,最终形成一个强大的 MSA 企业系统。

这里提到了微服务的一些挑战,我们将在本章后面讨论一些帮助解决这些挑战的方法。

复杂性

MSA 系统包含许多必须协同工作并相互通信以形成整体解决方案的组件。在大多数情况下,系统的微服务是用不同的框架、编程语言和数据结构构建的。

微服务之间的通信必须完美同步,系统才能正常运作。接口调用有时可能会压倒微服务本身或整个系统,因此,系统架构师和开发者必须持续寻找有效处理接口调用的机制,并尽可能消除依赖。

设计系统以处理调用负载、数据流和数据同步,以及其运营方面,可能是一个非常艰巨的过程,并创造出难以忽视的复杂层次。

复杂性是实现和运行 MSA 企业系统的主要权衡因素之一。

初始成本

MSA 系统通常需要大量的资源来处理每个微服务的个体处理需求、微服务之间的高度通信,以及开发这些微服务的不同开发和预演环境。

如果这些微服务是从零开始开发的,那么构建 MSA 系统的初始成本就会过高。你必须考虑到许多个体开发环境、许多需要开发和测试的微服务,以及执行所有这些任务并整合所有这些组件的不同团队的成本。所有这些都增加了初始系统开发成本。

紧密 API 控制

每个微服务都有自己的 API 调用,以便能够与系统中的其他微服务集成。API 命令参考集的任何变化——例如任何 API 调用参数的更新、废弃的 API 或返回值的变化——可能需要更改其他微服务处理从和到更新微服务的数据流的方式。这可能会带来真正的挑战。

开发者必须维护向后兼容性(有时这可能是一个很大的限制)或者更改系统中与更新后的微服务交互的每个其他组件的 API 调用代码。

因此,系统架构师和开发者必须对 API 更改保持非常严格的控制,以维护系统稳定性。

数据结构控制和一致性

企业系统中拥有独立应用程序的缺点是,每个微服务都必须维护自己的数据结构,这给在整个系统中维护数据一致性带来了挑战。

如果我们以客户订单履行为例,添加/更新客户信息微服务应该有自己的数据库,完全独立于系统中的任何其他数据库。同样,更新商品库存微服务应该是负责商品信息数据库的微服务,更新订单数据库微服务应该拥有订单数据库,等等。

现在的挑战是,发货数据库需要与客户信息数据库保持同步,订单数据库必须包含一些客户信息。此外,消息/电子邮件客户微服务必须有一种方式来访问客户信息(或通过 API 调用接收客户信息),等等。在一个更大的系统中,保持不同微服务之间数据一致性的过程变得复杂。微服务越多,数据同步就越复杂。

再次强调,在设计和发展一个系统时,考虑到所有这些工作,又给系统架构师和开发者带来了额外的负担。

性能

正如我们之前提到的,微服务必须相互通信以执行整个系统功能。这种通信、数据流、错误处理和容错设计——以及其他许多因素——容易受到网络延迟、网络拥塞、网络错误、应用程序数据处理时间、数据库处理时间和数据同步问题的影响。所有这些因素都极大地影响了系统性能。

性能是采用和运行 MSA 企业系统时的另一个主要权衡因素。

安全性

由于微服务的自主性和它们松散的耦合,MSA 要正常工作,不同服务之间必须进行大量的数据交换。这种数据流、每个微服务内的数据存储、数据处理、API 调用本身以及事务日志都显著增加了系统的攻击面,并引发了相当大的安全担忧。

组织文化

MSA 中的每个微服务都有自己的开发周期,因此有自己的架构师、开发者、测试员以及整个开发和发布周期团队,所有这些都是为了维护微服务的主要目标:它们的自主性。

MSA 企业系统是由大量微服务和用于管理不同系统组件之间交互的机制构建的。因此,开发者必须具备系统运营知识,而运营团队则需要具备开发知识。

测试 MSA 系统中将遇到的如此复杂的分布式环境,变成了一项非常艰巨的过程,需要一套不同的专业知识。

传统的组织结构,如一个专注于开发的大团队,一个仅进行基本测试的 QA 团队等,对于 MSA 的结构和运营方式来说已经不再足够。

敏捷开发和 DevOps 方法非常适合微服务开发。你需要敏捷流程来帮助维护 MSA 承诺的快速开发和发布周期。你需要 DevOps 团队,他们非常熟悉设计应用程序本身及其在大图景中的位置,测试应用程序,测试其在整个系统中的功能,发布周期以及发布后如何监控应用程序。

所有这些都要求文化转变和重大的组织转型,以实现 DevOps 和敏捷开发。

重要提示

我们很少看到由于技术限制而导致 MSA 采用失败的情况;相反,MSA 采用失败几乎总是由于未能将组织的文化转向真正的 DevOps 和敏捷文化。

利益大于弊端

你现在需要回答的主要问题是:构建 MSA 是否值得?在当前的组织文化下,我们能否实现它?组织需要多长时间才能转型并准备好采用 MSA?我们是否有等待的奢侈?我们能否同时进行组织转型和 MSA 企业系统的构建?我们是否有必要的新组织结构所需资源和能力?成本是否是一个问题,我是否有足够的预算来覆盖这些费用?

首先,如果你计划构建一个大型企业系统,并且你有启动这个项目的预算和必要的资源,那么以 MSA 的方式构建系统无疑是值得的。所有初始成本和时间投入最终都将由拥有 MSA 系统带来的长期成本和时间节省效益所抵消。

尽管如此,你仍然是最好回答所有这些先前问题的人。采用 MSA(微服务架构)具有压倒性和令人信服的优势,但我们已经看到,这并不是一项简单的任务;因此,一个组织是否愿意走这条路,只有它自己才能回答。

现在我们已经知道了部署 MSA 的优势以及 MSA 采用带来的挑战,我们将现在讨论不同的企业架构风格,它们是什么,以及它们之间的区别。

松散耦合与紧密耦合的单体系统

早期的传统应用程序大多使用单体架构构建,其中整个应用程序是一个庞大的代码库。所有系统组件和功能都紧密耦合在一起,以提供业务解决方案。

如以下图所示,系统功能都是同一代码的一部分,与集中式治理紧密耦合。每个系统功能都必须在应用程序的同一框架内开发。

然而,在 MSA 系统中,每个功能都保持了自己的匿名性——即与去中心化治理松散耦合,赋予每个团队使用其首选技术栈、工具、框架和编程语言的能力:

图 1.5:单体架构与微服务系统对比

图 1.5:单体架构与微服务系统对比

在单体架构的应用程序中,所有功能都被封装在应用程序本身中。在 MSA 中,这些功能是分别开发、打包和部署的。因此,我们可以在多个地点的本地基础设施、公共云或混合云模式下在本地和云之间运行这些服务。

在单体系统中,由于紧密耦合,同步不同的系统功能变化是开发和运营的噩梦。如果某个应用程序(无论什么原因)变得不稳定,它可能会使整个系统失败,而将系统恢复到稳定状态则变得非常痛苦。

然而,在微服务的情况下,由于每个微服务都是松散耦合的,更改和故障排除仅限于特定的微服务,只要微服务接口没有改变。

在单体架构的情况下,一大块代码很难管理和维护。它也难以理解,尤其是在大型组织中,多个开发者共同工作。

在许多情况下,例如员工流动,例如,开发者可能需要调试他人的代码,当应用程序是用一大块代码编写时,事情往往变得复杂,难以追踪和理解,也难以逆向工程和修复。代码维护成为一个严重的问题,而在微服务的情况下,这庞大的代码行被分解成更小的代码块,更容易阅读、理解、调试和修复,完全独立于系统的其他组件。

当单体架构需要代码更改时,对代码部分的单个更改可能需要更改应用程序的许多其他部分,因此,更改更新可能需要整个应用程序的重写和重新编译。

我们还可以将不同的应用程序一起打包到工作流程中,形成一个特定的服务,如之前在图 1中所示。4。

将复杂的应用程序分解成多个模块或微服务,每个在整个生态系统中执行特定功能,以实现更好的可扩展性、更高的可移植性和更高效的开发和运营,这是常识。

对于小型、简单和短期存在的系统,单体应用程序可能更适合您的组织,更容易设计和部署,开发成本更低,发布速度更快。随着业务需求的增长,MSA 成为一种更好的长期方法。

由于单体系统是紧密耦合的,不同系统功能之间不需要 API 通信;这显著降低了系统的安全面,降低了系统安全风险,并提高了系统的整体性能。

将单体和 MSA 之间的部署差异想象成经济型汽车和波音 787 之间的差异。汽车是更好的、更便宜、更快的工具,用于在相距 50 英里的两个城市之间旅行,无需在登机前在机场经历的安全检查。然而,随着距离的增加,开车变得更为麻烦。在 5000 英里时,波音 787 可能成为到达目的地的更好、更便宜、更快的途径,你可能会愿意忍受登机前必须经历的安全检查的麻烦。

以下是对单体和微服务应用程序的比较总结:

单体 MSA
架构 高度自主。系统功能被分割成独立松散耦合的小代码块。 无自主性。系统功能全部紧密耦合成一个大的代码块。
可移植性 高度可移植 可移植性非常有限
可复用性 高度可复用 复用代码的能力非常有限
模块化和可扩展性 高度模块化和可扩展 模块化有限,难以扩展
初始 TTM 高度依赖于单个系统服务的准备情况。代码复用越多,TTM 越短。如果系统微服务是从零开始设计和开发的,那么对于单体架构,TTM 通常会更长。 长期 TTM,尤其是在大型系统中。小型和简单系统中的 TTM 较短。
发布周期 非常短的发布周期,部署更改和补丁更新非常快 长期且通常非常耗时的发布周期和补丁更新
初始成本 通常较高。取决于系统大小。初始成本通过运营成本节省来抵消。 通常较低。在大型企业系统中,初始规模会更高。
运营成本 低。易于维护和操作。 高。难以维护和操作。
复杂性
API 控制
数据结构一致性 去中心化数据库,因此数据一致性更难维护 集中式数据库,因此更容易在整个系统中维护数据一致性
性能 通常较低 通常较高
安全性 存在许多安全担忧 安全担忧较低
组织采纳 采纳难度取决于组织结构。需要采纳敏捷开发和 DevOps。可能需要进行组织转型,并且可能需要很长时间才能实现。 采纳容易。需要的组织转型最小——如果有的话。
容错性 通常较高 通常较低

表 1.1:单体系统和 MSA 系统之间差异的总结

在本节中,我们讨论了单体系统的不同方面;接下来,我们将介绍服务驱动架构和 EDA,以及如何在 MSA 中结合这些架构风格来解决之前讨论的一些 MSA 挑战。

服务驱动、EDA 和 MSA 混合模型架构

人们经常混淆微服务架构(MSA)和服务驱动架构(又称面向服务架构SOA)。这两种架构类型都试图将单体架构系统分解成更小的服务。然而,在 MSA 中,系统服务的分解非常细粒度,将系统分解成非常精细的专业独立服务。在 SOA 中,系统服务的分解则是以领域级别为粗粒度。

所有领域,如以下图所示,共享相同的集中式数据库,并且实际上可能共享其他资源,从而在 MSA 中不存在的某些层面上产生耦合和系统依赖。数据存储是这两种架构风格的关键区别:

图 1.6:SOA 架构拆分为功能域

图 1.6:SOA 架构拆分为功能域

在前面讨论的简化 MSA 客户订单示例中,有八个不同的微服务。在 SOA 中类似的实现可能会将这些微服务构建在一起,并在单个域中紧密耦合。系统中的其他域可能是购物车处理、目录浏览和推荐等。

SOA 具有整体的企业视角,而在微服务中,开发关注的是功能本身,完全独立于微服务打算使用的企业系统。

EDA(事件驱动架构)是另一种广泛采用的架构风格。虽然 MSA(微服务架构)主要关注功能,而 SOA(面向服务架构)强调领域,EDA 则专注于系统事件。

EDA 通常与另一种主要系统架构(如 SOA 或 MSA)相辅相成。在 EDA 中,服务在其主要架构(MSA 或 SOA)确定的粒度级别上解耦,然后通过基于事件的事务相互通信。在我们的订单处理示例中,这些事件可能是订单创建订单取消订单发货等等。

为了在整个企业系统中维护事件同步和数据一致性,这些事件必须由消息代理处理。消息代理的唯一责任是确保将这些事件传递到系统中的不同服务。因此,它必须高度可用、高度响应、容错和可扩展,并且必须能够在高负载下运行。

当在 MSA 企业系统中采用 EDA 时,该情况下的消息代理将处理事件、API 调用以及 API 调用的响应。

当特定服务宕机或负载过重时,消息代理必须能够排队消息,并在该服务可用时传递该消息。

ACID 事务

任何具有某种形式数据存储的系统都必须确保数据的完整性、可靠性和一致性。在 MSA 中,系统在工作流事务中存储和消费数据,并且为了确保整个 MSA 系统的完整性和可靠性,系统内部存储的数据必须符合称为原子性、一致性、隔离性和持久性ACID)的某些原则:

  • 原子性:全部或无操作事务。要么工作流中的所有事务都成功执行并提交,要么它们全部失败并取消。

  • 一致性:任何服务中的数据更改都必须在整个系统中保持其完整性,或者被取消。

  • 隔离性:每个数据事务都有其主权,不应影响或被系统中的其他事务影响。

  • 耐用性:已提交的事务永久不变,即使在系统故障的情况下也是如此。

Saga 模式

MSA 中的一个主要挑战是分布式事务,其中数据流跨越了系统中的多个微服务。这种跨服务的数据流会带来违反微服务自治的风险。数据必须在微服务本身内进行管理,完全隔离于系统中的任何其他服务。

如果你再次查看我们的订单放置示例,你会发现客户数据(或其一部分)跨越了示例中的不同微服务,这可能会在 MSA 中创建不希望的依赖关系,并且应该不惜一切代价避免。

如果因为任何原因,更新项目库存服务失败,或者服务恰好报告说该商品不再可用怎么办?在这种情况下,系统需要回滚并更新所有单个服务的数据库,以确保工作流程的 ACID 事务。

saga 模式管理事务的整个工作流程。它将特定过程中执行的所有事务集合视为工作流程,并确保在该工作流程中的所有这些事务要么成功执行并提交,要么在因任何原因导致工作流程中断的情况下回滚,以维护系统中的数据一致性。

saga 参与者服务将包含工作流程中的本地事务部分。本地事务是在服务内部执行的事务,并在执行时产生一个事件来触发工作流程中的下一个本地事务。这些事务必须遵守 ACID 原则。如果这些本地事务中的任何一个失败,saga 服务将启动一系列补偿事务来回滚工作流程中已执行的本地事务引起的任何更改。

每个本地事务都应该有相应的补偿事务来执行,以回滚由本地事务引起的操作,如下面的图所示:

图 1.7:本地和补偿事务的处理

图 1.7:本地和补偿事务的处理

在 saga 服务中协调事务工作流程有两种方式:编排编排

在编排中,saga 参与者服务无需集中管理器即可交换事件。就像在 EDA 中一样,需要一个消息代理来处理服务之间的事件交换,如下面的图所示:

图 1.8:saga 服务中的编排

图 1.8:saga 服务中的编排

在编排中,引入了一个叙事模式集中控制器:一个编排器。工作流程在编排器中配置,编排器向每个叙事参与者服务发送请求,确定需要在哪个服务上执行本地事务,接收来自叙事参与者服务的事件,检查每个请求的状态,并通过执行必要的补偿事务来处理任何本地事务失败,如图下所示:

图 1.9:叙事服务中的编排

图 1.9:在叙事服务中的编排

编排器成为企业系统的“大脑”,是执行特定系统工作流程所需采取的所有步骤的唯一来源。因此,编排器必须以高度弹性和高度可用性的方式实现。

命令查询责任分离(CQRS)

在传统系统中,尤其是在单体应用中,通常会在后端部署一个公共的关系型数据库,并由前端应用程序访问。这个集中式数据库通过创建-读取-更新-删除CRUD)操作进行访问。

在现代架构中,尤其是在应用规模扩大的情况下,这种传统实现会带来问题。由于在数据库上处理多个 CRUD 请求,表连接创建的可能性很高,从而导致数据库锁定。表锁定引入了延迟和资源竞争,并极大地影响了整体系统性能。

复杂查询有大量的表连接,可能会锁定表,直到查询完成并且数据库解锁表,阻止对它们的任何写入或更新操作。数据库读取操作通常比写入操作多出数倍,在重交易系统中,问题可能会成倍增加。

你可以在这里看到 CRUD 和 CQRS 模式的对比:

图 1.9:叙事服务中的编排

图 1.10:CRUD 与 CQRS 模式对比

使用 CQRS,你只需简单地将一个对象分成两个对象。因此,而不是在一个对象上同时进行命令和查询,我们将该对象分成两个对象——一个用于命令,一个用于查询。命令是一种改变对象状态的操作,而查询不会改变系统的状态,而是返回一个结果。

在我们的情况下,这里的对象是系统数据库,而这个数据库的分离可以是物理的也可以是逻辑的。尽管对于 CQRS 来说,拥有两个物理数据库是一种最佳实践,但你仍然可以使用相同的物理数据库来处理命令和查询。例如,你可以将数据库分成两个逻辑视图——一个用于命令,一个用于查询。

在 CQRS 中使用两个物理数据库时,会从主数据库创建一个副本。当然,副本需要与主数据库同步以保持数据一致性。同步可以通过实现 EDA(事件驱动架构)来完成,其中消息代理处理所有系统事件。副本订阅消息代理,每当主数据库向消息代理发布事件时,副本数据库将同步该特定更改。

主数据库实际更改的确切时间和该更改反映在副本中的时间之间会有延迟;在这段时间内,两个数据库不是 100%一致的,但最终会一致。在 CQRS 中,这种同步称为最终一致性同步。

在 MSA 中应用 CQRS 设计时,数据库处理延迟大大降低,因此各个服务之间的通信性能得到显著提升,从而提高了整体系统的性能。

在 MSA 中使用的数据库可以是任何类型,具体取决于该特定服务在 MSA 中的业务案例。它可能非常可能是关系数据库RDB)、文档数据库、图数据库等等。NoSQL 数据库也是一个很好的选择。

我们之前已经从设计和架构的角度讨论了 MSA。运营 MSA 系统是整个组织在成功交付业务流程时必须考虑的另一个方面。在下一节中,我们将讨论 DevOps,它如何融入 MSA 生命周期,以及为什么它对于成功采用和运营 MSA 至关重要。

MSA 中的 DevOps

DevOps 围绕软件开发和发布周期中的一系列操作指南。传统的开发工程师不再生活在他们封闭的环境中,所有焦点都是将功能规格转换为代码;相反,他们应该对应用程序有端到端的认识。

DevOps 工程师将负责监督、理解并参与整个管道,从整个应用程序计划开始,将业务功能转换为代码,构建应用程序,测试它,发布它,监控其操作,并返回必要的反馈以进行增强和更新。

这并不一定意味着 DevOps 工程师将负责所有开发和运营任务细节。应用程序团队中的个人责任可能会有所不同,以确保应用程序的持续集成和持续部署CI/CD)管道的顺畅:

图 1.11:DevOps CI/CD 管道

图 1.11:DevOps CI/CD 管道

DevOps 的主要目标之一是加快 CI/CD 管道的速度;这就是为什么在 DevOps 中非常重视自动化。自动化对于高效执行管道是必不可少的。

自动化可以在每个步骤中提供帮助。在 DevOps 中,许多作为您的质量保证计划一部分的测试用例都是自动化的,这显著加快了质量保证过程。您的应用程序的发布管理和监控也是自动化的,以提供高可见性、持续学习和在需要时快速修复。所有这些都将帮助组织提高生产力、可预测性和可扩展性。

DevOps 是对应用开发和管理的整体视角。它不仅仅是为开发团队或运维团队采用的功能;相反,整个组织都应该采用它。因此,组织结构以及组织的愿景和目标与从传统软件开发方式转变所需的程序和功能变化相一致是至关重要的。

只为了给您一个关于传统和 DevOps 模型在应用开发和发布周期方面差异的概览,请查看以下比较表格:

传统 DevOps
规划 数月由于应用程序规模大且不同应用程序组件之间紧密耦合,规划时间较长 天到几周由于应用程序被分解成小型独立松散耦合的服务,规划时间非常短
开发 数月 天到几周,在补丁和修复的情况下甚至更短
测试 周到数月主要是手动密集型的质量保证用例测试,有时可能会危及测试结果的可信度 天主要是自动化的质量保证用例执行,为应用程序带来高可靠性
发布、部署 天通常需要大量手动工作,更容易受到人为错误的影响 小时主要是自动化的
运营、监控 指标报告主要是手动提取和分析 指标自动监控和分析,甚至可以在几秒钟内解决问题。此外,机器学习ML)工具可以进一步优化操作。

表 1.2:传统运维风格与 DevOps 对比

在传统的开发环境中,您需要编写、维护和更改一大块代码。由于代码量很大,发布周期长是正常的,只有在需要主要更改或高严重性修复时,才可行地部署补丁或新版本,如下面的图所示:

图 1.12:传统开发环境与 MSA DevOps 对比

图 1.12:传统开发环境与 MSA DevOps 对比

在 MSA 中,团队根据不功能的应用程序进行划分。那块大代码被拆分成一系列更小的代码(微服务),由于团队独立工作,每个团队专注于特定的微服务,因此开发和发布周期要短得多。

类似地,在 DevOps 中,应用程序被分解成更小的部分以实现 CI/CD 管道,这使得 DevOps 成为适合 MSA 的完美模型。

为什么是 ML?

在您的 MSA 企业系统中使用 ML 工具和算法可以进一步增强和加速您的 DevOps CI/CD 管道。有了 ML,您可以在测试中找到模式,监控管道的阶段,自动分析故障可能发生的地方,并在可能的情况下提出解决方案或自动修复操作问题。

ML 可以大大缩短您的 MSA 企业系统的 TTM(Time to Market),并使其更加智能、自我修复、弹性好、易于支持。

在本书中,我们将讨论机器学习(ML)的两个方面:首先,我们将详细解释如何将 CI/CD 管道智能添加到您的 MSA 企业系统中,其次,我们将探讨如何考虑 MSA 构建一个 ML 企业系统:

图 1.13:在 CI/CD 管道中使用 ML

图 1.13:在 CI/CD 管道中使用 ML

摘要

在本章中,我们介绍了 MSA 的概念以及 MSA 与传统的单体架构的不同之处。到目前为止,您也应该对 MSA 的优势以及组织在采用 MSA 时可能遇到的挑战有清晰的理解。

我们还涵盖了设计 MSA 时需要考虑的关键概念,如 ACID、saga 模式以及 CQRS。所有这些概念对于克服同步挑战和保持微服务匿名性都是必不可少的。

我们现在理解了 DevOps 的基本知识以及为什么它在 MSA 设计、部署和运营中很重要,以及 ML 在 MSA 企业系统中的集成如何有助于增强系统运营。

在下一章中,我们将介绍组织从运行传统的单体系统过渡到 MSA 系统时追求的常见方法。我们将讨论如何将现有系统分解成服务,这些服务构成了新的 MSA 企业系统。

第二章:重构单体

现在我们已经决定 MSA 是我们组织正确的架构风格,接下来是什么?

在 Kong Inc.最近发布的报告《2022 年 API 与微服务连接性报告》中,75%的组织缺乏创新和技术采用。

对一个能够快速响应客户和市场需求的 IT 系统的需求从未如此之高。单体应用程序无法再应对快速的市场更新和需求。这就是组织寻求更新其 IT 系统以保持业务运营的主要原因之一。

MSA 是灵活和可靠的企业系统的主要推动者。因此,从单体架构过渡到 MSA 对于现代化组织的 IT 系统变得至关重要。

在本章中,我们将讨论如何将现有运行中的单体应用程序的业务需求分解为微服务,以及过渡到 MSA 应用程序所需的步骤。

在我们浏览本章内容时,我们将涵盖以下领域:

  • 识别系统的微服务

  • ABC 单体

  • 功能分解

  • 数据分解

  • 请求分解

识别系统的微服务

不论是现有系统的棕色地带还是绿色地带企业系统实施,我们仍然需要尽可能细致地将业务需求分解为基本功能。这将有助于我们识别每个微服务并将其成功集成到我们的企业系统中。

在现有系统中,业务和系统需求已经被识别和实现。然而,根据新的业务标准、变化和要求,它们可能需要被重新审视和更新。

将您的应用程序重构为简单服务的目标是形成高度细粒度的功能,这些功能最终将被构建(或获取)为微服务。您很可能在从单体系统中提取的一些功能之外,在新的 MSA 中添加新功能。

因此,我们将迁移过程分为以下高级步骤:

  1. 定义待 MSA 系统和构建该 MSA 所需的功能。

  2. 确定在当前单体系统中哪些现有功能将在新的 MSA 中重用,并作为微服务实现。

  3. 确定现有要重用的功能和需要达到待 MSA 系统所需的功能之间的差异。这些是新 MSA 系统中要实现的新功能。

  4. 从步骤 3 中确定的功能列表中,确定哪些功能将作为内部微服务开发,哪些可以通过第三方获取。

使用以功能驱动的分解方法来分解单体是一个好的起点;然而,仅使用这种方法是不够的。由于数据存储在单体中是集中的,因此在维护微服务的自主性时,数据依赖仍然是一个很大的问题。

单体中不同功能之间的交互是另一个关注点。我们需要调查函数调用是如何被处理和处理的,这些功能之间共享了什么数据,以及返回了什么数据。

在重构过程中检查单体系统的功能、数据和函数调用(请求)对于维护微服务的自治性和实现期望的粒度级别至关重要。

重要注意事项

请记住,在整个单体分解过程中,我们必须维护微服务的自治原则。过多的微服务会导致 Nano-service 反模式 效应,而微服务过少则仍然会使你的系统面临与单体系统相同的问题。

Nano-service 反模式对大多数系统的操作产生了过多的期望,这反过来又可能进一步复杂化你的 MSA 系统,并导致稳定性降低、可靠性下降和其他系统性能问题。

重要注意事项

作为一般规则,应用 共同封闭原则,即由于相同原因而更改的微服务最好打包在单个微服务中。

为了更好地解释单体向 MSA 的转换过程,在接下来的章节中,我们将设计一个简单的假设单体系统,使用已经提到的系统分解三个阶段来分解系统,构建不同的微服务,然后将它们组织起来构建 MSA。

ABC 单块

ABC 是一个简化的假设产品订购单体系统,专门用于演示将单体应用程序重构为 MSA 所需的过程和步骤。我们将在这本书中使用这个 ABC 系统来演示如何应用概念和方法的一些示例。

请注意,我们仅为了演示目的而将 ABC-Monolith 系统组合在一起,我们的目标不是讨论如何更好地设计或结构化 ABC-Monolith。我们更关注 ABC-Monolith 系统重构的过程本身。

在 ABC-Monolith 中,用户可以从现有的产品目录下订单并跟踪订单的运输状态。为了简化,所有销售均为最终销售,产品不能退货。

系统将能够处理订单支付,为订单分配运输快递,并跟踪所有订单和运输更新。

下面的图显示了 ABC-Monolith 的高级架构。用户门户用于将项目添加到购物车,然后将订单详情发送到 ABC-Monolith。ABC-Monolith 有不同的紧密耦合功能与集中式数据库,所有这些都是为了处理从支付到交付的订单。在整个订单履行过程中,用户会收到所有订单和运输更新的通知。

图 2.1:ABC-Monolith 架构

图 2.1:ABC-Monolith 架构

为了进一步了解单体,我们将通过讨论现有单体的函数、单体数据库结构和订单放置过程的流程来回顾系统的当前状态。我们将通过比较当前状态和预期状态来结束本节。

ABC-Monolith 的当前函数

首先理解单体中实现了哪些当前函数以及它们在整体系统中的作用是至关重要的。以下表格列出了我们稍后需要在系统重构中考虑的系统函数:

函数 描述
place_order() 创建包含所有订单信息的记录,并将订单标记为“待处理”,等待订单放置过程的其余部分。
check_inventory() 检查已放置订单中项目的可用性。
process_payment() 验证订单总金额的支付。如果支付未完成,将返回错误代码。
update_inventory() 一旦订单得到验证并且支付成功处理,应相应地更新项目库存。
create_order() 订单现在已成功处理;是时候更改订单状态并启动订单准备过程(包装等)。
create_shipping_request() 启动订单运输请求,并通知快递员有可运输的订单。
order_status_update() 一个用于更新订单状态并记录任何更改(如准备、运输、异常、已接收等)的函数。
shipment_status_update() 一个用于更新运输状态并记录任何更改(如待取货、已取货、在途中、异常、已接收等)的函数。
notify_user() 通知用户任何关于已放置订单的更改或更新。
register_customer() 一个使用全名、地址、电话和其他详细信息创建客户记录信息的函数。

表 2.1:ABC-Monolith 函数列表

在前面的表中,我们专注于描述函数本身的作用,而不考虑参数传递的方式或返回值。

ABC-Monolith 的数据库

单体中识别的所有函数都共享一个集中式数据库。以下是被函数访问的数据库表:

数据库表 描述
CUSTOMER 存储所有客户信息的表,如姓名、电子邮件和电话。
ITEM 产品信息在目录中。产品信息包括产品名称、价格和库存数量。
ORDER 订单信息。
ORDER_ITEM ORDERITEM表之间多对多关系规范化的表。
ORDER_STATUS 每个已放置订单的状态,并参考status_code
STATUS_CODE 订单和运输状态代码的查找表。
COURIER 运输快递信息,包括快递名称、联系方式等。
SHIPMENT_REQUEST 订单放置的所有运输请求列表。
SHIPMENT_REQUEST_STATUS 每个运输请求的状态,包含对status_code的引用。

表 2.2:ABC-Monolith 数据库表列表

下面的内容是 ABC 的实体关系图ERD)。请注意,我们需要创建ORDER_ITEM规范化表来分解ORDERITEM表之间的多对多关系:

图 2.2:ABC-Monolith 的实体关系图

图 2.2:ABC-Monolith 的实体关系图

请记住,单体应用中的一些功能需要完全的读写访问特定表,并访问表中所有字段,而其他一些功能只需要访问表中的特定字段。这些信息在系统重构中非常重要。

在下一节中,我们将讨论工作流程以识别 ABC-Monolith 的当前状态(As-Is)并确定我们如何过渡到待实现状态(To-Be)。除了工作流程信息外,功能数据库访问需求将帮助我们重构单体数据库,将其分解为每个微服务的独立 MSA 数据库。

这是 ABC 的工作流程和当前功能调用

我们目前已知在单体应用中使用了哪些功能以及单体数据库的结构是如何组织的。下一步是检查订单放置的工作流程:

图 2.3:ABC-Monolith 的功能请求/工作流程

图 2.3:ABC-Monolith 的功能请求/工作流程

如前所述的工作流程图所示,各个功能都是顺序执行的。由于这是一个紧密耦合的系统,预期不会有同步问题,因此不需要编排。

然而,当我们朝着 ABC-MSA 迈进时,服务的解耦产生了在特定顺序中管理这些服务执行的需要。

下面的图中展示了 ABC 的当前状态(As-Is)和待实现状态(To-Be)。在当前状态(As-Is)中不需要集中管理;然而,在待实现状态中引入了一个编排器组件来管理服务之间的流程。

在待实现状态下,每个单独的服务都有一个专门的数据库,如图所示。相反,在当前状态(As-Is)中,数据库是集中的。

图 2.4:ABC 的当前状态(As-Is)和待实现状态(To-Be)

图 2.4:ABC 的当前状态(As-Is)和待实现状态(To-Be)

既然我们已经了解了当前的 ABC-Monolith 是如何组织的,以及当前状态(As-Is)和待实现状态(To-Be)是什么,那么现在是时候开始 ABC-Monolith 的重构过程,以转化为 ABC-MSA。

我们将分三个阶段重构单体应用。首先,我们将分解单体功能并将这些功能映射到微服务上。然后,我们将分解数据以查看如何设计各个数据库。最后,从单体应用的工作流程中,我们将分析功能请求,并据此构建我们的 MSA 叙事。

功能分解

重构 ABC 单体应用的第一步是根据我们之前确定的应用系统功能创建微服务。这是现有功能和微服务之间的一种直接映射。

关键点在于,仅通过单独查看每个函数,而不考虑任何函数调用或数据连接,你在函数分解中需要尽可能细化。

乍一看,notify_user() 函数对于一个微服务来说做了太多事情,包括显示网络用户消息状态/更新,通过电子邮件通知用户,以及/或者通过短信通知用户。每个这些功能都可以有自己的规则、设计、问题和关注点。从微服务架构(MSA)的角度来看,将 notify_user() 函数拆分为三个函数是一个更好的方法,以实现关注点的分离。

因此,我们将 notify_user() 函数拆分为一个处理网络消息和通知的函数,一个处理电子邮件通知的函数,以及一个处理短信消息通知的函数:

  • web_msg_notification()

  • email_notification()

  • sms_notification()

同样,process_payment() 函数也可以拆分为两个不同的、更细粒度的函数,一个用于处理直接信用卡支付,另一个用于处理 PayPal 支付:

  • verify_cc_payment()

  • verify_paypal_payment()

以下图表显示了 ABC-Monolith 目前的拆分情况。我们还没有研究系统函数之间是如何相互作用的。函数交互和整体订单履行的流程将在稍后的阶段处理。

图 2.5:ABC-Monolith 功能分解

图 2.5:ABC-Monolith 功能分解

在这一点上,我们对目前的粒度水平感到满意,并准备检查数据库表是如何被访问的,以查看是否需要进一步的分解。

数据分解

在这个阶段,我们需要查看每个函数是如何访问数据库的,以及访问了哪些表,甚至数据库表的哪些部分。

以下图表显示了 ABC-Monolith 函数访问数据库的哪些部分。确切知道哪个函数访问了哪些表以及为什么是至关重要的。这将帮助我们识别数据库依赖关系,以便稍后消除这些依赖关系,并将集中的 ABC-Monolith 数据库拆分为单独的数据存储,每个数据存储都专门用于每个微服务。

图 2.6:ABC-Monolith 数据库访问

图 2.6:ABC-Monolith 数据库访问

我们仍然受限于微服务自治规则。在图表和这个重构阶段中具有挑战性的部分是共享的表。在两个微服务之间共享一个表会创建违反自治规则的耦合。另一方面,在多个微服务中创建表的多个副本将导致严重的数据一致性问题。那么,我们如何解决这个难题呢?

例如,记住ORDER表,它在 ABC-MSA 系统的place_order()create_order()process_payment()服务中。对于check_inventory()update_inventory()等,采取类似的方法。

因此,考虑到叙事模式,让我们重新审视前面图表中显示的 ABC-Monolith 数据库访问,为 ABC-MSA 系统中的服务构建一个新的数据库访问图表。

协调数据事务有两种方式,即协调和编排。在协调中,ABC-MSA 叙事参与者服务将不得不自行协调数据事务。在编排中,一个集中的编排器执行协调过程并处理所有工作流事务。

当然,我们可以选择任何一种协调方法,但在我们这个例子中,我们会认为编排比协调提供了更好的解耦模型。因此,为了保持我们的例子简单,我们将使用编排来构建我们的 ABC-MSA 叙事模式。

下面的图表显示了 ABC-MSA 服务数据库访问。如图所示,系统中已经复制了一些数据库表。在下一节中,我们将使用叙事模式来维护复制表之间的数据一致性。

图 2.7:ABC-MSA 数据库访问

图 2.7:ABC-MSA 数据库访问

我们注意到在其他服务中,例如notify_user()函数可能根本不是一个好主意。在这些不同的服务之间,你应该只看到小的数据库访问相似性,而不是完全相同的数据库。在现实场景中,我们最好将这三个服务合并成一个最初的服务。

类似地,在现实场景中,process_payment()函数可能被映射到包含整体清除支付的单个服务,无论它是信用卡、PayPal 还是任何其他支付形式。为了演示目的,我们将notify_user()process_payment()分别拆分为三个和两个不同的服务。

到目前为止,我们已经能够从 ABC-Monolith 函数构建 ABC-MSA 的微服务,识别单体中的数据访问,并将单体分解为独立的微服务,每个微服务都有自己的数据库。在下一节中,我们将更多地关注如何通过查看新 ABC-MSA 系统中服务请求的编排来确保微服务的隔离和关注点分离。

请求分解

ABC-Monolith 功能请求流程已经确定并显示在 图 1**.3 中。我们现在将看到这个流程在 ABC-MSA 中是如何工作的。

在 ABC-MSA 中,sagas 是在集中式协调器中编程和配置的。协调器将根据定义的工作流程以同步或异步的方式对 sagas 中的每个服务发起单独的 API 调用,并等待每个 API 调用的响应,以确定接下来要发起的其他 API 调用以及如何进行。

下面的图示显示了 ABC-MSA 中工作流程的形态。请注意,我们场景中的所有 API 调用都是从协调器发起的。如您从序列号中看到的那样,有一些 API 调用是并行发起的,而在其他情况下,协调器根据从先前执行的服务收到的响应来决定下一步的行动。

图 2.8:ABC-MSA 工作流程

图 2.8:ABC-MSA 工作流程

ABC-MSA 工作流程图中的用户从网页界面启动订单履行过程,这将从协调器启动工作流程。协调器同时启动 place_ordercheck_inventory 服务。place_order 创建带有所有信息的订单,并将其状态标记为 pending,等待工作流程的其余部分处理。

check_inventory 服务检查已订购项目的库存,并根据项目是否可用发送 truefalse 的响应。如果任何订购的项目不可用,则触发 web_msg_notificationemail_notificationsms_notification 服务。

现在,这里有一个挑战:所有三个通知服务都需要访问 CUSTOMER 数据库以获取客户姓名、电子邮件地址、电话号码等。但是,为所有三个服务使用一个数据库会创建不希望的耦合,这会违反微服务自治原则。正如我们之前讨论的,我们应该在所有服务中创建该 CUSTOMER 数据库的副本,以避免服务耦合。但我们如何做到这一点呢?

图 2.9:在 MSA 中维护数据库一致性

图 2.9:在 MSA 中维护数据库一致性

CUSTOMER 数据库在编辑、创建或删除记录时主要由 CUSTOMER 数据库管理。

协调器需要等待来自所有四个服务的成功确认,CUSTOMER 数据库失败?你最终会得到数据不一致,这可能会成为以后的一个严重问题。

因此,所有参与 sagas 的本地事务都需要有一组补偿事务,以确保在执行事务过程中出现任何故障时能够回滚。在我们的示例中,协调器需要撤销对其他所有服务的 CUSTOMER 数据库的更新。

下面的图示展示了如何使用叙事模式回滚更新CUSTOMER数据库的失败。

图 2.10:为注册新客户信息和下订单进行补偿事务

图 2.10:为注册新客户信息和下订单进行补偿事务

摘要

在本章中,我们能够回顾将单体架构重构为微服务架构(MSA)的主要步骤,包括必要的步骤、需要考虑的主要事项以及实现的方法。简化的 ABC 单体系统是一个很好的例子;然而,随着系统变得更加复杂,工作流程变得更加复杂,数据和处理同步的挑战开始出现。

第一章中,我们简要讨论了面临的挑战以及应用的方法来克服这些挑战。在下一章中,我们将开始将这种方法应用到我们试图重构的 ABC 系统上。

在下一章中,我们将讨论如何进一步维护微服务的自主性和 MSA 的稳定性,克服一些其他操作挑战,以及 API 网关、编排器和微服务聚合器的作用。

第三章:解决常见 MSA 企业系统挑战

在上一章中,我们学习了如何分解单体并将其重构为 MSA 企业系统。我们以一个简化的系统为例,然后重构系统以展示此过程。通过这样做,我们解决了运行单体系统的一些挑战。然而,向 MSA 的迁移引入了完全不同的一组需要解决的问题。

在本章中,我们将讨论 MSA 引入的主要挑战,如何解决它们,以及我们需要应用的具体方法来维护 MSA 系统的可靠性、耐用性和平稳运行。

在本章中,我们将涵盖以下主题:

  • 使用反腐败层(ACL)进行 MSA 系统隔离

  • API 网关

  • 服务目录和编排器

  • 微服务聚合器

  • 微服务断路器

  • 网关与编排器与聚合器

  • ABC-MSA 增强

使用 ACL 进行 MSA 隔离

在棕色地带采用 MSA 时,您从单体系统迁移到 MSA 可以是大爆炸迁移滴答迁移

在大爆炸迁移中,您保持旧的单体系统按原样运行,同时构建整个 MSA 系统。一旦 MSA 系统完成、测试和部署,您就可以在组织维护窗口期间完全切换到新的 MSA 系统,然后退役旧的单体系统。虽然这种迁移在某些场景中可用,但在大型企业系统中通常不推荐使用。

在公司非高峰时段或公司标准迁移窗口期间切换用户从旧系统到新系统。由于高停机时间、潜在的回滚和将真实流量应用于新系统时可能出现意外结果的风险,用户突然切换可能是一个复杂且繁琐的过程,所有这些都可能在迁移窗口期间施加大量时间限制。

在我们的案例中,一种常见且更安全的迁移方法是滴答迁移,其中您逐步从旧的单体系统转移到新的 MSA 系统。一种常见的方法是逐步从单体中提取功能、服务和/或模块,并将它们作为您新 MSA 的一部分移动到独立的微服务中。逐渐地,我们逐步淘汰现有单体系统的功能,并逐步构建 MSA 系统。

要成功执行滴答迁移,您需要所谓的反腐败层ACL),它将充当一个中间层、缓冲区和旧混乱单体与您新清洁的 MSA 之间的网关。ACL 层将帮助暂时集成和粘合新提取的服务,以便能够与旧服务、数据库和模块通信,而不会破坏您的新 MSA 系统。您可以在以下图中看到 ACL 架构:

图 3.1:反腐败层(ACL)

图 3.1:反腐败层(ACL)

ACL 的生命周期与单体系统的生命周期一样长。一旦迁移完成,单体系统退役,ACL 就不再需要。因此,建议您将 ACL 编写为独立服务或作为单体的一部分。

ACL 有三个主要组件:

  • API 组件,允许 ACL 使用与 MSA 系统相同的语言与 MSA 系统通信。

  • ACL 外观层,这是允许 ACL 使用单体的语言(们)与单体通信的接口。

外观层可以放置的位置有两种选择;一种如图 3.1所示,外观层被放置为新独立的 ACL 微服务的一部分。另一种选择是将外观层作为单体内部的一个组件,如图 3.2所示:

图 3.2:外观层的两种实现选项

图 3.2:外观层的两种实现选项

选择将取决于架构师和开发者是否希望在内聚体内部添加更多粘合代码,或者完全将开发工作与内聚体隔离。

  • ACL 适配器,作为 ACL 的一部分,位于 ACL 的北向 API 和外观层之间。适配器的主要功能是使用ACL 翻译器接口在单体和 MSA 之间进行转换,如图 3.1所示。

只有在采用渐进式迁移时才需要 ACL。在大爆炸式迁移的情况下,不需要实现 ACL。由于在两种迁移方式之间都有资源消耗,以及优势、风险和权衡,MSA 项目的利益相关者需要决定哪种方式更适合项目和组织。

无论是否实施 ACL,MSA 系统仍然需要一个组件作为 MSA 系统和外部客户端之间的接口。在 MSA 和外部 API 调用之间使用 API 网关被认为是良好的 MSA 设计实践。下一节将讨论 API 网关在 MSA 系统中的作用,以及采用 API 网关在 MSA 设计中的权衡。

使用 API 网关

正如我们在第一章中解释的,微服务可以直接相互通信,无需集中式管理。随着 MSA 系统变得更加成熟,微服务的数量逐渐增加,微服务之间的直接通信可能会成为很大的开销——特别是需要 API 消费者和 API 提供者之间多次往返调用的通信。

根据微服务的自治原则,每个微服务都可以使用其技术栈,并且可能与同一 MSA 系统中的其他微服务使用不同的 API 合约进行通信。例如,一个微服务可能只理解具有 JSON 数据结构的 RESTful API,而其他微服务可能只与 Thrift 或 Avro 进行通信。

此外,活动实例化微服务的位置(IP 和监听端口)在 MSA 系统中动态变化。因此,系统需要有一种机制来识别 API 消费者可以指向的位置。

也有这样的情况,你需要将你的 MSA 系统与旧系统(如大型机、AS400 等)集成。

所有上述情况都需要在 MSA 系统中的每个微服务中嵌入代码。这段代码将帮助微服务理解旧的和非 REST 通信模式,发现系统中其他微服务的网络位置,并了解每个微服务的一般需求。现在,这样一套微服务将有多大的独立性和可移植性?

解决上述挑战的更好方法是使用 API 网关,其中所有系统服务都通过该网关相互通信。API 网关接收来自系统 API 消费者的 API 调用,然后将接收到的数据映射到 API 提供者可以理解和处理的格式和协议:

图 3.3:从服务直接通信迁移到 API 网关通信

图 3.3:从服务直接通信迁移到 API 网关通信

使用 API 网关,我们显著减少了服务之间的直接 1 对 1 通信。此外,我们减轻了系统微服务进行多次翻译、映射代码和认证-授权-计费AAA)任务的压力。相反,我们将发现微服务位置的责任从客户端转移到 API 网关,这反过来又进一步减少了代码开销,使微服务尽可能轻便和独立。

重要提示

MSA 系统的可用性与 API 网关的可用性相当。因此,API 网关需要作为一个高性能和高可用性的关键任务服务来开发和部署管理。

API 网关可以作为 MSA 系统的一部分独立服务部署。网关的主要功能如下:

  • 最小化微服务之间的 API 调用,这使得 MSA 系统间的通信更加高效。

  • 最小化 API 依赖和破坏性变更。在没有 API 网关的 MSA 系统中,如果由于任何原因,其中一个 API 提供者更改了其 API,很可能会发生破坏性变更。这意味着我们需要在每个与 API 提供者通信的微服务中创建一个变更。通过使用 API 网关,API 提供者的变更将仅限于 API 网关,以匹配提供者和消费者之间的 API 合约。

  • 在 API 合约之间进行翻译和映射,从而减轻了微服务在核心功能中嵌入翻译代码的负担。

  • 运行服务发现机制,并减轻客户端运行该功能的负担。

  • 作为 MSA 系统外部客户端调用的入口点。

  • 在高可用性微服务的不同实例之间负载均衡 API 调用,并在高流量情况下减轻微服务的负担。

  • 通过限制在分布式拒绝服务DDoS)和类似攻击期间 API 调用的突然增加,提供更好的安全性。

  • 验证和授权用户访问 MSA 系统中的不同组件。

  • 提供全面的分析,以深入了解系统指标和日志,这有助于进一步改进系统设计和性能。

尽管 API 网关具有所有这些优点和功能,但在你的 MSA 系统中使用 API 网关仍然存在一些缺点:

  • 最明显的是复杂性。系统中协议和 API 合约数据结构越多,网关就越复杂。

  • MSA 的操作高度依赖于 API 网关的性能和可用性,这可能会创建一个不受欢迎的系统性能瓶颈。

  • 在微服务内部通信路径中引入额外的中介组件,如 API 网关,会增加服务响应时间。对于健谈的服务,增加的响应时间可能会变得相当可观。

即使 API 网关提供了所有这些功能,我们仍然需要一种方法来将每个用户请求映射到 MSA 系统需要运行以完成该请求的特定任务。在下一节中,我们将讨论 MSA 系统任务如何映射到特定的用户服务,以及这些任务如何在 MSA 中编排。

服务目录和编排器

编排是 MSA 系统中最常用的通信模式之一。我们在第一章中简要讨论了这一概念。在本节中,我们将深入了解编排的更多细节。

确定不同微服务之间最合适的通信模式取决于许多因素。在确定编排或编排是否是最适合系统的通信模式时,您必须考虑系统中微服务的数量、不同微服务之间的交互水平、业务逻辑本身、动态业务需求如何变化以及系统更新如何动态变化。

编排器作为中央管理者,控制系统微服务之间的所有通信。它们通常通过包含所有服务目录的仪表板界面与用户交互。服务目录是 MSA 系统向用户提供的一组服务。目录中的每个服务都链接到一组工作流程。工作流程是编排器将在系统微服务之间触发和协调的动作,以提供用户从目录中选择的服务:

图 3.4:MSA 中的编排器

图 3.4:MSA 中的编排器

编排器的功能可以扩展到管理工作流程之外。编排器还可以管理微服务的整个生命周期;这包括提供和部署微服务、配置微服务以及根据需要执行升级、更新、监控、性能审计和关闭。

重要提示

编排器是 MSA 系统的主要大脑,确保编排器作为高绩效和关键任务组件部署和管理是至关重要的。

在 MSA 中运行编排器的一些好处包括以下内容:

  • 您拥有一个集中式管理平台,作为您所有工作流程的唯一真相来源。因此,您可以在复杂的 MSA 中构建复杂的工作流程,而无需担心您有多少微服务以及它们如何扩展。

  • 就像在 API 网关中一样,您可以连接您的遗留系统或旧单体系统的一部分,并完全隔离微服务,使其无需与其他系统组件耦合。这可以节省大量将代码构建到独立微服务中的努力,并在扩展 MSA 方面提供巨大帮助。

  • 微服务对编排器可见,因此可以完全由编排器管理、审计和监控。这可以产生非常有帮助和有洞察力的分析,可以进一步增强 MSA 系统的可支持和操作能力。

  • 编排器的可见性有助于解决任何操作问题并快速识别问题。

  • 协调器可以自动检测并自行解决一些操作问题。例如,协调器可以检测资源饥饿并重新路由请求到备份微服务。当检测到问题时,协调器可以自动垂直或水平扩展特定的微服务。协调器还可以尝试自动重启服务,如果服务没有响应。

协调器解决了许多 MSA 操作问题,包括一些数据同步挑战。然而,在扩展系统时,数据同步和数据一致性成为协调器必须自行解决的大挑战。微服务聚合器帮助解决 MSA 系统扩展时的数据同步问题。在下一节中,我们将讨论聚合器模式是什么,它用于什么,以及它是如何工作的。

微服务聚合器

第二章中,我们不得不在多个微服务之间复制一些模式,并通过协调器使用 saga 模式来保持数据一致性和保持微服务的自治性。这种解决方案在 MSA 系统中微服务数量有限的情况下可能是可行的。在大量微服务的系统中,将模式复制到不同的微服务以保持单个微服务数据库的同步并不适用,并且可能会严重影响系统的整体性能。

考虑一个拥有 100 个微服务的 MSA 系统,并将模式复制到大约 20 个微服务中,以保持微服务的自治性。每当任何模式数据的任何部分被更新时,协调器都必须同步这 20 个模式。

此外,即使我们拥有所有 100 个完全自治的微服务,如果用户的某个操作需要从那 20 个微服务中获取信息,会怎样呢?协调器将不得不向 20 个不同的微服务发出至少 20 个不同的 API 调用,以获取用户所需的信息。更不用说其中的一些 20 个微服务可能需要交换多个 API 调用,以便将结果发送回用户。

为了更清晰地理解,让我们回顾一下我们在第二章中构建的 ABC-MSA 系统。我们拥有订单产品库存微服务。产品微服务用于管理产品信息,库存微服务用于管理产品库存,而订单微服务用于下单和管理订单。

假设我们处于这样一个情况:销售分析师正在生成一个报告,以检查一个产品按订单平均购买数量以及订单下单时的产品库存水平,如下面的图所示:

图 3.5:一个示例产品订单报告

图 3.5:一个示例产品订单报告

调度器必须至少发送三个 API 调用,每个调用分别针对orderproductinventory微服务,如下所示:

图 3.6:跨越多个微服务的用户操作

图 3.6:跨越多个微服务的用户操作

为了最小化依赖和响应时间,在这种情况下更好的方法是使用orderproductinventory微服务,并使用合并的信息更新其数据库。

API 网关或消费者只需向聚合器发送一个 API 调用,即可获取所需的所有信息。API 调用的次数最小化,整体响应时间大大减少,尤其是在所需信息分布在大量微服务中的情况下:

图 3.7:聚合器通信模式

图 3.7:聚合器通信模式

聚合器通信模式减少了用户在各种操作请求中可能触发的 API 调用次数,进一步增强了数据同步的设计和性能,以及整体系统性能,尤其是在高延迟网络中。

现在,我们已经了解了 API 网关、调度器和聚合器的角色。在下一节中,我们将讨论这三个组件如何在 MSA 中相互交互。

网关与调度器与聚合器

从我们迄今为止所描述的内容来看,API 网关、调度器和聚合器之间存在一些重叠的功能。在本节中,我们将回答一些关于这三个组件如何在单个 MSA 系统中交互的基本问题:

  • 这三个 MSA 组件是如何协同工作的?

  • API 网关能否执行聚合器和调度器的功能?

  • 在我们的 MSA 中部署所有这些通信模式的最佳实践是什么?

首先,从理论上讲,您可以让客户端直接与 MSA 微服务交互,而不需要 API 网关。然而,这并不是一个好的做法。在没有网关的 MSA 系统中,您需要在系统中每个微服务中实现大部分网关功能。

为了使微服务尽可能轻量化和自主,强烈建议在 MSA 系统中拥有一个 API 网关。API 网关将处理来自不同类型客户端的所有入站和出站 API 流量。客户端可以是网络仪表板、移动应用、平板电脑、第三方集成系统等。

是否添加聚合器将高度取决于您的业务逻辑和系统设计。只有当您有客户端用例,请求需要在后端跨越多个微服务时,您才需要聚合器。

聚合器可以作为网关本身的一部分实现;然而,最佳实践是在需要时才添加聚合器,并使其成为一个独立的独立微服务。

MSA 系统可以拥有多个聚合器,每个聚合器都有特定的业务逻辑,并从不同的微服务集合中收集特定的数据集。

同样,编排模式也可以在 API 网关中实现;然而,你需要让 API 网关专注于执行其创建时的主要功能,并将编排任务留给编排器。编排器也最好作为独立的独立微服务部署:

图 3.8:MSA 高级架构

图 3.8:MSA 高级架构

前面的图显示了所有这些组件在 MSA 系统中协同工作的高级架构。客户端始终与 API 网关交互,API 网关将请求路由到 MSA 系统中的适当服务。

客户端 API 调用根据 API 的配置路由到适当的微服务。如果客户端请求是通过与单个微服务通信来满足的,那么网关将直接将该请求发送到微服务。跨越多个微服务并分配给系统中的特定聚合器的 API 调用将被转发到该特定聚合器。最后,对于调用特定工作流程的 API 请求,API 网关将将其转发到编排器。

在本节中,我们学习了 API 网关、编排器和聚合器如何在同一 MSA 系统中共存。在下一节中,我们将尝试将所有这些三种通信模式的概念应用到我们之前开发的 ABC-MSA 系统中。

微服务断路器

在 MSA 系统中,另一个挑战是工作流程执行的稳定性和保证。我们在第一章中讨论的 Sagas 模式用于确保特定工作流程内的所有事务要么全部成功执行,要么全部失败。但这是否足以确保微服务的可靠执行?

让我们考虑一个场景,即被调用的微服务对 API 调用的响应太慢。请求成功执行,但微服务的响应超时。微服务消费者随后可能会假设执行失败,并相应地重复操作,这可能会非常麻烦。

如下图中所示,当支付微服务发生响应超时时,支付微服务将处理支付,但微服务消费者会假设支付尚未处理,并可能自动(或根据用户请求)重试该过程。这种行为会导致支付被多次处理,从而对同一订单产生多次收费,或者导致订单被多次提交:

图 3.9:响应时间过慢的支付微服务

图 3.9:响应时间过慢的支付微服务

在 MSA 中,当微服务被实例化时,它们会从有限的资源和线程开始,以避免某个特定的微服务占用所有系统资源。

考虑到系统资源,考虑另一个场景,如图图 3.10所示,其中库存微服务是服务工作流的一部分,而支付微服务由于某种原因既不处理也不响应 API 调用。在这种情况下,订单和支付微服务都将继续等待库存的确认,然后才开始释放它们的资源。

当库存微服务超时请求,并且在系统负载较重或订单量较高的情况下,请求开始堆积到订单和支付微服务。最终,订单和支付微服务开始耗尽资源,无法响应请求:

图 3.10:库存微服务已关闭

图 3.10:库存微服务已关闭

MSA 中的类似场景可能导致多米诺效应,导致多个微服务发生级联故障,进而导致整个系统故障。

微服务电路断路器用于防止系统级联故障的发生。电路断路器使用真实流量指标监控微服务性能。它分析响应时间和成功响应率等参数,并实时确定微服务的健康状况。如果微服务变得不健康,电路断路器立即开始向微服务消费者返回错误。

电路断路器并不能防止被监控的微服务失败;相反,它防止了级联故障的发生:

图 3.11:具有内联电路断路器的库存微服务

图 3.11:具有内联电路断路器的库存微服务

当电路断路器假定微服务不健康时,它仍然需要监控和评估微服务的运行性能。电路断路器切换到半开状态,此时它只允许一小部分请求通过到被监控的微服务。一旦电路断路器检测到健康的微服务响应,电路断路器将状态切换回关闭状态,此时 API 流量正常流向微服务。

在 MSA 系统中,并非每个微服务都需要电路断路器。我们只需要在可能引起级联故障的微服务上部署电路断路器。架构师需要研究和确定哪些微服务需要电路断路器保护。

断路器可以作为独立的微服务部署,也可以是 API 网关的一部分。断路器是作为独立微服务实现还是作为 API 网关的一部分,高度依赖于系统的业务和运营需求,以及架构本身采用的模式。

断路器模式、协调器、聚合器、ACL 和 API 网关都是可以应用于 MSA 系统以实现更好可靠性、弹性和整体性能的架构增强。在下一节中,我们将学习如何将本章讨论的每个模式应用于我们的 ABC-MSA 系统。

ABC-MSA 增强

第二章中,我们将我们的 ABC-Monolith 重构为一个简单的 ABC-MSA。我们在第二章中设计的 ABC-MSA 缺少我们在本章考虑的许多增强功能。现在是时候将我们在本章学到的知识应用到 ABC-MSA 系统中,以增强其设计和操作了。

首先,在 ABC-MSA 的第二章中,协调器同时执行 API 网关功能和协调功能。到目前为止,我们已经了解到在一个服务中结合网关和协调功能并不是最佳选择。因此,我们将向我们的 ABC-MSA 系统添加一个专门的 API 网关,用于入口和出口 API 调用,以及我们在本章前面讨论的其他 API 网关功能,如身份验证、授权、审计、监控等。

API 网关将作为一个独立的独立微服务运行,服务于直接客户端请求,包括系统仪表板和用户前端。协调器也将作为一个独立的独立微服务运行,服务于 MSA 的工作流程。

聚合器(s)将取决于使用多个 ABC-MSA 微服务来满足用户请求的用例。

使用聚合器的简单用例可以是用户检查订单的运输状态。状态应包括订单信息、订单中包含的产品以及该订单的运输状态。

为了向用户展示所有这些信息,我们需要从三个不同的微服务中提取信息:订单管理、产品管理和运输管理。我们将部署一个聚合器作为独立的微服务,从所有这些微服务中提取数据,并使其可供用户 API 消费:

图 3.12:增强的 ABC-MSA 架构

图 3.12:增强的 ABC-MSA 架构

在前面的图中,我们将管理和协调层作为 ABC-MSA 系统的一部分添加。这一层将管理协调工作流程和微服务的生命周期,包括安装、配置、实例化、更新、升级和关闭。

我们还需要在从 ABC-Monolith 到 ABC-MSA 的过渡期间激活 ACL。ACL 将作为两个系统之间的缓冲区,以保持架构及其操作的整洁性。一旦所有 ABC-Monolith 功能都已重新部署到 ABC-MSA 中,旧的 ABC-Monolith 和 ACL 都可以退役。

摘要

在本章中,我们讨论了可以引入以维持系统稳定性和提高其性能的 MSA 的不同组件。

我们讨论了如何使用 ACL 在 MSA 从旧的单体系统过渡期间保护我们的新 MSA。然后,我们介绍了 API 网关、聚合器和编排器的角色和功能。我们还讨论了在采用 MSA 中的各种通信模式时可能遇到的一些缺点。

最后,我们对 ABC-MSA 进行了重新设计,以展示这些不同的组件如何在典型的 MSA 中协同工作。

第一章第三章 介绍了 MSA 的基础知识。在下一章中,我们将通过实际案例开始讨论 MSA 企业系统中使用的核心机器学习和深度学习算法,并介绍一些构建机器学习和深度学习算法的编程和工具示例。

第二部分:机器学习算法和应用概述

在本部分中,我们将把重点转向机器学习。我们将了解机器学习算法的不同概念,以及如何设计和构建机器学习系统,维护模型,并将机器学习应用于智能企业 MSA。

我们将首先学习如何识别不同机器学习模型及其用例之间的差异。一旦我们掌握了基础知识,我们将开始学习如何设计机器学习系统管道。一旦我们建立了机器学习系统管道,我们将学习数据偏移是什么,它们如何影响我们的系统,以及我们如何识别和解决它们。最后,在掌握了所有基础知识之后,我们将开始探索构建我们自己的智能企业 MSA 的不同用例。

第二部分 的结尾,我们将对机器学习和不同的算法有一个基本的了解,学习如何构建和维护机器学习系统,以及最终,我们可以在我们的智能企业 MSA 中使用的不同用例。

本部分包括以下章节:

  • 第四章关键机器学习算法和概念

  • 第五章机器学习系统设计

  • 第六章稳定机器学习系统

  • 第七章机器学习和深度学习在 MSA 企业系统中的应用

第四章:关键机器学习算法和概念

在前面的章节中,我们探讨了 MSA 的不同概念及其在创建企业系统时所起的作用。

在接下来的章节中,我们将开始将我们的关注点从学习 MSA 概念转移到学习关键机器学习概念。我们还将学习使用 Python 在机器学习模型中使用到的不同库和包。

在本章中,我们将涵盖以下内容:

  • 人工智能、机器学习和深度学习之间的区别

  • 在 Python 中常用的常见深度学习包和库

  • 构建回归模型

  • 构建多类分类

  • 文本情感分析和主题建模

  • 使用机器学习进行模式分析和预测

  • 使用深度学习构建增强模型

人工智能、机器学习和深度学习之间的区别

尽管人工智能和机器学习在最近几年越来越受欢迎,但人工智能领域自 20 世纪 60 年代以来就已经存在。随着不同子领域的出现,能够区分它们并理解它们及其所包含的内容变得很重要。

首先,人工智能是涵盖我们今天看到的所有子领域的总领域,例如机器学习、深度学习等。任何从其环境中感知或接收信息并执行动作以最大化奖励或实现其目标的系统都被认为是人工智能机器。

这在今天与机器人技术相关时非常常见。我们的大多数机器被设计成能够使用它们的传感器,如摄像头、声纳或陀螺仪来捕获数据,并使用捕获到的数据以最高效的方式响应特定任务。这个概念与人类的功能非常相似。我们使用我们的感官来“捕获”来自环境的信息,并根据我们接收到的信息执行某些动作。

人工智能是一个广泛的领域,但它可以被划分为不同的子领域,我们今天普遍知道的一个子领域是机器学习。使机器学习独特的是,这个领域致力于创建可以持续学习和改进其模型而无需明确编程的系统或机器。

机器学习通过收集数据,也称为训练数据,并试图在数据中找到模式以进行准确预测,而不需要被编程来做这样的事情来实现这一点。机器学习中使用了许多不同的方法来学习数据,这些方法针对我们遇到的不同问题进行了定制。

图 4.1:人工智能的不同领域

图 4.1:人工智能的不同领域

机器学习问题可以分为三个不同的任务:监督学习无监督学习强化学习。现在,我们将专注于监督学习和无监督学习。这种区分基于我们拥有的训练数据。监督学习是我们有特定数据集的输入数据和预期输出时的情况,这也被称为标签。另一方面,无监督学习只包含输入而没有预期输出。

监督学习通过理解输入和输出数据之间的关系来工作。监督学习的一个常见例子是预测某个城市的房价。我们可以通过捕捉现有房屋的规格和它们当前的价格来收集现有房屋的数据,然后学习这些房屋的特征和它们价格之间的模式。然后我们可以取一个不在我们的训练集中的房屋,通过将房屋的特征输入到我们的程序中来测试我们的模型,并让模型预测该房屋的价格。

无监督学习通过使用分组或聚类方法来学习数据的结构。这种方法通常用于营销目的。例如,一家商店想要将其客户聚类到不同的群体中,以便它可以有效地针对不同的细分市场调整其产品。它可以捕捉其客户的购买历史,使用这些数据来学习购买模式,并建议某些可能会引起他们兴趣的项目或商品,从而最大化其收入。

在我们能够理解深度学习——它是机器学习的一个子领域——之前,我们首先必须了解什么是人工神经网络ANNs)。ANNs 是从大脑中的神经元得到灵感,由一组完全连接的节点组成的模型,也称为人工神经元。它们包含一组输入、连接神经元的隐藏层,以及一个输出节点。每个神经元都有一个输入和输出,这些可以通过整个网络传播。为了计算神经元的输出,我们取所有输入的加权和,乘以神经元的权重,然后通常加上一个偏差项。

我们继续执行这些操作,直到我们达到最后一层,即输出神经元。我们执行一个非线性激活函数,例如 Sigmoid 函数,以给出最终的预测。然后我们将预测的输出值输入到一个成本函数中。这个函数告诉我们我们的网络学习得有多好。我们取这个值,并通过反向传播通过我们的层回到第一层,根据我们的网络表现调整神经元的权重。通过这种方式,我们可以创建强大的模型,可以执行诸如手写识别、游戏 AI 等任务。

重要提示

如果一个程序能够接受输入数据并学习模式以进行预测,而不需要明确编程,那么它被认为是一个机器学习模型。

图 4.2:一个 ANN

图 4.2:一个 ANN

虽然 ANNs 能够执行许多任务,但它们在当今市场的使用中存在显著的缺点:

  • 理解模型的性能可能很困难。随着你在网络中添加更多的隐藏层,尝试调试网络变得复杂。

  • 训练模型需要很长时间,特别是当有大量的训练数据时,并且可能会耗尽硬件资源,因为在 CPU 上执行所有这些数学运算很困难。

  • ANNs 最大的问题是过拟合。随着我们添加更多的隐藏层,存在一个点,此时分配给神经元的权重将高度定制于我们的训练数据。这使得当尝试用之前未见过的数据测试网络时,我们的网络表现非常糟糕。

这就是深度学习发挥作用的地方。深度学习可以根据以下关键特征进行分类:

  • 层的层次结构组成:而不是在网络中只有全连接层,我们可以创建和组合多个不同的层,包括非线性变换和线性变换。这些不同的层在数据中提取关键特征中发挥作用,这些特征在 ANN 中可能很难找到。

  • 端到端学习:网络从一种称为特征提取的方法开始。它查看数据,并找到一种方法来分组冗余信息并识别数据的重要特征。然后,网络使用这些特征通过全连接层进行训练和预测或分类。

  • 神经元的分布式表示:通过特征提取,网络可以将神经元分组以编码数据的一个更大特征。与 ANN 不同,没有单个神经元编码所有内容。这允许模型在保留数据中的关键元素的同时,减少它必须学习的参数数量。

深度学习在计算机视觉中非常普遍。由于捕捉照片和视频技术的进步,当涉及到图像检测时,人工神经网络(ANNs)学习和表现良好变得非常困难。首先,当我们使用图像来训练我们的模型时,我们必须将图像中的每一个像素都视为模型的一个输入。因此,对于一个 256x256 分辨率的图像,我们将会查看超过 65,000 个输入参数。根据你全连接层中神经元的数量,你可能需要查看数百万个参数。由于参数数量庞大,这必然会导致过拟合,并且可能需要数天的训练时间。

通过深度学习,我们可以创建一组称为卷积神经网络CNNs)的层。这些层负责减少我们在模型中需要学习的参数数量,同时仍然保留数据中的关键特征。有了这些添加,我们可以学习如何提取某些特征,并使用这些特征来训练我们的模型,以高效和准确地进行预测。

图 4.3:一个 CNN

图 4.3:一个 CNN

在下一节中,我们将探讨用于机器学习和深度学习的不同 Python 库及其不同的用例。

Python 中常用的深度学习和机器学习库

现在我们已经了解了人工智能和机器学习的基本概念,我们可以开始探讨实现这些概念编程方面的内容。在创建机器学习模型时,今天使用的编程语言有很多。常用的有 MATLAB、R 和 Python。其中,Python 由于其作为编程语言的灵活性和丰富的库,已经成为机器学习中最受欢迎的编程语言。在本节中,我们将介绍今天最常用的库。

NumPy

NumPy 是在 Python 中构建机器学习模型时必不可少的包。在构建模型时,你将主要与大型、多维矩阵打交道。大部分的工作都花在了对矩阵进行转换、拼接以及执行高级数学运算上,而 NumPy 提供了执行这些操作所需的同时保持速度和效率的工具。

想要了解更多关于 NumPy 提供的不同 API 的信息,您可以访问其网站上的文档:https://numpy.org/doc/stable/reference/index.html。

这里,我们将查看示例代码。本节展示了我们如何初始化一个 NumPy 数组。在这个例子中,我们将创建一个 3x3 矩阵,其初始化值为 1 到 9:

import numpy as np
# creates a 3x3 numpy array
arr = np.array([[1,2,3],[4,5,6],[7, 8, 9]])

这里,我们将打印出结果:

print(arr)
[[1 2 3]
 [4 5 6]
 [7 8 9]]

现在,我们可以展示如何从我们的数组中拼接和提取某些元素。

这行代码允许我们从数组中提取第二列的所有值。请注意,在 NumPy 中,我们的数组和列表是零索引的,这意味着零索引指的是数组或列表中的第一个元素:

print(arr[:,1]) # print the second column of the array
[2 5 8]

在这个例子中,我们提取了我们数组中第2行的所有值:

print(arr[2,:]) # print the last row of the array
[7 8 9]

NumPy 数组的另一个有用之处在于,我们可以对矩阵应用数学函数,而无需编写执行基本函数的代码。这不仅更容易,而且更快、更高效。

在这个例子中,我们只是将我们的矩阵与标量值-1进行乘法运算:

print(np.multiply(arr, -1)) # multiplies every element in the array by -1
[[-1 -2 -3]
 [-4 -5 -6]
 [-7 -8 -9]]

Matplotlib

为了看到您的模型是如何学习和表现的,能够可视化您的结果和数据非常重要。Matplotlib 提供了一个简单的方法来绘制您的数据,从简单的线图到更高级的图表,如等高线图和 3D 图。使这个库如此受欢迎的是它与 NumPy 工作的无缝性。

关于它们不同功能的更多信息,您可以访问它们的网站:matplotlib.org/stable/index.html

在这个例子中,我们将创建一个简单的线图。我们首先初始化两个数组,xy,这两个数组都将包含从 0 到 9 的值。然后,使用 Matplotlib 的 API,我们可以绘制并显示我们的简单图表:

import matplotlib.pyplot as plt
import numpy as np
x = np.arange(10) # creates an array from 0-9
y = np.arange(10)
plt.plot(x,y)
plt.show()

图 4.4:使用 Matplotlib 的简单线图

图 4.4:使用 Matplotlib 的简单线图

Pandas

随着最近将数据存储在 CSV 文件中的趋势,Pandas 因其易用性和多功能性而成为 Python 社区中的必备工具。Pandas 通常用于数据分析。它以表格格式存储数据,并为用户提供简单的函数来预处理和操作数据以满足他们的需求。在处理时间序列数据时,它也变得非常有用,这对于构建预测模型很有帮助。

关于不同函数的更多信息,您可以在其网站上查看文档:pandas.pydata.org/docs/

在这个例子中,首先,我们将简单地初始化一个 DataFrame。这是在 Pandas 中以二维表格格式存储我们数据的数据结构。通常,我们存储从读取的文件中读取的数据,但也可以使用自己的数据创建 DataFrame:

import pandas as pd
data = {
    "Number of Bedrooms": [5, 4, 2, 3],
    "Year Build": [2019, 2017, 2010, 2015],
    "Size(Sq ft.)": [14560, 12487, 9882, 10110],
    "Has Garage": ["Yes", "Yes", "No", "Yes"],
    "Price": [305000, 275600, 175000, 235000],
}
df = pd.DataFrame(data)
print(df)

图 4.5:我们的 DataFrame 的输出

图 4.5:我们的 DataFrame 的输出

与 NumPy 一样,我们可以从我们的 DataFrame 中提取某些列和行。在这段代码中,我们可以查看我们的 DataFrame 的第一行:

print(df.iloc[0]) # view the first entry in the table

图 4.6:我们的 DataFrame 的第一行输出

图 4.6:我们的 DataFrame 的第一行输出

使用 Pandas,我们还可以通过使用列名而不是索引来从我们的 DataFrame 中提取某些列:

print(df["Price"]) # print all the values in the Prices column

图 4.7:价格列中所有值的输出

图 4.7:价格列中所有值的输出

TensorFlow 和 Keras

TensorFlow 和 Keras 是构建深度学习模型的基础。虽然两者都可以单独使用,但 Keras 被用作 TensorFlow 框架的接口,使用户能够轻松创建强大的深度学习模型。

由 Google 创建的 TensorFlow 在创建机器学习模型时充当后端。它通过创建静态数据流图来工作,这些图指定了数据在深度学习管道中的流动方式。图中包含节点和边,其中节点代表数学运算。它使用称为张量的多维数组传递这些数据。

Keras,后来与 TensorFlow 集成,可以看作是设计深度学习模型的界面。它被设计成用户友好,允许用户专注于设计他们的神经网络模型,无需处理复杂的后端。它类似于面向对象编程,因为它复制了创建对象的方式。用户可以自由添加不同类型的层、激活函数等。他们甚至可以使用预构建的神经网络进行简单的训练和测试。

在下面的示例代码中,我们可以看到如何创建一个简单的、隐藏的两层神经网络。这段代码允许我们初始化一个Sequential模型,它由简单的层堆叠组成:

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from keras.models import Sequential
model = Sequential()
model.add(Flatten(input_shape=[256,256]))

根据我们的应用,我们可以添加具有不同配置的多个层,例如节点数、激活函数和核正则化器:

 #Adding First Hidden Layer
model.add(tf.keras.layers.Dense(units=6,kernel_regularizer='l2',activation="leaky_relu"))
 #Adding Second Hidden Layer
model.add(tf.keras.layers.Dense(units=1,kernel_regularizer='l2',activation="leaky_relu"))
#Adding Output Layer
model.add(tf.keras.layers.Dense(units=1,kernel_regularizer='l2',activation="sigmoid"))

最后,我们可以编译我们的模型,这实际上是将所有不同的层聚集在一起,并将它们组合成一个简单的神经网络:

#Compiling ANN
model.compile(optimizer='sgd',loss="binary_crossentropy",metrics=['accuracy'])

PyTorch

PyTorch 是由 Meta(前 Facebook)创建的另一个机器学习框架。与 Keras/TensorFlow 类似,它允许用户创建机器学习模型。该框架非常适合自然语言处理(NLP)和计算机视觉问题,但可以定制以适应大多数应用。PyTorch 的独特之处在于其动态计算图。它有一个名为 Autograd 的模块,允许你动态地进行自动微分,与 TensorFlow 中的静态微分相比。此外,PyTorch 更符合 Python 语言,这使得它更容易理解,并利用 Python 的有用特性,如并行编程。有关更多信息,请访问他们网站上的文档:pytorch.org/docs/stable/index.html

在这段代码中,我们可以创建一个简单的单层神经网络。类似于 Keras,我们可以初始化一个Sequential模型,并根据需要添加层:

import torch
model = torch.nn.Sequential( # create a single layer Neural Network
    torch.nn.Linear(3, 1),
    torch.nn.Flatten(0, 1)
)
loss = torch.nn.MSELoss(reduction='sum')

SciPy

这个库是为科学计算设计的。它包含许多用于线性代数、优化和积分的内置函数和方法,这些在机器学习中常用。当你构建机器学习模型时,尝试计算某些统计和转换时,这个库很有用。有关它提供的不同函数的更多信息,请查看其网站上的文档:docs.scipy.org/doc/scipy/

在这个示例代码中,我们可以使用 NumPy 创建一个 3x3 的数组,然后我们可以使用 SciPy 来计算行列式:

import numpy as np
from scipy import linalg
a = np.array([[1,4,2], [3,9,7], [8,5,6]])
print(linalg.det(a)) # calculate the matrix determinate
57.0

scikit-learn

scikit-learn 是一个机器学习库,它是 SciPy 的扩展,使用 NumPy 和 Matplotlib 构建。它包含许多预构建的机器学习模型,如随机森林、K-means 和支持向量机。有关它提供的不同 API 的更多信息,请访问其网站:scikit-learn.org/stable/user_guide.html

在下面的例子中,我们将使用 scikit-learn 提供的示例数据集来构建一个简单的逻辑回归模型。首先,我们导入所有必需的库,然后加载 scikit-learn 提供的 Iris 数据集。我们可以使用 scikit-learn 的一个便捷 API 将我们的数据分成训练集和测试集:

from sklearn import datasets
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import numpy as np
# Load the iris dataset
X, y = datasets.load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=1) Create linear regression object

我们可以初始化我们的逻辑回归模型,并简单地使用我们的训练数据运行fit函数来训练模型。一旦我们训练了我们的模型,我们就可以用它来进行预测,然后测量其准确度:

# Create Logistic Regression model
model = LogisticRegression()
# Train the model using the training sets
model.fit(X_train, y_train)
# Make predictions using the testing set
y_pred = model.predict(X_test)
print(accuracy_score(y_test, y_pred))

在接下来的几节中,我们将开始探讨我们可以使用这些库构建的不同模型。我们将了解是什么使这些模型独特,它们的结构如何,以及它们可以最好地服务于哪些目的和应用程序来满足我们的需求。

构建回归模型

首先,我们将探讨回归模型。回归模型或回归分析是用于寻找独立变量和依赖变量之间关系的建模技术。回归模型的输出通常是连续值,也称为定量变量。一些常见的例子是根据房屋的特征预测房价,或者根据之前的销售信息预测新商店中某种产品的销售情况。

在构建回归模型之前,我们首先必须理解数据和它的结构。大多数回归模型涉及监督学习。这包括特征和一个输出变量,称为标签。这将通过调整权重来帮助模型更好地拟合我们迄今为止观察到的数据。我们通常将我们的特征表示为 X,将我们的标签表示为 Y,以帮助我们理解用于解决回归模型的数学模型:

图 4.8:监督学习数据结构的示例

图 4.8:监督学习数据结构的示例

通常,我们的数据被分成两个子集,训练集测试集。训练集通常包含原始数据的 70-80%,而测试集包含剩余的部分。这是为了让模型在训练集上学习,并在测试集上验证其结果以展示其性能。从结果中,我们可以推断出我们的模型在数据集上的表现。

为了线性回归模型能够有效运行,我们的数据必须以线性方式组织。模型使用以下公式在数据上进行训练和学习:

公式 4.1

在这个方程中,公式 4.002代表模型的输出,或者我们通常所说的预测。预测是通过取截距公式 4.003和斜率公式 4.004来计算的。斜率,也称为权重,应用于数据中的所有特征,代表公式 4.005。在处理数据时,我们通常将其表示为矩阵,这使得它易于理解,并且在使用 Python 时易于操作:

公式 4.06公式 4.07公式 4.08公式 4.09

特征数量描述了你正在解决什么类型的问题。如果你的数据只有一个特征,它被认为是一个简单的线性回归模型。虽然它可以解决直接的问题,但对于更复杂的数据和问题,映射关系可能会很困难。因此,你可以通过添加更多特征来创建一个多元线性回归模型(公式 4.10)。这使得模型更加稳健,并能找到更深层次的关系。

图 4.9:一个简单的线性回归模型

图 4.9:一个简单的线性回归模型

一旦我们训练了我们的模型,我们需要学习如何评估我们的模型并理解它在测试数据上的表现。当涉及到线性回归时,我们用来评估模型的两个常用指标是均方根误差RMSE)和R2 指标。

RMSE 是预测中残差误差的标准差。残差是实际数据点到回归线的距离的度量。所有点的平均距离越远,误差就越高。这表明模型较弱,因为它无法找到数据点之间的相关性。这个指标可以通过以下公式计算,其中公式 4.11是实际值,公式 4.12是预测值,公式 4.13是数据点的数量:

公式 4.14图 4.10:计算线性回归模型的残差

图 4.10:计算线性回归模型的残差

R2,也称为确定系数,衡量了因变量(Y)的方差中可以被自变量(X)解释的比例。它本质上告诉我们数据与模型拟合得有多好。与 RMSE 不同,RMSE 可以是一个任意数,R2 以百分比的形式给出,这可能更容易理解。百分比越高,数据的关联性越好。尽管很有用,但高百分比并不总是意味着模型强大。决定一个好的 R2 值取决于应用和用户如何理解数据。R2 可以通过以下公式计算:

公式 4.15

有许多更多的指标可以评估你的回归模型的有效性,但这两个指标已经足够让你了解你的模型是如何表现的。在构建和评估你的模型时,重要的是绘制和可视化你的数据和模型,因为这可以识别关键点。图表可以帮助你确定你的模型是过度拟合还是欠拟合

当你的模型过于适合你的训练数据时,会发生过度拟合。你的 RMSE 将会非常低,并且你的训练准确率几乎达到 100%。虽然这看起来很有吸引力,但这表明模型不好。这可能是由于以下两个原因之一:数据不足或参数过多。因此,当你用之前未见过的新数据测试你的模型时,由于它无法泛化数据,它的表现会非常差。

图 4.11:过度拟合的线性回归模型

图 4.11:过度拟合的线性回归模型

为了解决过度拟合问题,你可以尝试增加训练数据量,或者使模型更简单。在将数据分成训练集和测试集之前随机打乱数据也有帮助。另一个重要的技术称为正则化。虽然根据模型的不同,有许多不同的正则化技术(L1 或 L2 正则化),但它们在防止过度拟合方面的工作方式相似,即它们向模型中添加偏差或噪声。在我们之前看到的回归方程中,我们可以添加另一个项,,以表明正则化正在应用于我们的模型:

在另一端,当你的模型无法在数据中找到任何有意义的相关性时,会发生欠拟合。由于在大多数数据中很容易找到模式,所以这不如过度拟合常见。如果发生这种情况,要么是因为你的数据噪声太多且严重不相关,要么是因为你的模型太简单且参数不足,或者模型对当前的应用不有效。在调试代码并确保在预处理数据或设置模型时没有错误也是很有用的:

图 4.12:欠拟合的线性回归模型

图 4.12:欠拟合的线性回归模型

因此,目标是找到一个最佳拟合模型,介于过度拟合和欠拟合之间。找到适合你需求的模型需要时间和实验,但使用这里讨论的关键指标和度量可以帮助你指引正确的方向:

图 4.13:最佳拟合的线性回归模型

图 4.13:最佳拟合的线性回归模型

重要提示

特征工程是构建全面模型的关键部分。理解你的数据可以帮助确定在模型中包含哪些特征或参数,这样你就可以捕捉独立变量和因变量之间的关系,而不会导致过度拟合。

在收集和处理模型数据时,有一些关键点需要注意:

  • 归一化数据:可能存在具有非常高的或很低的数值的特征,为了防止它们压倒模型并产生偏差,将所有数据归一化以使它们在特征上保持一致是至关重要的。

  • 清理数据:在现实世界中,我们收集的数据并不总是完美的,可能包含缺失或严重的错误数据。处理这些问题很重要,因为它们可能导致异常值并负面影响模型。

  • 理解数据:对数据进行统计分析,也称为探索性数据分析EDA),是常见的做法,以更好地了解数据如何影响模型。这可以包括绘制图表、运行统计方法,甚至使用机器学习技术来降低数据的维度,这些将在本章后面讨论。

在下一节中,我们将讨论分类模型。

构建多类分类

与产生连续输出的回归模型不同,当模型产生有限输出时,它们被认为是分类模型。一些例子包括垃圾邮件检测、图像分类和语音识别。

分类模型被认为是多才多艺的,因为它们可以应用于监督学习和无监督学习,而回归模型主要用于监督学习。有一些回归模型(如逻辑回归和支持向量机)也被认为是分类模型,因为它们使用阈值将连续值的输出分割成不同的类别。

无监督学习是当今市场上常用的应用。尽管监督学习通常表现更好,并提供了有意义的成果,因为我们知道预期的输出,但我们收集的大多数数据都是未标记的。对于公司来说,让人类专家筛选数据并进行标记既耗时又费钱。无监督学习通过让模型尝试为数据确定标签并提取有意义的信息,有助于降低成本和时间。有时它们甚至可以比人类表现得更好。

分类模型输出的类别数量决定了它的类型。对于只有两个输出(即垃圾邮件和非垃圾邮件)的模型,这被称为二元分类器,而具有两个以上输出的模型被称为多类分类器:

图 4.14:二元和多类分类器

图 4.14:二元和多类分类器

从那些分类器中,有两种类型的学习者:懒惰学习者急切学习者

懒惰学习者基本上存储训练数据,并等待接收到新的测试数据。一旦他们获得测试数据,模型就会根据已有的数据对新数据进行分类。这些类型的学习者训练时所需时间较少,因为你可以连续添加新数据,而无需重新训练整个模型,但在执行分类时需要更多时间,因为他们必须通过所有数据点。一种常见的懒惰学习者类型是K-最近邻KNN)算法。

另一方面,急切的学习者以相反的方式工作。每当有新数据添加到模型中时,他们必须重新训练模型。虽然与懒惰学习者相比,这需要更多的时间,但查询模型的速度要快得多,因为他们不必通过所有数据点。一些急切学习者的例子是决策树、朴素贝叶斯和人工神经网络。

重要提示

监督学习通常比无监督学习表现更好,因为我们知道在训练过程中预期的输出应该是什么,但是收集和标记数据成本高昂,因此无监督学习在训练未标记数据方面表现出色。

在接下来的几节中,我们将探讨一些用于解决大多数基本分类或回归模型无法解决的独特问题的利基模型。

文本情感分析和主题建模

机器学习领域的一个热门领域是主题建模和文本分析。随着互联网上文本的激增,能够理解这些数据并创建复杂的模型,如聊天机器人和翻译服务,已经成为一个热门话题。使用软件与人类语言交互被称为NLP

尽管我们可以使用大量数据来训练我们的模型,但创建有意义的模型是一项困难的任务。语言本身很复杂,包含许多语法规则,尤其是在尝试翻译不同语言时。某些强大的技术可以帮助我们在创建 NLP 模型时。

重要提示

在实施任何 NLP 模型之前,以某种方式预处理数据至关重要。文档和文本往往包含一些无关数据,如停用词(the/a/and)或随机字符,这些可能会影响模型并产生错误的结果。

我们将要讨论的第一个想法是主题建模。这是一个将文档或文本中的文本或单词分组到不同主题或领域的进程。当你有一份文档或文本,并希望将其分类和分组到某个特定类型,而不必逐个阅读文档时,这非常有用。用于主题建模有许多不同的模型:

  • 潜在语义分析LSA

  • 概率潜在语义分析PLSA

  • 潜在狄利克雷分配LDA

我们将重点关注 LDA。LDA 使用统计学来寻找单词或短语的重复出现模式,并将它们分组到相应的主题中。它假设每个文档包含主题的混合,每个主题包含单词的混合。LDA 首先开始处理文档的过程,并保持一个词矩阵,其中包含每个文档中每个单词的计数:

图 4.15:词矩阵

图 4.15:词矩阵

在创建词矩阵后,我们确定要分割单词的主题数量,,并使用统计学来找到单词属于某个主题的概率。使用贝叶斯统计学,我们可以计算出概率,并使用该概率将单词聚类到不同的主题中:

图 4.16:LDA 模型

图 4.16:LDA 模型

另一个在自然语言处理(NLP)中日益增长的应用是情感分析。这涉及对单词或文本进行理解,以了解用户的意图或情感。这在处理在线评论或社交媒体帖子时很常见。它决定了文本是否包含积极、中性或消极的情感。

许多不同的方法和模型可以解决这个问题。最简单的方法是通过使用贝叶斯定理进行统计学。这个公式用于预测分析,因为它使用文本中的先前单词来更新模型。可以使用此公式计算概率:

深度学习已成为 NLP 的强大工具,对情感分析很有用。卷积神经网络(CNNs)和循环神经网络RNNs)是两种可以显著提高 NLP 模型,特别是情感分析模型的深度学习模型。我们将在本章后面讨论这些神经网络及其性能。

机器学习中的模式分析和预测

随着时间的不确定性,能够预测某些趋势和模式已成为当今行业的热门话题。尽管大多数回归模型功能强大,但它们无法做出自信的时间预测。因此,一些研究人员设计了在做出某些预测时考虑时间的模型,例如油价、股市和销售预测。在我们深入探讨不同的模型之前,我们首先必须了解时间序列分析中的不同概念。

处理时间序列问题的第一步是熟悉数据。数据通常包含以下四种数据组件之一:

  • 趋势 – 数据遵循增加或减少的连续时间线,没有周期性变化

  • 季节性 – 数据在一定的周期性时间线上发生变化

  • 周期性 – 数据发生变化,但没有固定的周期性时间线

  • 不规则 – 数据随机变化,没有模式

图 4.17:时间序列数据的不同组成部分

图 4.17:时间序列数据的不同组件

这些不同的趋势可以分为两种不同的数据类型:

  • 平稳 – 数据的某些属性,如均值、方差和协方差,随时间不变

  • 非平稳 – 数据的属性随时间变化

通常,你将处理非平稳数据,使用这类数据创建机器学习模型将产生不可靠的结果。为了解决这个问题,我们使用某些技术将我们的数据转换为平稳数据。它们包括以下方法:

  • 差分 – 一种用于标准化均值并消除方差数学方法。它可以通过以下公式计算:

  • 转换 – 使用数学方法来消除方差的变化。在转换方法中,以下三种是常用的:

    • 对数变换

    • 平方根

    • 幂变换

图 4.18:差分非平稳数据

图 4.18:差分非平稳数据

重要提示

时间本身就是不确定的,这使得创建一个可以自信预测未来趋势的模型几乎不可能。我们能在数据中消除的不确定性越多,我们的模型就能更好地找到数据中的关系。

一旦我们可以转换我们的数据,我们就可以开始考虑使用模型来进行预测。在众多模型中,用于时间序列分析最流行的模型是自回归积分移动平均ARIMA)模型。这个线性回归模型由三个子组件组成:

  • 自回归AR) – 使用当前时间和前一时间依赖性进行预测的回归模型

  • 积分I) – 差分的过程,以便使数据平稳

  • 移动平均MA) – 通过计算滞后观测数据的移动平均来模型预期数据和残差误差

除了 ARIMA,其他机器学习模型也可以用于时间序列问题。另一个著名的模型是 RNN 模型。这是一种用于具有某种序列数据的深度学习模型。我们将在下一节中更详细地介绍它们是如何工作的。

使用深度学习增强模型

在本章前面,我们简要讨论了深度学习及其在增强简单机器学习模型时带来的优势。在本节中,我们将更深入地介绍不同的深度学习模型。

在我们构建模型之前,我们将简要介绍深度学习模型的结构。一个简单的 ANN 模型通常包含大约两到三个全连接层,并且通常足够强大,可以模拟大多数复杂的线性函数,但随着我们添加更多层,模型改进的幅度显著减小,并且由于过拟合,它无法执行更复杂的应用。

深度学习使我们能够在我们的 ANN 中添加多个隐藏层,同时减少训练模型的时间并提高性能。我们可以通过添加深度学习中常见的隐藏层类型之一或两种来实现这一点——CNN 或 RNN。

由于神经网络的结构,CNN 主要应用于图像检测和视频识别领域。CNN 架构包括以下关键特性:

  • 卷积层

  • 激活层

  • 池化层

卷积层是 CNN 模型的核心元素。其主要任务是使用对数据进行卷积或分组,并产生一个称为特征图的输出。这个图包含了从数据中提取的所有关键特征,这些特征随后可以在全连接层中进行训练。特征图中的每个元素都表示一个感受野,用于表示输入的哪一部分被用来映射到输出。随着你添加更多的卷积层,你可以提取更多的特征,这允许你的模型适应更复杂的模型:

图 4.19:卷积层输出

图 4.19:卷积层输出

图 4.19中,我们可以看到特征图是如何在卷积层中创建的。该层将核在输入数据上滑动,执行点积操作,并产生一个输出矩阵,这是卷积函数的结果。核的大小和步长可以决定特征图的输出大小。在这个例子中,我们使用 2x2 的核大小和 2 的步长或步长大小,根据我们的输入大小得到一个 2x2 大小的特征图。特征图的输出可以根据用户的需求用于更多的未来卷积层。

在我们将新创建的特征图作为另一个卷积层或全连接层的输入之前,我们需要将特征图通过一个激活层。在训练过程中,通过某种类型的非线性函数传递数据非常重要,因为这允许模型映射到更复杂的函数。在整个模型中可以使用许多不同类型的激活函数,并且根据你计划构建的模型类型具有各自的优势。在众多激活函数中,以下是最常用的:

  • 线性 修正 激活ReLU

  • 逻辑(sigmoid

  • 双曲正切(Tanh

ReLU 函数是目前最流行的激活函数。它非常简单,有助于模型比大多数其他激活函数更快地学习和收敛。它是通过以下函数计算的:

图 4.20:ReLU 函数

逻辑函数是另一种常用的激活函数。这是在逻辑回归模型中使用的相同函数。这个函数有助于将特征图的输出限制在 0 到 1 之间。虽然有用,但这个函数计算量大,可能会减慢训练过程。它使用以下函数计算:

图 4.21:Sigmoid 函数

Tanh 函数与 sigmoid 函数类似,它限制了特征图的值。而不是从 0 到 1 限制,它从-1 到 1 限制值,并且通常比 sigmoid 函数表现更好。该函数使用以下公式计算:

图 4.22:Tanh 函数

每个激活函数都有其用途和好处,这取决于手头的任务或模型。ReLU 函数在 CNN 模型中常用,而 sigmoid 和 Tanh 主要在 RNN 模型中找到,但它们可以互换使用并产生不同的结果。

在我们将特征图通过激活层处理后,我们来到了最后一部分——池化层。正如之前提到的,深度学习中的一个关键元素是参数的减少。这允许模型在保持从卷积层提取的重要特征的同时,使用更少的参数进行训练。池化层负责这一步缩小我们的参数大小。有许多常见的池化函数,但最常用的是最大池化。这与卷积层类似,我们在输入数据上滑动一个核或过滤器,并只从每个窗口中取最大值:

Figure 4.23: Max pooling layer output

图 4.23:最大池化层输出

图 4**.23中,我们使用 2x2 的核大小和 2 的步长大小。在这里,我们可以看到我们的输出,其中只选择了每个窗口的最大值。

可以向模型添加其他层和函数来帮助解决某些问题或应用,例如批量归一化层,但有了这三个基础层,我们仍然可以添加不同大小的多层,并构建一个强大的模型。

在最后一层,我们将输出输入到全连接层。到那时,我们能够提取数据的重要特征,并且与简单的 ANN 模型相比,在更短的时间内学习更复杂的模型。

接下来,我们将介绍 RNN 架构。由于 RNN 模型的结构特性,它被设计用于需要考虑一系列序列数据的任务,其中序列中的数据后依赖于前面的数据。这个模型通常用于某些领域,如 NLP 和信号处理。

RNN 的基本结构是通过在隐藏层中有一个输出,该输出被送回到同一个隐藏层来构建的。这样,模型能够根据先前数据学习,并相应地调整权重。

图 4.24:RNN 架构

图 4.24:RNN 架构

为了更好地理解模型的工作原理,你可以想象每个数据点都有一个单独的层。每一层将数据点作为输入 并产生一个输出 。然后我们在层之间传递权重,并取模型中所有层的总成本函数的平均值。

图 4.25:展开的 RNN

图 4.25:展开的 RNN

这个模型适用于大多数简单问题。随着序列的增加,它遇到了一些问题:

  • 梯度消失:在训练过程中,当梯度接近零时发生这种情况。结果权重没有得到适当的更新,模型表现不佳。

  • 缺乏上下文:该模型是单向的,不能进一步或先前查看数据。因此,模型只能根据当前序列点周围的数据进行预测,并且更有可能基于错误上下文做出较差的预测。

为了解决这里提到的一些问题,已经创建了不同版本的循环神经网络(RNNs)。其中,今天最常用的一个是长短期记忆LSTM)模型。LSTM 模型由三个组件组成:

  • 输入门

  • 一个输出门

  • 一个忘记门

图 4.26:LSTM 神经网络

图 4.26:LSTM 神经网络

这些门通过调节哪些数据点需要上下文化序列来工作。这样,模型可以更准确地预测,而不容易被操纵。

忘记门专门负责移除不再需要的前置数据或上下文。这个门使用 sigmoid 函数来确定是否使用或“忘记”数据。

输入门用于确定新数据是否与当前序列相关。这样,只有重要的数据被用来训练模型,而不是冗余或不相关信息。

最后,输出门的主要功能是过滤当前状态的信息,并且只将相关信息发送到下一个状态。与其他门一样,它使用先前状态中的上下文来应用过滤器,这有助于模型正确地上下文化数据。

卷积神经网络(CNN)和循环神经网络(RNN)模型主要设计用于监督学习问题。当涉及到无监督学习时,需要不同的模型来解决某些问题。让我们讨论自编码器

自编码器通过接收输入数据,压缩它,然后通过解压缩它来重建它。虽然简单,但它可以用于一些高级应用,例如生成音频或图像,或者它可以用作异常检测器。

自动编码器由两部分组成:

  • 编码器

  • 解码器

图 4.27:自动编码器的组件

图 4.27:自动编码器的组件

编码器和解码器通常使用单层人工神经网络(ANN)构建。编码器负责接收数据并压缩或展平数据。然后,解码器处理展平的数据,并尝试重建输入数据。

编码器和解码器中间的隐藏层通常被称为瓶颈。隐藏层中的节点数必须少于编码器和解码器中的节点数。这迫使模型尝试在输入数据中找到模式或表示,以便用少量信息重建数据。因此,成本函数用于计算并最小化输入和输出数据之间的差异。

自动编码器的一个方面是降维,这是深度学习的一个基本组成部分。这是在训练模型时减少参数或特征数量的过程。正如本章前面提到的,为了构建一个能够构建数据更深层次表示的复杂模型,包括更多特征是很重要的。然而,添加过多的特征可能导致过拟合,那么我们如何找到在模型中使用最佳特征数量呢?

有许多模型和技术,例如自动编码器,可以执行降维以帮助我们找到在模型中使用最佳特征。在众多技术中,主成分分析PCA)是最受欢迎的。这项技术可以将 N 维数据集通过线性代数减少数据的维度。在用数据训练模型之前使用降维技术是一种常见做法,因为这有助于去除数据中的噪声并避免过拟合。

摘要

在本章中,我们讨论了什么是人工智能以及它包含的不同子领域。

我们讨论了回归和分类模型的不同特性。从那里,我们还了解了数据结构以及模型在数据上训练时的表现。然后,我们讨论了分析模型性能的不同方法以及解决在训练模型时可能遇到的不同问题的方法。

我们简要地了解了今天在机器学习模型中使用的不同包和库及其不同的用例。

我们还分析了不同主题,如主题建模和时间序列分析及其包含的内容。有了这些,我们能够查看解决这些类型问题的不同方法和技术。

最后,我们深入探讨了深度学习及其如何改进机器学习。我们讨论了两种不同的神经网络类型——卷积神经网络(CNNs)和循环神经网络(RNNs)——它们的结构以及它们的优点和用例。

在下一章中,我们将运用所学知识,开始探讨如何设计和构建一个端到端的机器学习系统及其包含的不同组件。

第五章:机器学习系统设计

在上一章中,我们深入探讨了不同的机器学习概念以及创建这些模型所使用的包和库。利用这些信息,我们将开始讨论构建机器学习管道时的设计过程以及大多数机器学习管道中找到的不同组件。

在本章中,我们将涵盖以下领域:

  • 机器学习系统组件

  • 适配和转换接口

  • 训练和提供接口

  • 编排

机器学习系统组件

构建一个健壮的机器学习系统需要许多组成部分。从收集数据到将模型部署给用户,每个部分都在保持系统动态和可扩展性方面发挥着至关重要的作用。在这里,我们将简要讨论机器学习系统生命周期中的不同阶段以及它们所起的作用。这些阶段可以根据模型或应用进行编辑。

大多数机器学习系统包括以下阶段,其中一些阶段取决于业务需求:

  • 数据收集

  • 数据预处理

  • 模型训练

  • 模型测试

  • 模型提供

实际上,构建机器学习系统的大部分时间都花在了数据上。这是决定你系统有效性的关键要素,因为模型依赖于训练期间使用的训练数据。就像人体一样,如果你给模型提供差的数据或数据不足,它将输出差的结果。

数据的第一部分是收集过程。理解应用和任务的目的是有助于决定如何收集数据以及收集哪些数据。然后我们确定我们想要预测的目标值,例如房屋价格或某种疾病的发病率。这些目标值可以是显式收集或隐式收集。当我们可以直接确定我们试图捕捉的变量的值时,目标变量是显式的,而隐式目标值是通过使用上下文数据来确定目标值。

根据任务的不同,我们通常将数据存储在数据库中(用于元数据或表格数据)如 MySQL 或云存储(用于图像、视频或音频)如 Amazon S3:

图 5.1:数据收集

图 5.1:数据收集

一旦我们设置了持续的数据收集,我们必须制定一个数据清洗和处理的程序。我们收集的并非所有数据都是完美的。你总会发现缺失数据和某些异常值,这些可能会对我们的模型产生负面影响。无论你的模型多么直观,它都会在垃圾数据上表现不佳。

处理不干净数据的一些做法包括移除异常值、标准化某些特征,或者根据你收集的数据量来填补缺失数据。一旦数据通过了清理过程,下一步就是特征选择/工程过程。

当你的模型试图在其数据中找到关系时,理解你的数据包含的不同特征起着重要作用。探索性数据分析EDA)是在理解你收集的数据及其结构时常用的过程。这有助于确定在模型中使用哪些特征。正如我们之前在第四章中提到的,当我们向模型中包含更多特征时,这允许它们映射到更复杂的问题。然而,添加过多的特征可能导致过拟合,因此研究对你模型最重要的特征非常重要。

虽然大多数机器学习模型可以在数据中找到模式和关系,但理解你收集的数据的最佳方式是通过解决你试图解决的问题的任务领域的专家。领域专家可以提供关于在创建模型时应关注哪些特征的最好见解。一些无监督机器学习模型,如 PCA 和 t-SNE,可以分组并找到为你的模型提供最有价值信息的特征。

重要提示

了解你试图解决的问题的领域知识是理解你的数据并确定用于训练机器学习模型的特征的最有效方式。

一旦你设置了收集和清理数据的流程,下一步就是创建和训练你的模型。多亏了大多数机器学习库,你可以导入预构建的模型,甚至可以使用已经训练好的模型的权重来应用于你自己的模型。在这里,常见的做法是使用不同的模型和技术来查看哪种能产生最佳结果,然后你可以从中选择最佳模型,并开始通过更新超参数来微调它。这个过程可能需要时间,具体取决于你使用的数据量。

测试你的模型是系统管道中的关键要素。根据应用的不同,一个糟糕的模型可能会对你的业务产生负面影响,并给你的用户带来糟糕的体验。为了避免这种情况,你需要确定模型要达到生产就绪状态所需满足的不同指标和阈值。如果模型无法满足这些期望,那么你需要回过头来理解模型的弱点,并在再次训练之前解决它们。

在对模型进行测试并获得稳定的结果后,你现在可以将模型部署到用户应用程序中。这因应用而异。从那时起,整个过程可以从开始处重新开始,即新数据被插入并遵循机器学习管道,以便根据用户行为动态增长:

图 5.2:机器学习管道

图 5.2:机器学习管道

在接下来的章节中,我们将探讨构成我们机器学习管道的不同接口的细节。

拟合和转换接口

现在我们已经了解了整个管道过程,我们将详细探讨构成机器学习系统的不同接口。大多数系统包括以下接口:

  • 拟合

  • 转换

  • 训练

  • 服务

当涉及到数据和创建模型时,我们会遇到拟合和转换接口。我们将首先查看转换接口。

转换

转换接口是将收集到的数据预处理的过程,以便模型能够正确训练并提取有意义的信息。

我们收集的数据通常会有缺失值或异常值,这可能会在我们的模型中引入偏差。为了消除这种偏差,我们可以应用某些技术来帮助消除数据中的偏斜,并产生有意义的机器学习模型。以下我们将学习的一些技术可以分为以下三种类型的转换:

  • 缩放

  • 裁剪

  • 对数

对数转换是我们能应用到数据上的最常见和简单的转换技术。很多时候,我们的数据会偏向一个方向,这可能会引入偏差。为了帮助减轻这种偏斜分布,我们可以简单地应用对数函数到我们的数据上,这将使我们的数据更接近正态分布,从而使数据更加平衡。

我们可以通过以下代码执行这种转换:

import numpy as np
dataframe_log = np.log(dataframe["House Price"])

图 5.3:对偏斜数据进行对数转换

图 5.3:对偏斜数据进行对数转换

一旦应用了对数转换,我们就可以开始查看其他转换。我们可以使用的第二种转换是截断转换。我们使数据更符合正态分布的程度越高,效果越好,但我们可能会遇到可以扭曲数据的异常值。为了帮助减少异常值对我们数据的影响,我们可以应用分位数函数。人们最常用的分位数范围是 0.05 和 0.95 百分位数。这意味着任何低于 0.05 百分位数的数据将被四舍五入到下限,而任何高于 0.95 百分位数的数据将被四舍五入到上限。这使我们能够在减少异常值对模型影响的同时保留大部分数据。上下限也可以根据数据的分布进行修改。

此转换可以使用以下代码执行:

from sklearn.preprocessing import QuantileTransformer
quantile = QuantileTransformer(output_distribution='normal', random_state=0)
x_clipped = quantile.fit_transform("House Price")

图 5.4:数据上的截断转换

图 5.4:数据上的截断转换

最后一个主要转换技术是缩放转换。很多时候,我们收集的数据具有不同类型的指标和值,这可能会扭曲我们的数据并混淆我们的模型。例如,一个特征衡量公司的收入以百万为单位,而另一个特征衡量员工数量以千为单位,当使用这些特征来训练模型时,这种差异可能会使一个特征比另一个特征更受重视。为了防止这些问题,我们可以应用缩放转换,这些转换可以是以下类型:

  • MinMax

  • Standard

  • Max Abs

  • Robust

  • 单位向量

MinMax 缩放器是最简单的缩放转换。当数据没有扭曲时,它效果最好。这种转换将数据缩放到 0 到 1 之间。可以使用以下公式进行计算:

公式 _05_001.jpg

我们可以使用以下代码执行此缩放转换:

from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
x_scaled = scaler.fit_transform("House Price")

MaxAbs 缩放器与 MinMax 类似,但它不是将数据缩放到 0 到 1 之间,而是将数据缩放到-1 到 1 之间。可以使用以下公式进行计算:

公式 _05_002.jpg

我们可以使用以下代码执行此缩放转换:

from sklearn.preprocessing import MaxAbsScaler
scaler = MaxAbsScaler()
x_scaled = scaler.fit_transform("House Price")

标准缩放器是另一种流行的缩放转换。与 MinMax 缩放器不同,它不是使用最小值和最大值,而是将数据缩放到均值是 0,标准差是 1。这种缩放器基于数据呈正态分布的假设。可以使用以下公式进行计算:

公式 _05_003.jpg

我们可以使用以下代码执行此缩放转换:

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
x_scaled = scaler.fit_transform("House Price")

MinMax、MaxAbs 和 Standard 缩放器虽然功能强大,但可能会受到异常值和偏斜分布的影响。为了解决这个问题,我们可以使用 Robust 缩放器。而不是使用均值或最大值,这个缩放器通过从数据中移除中位数,然后使用四分位距缩放数据来工作。可以使用以下公式进行计算:

我们可以使用以下代码执行这种缩放转换:

from sklearn.preprocessing import RobustScalar
scaler = RobustScalar()
x_scaled = scaler.fit_transform("House Price")

最后,我们有单位向量标量,也称为归一化器。虽然其他标量函数是基于列工作的,但这个标量是基于行进行归一化的。它使用 MinMax 标量公式,将正值转换为 0 到 1 之间,负值转换为-1 到 1 之间。执行这种缩放有两种方式:

  • L1 范数 – 列中的值被转换,使得行中它们绝对值的和等于 1

  • L2 范数 – 列中的值被平方并相加,使得行中它们绝对值的和等于 1

我们可以使用以下代码执行这种缩放转换:

from sklearn.preprocessing import Normalizer
scaler = Normalizer()
x_scaled = scaler.fit_transform("House Price")

还有许多其他的缩放和转换技术,但这些都是最常用的,因为它们提供稳定和一致的结果。

重要提示

大部分开发过程发生在转换阶段。了解数据的结构和分布有助于决定你将在数据上执行哪些转换方法。无论你的模型多么先进,结构不良的数据都会产生弱模型。

拟合

现在,我们将查看拟合接口。这个接口指的是创建将在训练中使用的机器学习模型的过程。凭借今天的技术,创建用于机器学习管道训练的模型不需要太多的工作或努力。已经有预构建的模型可以导入和使用,适用于任何类型的应用。

这里有一个使用 scikit-learn 库创建 KNN 分类模型的小例子。

首先,我们导入所有必需的库:

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KneighborsClassifier
from sklearn.datasets import load_iris

然后,我们导入数据,将数据分为训练和测试批次,并应用标准标量转换:

iris = load_iris()
X = iris.data
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.30)
scaler = StandardScaler()
scaler.fit(X_train)
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)

然后,我们初始化一个 k = 3的 KNN 模型,并在该模型上进行训练:

classifier = KNeighborsClassifier(n_neighbors=3)
classifier.fit(X_train, y_train)

使用拟合接口的主要工作是设置将在机器学习管道的训练阶段使用的模型。由于导入多个预构建模型很简单,因此通常的做法是导入多种类型的机器学习模型,并一次性训练所有这些模型。这样,我们能够测试不同类型的模型,并确定哪一种表现最好。一旦我们决定使用哪种模型,我们就可以开始尝试不同的超参数,以进一步微调我们的模型。

训练和提供接口

转换和拟合接口负责准备数据和设置我们的机器学习模型以供管道使用。现在我们已经预处理了数据,我们需要开始考虑如何开始实际的训练过程,并将我们的训练模型部署给客户使用。

训练

现在我们已经预处理了数据并创建了模型,我们可以开始训练过程。这个阶段可能会因训练数据的品质或训练过程中使用的模型类型而有所不同。

一旦我们预处理了数据,我们需要将数据集分为训练集和测试集。这样做是为了防止过拟合。我们需要模型能够泛化数据,而使用所有数据进行训练将违背初衷。

常见的做法是将数据分为 70%的训练集和 30%的测试集。这样,模型就有足够的数据来学习关系,并使用测试数据来自我纠正训练过程。

有一种更稳健的数据分割方法,称为K-Fold 交叉验证。这种方法在可能没有足够训练数据的情况下效果最好。为了执行这个过程,我们将数据分成k个子集,然后我们在除了一个以外的所有子集上训练。然后我们迭代这个过程,选择一个新的子集作为测试数据。最后,我们通过平均每个迭代的指标来衡量模型的性能。这样,我们可以使用所有数据来训练和测试,而不会遗漏任何可能对学习数据有用的重要特征。

图 5.5:K-Cross Validation

图 5.5:K-Cross Validation

一旦我们分割了数据,接下来就是实际的训练部分。这部分就像设置用于训练模型的函数一样简单。这部分取决于你使用的库类型以及它提供的不同 API。

我们可以使用 scikit-learn 库创建一个简单的例子:

from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn import metrics
diabetes = load_diabetes()
features = diabetes.data
target = diabetes.target
x_train, x_test, y_train, y_test = train_test_split(features, target, test_size=0.3, random_state=1)
linear_regression = LinearRegression()
linear_regression.fit(features, target)
y_pred = linear_regression.predict(x_test)
print("Linear Regression model MSE:", metrics.mean_squared_error(y_test, y_pred))

训练完模型后,你必须衡量其性能。为了防止性能差的模型被部署给用户,通常的做法是衡量某些指标并设置某些阈值,只有当模型达到这些阈值时,才被认为适合生产。

根据你创建的模型类型,某些指标需要被评估。例如,回归模型通常会查看以下指标:

  • 平均绝对 误差 (MAE)

  • 均方 误差 (MSE)

  • 均方根 误差 (RMSE)

  • 决定系数 (R2)

对于分类模型,你需要监控以下指标以确定模型的强度:

  • 准确率

  • 精确率和召回率

  • F1 分数

  • 受试者工作特征曲线下面积 (AUROC)

在确定适用于你正在训练的模型的阈值时,拥有领域知识非常有帮助。在某些情况下,例如癌症检测模型,避免假阴性很重要,因此需要为模型可以自信使用的阈值设定更严格的限制。

重要提示

在部署模型之前,你需要确保模型适用于生产。设置模型需要通过的指标阈值是部署前验证模型的基本方法。如果模型未能通过这些标准,那么应该有一个流程来重新进行数据转换和模型训练阶段,直到它能够通过阈值。

服务

当涉及到服务我们的模型时,这取决于用户的需求,是开放的且灵活的。在大多数情况下,我们将模型部署到以下两种系统之一:

  • 模型服务,即我们将模型作为 API 部署

  • 模型嵌入,即我们直接将模型部署到应用程序或设备中

模型嵌入是部署模型最简单的方式。你创建一个包含你的模型的二进制文件,并将该文件嵌入到你的应用程序代码中。这种简单性在做出预测时提供了最佳的性能,但这也带来了代价。因为你直接将文件嵌入到应用程序中,所以很难扩展你的模型,因为你每次更新模型时都必须重新创建和重新上传文件。因此,这不被视为推荐的做法。

模型服务是当今市场上最常用的方法。应用程序和模型之间的这种分离使得开发者可以轻松地维护和更新模型,而无需更改应用程序本身。你只需创建一个用户可以访问以进行调用和预测的 API 服务。由于这种分离,你可以持续更新模型,而无需重新部署整个应用程序。

模型服务的一种替代方法是创建一个包含模型二进制文件的微服务,其他应用程序可以访问:

图 5.6:服务机器学习模型

图 5.6:服务机器学习模型

更直观的一种方法是你创建自己的包或库,其中包括你训练的所有模型。这样,你可以通过允许多个应用程序访问你创建的不同模型来有效地进行扩展。

我们迄今为止所看到的一切都是构建简单机器学习管道所需的内容。虽然这对大多数应用来说都是可行的,但要实现动态性和健壮性,我们需要考虑编排以及它能为支持更高级的应用和问题提供什么。

编排

现在我们已经了解了不同的接口以及它们在机器学习管道中的作用,下一步是了解如何将所有这些整合成一个无缝的系统。为了理解整个系统,我们首先必须了解自动化和编排。

自动化指的是自动化小型或简单任务的过程,例如将文件上传到服务器或部署应用程序,而不需要人工干预。我们不必让人员执行这些重复性任务,而是可以编程我们的系统来处理这些简单任务,从而减少浪费的时间和资源。

由于管道的线性特性,这对大多数系统都很有用。这突出了自动化常见的局限性——缺乏灵活性。今天的大多数系统需要更动态的过程才能适应某些应用程序和流程,而自动化本身是不够的:

图 5.7:线性系统管道

图 5.7:线性系统管道

这就是编排发挥作用的地方。“编排”是指配置和协调自动化任务以创建整个工作流程。我们可以根据一组特定的规则创建一个系统来执行某些工作或任务。由于用户决定系统需要针对某些情况采取哪些行动,因此创建一个全面编排工作流程需要一些规划和理解。

一个简单的例子是将应用程序部署给用户。系统中可能包含许多动态部分,例如以下内容:

  • 连接到服务器

  • 将特定文件上传到特定服务器

  • 处理用户请求

  • 在数据库中存储数据或日志

假设最近部署了更改后,应用程序出现了关键错误,这可能会使应用程序崩溃。系统管理员可以设置恢复和恢复系统的规则,例如回滚到稳定版本。由于系统能够自我恢复,开发者可以花更多的时间在开发上,而不是在恢复时处理开销。

根据某些结果,可能不需要执行所有任务。可能需要执行备份操作,或者系统需要通过不同的路径来维持稳定的工作流程。这样,系统可以适应其环境并自我维持,而无需太多的人工干预:

图 5.8:动态系统管道(编排)

图 5.8:动态系统管道(编排)

机器学习系统中可以自动化的不同任务如下:

  • 收集和预处理数据

  • 训练机器学习模型

  • 对训练好的模型进行测试和诊断以评估其性能

  • 提供机器学习模型服务

  • 监控生产中的模型

使用这些自动化任务,系统管理员需要编排管道的阶段,使其动态且可持续。以下组件有助于创建一个健壮的系统:

  • 调度:系统必须能够调度并单独运行管道中的不同自动化任务,同时保持系统依赖性。

  • CI/CD 测试:模型训练完成后,对模型进行自动测试以衡量其性能是至关重要的。如果模型未能通过某些指标,你必须从头开始重复训练过程,以解决模型的弱点;否则,它不能部署到生产环境中。

  • 部署:根据您将模型部署到生产环境的位置,设置自动化流程可以帮助减少部署时间,同时保持模型的更新版本。

  • 监控:在部署模型后,持续监控模型在生产环境中的性能,以保持模型的健康状态,防止其退化。这将为我们提供何时需要更新我们的管道或模型以保持效率的指示。

重要提示

了解您的业务需求以及您的模型如何工作,可以帮助您更好地规划您的机器学习管道。设置备份阶段以解决系统中的一些潜在问题,可以使系统更具动态性和适应行业需求的能力。

摘要

在本章中,我们探讨了构成机器学习管道的不同关键组件。

从那里,我们详细研究了构成组件的接口。我们从转换接口开始,它负责管道的数据方面。它接收数据并应用不同类型的数据转换,使我们能够保持干净和稳定的数据,这些数据我们可以在后续的机器学习模型中使用。

在转换阶段之后,我们开始在 fit 接口中创建我们的模型。在这里,我们可以使用库和包提供的预构建模型来初始化我们的模型。由于创建模型的简便性,测试不同类型的模型以查看哪种模型基于我们的数据表现最佳是一种良好的实践。

一旦我们创建了模型,我们就可以开始模型的实际训练。我们需要将数据分成训练集和测试集,以便我们的模型理解数据中的关系。然后,我们可以测量模型中的不同指标,以验证模型的表现。

一旦我们对模型的性能感到满意,我们就可以开始将我们的应用程序部署到生产环境中。部署我们的模型主要有两种方式,无论是将其嵌入到我们的应用程序中,还是作为服务供我们的客户使用。

最后,我们将所有内容整合起来,了解了机器学习中的编排包含哪些内容。我们学习了在编排机器学习管道时需要考虑哪些概念,以及如何保持系统动态和稳健,以跟上日常需求。

随着时间的推移和数据的变化,我们调整和维护我们的模型以应对现实世界中可能出现的某些情况变得尤为重要。在下一章中,我们将探讨当我们的数据开始发生变化和转移时,我们如何维护我们的机器学习模型。

第六章:稳定机器学习系统

在最后两章中,我们介绍了机器学习的不同概念以及我们如何创建一个综合的机器学习系统管道,该管道可以工作并适应我们的需求。

虽然我们的管道可以满足我们的期望,但在面对系统可能难以自我调整的外部因素时,对我们来说,能够维持我们的系统是非常重要的。

在本章中,我们将讨论数据集变化的现象以及我们如何优化我们的机器学习系统来帮助解决这些问题,同时保持其功能目标,而无需从头开始重建我们的系统。

我们将介绍以下概念:

  • 机器学习参数化和数据集变化

  • 数据集变化的原因

  • 识别数据集变化

  • 处理和稳定数据集变化

机器学习参数化和数据集变化

维护我们的机器学习模型是创建一个健壮模型的重要组成部分。随着时间的推移,我们的数据开始根据我们的环境发生变化和移动,尽管大多数模型可以检测和自我修复,但有时需要人为干预来引导它们回到正确的轨道。

在本节中,我们将简要介绍两个主要概念,这些概念将帮助我们了解它们对我们模型的影响:

  • 参数化

  • 数据集变化

我们机器学习模型的表示由某些规格定义,这些规格有助于定义我们模型的学习过程。这些包括以下内容:

  • 参数

  • 超参数

我们首先来看参数。这些规格在模型内部。在训练过程中,这些参数在模型试图学习输入特征与目标值之间的映射关系时被更新和学习。

大多数情况下,这些参数被设置为初始值为零或随机值。随着训练过程的进行,这些值通过优化方法(如梯度下降)不断更新。在训练过程结束时,这些值的最终权重构成了模型本身。这些权重甚至可以用于其他模型,特别是那些具有相似应用场景的模型。

参数的一些例子包括以下内容:

  • 人工神经网络中的节点权重和偏置值

  • 线性和逻辑回归模型的系数

  • 聚类模型的聚类中心

虽然参数在确定模型性能方面起着核心作用,但由于模型本身更新权重,它们大多不受我们的控制。这导致我们转向超参数。

超参数是控制我们机器学习模型学习过程的参数,反过来,它又影响模型学习的输出权重。这些值从开始设置并保持在整个学习过程中固定。

作为用户,我们决定在训练过程中为我们的模型设置哪些初始值。因此,找出哪些值能产生最佳结果需要时间和经验。测试和训练多个超参数变体以查看哪个表现最佳需要付出努力。

存在许多超参数,每个模型都有其独特的超参数集,用户可以修改。这些超参数可能包括以下内容:

  • 训练集和测试集之间的分割比例

  • 优化算法中使用的学习率

  • 优化算法的选择

  • 批大小

  • 迭代次数或周期数

  • 隐藏层的数量

  • 每个隐藏层中的节点数量

  • 成本或损失函数的选择

  • 激活函数的选择

  • 需要调整的聚类数量 ![img/Formula_06_001.png]

由于可能有许多超参数需要调整,以及许多不同的组合需要尝试,逐一测试这些变化可能会非常耗时。正如上一章所讨论的,在我们的流程中有一个部分自动化这个过程,通过运行具有不同超参数组合的多个模型来加速测试过程并找到最优的超参数组合,这可能是有用的。

图 6.1:超参数和参数调整

图 6.1:超参数和参数调整

可能存在调整我们的参数和超参数不足以防止我们的模型退化的情况。

例如,假设我们创建了一个模型准确率为 85%的机器学习模型。这个模型在一段时间内继续表现良好。然后我们开始看到我们的模型准确率下降,直到变得无法使用,因为模型无法正确预测我们收集的新测试数据。

在分析我们的模型时,我们可以开始看到我们的训练数据并不反映我们最近收集的测试数据。在这里,我们可以看到我们的训练和测试数据集的数据分布之间存在变化。

在我们着手解决数据集变化问题之前,我们首先必须了解数据集变化的背景、它们是如何发生的,以及我们如何调整我们的机器学习系统以帮助防止数据集变化影响我们的模型。

机器学习系统是在假设训练集和测试集之间的数据分布相似的情况下构建的。由于现实世界不断变化,新的数据分布出现,训练集和测试集之间可能存在显著差异。

训练集和测试集之间数据分布的主要差异被认为是数据集变化。这种剧烈的差异最终会降低模型性能,因为模型偏向于训练集,无法适应测试集:

图 6.2:由于数据集变化导致的机器学习模型结果

图 6.2:由于数据集变化导致的机器学习模型结果

这种情况的一些例子包括消费者习惯的变化、社会经济变化或全球影响,如大流行。这些事件可以严重影响我们收集和观察到的数据,进而影响我们模型的性能。

重要提示

首先,尝试调整你的机器学习模型的超参数,看看新学习的参数是否能显著提高你的模型。如果你仍然遇到主要问题,那么最好分析数据,看看是否发生了数据集偏移。

数据集偏移的原因

现在我们已经了解了数据集偏移是什么,我们可以开始调查数据集偏移的不同原因。虽然数据集偏移可能发生的原因有很多,但我们可以将它们分为两类:

  • 样本选择偏差

  • 非平稳环境

样本选择偏差在标签或收集用于模型的训练数据时是显而易见的,存在偏差或问题。收集有偏差的数据会导致训练集的非均匀样本选择。这种偏差本质上无法代表实际的样本分布。

非平稳环境是数据集偏移的另一个原因——我们将在本章后面进一步详细讨论不同类型。假设我们有一个包含一组输入特征,目标或输出变量的模型。从那里,我们还可以定义先验概率为,条件概率为,联合分布为。这种数据集偏移是由时间或空间变化引起的,定义为,这非常反映现实世界的运作方式。

这种因果效应可能导致不同类型的偏移:

  • 对于 公式 _06_008.png 问题,非平稳环境可以改变,给我们带来协变量或概念上的偏移

  • 对于 公式 _06_011.png 问题, 的变化可以给我们提供先验概率或概念上的转变

在下一节中,我们将探讨不同类型的偏移以及我们如何识别它们。

识别数据集偏移

在研究了数据集偏移的不同原因之后,我们可以开始将某些偏移分类到不同的组中,这有助于我们轻松识别我们正在处理的数据集偏移类型。

在我们可能遇到的不同数据集偏移中,我们可以将数据偏移分为以下类别:

  • 协变量偏移

  • 先验概率偏移

  • 概念偏移

我们首先将研究协变量偏移。这是最常见的数据集偏移,因为协变量偏移发生在训练或测试数据的输入特征之一或多个的分布发生变化时。尽管发生了变化,但目标值保持不变。

从数学的角度来看,这种数据集偏移只发生在 X > Y 问题中。每当输入分布,,在训练集和测试集之间发生变化,,但训练集和测试集的条件概率保持不变,,这将会导致协变量偏移。

例如,我们可以创建一个模型来预测某个城市员工的薪水。假设你的训练集中大多数员工是年轻人。随着时间的推移,员工会变老。如果你尝试预测老员工的薪水,你会开始看到显著的误差。这是由于模型对主要由年轻人组成的训练集存在严重偏差,无法找到老年员工之间的关系。

图 6.3:协变量数据集偏移

图 6.3:协变量数据集偏移

接下来,我们将探讨先验概率偏移,也称为标签偏移。这与协变量偏移相反,因为这种偏移发生在给定输出分布发生变化,但输入分布保持不变的情况下。

从数学的角度来看,这种情况只发生在 Y -> X 问题中。当先验概率发生变化时,,但条件概率保持不变,,就会发生先验概率偏移:

图 6.4:先验概率偏移

图 6.4:先验概率偏移

最后,我们将讨论概念偏移,也称为概念漂移。这种偏移发生在训练数据的分布保持不变,但给定训练数据的输出条件分布发生变化时。

从数学的角度来看,这种情况可以在 X -> Y 或 Y -> X 问题中发生:

  • 对于 X -> Y 问题,这种情况发生在输入变量的先验概率在训练集和测试集中保持不变时,(,但条件概率发生变化,)。

  • 对于 Y -> X 问题,这种情况发生在目标变量的先验概率在训练集和测试集中保持不变时,,但条件概率发生变化,

例如,由于经济原因,一个用户的购买行为受到影响,但我们的训练数据和测试数据中都没有包含关于经济表现的任何信息。因此,我们模型的性能将下降。

图 6.5:概念偏移

图 6.5:概念偏移

这可能是一个棘手的数据集偏移,因为分布偏移与我们训练的数据无关,而是与我们的模型可能没有的外部信息有关。大多数情况下,这些数据集偏移是周期性的和/或季节性的。

重要提示

可视化你的数据并计算与你的数据相关的不同概率是帮助确定和识别你所处理的数据集偏移的最佳方式。从那里,你可以决定你将如何解决你的数据集偏移。

当涉及到识别大多数数据集偏移时,我们可以遵循一个过程来帮助我们。它包括以下步骤:

  • 数据预处理

  • 在各自的训练和测试集上创建随机样本

  • 将随机样本合并成一个数据集

  • 使用一个特征一次创建一个模型,同时使用原始值作为输出值

  • 在测试集上预测并计算曲线下面积-接收者操作特征 曲线AUC-ROC

  • 如果 AUC-ROC 大于某个特定阈值,例如,80%,我们可以将数据分类为经历了数据集偏移

图 6.6:AUC-ROC 图示例(接近 1 的值表示模型强大)

图 6.6:AUC-ROC 图示例(接近 1 的值表示模型强大)

处理和稳定数据集偏移

现在我们已经建立了识别不同类型数据集偏移的方法,我们可以讨论解决这些偏移和稳定我们的机器学习模型的不同方式。

尽管有许多方法可以解决数据集偏移,但我们将关注三种主要方法。它们包括以下内容:

  • 特征丢弃

  • 对抗搜索

  • 密度 比率估计

我们首先将查看特征丢弃。这是调整数据集偏移的最简单形式。当我们确定哪些特征被分类为漂移时,我们可以简单地从机器学习模型中丢弃它们。我们还可以定义一个简单的规则,即任何漂移值大于某个特定阈值(例如,80%)的特征都可以被丢弃:

图 6.7:特征丢弃过程

图 6.7:特征丢弃过程

虽然这是一个简单的变化,但这需要仔细考虑。如果在训练你的机器学习模型时认为这个特征很重要,那么重新考虑这个特征是否需要被丢弃是值得的。此外,如果你的大多数特征都通过了被丢弃的阈值,你可能需要重新审视你的整体数据,并在解决数据集偏移时考虑不同的方法。

接下来,我们将探讨对抗搜索。这是一种需要训练二元分类器来预测样本数据是否在训练或测试数据集中的技术。然后我们可以评估分类器的性能,以确定是否发生了数据集偏移。如果我们的分类器性能接近随机猜测(约 50%),我们可以自信地确定我们的训练和测试数据集分布是一致的。另一方面,如果我们的分类器表现优于随机猜测,那么这表明训练和测试数据集的分布之间存在不一致。

对抗搜索可以分为三个部分:

  1. 从原始数据集中,我们将移除目标值列,并用一个新列来表示数据的来源(训练 = 0 和测试 = 1)。

  2. 我们将使用新的数据集创建和训练新的分类器。分类器的输出是样本数据属于测试数据集的概率。

  3. 最后,我们可以观察结果并衡量分类器的性能。如果我们的分类器性能接近 50%,那么这表明模型无法区分数据是来自训练集还是测试集。这可以告诉我们训练和测试数据集之间的数据分布是一致的。另一方面,如果我们的性能接近 100%,那么模型有足够的信心找到训练和测试数据集之间的差异,这表明训练和测试数据集的分布存在重大差异。

图 6.8:对抗搜索过程

图 6.8:对抗搜索过程

使用对抗搜索,我们可以建立三种方法来解决我们遇到的数据集偏移问题:

  • 使用结果,我们可以将它们用作训练过程的样本权重。权重对应于数据分布的性质。在实际情况中分布相似的数据将被分配更大的权重,而分布不一致的数据将被分配更低的权重。这将有助于模型强调代表它试图学习的真实分布的实际数据。

  • 我们可以使用仅排名靠前的对抗验证结果。而不是减轻测试数据集中不一致样本的权重,我们可以完全删除它们。

  • 除了排名靠前的对抗验证结果外,所有数据都用于训练。这种方法通过使用所有数据而不是丢弃特征来解决可能由第二种方法引起的问题。在训练过程中使用 K 折交叉验证时,我们可以将一些数据纳入每个折的训练数据中,而不是丢弃不重要的数据。这样可以帮助我们在使用所有数据的同时保持一致性。

解决数据集偏移的最终方法被称为密度比估计法。这种方法仍在研究之中,并不是解决数据集偏移的常用方法。

采用这种方法,我们首先分别估计训练集和测试集的数据集密度。一旦我们完成了这个步骤,我们就会通过计算训练集和测试集估计密度的比率来估计数据集的重要性。使用这个密度比率,我们可以将其用作训练数据集中每个数据条目的权重。

这种方法不受青睐且仍在研究中的原因是它计算成本高昂,尤其是对于高维数据集。即便如此,它对解决数据集偏移的改进微乎其微,不值得追求这种方法。

重要提示

特征删除是解决数据集偏移最简单、最直接的方法。在考虑使用对抗搜索方法之前,考虑使用这种方法,因为虽然这种方法有效,但它可能比较复杂,可能需要更多的努力和资源来帮助减轻数据集偏移的影响。

摘要

在本章中,我们讨论了数据集偏移的一般概念以及它们如何对我们机器学习模型产生负面影响。

从那里,我们进一步深入研究了导致这些数据集偏移发生的原因以及数据集偏移可能展现的不同特征。利用这些特征,我们可以更好地识别数据集偏移的类型——是协变量偏移、先验概率偏移还是概念偏移。

一旦我们能够分析我们的数据并识别数据集偏移的类型,我们就研究了不同的方法来帮助我们处理和稳定这些数据集偏移,以便我们能够维持我们的机器学习模型。我们回顾了一些技术,如特征搜索、对抗搜索和密度比估计,这些技术可以帮助我们在处理数据集偏移时。

使用这些过程和方法,我们可以防止我们的模型遭受在现实世界中发生的常见数据集偏移,并持续维护我们的机器学习模型。

现在我们已经对机器学习和如何维护一个健壮的模型有了牢固的理解,我们可以开始研究如何将我们的机器学习模型集成到我们的微服务架构MSA)中。

第七章:机器学习和深度学习如何帮助 MSA 企业系统

在前面的章节中,我们分析了人工智能、机器学习和深度学习的不同一般概念,以及它们如何用于特定的应用和用例。从那里,我们探讨了如何创建端到端的机器学习系统管道,以及它在建立稳健系统时带来的优势。最后,我们考察了我们的机器学习模型如何随着时间的推移通过数据变化而退化,以及我们如何识别和解决这些问题。

对机器学习的基本原理有牢固的理解后,我们现在可以开始探索机器学习在我们微服务架构MSA)企业中的应用场景。在本章中,我们将讨论在将机器学习集成到 MSA 企业系统中时,我们将提出的不同概念,以建立一个智能的 MSA。

机器学习 MSA 企业系统用例

在 MSA 企业系统中添加机器学习的空间很广,可以适用于许多用例。我们可以利用机器学习解决 MSA 中可能遇到的不同类型的问题,例如以下内容:

  • 系统负载预测:这将确定何时服务经历高于正常水平的负载,并触发措施以防止系统因服务器负载过重而退化。

  • 系统退化预测:类似于系统负载预测,这将监控微服务并尝试预测和确定 MSA 企业中的异常,使用户能够采取行动,防止某些问题出现并对性能产生负面影响。

  • 系统安全:在网络安全时代,能够保护您的 MSA 系统免受针对性攻击至关重要。通过研究您的 MSA 系统行为,模型可以预测和检测可能影响您系统的攻击。

  • 系统资源规划:随着系统的发展和演变,能够正确分配资源并适应系统需求是建立 MSA 企业系统时的关键部分。利用机器学习,我们可以学习哪些服务需要更多资源,以及我们需要扩展多少才能有效地分配所需资源。

图 7.1:MSA 中机器学习的用例

图 7.1:MSA 中机器学习的用例

尽管 MSA 企业系统中还有许多其他机器学习的用例,但大多数用例都归入这四个类别。在开始实施不同的模型之前,我们首先需要了解不同的案例以及我们如何解决这些问题。

我们可以从查看系统负载预测开始。这是我们在处理服务时经常会遇到的一个常见问题。与资源分配给每个微服务的单体系统相比,MSA 具有优势,这允许更容易的维护和可扩展性。然而,正如前几章所讨论的,在 MSA 中,一个微服务可能会经历高负载,从而导致级联效应,失败扩展到其他微服务。

使用智能 MSA,我们可以使用不同的特征,如响应时间,来训练模型,学习 MSA 系统的模式。类似于微服务熔断器,这个模型将能够迅速确定微服务是否正在经历高负载,并在问题变得太晚并开始对其他微服务产生负面影响之前解决它。

图 7.2:系统负载预测模型

图 7.2:系统负载预测模型

就像系统负载预测模型一样,我们可以构建一个模型来寻找 MSA 中的异常,这些异常可能导致服务衰减。我们不仅关注特定微服务的服务负载,还可以研究整个 MSA 以及它在更大规模上运作的不同模式。

某些系统在特定的时间和时期可能会经历不同的系统负载和错误。例如,我们的服务可能在假期和季节性事件等特定时期遇到请求激增,用户数量可能急剧增加。允许模型学习和理解 MSA 及其随时间如何运作,可以使模型更好地检测异常并防止误报。

此外,我们不仅监控单独的微服务,还可以评估微服务集群以及它们与整个 MSA 的交互。这样,我们可以识别出可能在我们的 MSA 中出现的某些瓶颈和错误。

图 7.3:系统衰减预测模型

图 7.3:系统衰减预测模型

机器学习在安全领域蓬勃发展。随着更高级的攻击和方法,用户保护他们的系统变得至关重要。机器学习使用户能够创建强大的模型,这些模型可以在攻击甚至影响他们的系统之前进行分析和预测,MSA 也不例外。

拒绝服务DoS)是一种旨在阻止用户访问某些服务的网络攻击。随着技术的进步,这些攻击变得越来越复杂。利用机器学习,我们可以训练我们的模型了解我们的 MSA 并模拟 DoS 攻击,以便它能够确定我们的 MSA 是否受到攻击。有了这个,我们可以通知安全团队或部署对策来对抗某些攻击并维护我们 MSA 的完整性。

图 7.4:系统安全模型

图 7.4:系统安全模型

自愈过程的一部分包括在 MSA 开始增长和扩展时为某些微服务分配资源。经过一段时间,你可能会经历用户增长,结果,你的微服务的请求量会增加。一个模型可能错误地识别问题并提供不会解决核心问题的解决方案。

因此,构建一个高级模型,它可以跟踪 MSA 的逐渐增长并确定何时某些服务需要更多资源,可能是系统自愈过程的关键部分。该模型的成功实施可以大大提高系统的可靠性,因为它可以更有效地合理和高效地分配资源。

图 7.5:系统资源规划模型

图 7.5:系统资源规划模型

重要提示

我们可以在我们的 MSA 中使用的不同类型的模型不是互斥的。将不同的用例组合起来构建一个更智能的 MSA 是可能的,也是常见的。了解你的 MSA 如何运行以及确定它可能存在的不同弱点,使用户更容易确定要采用哪些模型。

在某些用例中,由于问题的性质,某些模型可能比其他模型工作得更好。现在我们已经研究了我们可以将机器学习应用于我们的 MSA 的不同概念,我们可以在下一节开始深入探讨我们可以用来构建我们的机器学习模型的不同的实现和模型。

使用模式分析机器学习增强系统支持性和问题解决时间(TTR)

在我们开始使我们的 MSA 智能化之前,我们必须首先通过利用机器学习模型来学习我们服务的性能的常见趋势和模式,来了解我们的系统性能。从那里,我们可以建立一个基线,作为其他高级模型使用的参考。

第四章所述,当我们有一个标记的测试集时,可以发生监督学习。在我们的案例中,我们主要可以使用监督学习,因为我们可以在 MSA 中轻松捕获我们服务的响应时间,并将其用作我们的数据标签。

从那里,我们有各种各样的技术可以用来创建我们的机器学习模型。为了简单起见,我们可以使用线性回归模型来预测特定微服务的预期响应时间。使用这个输出,我们可以设计一个系统,其中我们可以配置一个阈值,如果我们检测到我们的 MSA 将达到一定的响应时间,我们可以通知开发者或启动一个程序来在问题发生之前解决它。

如果我们回顾第六章,我们讨论了数据漂移及其对我们模型的影响。由于用户数量增加或季节性事件,MSA 随着时间的推移而增长和扩展是很常见的。因此,我们可能会看到我们的 MSA 响应时间和指标的增长。这可能会错误地触发一个警报,通知我们异常的响应时间,而实际上,它准确地描述了 MSA 的正常行为。

因此,持续收集数据并训练我们的模型以适应预期的变化,使其能够学习系统的增长方式并正确识别不常见于我们 MSA 的变化,这一点非常重要。

图 7.6:性能基线系统流程

图 7.6:性能基线系统流程

虽然这个系统对于简单问题已经足够,但我们可以将这个模型输出与其他高级模型结合,创建一个更完整的端到端系统,其中我们可以了解 MSA 的健康状况并做出更好的决策。在下一节中,我们将讨论如何使用深度学习来实现我们系统的自我修复。

重要提示

从一个简单的模型开始,例如线性回归模型,是很重要的。一旦概念验证成功,你就可以通过结合更高级的模型和技术来改进你的系统。

使用深度学习实现系统自我修复

现在我们已经确定了系统的基线,我们可以利用这一点来创造一个更智能的 MSA,其中我们可以检测异常并执行系统自我修复。这样,我们可以在问题出现之前更加主动地解决问题,节省成本和时间。

异常检测是识别系统或服务中可能发生的任何异常事件或趋势的有效方法。例如,我们可以使用异常检测来确定信用卡欺诈。我们可以使用用户的购买趋势,并根据这些信息确定用户何时成为信用卡欺诈的受害者。

类似于信用卡欺诈检测,我们可以将我们的异常检测应用于我们的 MSA。在我们探讨可以用来实现异常检测的不同模型之前,让我们首先了解不同类型的异常:

  • 点异常:当某个数据点与其他数据点相差甚远时,这种情况发生

  • 上下文异常:当数据因数据上下文的原因不符合一般数据趋势时,数据被视为此类

  • 集体异常:当一组相关数据实例相对于整个数据集出现异常时

图 7.7:异常数据

图 7.7:异常数据

异常检测模型可以通过以下方式完成:

  • 监督 异常检测

  • 无监督 异常检测

我们可以用于无监督学习的一个常见模型是自动编码器。如第四章中提到的,自动编码器是由编码器和解码器组成的神经网络。自动编码器的一般目的是将数据压缩到与 PCA 类似的一个较低维度。这样,它能够学习不同数据特征之间的相关性和模式。一旦它学会了模式,它可以将压缩后的数据传递给解码器,解码器尝试使用编码器阶段学到的知识“重建”原始数据。

当专家研究数据以确定对于特定的 MSA,哪些响应时间被认为是异常时,我们可以利用机器学习来帮助我们找到即使是经验丰富的开发者可能也难以看到的模式和关系。

使用学到的参数,我们可以在我们的监督回归模型中使用它,以在检测异常时实现更准确的结果,并防止假阳性发生。

图 7.8:使用深度学习的自我修复

图 7.8:使用深度学习的自我修复

重要提示

标记用于监督式机器学习问题中的数据可能需要花费时间和金钱。你可以利用无监督机器学习模型来帮助你预测和标记你的未标记数据。从那里,你可以将新标记的数据输入到你的监督式机器学习问题中,从而利用无监督学习。记住新标记的数据,并确保它不会对你的监督式机器学习问题产生负面影响。

这些是我们可以利用机器学习和深度学习创建智能 MSA 的一些方法,它可以检测系统中的异常并迅速反应。这些用例可以根据用户的需求和他们的 MSA 的需求,通过使用不同的模型和技术进行调整和增强。

摘要

本章讨论了如何在我们的 MSA 中实现机器学习和深度学习。

我们首先探讨了机器学习如何用于构建智能 MSA 的不同用例。这些用例可以分为四个类别:

  • 系统负载预测

  • 系统衰减预测

  • 系统安全

  • 系统资源规划

我们讨论了每个类别及其在创建智能 MSA 时扮演的角色。

从那里,我们开始探讨使用监督机器学习来创建一个模式分析模型,它可以学习我们的 MSA 并创建一个性能基线模型。使用这个模型,我们可以确定我们的微服务性能是否异常。然后我们可以根据阈值执行操作,或者使用这个基线构建一个更高级的模型。

除了我们的监督机器学习模型外,我们还可以使用深度学习来创建更复杂的模型,例如自动编码器,以在我们的 MSA 中找到异常。通过结合这两种模型,我们可以创建一套基于某些预测的规则来执行,例如我们的 MSA 可以在最小人工干预下自我修复。这使我们能够在修复和调试我们的 MSA 时节省时间和金钱。

在接下来的章节中,我们将把迄今为止所学的内容应用到实践中,并开始使用实际示例和集成机器学习来构建我们自己的简单智能 MSA。

第三部分:在 MSA 系统中部署机器学习的实用指南

本书最后一部分将把迄今为止所涵盖的内容生动地呈现出来。它将逐步引导你通过设计和发展一个智能的微服务架构MSA)系统,包括实际可导入用于实际用例的动手示例和实际代码。本部分将深入理解如何将 DevOps 流程应用于构建和运行智能企业 MSA 系统,从最初开始到运营和维护。

本部分从容器、Docker 的基本知识开始,以及如何安装和运行 Docker 容器。我们还将通过构建一个简单项目来获得处理容器间数据流的手动经验。此外,本章还将涵盖构建特定用途 AI 的实用指南以及如何将 AI 服务注入 MSA 系统。

本部分深入探讨 DevOps 在企业 MSA 系统中的应用,重点关注组织结构对齐以及 DevOps 如何影响 MSA 及其运营。我们将学习如何在项目生命周期中应用 DevOps,从开始到运营、变更管理和维护。

本部分还涵盖了如何识别和最小化系统依赖,应用质量保证QA)测试策略,构建微服务和 MSA 测试用例,以及部署系统更改和热更新。本节还将提供如何克服系统依赖和有效应用测试策略的实用示例。

总之,本书的最后一部分将为您提供如何设计、开发和维护智能企业 MSA 系统的全面指南,重点关注实践、动手经验和实际用例。到本部分结束时,我们将具备构建自己的智能 MSA 系统并迈向实现更好的业务成果、运营绩效和业务连续性的第一步所需的技能和知识。

本部分包括以下章节:

  • 第八章**,DevOps 在构建智能 MSA 系统中的作用

  • 第九章**,使用 Docker 容器构建 MSA

  • 第十章**,构建智能 MSA 系统

  • 第十一章**,管理新系统的部署——绿地与棕地对比

  • 第十二章**,部署、测试和运营智能 MSA 系统

第三部分:在 MSA 系统中部署机器学习的实用指南

第八章:DevOps 在构建智能 MSA 企业系统中的作用

在前面的章节中,我们介绍了 MSA 是什么,以及 MSA 相对于单体架构的优势。然后,我们通过示例讨论了如何将单体应用程序重构为 MSA,接着又讨论了不同的模式和技巧来增强 MSA 系统的性能。

我们还讨论了不同的机器学习和深度学习算法,通过实际案例展示了如何优化它们,以及这些机器学习和深度学习算法如何有助于进一步增强 MSA 系统的稳定性、弹性和可支持性,以便构建“智能 MSA”或“智能 MSA”系统。

在接下来的几章中,我们将进一步增强我们的 ABC-MSA 系统,并尝试通过一些实际安装和代码示例应用到目前为止所学到的知识。然而,在我们这样做之前,我们需要在本章中讨论 DevOps 的不同概念,以及如何将 DevOps 流程应用于构建和运行 MSA 系统。

第一章中,我们简要介绍了 MSA 中的 DevOps。在本章中,我们将扩展这个主题,深入探讨 DevOps 在构建智能 MSA 中的作用。

本章涵盖了以下主题:

  • DevOps 与组织结构对齐

  • 企业 MSA 系统运维中的 DevOps 流程

  • 从一开始就将 DevOps 应用于运维和维护

DevOps 与组织结构对齐

在传统的软件开发组织中,软件交付流程已经成熟,并且是根据该传统组织的结构构建的。通常,我们有一个业务团队定义核心业务规范和需求,随后是另一组架构师团队构建系统的结构。在传统的软件模型中,我们还有设计工程师编写功能规范,一个负责编写代码的开发团队,一个负责测试代码质量的 QA 团队,然后是发布团队、发布后的运维团队、支持团队等等。

图 8.1:传统开发结构

图 8.1:传统开发结构

在传统的软件发布周期中,所有这些团队都参与其中,大多数是团队之间的顺序交接,存在隔阂、依赖关系,跨沟通问题,以及在过程中可能出现的推诿责任,发布周期可能需要数周或数月才能完成。对于 MSA 来说,这是不可接受的。

重要提示

MSA 的整个目的在于简化、加速和优化软件的发布和更新。将传统方法应用于 MSA 系统开发根本不起作用,也违背了最初采用 MSA 的目的。

DevOps

DevOps 是现代软件开发组织中采用的主要流程之一,旨在帮助简化发布流程并优化它,以便组织每天可以无任何服务中断地进行多次无缝发布更新。

DevOps 是一组流程的组合,允许您将应用程序从开发到运营平稳过渡。企业需要专门的、定义明确的 DevOps 流程来管理他们的解决方案开发、托管和运营。

DevOps 团队的主要需求是实现工程技术在管理应用程序操作中的应用。虽然这听起来很简单,但运营团队会执行许多日常和随机活动。简化这些任务是采用 DevOps 的最大挑战。

图 8.2:以 DevOps 方式共同工作的团队

图 8.2:以 DevOps 方式共同工作的团队

开发团队的主要责任是构建应用程序。然而,他们还需要关注应用程序的其他方面,例如应用程序性能、使用分析、代码质量、活动日志和解决代码级别的错误。

另一方面,运营团队面临着完全不同的问题。他们的关注点包括管理应用程序的可用性,通过更高的可扩展性确保性能,并改进解决方案生态系统、资源分配和整体系统分析。DevOps 流程处理所有参与方的所有这些关注点。

图 8.3:DevOps 生命周期

图 8.3:DevOps 生命周期

图 8.3 与我们之前在 图 1.11 中讨论的内容相似。这里新增的一点是,计划阶段是定义软件路线图并将其分解为主要需求,称为史诗。这些史诗被分解为一系列短端用户需求,称为用户故事。更多相关信息将在下一节中介绍。

好吧,那么,如果一个组织要采用 MSA,他们应该接受 DevOps 文化。简单,对吧?并不完全是这样!

在传统的组织结构中采用 DevOps 文化将会有许多不匹配,这肯定会阻碍 DevOps 周期。您的发布周期效率将取决于周期中最慢的过程。软件开发组织本身必须转变其文化以与 DevOps 保持一致,而不是相反。许多其他方法和技术也需要作为新 DevOps 转变的一部分被采用。组织结构本身可能也需要调整以适应新的 DevOps 方法。

DevOps 团队结构

建立 DevOps 团队是组织转型的第一步。然而,如果不考虑现有的组织结构和组织如何与现有开发周期保持一致,你不能期望拥有一个完整的 DevOps 团队。

在现有的传统组织中,让开发和运维团队能够合理运作的过渡阶段是必不可少的。随着组织结构现代化以适应新的文化,传统的开发和运维团队将逐渐转变为真正的 DevOps 结构。

在组织转型场景中,推荐的方法之一是建立一个小的 DevOps 团队,作为现有开发团队和运维团队之间的桥梁。在这个特定情况下,DevOps 团队的主要目标是跨职能地在开发和运维团队之间进行协调,映射交付成果,逐渐使两个团队熟悉新的方法论,并在两个团队中开始应用基本的 DevOps 方法论,以便他们可以在未来实现统一。

图 8.4:在组织转型期间,DevOps 团队作为开发和运维之间的桥梁

图 8.4:在组织转型期间,DevOps 团队作为开发和运维之间的桥梁

团队沟通、协作、活力、信任以及对整个开发周期的深入了解对于新 DevOps 团队的成功至关重要。因此,你必须识别出能够推动 DevOps 团队前进的正确技能和人员。这些技能可能包括但不限于编码技能、掌握 DevOps 和持续集成/持续开发CI/CD)工具,以及自动化。

随着组织结构和团队成熟,并对新方法论更加熟悉,将旧的 Dev、旧的 Ops 和过渡期的 DevOps 团队合并为一个单一的新的 DevOps 团队变得至关重要。在过渡阶段停留时间过长可能会比使用传统开发周期来开发 MSA 系统造成更多的干扰。

DevOps 团队的大小可以从 3 名工程师小到 12 名工程师,这取决于组织的规模、现有结构和投入的组织转型努力。通常,3 到 12 人之间的数量是理想的。拥有一个更大的团队可能会带来更多挑战而不是好处,并开始对团队的整体表现产生负面影响。

以逐步的方式进行转型过程,从基础设施编码开始,包括基础设施供应自动化、源代码版本控制、基础设施监控、代码构建自动化、部署自动化、测试编排、云服务管理等等。

我们现在知道,在采用 DevOps 时,组织结构的相关性和重要性。我们仍需要了解一些其他细节,这些细节将补充 DevOps,以便实现我们开发一个高效、高质量、上市时间短且更新无缝的 MSA 系统的目标。

在下一节中,我们将检查在开发 MSA 系统时需要考虑的其他一些因素。

企业 MSA 系统操作中的 DevOps 流程

微服务开发是一个快速的过程,需要所有其他开发过程以相同的速度运行。从 MSA 系统的开发一开始,就需要源代码管理和配置管理来为 DevOps 团队提供正确的支持。这随后是在开发环境中的代码扫描和单元测试编排。

在不同的团队成员之间应用特定的标准方法和最佳实践对于管理开发周期的效率和快速节奏至关重要。以下将讨论敏捷开发方法是什么以及它如何帮助 DevOps 操作,以及自动化在 DevOps 中的重要性。

开发敏捷方法

定义和实现 DevOps 流程与采用能够完全支持和利用 DevOps 力量的开发方法密不可分。尽管在您的组织中应用 DevOps 方法有多种方式,但敏捷方法是最佳选择。

敏捷开发方法将主要需求分解为可消费的小变化——故事和史诗。这些小而可消费的增量帮助团队在整个从开始到结束处理项目的旅程中实现短期胜利。

图 8.5所示,敏捷团队成员定期会面,通常是每周或每两周,以规划、定义和同意史诗和故事。然后,将这些需求放入待办事项列表中,直到下一次敏捷团队会议,团队成员将努力从待办事项列表中交付需求:

图 8.5:敏捷开发中的冲刺周期

图 8.5:敏捷开发中的冲刺周期

在敏捷开发中,每周或每两周定期召开的会议被称为冲刺规划会议,而开发人员在处理待办事项之间的这段时间被称为冲刺

为了让团队成员检查每个定义好的史诗和故事的状态,他们通常每天都会开会检查冲刺待办事项列表,并细化需要细化的任何内容,以确保及时交付。这种每日会议被称为每日站会

敏捷团队在冲刺周期内处理不断演变的故事和要求。

为了快速、低成本地交付高质量的产品,敏捷团队应用以下原则:

  • 没有阻碍日终活动的时间,例如构建和部署最新代码

  • 对最新代码的质量和功能质量的即时反馈

  • 对开发团队日常活动的严格控制、精确监控和持续改进

  • 对接受新故事、发布开发故事和缓解风险的决策速度更快

  • 与测试人员、最终用户和客户的反馈循环缩短

  • 定期审查和反思开发和交付流程

遵循敏捷宣言并遵循所有敏捷原则的开发团队应始终寻找从其流程模型中移除不必要障碍的方法。

开发方法论的敏捷方法可以应用于开发和交付所有类型的软件项目;然而,它更适合于基于微服务的应用程序的开发。重要的是要考虑微服务的范围和结构,以便与敏捷和 DevOps 实践相一致。

敏捷和 DevOps 流程最重要的支柱之一是使用按需、基于需求的资源。这通常通过使用基于云的基础设施来实现。开发微服务的敏捷团队所需的所有资源都需要及时、正确数量或足够容量地提供。云基础设施最适合这些需求。资源可以根据需求和需求进行扩展和缩减。

在 DevOps 周期中需要的按需云工作负载不一定部署在组织的私有基础设施上;它们可能非常可能通过公共云提供商部署,或者以混合云的方式部署。

自动化

随着 IT 基础设施的复杂性和 MSA 采用率的增加,以及对敏捷开发周期和短时间上市的需求,简化基础设施管理流程的需求成为任何组织最紧迫的需求。管理 MSA 基础设施、DevOps、CI/CD 和敏捷开发的一个很大部分是自动化。

自动化给现代组织带来了巨大的好处。其中一些好处包括但不限于以下内容:

  • 更优的人力资源利用:有了自动化,员工可以专注于其他可能无法自动化的活动,从而优化组织的人力资源使用,更好地在其他项目上进行扩展,并根据可用的和所需的技能集分配责任。

  • 更快的上市时间和更好的业务敏捷性:自动化流程无疑可以节省大量时间,这些时间原本会被手动重复工作和潜在的依赖性所消耗。在自动化实施的情况下,一项可能传统上需要几天才能完成的工作可以在几分钟内完成。

  • 更高的可靠性和更大的业务连续性:复杂和耗时的任务简化为简单的按键或鼠标点击。相应地,人为错误显著减少,运营可靠性大幅提高。

  • 更好的合规性:合规性可以集成到自动化工具中,以最小的努力提供更好的政策执行。合规性包括行业合规性、最佳实践和组织标准。行业标准可能包括通用数据保护条例GDPR)、支付卡行业数据安全标准PCI DSS)、健康保险可携带性和问责法案HIPAA)和计算机安全保护评估矩阵SCSEM)。

自动化常用于快速和高品质的应用交付。DevOps 是帮助自动化开发交付各个阶段的关键过程。实际上,DevOps 是一种文化,帮助组织避免重复的、耗时的人工步骤和努力。DevOps 范围内有各种工具、框架和流程,这些都是成功自动化所必需的。

DevOps 和 MSA 操作中的大多数挑战都无法手动解决——因此,在 DevOps 和 MSA 中自动化需求极高。从微服务开发到部署在生产环境中的每个交付环节都需要自动化。

图 8.6:DevOps 的四个支柱

图 8.6:DevOps 的四个支柱

从本质上讲,现代企业系统开发需要 DevOps 来应对组织动态和不断增长的需求,而 DevOps 在很大程度上依赖于四个支柱:MSA敏捷开发CI/CD自动化。如图所示,这四个支柱在 DevOps 成功中起着重要作用,因此,在现代企业系统开发的成功中也起着重要作用。

此外,正如我们将在本章后面讨论的,人工智能应用非常难以手动测试和管理,自动化在管理人工智能应用的整个 DevOps 周期中起着至关重要的作用。

从一开始就将 DevOps 应用于运维和维护

微服务部署的每一步都需要相应的 DevOps 步骤。微服务开发过程与 DevOps 过程的结合有助于赋予开发和运维团队权力。以下是对 DevOps 过程不同方面的详细探讨。

源代码版本控制

在微服务上工作的敏捷团队需要实施特定的版本控制。对于每个微服务,需要仔细定义版本控制的三个方面:

  • 版本控制工具(如 Git、SVN、CVS 和 Mercurial)的设置和管理。

  • 应用程序的版本格式和命名规范,例如表示应用程序版本的格式、主要更改版本、次要更改版本以及构建或补丁号——例如,版本 2.3.11.7。

  • 源代码的分支策略。这对于由多个团队分别开发不同微服务的微服务开发来说至关重要。团队需要为每个微服务创建单独的仓库,并为每个主要或次要增强创建不同的分支。

配置管理和一切作为代码

配置管理是一种系统性地管理跨各种环境变化的实践,以确保系统的功能和性能处于最佳状态。这包括开发、测试、部署和运行 MSA 系统组件所需的所有环境。

在 MSA 企业系统中,有如此多的动态部分,确定哪些系统部分需要维护和管理其配置至关重要。一旦确定了这些部分,它们的配置就需要得到控制并定期审计,以保持整个 MSA 系统的整体健康。

随着 DevOps 流程的成熟以及 MSA 系统组件的成熟,手动管理和配置变得越来越复杂,自动化对于顺利和成功地进行配置管理变得至关重要。

配置管理工具可以自动且无缝地管理系统组件的不同方面。这些工具在运行时以及任何时候根据需要做出调整,并按照应用程序的版本、更改类型和系统负载进行操作。

DevOps 的一个目标是将开发以及部署的所有方面进行编码化,包括基础设施和配置。整个环境可以从头开始构建,并使用基础设施即代码IaaC)和配置即代码CaaC)快速部署。

IaaC 和 CaaC 是配置管理的必要组件。两者通常都是描述性文件,通常用 Ansible、Terraform、Puppet、Chef 或 CloudFormation 等语言编写。

使用 IaaC 和 CaaC,DevOps 团队可以轻松地为不同目的启动新的工作负载。例如,可以根据测试用例配置工作负载,指定每个工作负载的属性,并控制与主要工作负载参数的偏差。

CI/CD

如前文在第一章中指出的,CI/CD 是 DevOps 的一个组成部分,在发布 MSA 系统更新中扮演着最重要的角色。CI/CD 确保代码立即且定期构建并推送到 CI/CD 管道,以便快速测试和反馈。

如以下 CI/CD 管道图所示,开发者主要专注于处理冲刺待办事项,并将代码更新推送到团队仓库,然后从那里下载到 CI 服务器。

图 8.7:CI/CD 管道和流程

图 8.7:CI/CD 管道和流程

CI 服务器会自动对代码运行预设的测试用例,然后在通过所有测试用例后推送代码到测试人员。如果任何自动化测试失败,代码不会继续沿着管道前进,并将所有测试失败的错误报告发送回开发者。

与传统的开发周期相比,在传统开发周期中,开发者可能要在代码提交测试后几天或几周后才能得知代码的测试结果,而在 CI/CD 中,开发者将在几分钟内收到代码问题的报告。这种对代码错误的早期可见性给了开发者立即在编写原始代码的同时修复这些错误的机会。因此,他们可以持续改进代码以进行发布和部署。

当代码成功通过所有 CI 服务器测试后,DevOps 团队测试人员将进一步测试代码。如果未发现错误,测试人员将代码推送到发布和部署,或者将其退回以进行进一步的修复和改进。

这个 CI/CD 管道使开发者能够频繁地进行代码合并;进行单元测试、集成测试、代码扫描和冒烟测试;每天多次发布和部署——这在传统的开发周期中几乎是不可能的。

DevOps 团队需要确定一个可以管理整个 CI/CD 管道的工具。DevOps 有助于添加钩子和步骤,以在代码构建和部署期间执行额外的活动,包括外部可执行文件和脚本。一些最常见和广泛使用的 CI/CD 工具包括 Jenkins、Bamboo 和 CircleCI。

代码质量保证

确保代码质量,无论是从编码标准还是安全漏洞的角度来看,都是 DevOps 中的重要活动之一。这还包括确保应用程序本身业务逻辑的准确性。

代码质量涉及到代码的静态和动态分析概念。代码的静态分析在代码执行之前进行。它的目的是揭示代码异味、脏代码、易受攻击的库、代码中的恶意漏洞以及违反代码标准或最佳实践。

动态代码分析在应用程序执行期间或之后进行。它的目的是揭示由于负载、意外输入或一般意外运行条件导致的运行时错误。

许多工具可以帮助在 CI/CD 中执行代码扫描。这些工具包括但不限于 SonarQube、Fortify SCA 和 Raxis。

重要提示

测试 AI 应用程序比测试常规应用程序更具挑战性。AI 应用程序的某些方面在常规应用程序中不存在。

AI 应用是非确定性的——它们在实际情况下会如何表现有些不确定。因此,在 AI 应用测试期间期望特定的结果可能并不可行。完全有可能发生的情况是,正在测试的应用在相同的输入或测试标准下产生不同的结果。

大多数 AI 应用的质量与其训练数据质量相当,这使得 AI 应用容易受到训练数据偏差或无意识偏差的影响。例如,假设你正在编写一个 AI 模块来预测美国任何地区的房价,但你的训练数据 90%来自一个特定州内的特定地区。你的 AI 模型将相应地偏向于 90%训练数据来源的地区,因此测试 AI 应用可能需要针对训练数据进行测试。这在房价预测案例中可能听起来很简单,但在更复杂的情况下,你如何理解其他训练数据呢?

假设尽管存在所有这些训练数据挑战及其非确定性行为,我们仍然可以准确地测试 AI/深度学习(DL)应用。AI/深度学习应用不断学习、训练和改变其行为,因此当代码在生产环境中运行时,应用已经在学习和改变其行为。一天或几天前完成的测试可能已经不再有效。

当然,有方法可以克服所有这些挑战。首先,你需要对训练数据进行整理和验证。你可能需要执行自动和手动任务来验证训练数据,包括检查数据偏差、数据倾斜、分布水平等。

我们还需要测试 AI 算法以及回归模型对不同测试数据集的表现。模型的方差和均方误差也需要被检查和分析。

目前市场上已有 AI 应用测试工具,并且每天都在增加。这些工具的质量在不断提高,并且可以为 DevOps 团队提供巨大的帮助。AI 测试工具通常根据所使用的 AI 算法进行专门化。不同的 AI 测试工具的例子包括但不限于 Applitools、Sauce Labs 和 Testim。

监控

随着 DevOps 的出现,标准监控已升级为持续监控,并涵盖了整个开发周期,从规划到部署和运营。

监控涵盖了 DevOps 流程的不同方面以及整个应用程序开发、测试、部署和发布所需的组件,以及发布后的运营。这包括基础设施监控和应用程序本身。

基础设施监控包括本地基础设施、虚拟云环境、网络、通信和安全。另一方面,应用程序监控涉及性能、可伸缩性、可用性和可靠性。资源监控包括在物理或虚拟工作负载内部和外部多个 Pod 副本中管理和分配资源。

DevOps 监控帮助团队成员响应 DevOps 预发布或发布周期中出现的任何运营问题,从而使 DevOps 团队能够在 CI/CD 管道中纠正、调整并做出任何必要的更改。

理想情况下,监控警报会触发自动操作,尝试响应和修复已检测到的问题。然而,知道这并不总是可能的,通常需要手动干预。监控有助于 DevOps 团队将测试用例向开发周期的早期阶段转移,从而提高应用程序质量并减少开发周期晚期的运营问题。

如前所述的第七章中讨论的 AI 算法,以及我们将在本书后面给出更多示例,可以检测任何应用程序行为异常,并自动尝试自我修复,以防止应用程序操作中断。

在 DevOps 监控方面,有许多针对特定环境的工具可用,包括 Nagios、Prometheus、Splunk、Dynatrace 以及适用于 AWS 云环境的 AWS CloudWatch。

灾难管理

灾难管理是 DevOps 流程中的重要部分,但往往被忽视。在大多数情况下,应用程序恢复被视为部署过程的扩展部分。在云中,它通常被视为配置应用程序托管区域和区域以配置可用性的一个分支,而不是一个全面的环境挑战。

在微服务的情况下,识别灾难比避免、减轻或管理它更具挑战性。幸运的是,CI/CD 环境本身可以被利用来测试和模拟灾难场景。此外,利用外部仓库可以恢复到特定的版本号。

尽管如此,在不同的地理位置设置一组完全独立的副本环境,设置自动故障转移和中间的负载均衡器,可以成为维护业务连续性和不间断的 CI/CD 管道的绝佳方式。

使用 IaaC 和 CaaC 工具来自动化恢复,在发生中断的情况下,可以在最短的时间内将应用程序和系统恢复在线,这非常有帮助。

你仍然需要定义一个事件响应手册作为 DevOps 的一部分。这个手册应包括在每个场景中应执行的具体计划。例如,对自然灾害的响应可能与对数据泄露事件的响应不同。该手册需要包含不同的场景以及需要采取的角色和程序列表,以防止或最小化系统中断或数据丢失。

摘要

为了实现 MSA 系统创建的目标,需要与开发 MSA 系统相结合的一定方法。在本章中,我们讨论了在开发 MSA 系统时需要采纳的一些最关键实践:敏捷开发方法、DevOps 流程和实践,以及 CI/CD 管道管理。

我们还讨论了建立 DevOps 团队来管理微服务的重要性。我们提供了在构建我们的 MSA 系统时应用和管理 DevOps 的工具示例。

在下一章中,我们将迈出构建智能 MSA 系统的第一步。我们将讨论 Docker,它是什么,以及为什么它相关。我们还将使用 Docker 创建隔离和独立的虚拟环境,然后将这些环境(或容器)连接起来,以提供我们 MSA 系统的一个简单功能部分。

第九章:使用 Docker 容器构建 MSA

在上一章中,我们讨论了如何应用构建和运行 MSA 系统的 DevOps 流程,以及将组织结构与 DevOps 对齐的重要性。

我们还强调了在整个 MSA 项目生命周期以及 CI/CD 运作中拥抱自动化和适应敏捷开发方法的重要性。

本章将介绍什么是 容器,如何安装容器,如何使用它们,以及如何处理容器之间的数据流以构建一个简单的项目。我们将使用 Docker 作为我们的平台,因为它是目前该领域最受欢迎和最广泛使用的平台之一。

本章将涵盖以下主题:

  • 容器究竟是什么,为什么要使用它们?

  • 安装 Docker

  • 创建 ABC-MSA 容器

  • ABC-MSA 微服务间通信

容器究竟是什么,为什么要使用它们?

容器被定义为一种操作系统级别的虚拟化实体,通过将不同的有限计算资源组合成一个自包含的执行环境来创建。

如以下集装箱船的图所示,容器是自包含的单元,独立于船上的任何其他容器。船是用于携带和运输容器的引擎:

图 9.1:一艘集装箱船

图 9.1:一艘集装箱船

类似地,容器化的想法是在操作系统级别创建虚拟化。这意味着,在内核内部,你可以将不同的物理机器资源、应用程序和 I/O 功能组合成一个自包含的执行环境。这些自包含的资源形成一个单独的容器,因此得名容器。容器引擎与前面示例中的船只类似,其中容器引擎用于携带、运行和运输容器。

容器已经存在很长时间了,可以追溯到 Unix 的 chroot 在 1970 年代后期和 1980 年代初期,以及在我们甚至了解到今天我们称之为 虚拟机管理程序 之前。虚拟机管理程序是一个组件,它使我们能够启动 虚拟 机器VMs)。

Unix 的 chroot 在 1990 年代后期演变为 Linux 容器,或我们称之为 LXC,然后在 2000 年代初演变为 Solaris Zones。这些概念随着时间的推移从 cgroups(最初由 Google 开发)和 namespaces(由 IBM 开发)发展到我们今天看到的容器引擎,如 Docker、RktCRI-OContainers、Microsoft Hyper-V 容器等。

尽管容器和虚拟机之间存在相似之处,但两者仍然有一些基本的不同之处。

如以下图所示,容器共享宿主操作系统的同一内核,但隔离并限制分配的资源,给我们带来了一种类似于虚拟机的感觉,但在资源方面要轻得多:

图 9.2:虚拟机与容器

图 9.2:虚拟机与容器

在虚拟机管理程序虚拟化中,每个虚拟机都将拥有自己的虚拟硬件和自己的客户操作系统。除此之外,虚拟机管理程序中还有大量的仿真。因此,每个虚拟机需要的资源比容器需要的资源要多得多。资源包括 CPU 周期、内存、存储等。此外,你可能会在多个虚拟机上部署相同的客户操作系统,以便虚拟机能够提供所需的功能,从而产生更多的开销和资源浪费。

图 9**.2 展示了部署在宿主操作系统之上的虚拟机管理程序。然而,更常见的虚拟机管理程序虚拟化模型是在硬件上直接部署裸机虚拟机管理程序。在两种情况下,开销仍然比部署容器要高得多。

容器的轻量级特性使得公司能够在数据中心运行比虚拟机更多的虚拟化环境。由于容器比虚拟机更有效地共享资源,并且数据中心有有限的物理资源,容器在很大程度上增加了数据中心基础设施的容量,这意味着容器在托管应用程序方面成为一个更好的选择,尤其是在我们的 MSA 案例中。

容器性能是另一个需要考虑的因素。与虚拟机管理程序虚拟化环境相反,容器中的 I/O 虚拟驱动程序通信、硬件仿真和资源开销都极小。因此,容器通常比虚拟机表现更好。容器的启动时间只需 1-3 秒,而虚拟机则需要几分钟。

在容器上运行的应用程序可以直接与硬件交互和访问。在虚拟机管理程序虚拟化的情况下,应用程序和虚拟机之间始终有一个虚拟机管理程序(除非启用了虚拟机管理程序绕过,这有其自身的限制)。

使用容器,你可以在一个可重复使用且完全可移植到任何其他平台的环境中打包应用程序及其所有依赖项。这是容器最重要的特性之一。开发者可以在他们的开发服务器上开发应用程序,将其发送到测试环境,然后是预生产和生产,而无需担心可移植性和依赖性问题。

重要提示

由于所有上述原因,微服务最流行的部署模型是每个服务一个容器的模型。这就是 MSA 的每个微服务都部署在运行特定应用程序的专用容器上。

容器和虚拟机之间的另一个重要区别是安全性。由于容器使用相同的内核,多个容器可能会在容器边界之外访问相同的内核子系统。这意味着容器可以访问同一物理主机上运行的另一个容器。例如,一个具有 root 访问权限的单个应用程序可以访问任何其他容器的数据。

有许多方法可以加强容器安全性,但没有任何这些技术可以帮助容器达到虚拟机的完全隔离安全性。

当然,在某些情况下,使用虚拟机可能比使用容器更好。或者在某些场景中,虚拟机和容器的混合部署模式可能是最合适的。这完全取决于您在组织中部署的使用案例、应用程序或系统。

在需要完全工作负载隔离的多租户环境中,使用虚拟机会是一个更好的选择。或者,如果您正在尝试构建一个用于托管关键知识产权、高度机密数据或应用程序的研发环境,也需要完全的工作负载隔离。因此,在这种情况下,使用虚拟机将是更好的选择。

对于我们的 MSA 示例,我们需要一个非常轻量级、快速启动、高度可移植且高性能的虚拟化环境来构建我们的 MSA 系统。因此,在我们的场景中,使用容器,采用每个微服务一个容器的部署模式,是更好的选择。MSA 系统中的每个微服务都会部署在其自己的容器中,并拥有自己的开发团队、开发周期、质量保证周期、更新、运行生命周期和发布周期。

以下表格总结了容器和虚拟机管理程序之间的区别:

容器 虚拟机管理程序
资源使用和开销 轻量级,请减少开销,并更高效地使用资源 高开销和资源密集型
容器和应用程序大小 平均 5-20 MB 以 MB 或 GB 为单位衡量
性能 高性能 性能较低
可扩展性 容易扩展/高横向扩展 扩展难度较大且消耗资源
启动时间 非常短的启动时间(1-3 秒) 启动时间以分钟计
可移植性 系统无关性和高度可移植性 可移植性有限
DevOps 和 CI/CD 适用性 使 DevOps 更加敏捷,CI/CD 流程更加顺畅 可能会减慢 CI/CD 操作
主机硬件访问 应用程序直接访问硬件 无法直接访问硬件
安全性 安全性较低;共享相同的内核 安全性更高;每个虚拟机都有自己的操作系统内核

表 9.1:容器和虚拟机管理程序的区别

尽管我们在选择容器引擎时有许多选项,但 Docker 是目前使用最广泛的引擎,以至于 Docker 今天与容器同义。这就是我们选择在本书中使用 Docker 作为容器引擎的主要原因。

Docker 也非常适合敏捷的 DevOps 和 CI/CD 操作。在 CI/CD 环境中,从构建 Docker 镜像到其在生产环境中启动并运行的总时间通常在 1-5 分钟之间:

图 9.3:Docker Engine 容器虚拟化

图 9.3:Docker Engine 容器虚拟化

图 9**.3显示了在宿主操作系统上安装的 Docker Engine,以实现微服务或应用程序的一般容器化。

Docker 本身可能不足以管理所有的 CI/CD 操作。组织通常通过使用KubernetesMarathon等集群技术来补充 Docker,以便在您的系统运行的集群中平滑地部署、管理和操作容器。然而,在本书中,我们将专注于 Docker 本身以及如何使用 Docker 构建我们的 MSA 系统。

此外,为了移动、测试和部署容器,我们需要有一个仓库来保存这些容器,并能够将它们移动到不同的环境。许多工具可以帮助我们完成这项工作,其中Docker HubGitHub是两个最常用的仓库。对于我们的项目,我们将使用 GitHub 作为我们的项目仓库。

到目前为止,我们已经介绍了容器是什么,容器与 VM 之间的区别,以及为什么我们更倾向于在 MSA 中使用容器。在下一节中,我们将解释 Docker 的不同组件,如何安装 Docker,以及如何与 Docker 的组件一起工作来创建系统的微服务。

安装 Docker

我们将从这个节开始,先谈谈 Docker 的安装。然后,我们将介绍 Docker 的主要组件,每个组件的目的,以及这些组件是如何相互关联的。本节将帮助我们为后续的 ABC-MSA 演示项目准备环境。

重要提示

为了最大限度地提高您的动手学习体验,您需要遵循我们所有的动手安装步骤。但在我们深入这一节之前,请确保您有一个物理或虚拟主机可用于 Docker 安装演示。可以使用虚拟化软件(如 VirtualBox 或 VMWare)创建虚拟主机。

虽然您可以在 Windows 或 Mac 上安装 Docker,但在我们的演示中,我们将使用 Ubuntu Server 22.x Linux 环境来安装 Docker社区版CE)。我们建议您使用类似的环境,以便能够跟随我们的安装步骤。

Docker Engine 安装

现在我们已经了解了 Docker 的主要组件,让我们退一步,学习如何安装 Docker 并为 ABC-MSA 系统创建不同的 Docker 镜像。

安装 Docker 引擎的最佳方式是遵循 Docker Docs 中的官方安装指南,网址为 docs.docker.com/engine/install/。从列表中选择你的服务器系统平台安装指南。

你可能还希望在你的工作站上安装 Docker Desktop。Docker Desktop 可以从之前提到的安装指南中下载。

安装完成后,通过运行以下命令来验证 Docker 的功能:

$ docker --version
Docker version 20.10.18, build b40c2f6
$

然后,

$ docker run hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
:
:

你可能需要 root 权限才能成功执行 Docker 命令。

现在我们已经安装了 Docker,让我们来回顾 Docker 的主要组件以及如何使用它们。

Docker 组件

Docker 有四个主要组件:Docker 文件、Docker 镜像、Docker 容器和 Docker 卷。以下是这些组件的简要描述。

Docker 文件

Docker 文件是一个文本文件,它作为一个清单,描述了如何构建 Docker 镜像。Docker 文件指定了用于创建 Docker 镜像的基础镜像。例如,如果你打算使用最新的 Ubuntu 版本作为你的基础 Linux 镜像,你会在 Docker 文件的顶部指定以下行:

FROM ubuntu

注意 ubuntu 没有指定任何版本号,这将指示 Docker 拉取该基础镜像的最新版本。例如,如果你希望使用 CentOS 7.0 版本,你必须在该基础镜像上指定版本号,如下所示:

FROM centos:7

特定的镜像标签可以在 Docker Hub 上找到。Docker Hub 是一个公共仓库,存储了许多免费 Docker 官方镜像,供 Docker 用户重用。其中许多镜像包括 Linux、Windows、Node.js、Redis、Postgres 或其他关系型数据库镜像。

在指定了基础操作系统镜像之后,你可以使用 RUN 命令来运行你希望在 Docker 镜像创建过程中执行的命令。这些是常规的 shell 命令,通常用于下载和安装将在你的 Docker 镜像中使用的软件包和库。

Docker 文件必须命名为 Dockerfile,Docker 才能使用它。以下是一个简单的 Dockerfile 示例:

图 9.4:Docker 文件(Dockerfile)示例

图 9.4:Docker 文件(Dockerfile)示例

之前示例中的 Dockerfile 执行以下操作:

  1. 使用 Ubuntu 22.10 版本作为基础镜像,在稍后创建的容器上运行。

  2. 获取最新的软件包列表。

  3. 安装 Python 3 和 PIP Python 包管理器。

  4. 安装了一个名为 Ansible 的软件包(Ansible 是一个自动化工具)。

Docker 镜像

一旦你完成了 Dockerfile 的编写,你需要将其保存为 Dockerfile,以便能够使用它来创建 Docker 镜像。

Docker 镜像是一个二进制文件,它作为一个模板,包含了一组关于如何创建 Docker 容器的指令。

请注意,Docker 镜像可以是 Dockerfile 创建的,正如我们在这里解释的,也可以从公共或私有仓库(如 Docker Hub 或 GitHub)下载。

要构建 Docker 镜像,请使用以下命令,同时指向Dockerfile的位置。以下示例假设 Dockerfile 位于用户的家目录中:

$ docker build –t packt_demo_image ~/

上述命令将构建一个名为packt_demo_image的镜像。这个镜像将用于稍后根据 Dockerfile 中定义的规范创建容器。

-t选项表示tty,它将一个终端附加到容器。

要验证您的镜像已创建,请使用以下命令:

$ docker image ls

您可以将-a选项添加到上一个命令的末尾,以显示在主机机器上创建的所有镜像。

在 CI/CD 操作中,构建的镜像通常被共享在公共或私有仓库中,以便项目团队使用,在某些情况下甚至对公众开放。

Docker 容器

最后一步是根据您创建的 Docker 镜像(或从镜像仓库拉取的镜像)运行容器。要运行容器,请使用以下命令:

$ docker run packt_demo_image

要验证容器正在运行,请使用以下命令:

$ docker container ls

上述命令将只显示正在运行的容器。要显示主机机器上的其他容器,请将-a选项添加到命令的末尾。

您也可以使用上述命令的旧版本来验证容器正在运行:

$ docker ps

下图显示了所有四个 Docker 组件之间的关系,并总结了运行容器的整个过程。首先,我们创建一个 Dockerfile。然后,我们使用该文件创建 Docker 镜像。Docker 镜像可以用来在本地创建 Docker 容器(s),或者首先上传到私有或公共仓库,其他人可以从那里下载并创建他们的 Docker 容器(s):

图 9.5:Docker 组件

图 9.5:Docker 组件

Docker 容器有自己的生命周期——它们可以运行特定的任务,而不考虑它们之前的状态,一旦特定任务完成,Docker 容器就会自动终止。

在其他情况下,容器需要知道它们之前的状态。如果是这样,它们需要持久化以在终止后保留容器数据。这时,Docker 卷就变得非常有用。接下来,我们将讨论什么是 Docker 卷以及如何创建它。

Docker 卷

Docker 卷是 Docker 容器可以附加的一种存储形式。容器通过附加卷来读取和写入持久数据,这对于容器的功能是必要的。

为了更详细地说明,考虑 Customer Management 微服务(customer_management)的 Docker 容器。如果您需要在 customer_management 容器中创建新客户,您需要更新该容器中安装的本地数据存储。如果容器不是持久的,一旦容器终止,容器内部创建或更改的所有数据都将丢失。

为了避免这个问题,我们需要创建一个 Docker 卷并将容器附加到该卷。然后容器本身可以运行并更新其卷中需要更新的任何数据,然后终止。当它下次启动时,它会以最后一次终止前的所有状态和数据实例化。

要为 customer_management 容器创建 Docker 卷,例如,请使用以下命令:

$ docker volume create customer_management_volume

以下命令将列出我们主机机器上创建的所有卷并验证我们刚刚创建的卷:

$ docker volume ls

一旦我们创建了卷,Docker 将在主机机器上挂载一个本地驱动器空间以保留容器的数据及其挂载的文件系统。

要显示有关卷的更多详细信息,包括卷的名称、本地主机和容器的目标挂载位置,以及卷的创建日期和时间,请使用 docker volume inspectdocker inspect 命令,如下所示:

$ docker volume inspect customer_management_volume
[
    {
        "CreatedAt": "2022-10-14T22:24:46Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/customer_management_volume/_data",
        "Name": "customer_management_volume",
        "Options": {},
        "Scope": "local"
    }
]

假设我们之前创建了 packt_demo_image 镜像,要创建持久的 customer_management 容器,我们需要使用 docker volume inspect 命令的输出中显示的挂载点将容器附加到我们刚刚创建的卷。以下命令将创建容器,将卷附加到容器,然后运行容器:

$ docker run -itd --mount source=customer_management_volume,target=/app_data --name customer_management_container packt_demo_image

docker run 命令中的 it 选项用于交互式 tty 模式,而 d 选项用于在后台运行容器。

/app_data 是容器内的一个绝对路径,挂载到本地主机的挂载点。从前面的检查数据中可以看出,/var/lib/docker/volumes/customer_management_volume/_data 挂载点映射到容器中的 /app_data

要验证容器正在运行,请使用以下命令:

$ docker container ls

如果容器因任何原因终止,请在上一条命令的末尾使用 -a 选项来显示主机上可用的容器。您可以使用 docker container startdocker container stop 命令,后跟容器名称,来运行或终止在主机上构建的任何可用容器。

现在我们已经安装了 Docker 引擎并了解了 Docker 的不同组件,我们将介绍如何创建主要的 ABC-MSA 容器作为微服务,并提供一个示例说明这些微服务如何相互通信。

创建 ABC-MSA 容器

在我们的 ABC-MSA 系统中,我们采用每个微服务一个容器的策略。因此,我们需要确定我们将构建的主要容器,我们 ABC-MSA 系统中每个容器所需的组件,然后构建必要的 Dockerfile(s)来使用。

我们使用Flask构建我们的微服务应用程序。Flask 是一个Web 服务器网关接口WSGI)微框架,它使应用程序能够以简单、灵活和可扩展的方式响应 API 调用。我们不会在本书中讨论我们的应用程序代码,但代码可在我们的 GitHub 上找到,并提供详细的文档供您参考。

在本节中,我们将解释我们如何构建 ABC-MSA 的 Dockerfile(s)、镜像和微服务,我们将如何开始在容器中监听 API 调用,以及系统微服务如何相互通信。

为了演示目的,我们将在容器中使用 HTTP/8080 端口来监听 HTTP API 请求。生产环境应使用 HTTPS/443,并考虑使用tomcat服务器来处理所有 Web 连接。

以下只是完整系统容器设置的其中一部分。所有 ABC-MSA 系统创建的文件和 Docker 镜像都可以在我们的 GitHub 仓库github.com/PacktPublishing/Machine-Learning-in-Microservices中找到。

ABC-MSA 容器

以下是之前为我们的 ABC-MSA 系统识别出的服务:

  1. API 网关

  2. 前端 Web 仪表板界面

  3. 客户管理

  4. 产品管理

  5. 订单管理

  6. 库存管理

  7. 快递管理

  8. 发货管理

  9. 付款授权

  10. 通知管理

  11. 聚合器: “已订购产品数量”

  12. 管理和编排

我们可以从头开始编写和构建这些服务中的每一个,但好消息是,我们不必这样做。Docker Hub 提供了一个丰富的库,其中包含许多我们可以利用来构建微服务的 Docker 镜像。Docker Hub 可以通过hub.docker.com/访问。

我们不会逐一介绍这些服务。相反,我们将关注那些提供不同开发和部署方法的服务。其中一些服务已经可以通过 Docker Hub 获得,还有一些其他的服务类似,所以举一个这些服务的例子就足够了。尽管如此,所有项目文件都将在这个书籍的 GitHub 仓库中提供。

API 网关

许多开源和商业 API 网关可以从不同的互联网仓库中获取,包括 Tyk、API Umbrella、WSO2、Apiman、Kong 和 Fusio 等,仅举几例。我们将在 ABC-MSA 系统中使用 Tyk,因为它易于使用,具有包括身份验证和服务发现在内的全面功能,并且是完全开源的产品,没有任何功能限制。

要安装 Tyk Docker 容器,只需遵循tyk.io/docs/tyk-oss/ce-docker/中的说明。

默认情况下,Tyk API 网关监听 TCP 端口 8080。要验证您的安装,请使用 curl 命令向 Tyk 发出 API 调试请求,如下所示:

$ curl localhost:8080/hello
{"status":"pass","version":"4.1.0","description":"Tyk GW"}

如果 Tyk 已成功安装并在您的主机上运行,您应该会得到一个字典输出,显示 Tyk 的状态和当前版本,如前一个命令输出所示。

您还可以使用以下命令验证 Tyk Docker 镜像和容器是否已成功创建:

$ docker images
REPOSITORY                              TAG            IMAGE ID       CREATED         SIZE
redis                                   6.2.7-alpine   48822f443672   3 days ago      25.5MB
docker.tyk.io/tyk-gateway/tyk-gateway   v4.1.0         0c21a95236de   8 weeks ago     341MB
hello-world                             latest         feb5d9fea6a5   12 months ago   13.3kB
$
$ docker container ls
CONTAINER ID   IMAGE                                          COMMAND                   CREATED          STATUS          PORTS                                        NAMES
ac3ac1802647   docker.tyk.io/tyk-gateway/tyk-gateway:v4.1.0    "/opt/tyk-gateway/ty…"   54 minutes ago   Up 54 minutes    0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   tyk-gateway-docker_tyk-gateway_1
9e0f1ecfb148   redis:6.2.7-alpine                             "docker-entrypoint.s…"   54 minutes ago   Up 54 minutes   0.0.0.0:6379->6379/tcp, :::6379->6379/tcp   tyk-gateway-docker_tyk-redis_1

我们可以在前一个命令输出中看到 tyk 镜像的详细信息,以及正在运行的容器和它监听哪个端口。我们还可以看到一个 Redis 镜像和容器。这是因为 Redis 是 Tyk 的先决条件,并且包含在 Tyk 安装包中。

以客户管理微服务为例

在构建和部署容器方面,客户管理、产品管理、订单管理、库存管理、快递管理、发货管理、支付授权和通知管理微服务在本质上都是相似的。在本节中,我们将学习如何创建一个可以用来创建系统微服务的镜像。我们选择了客户管理微服务作为示例。

如前所述,为了使这些微服务能够与 API 网关或 ABC-MSA 系统中的任何其他组件进行通信,我们需要安装并运行 Flask,监听运行容器中的 HTTP/8080 端口。

我们还需要一个内部数据存储库,以便我们的应用程序使用和管理。由于我们的代码将用 Python 编写,因此我们还需要安装 Python。所有这些必需的组件以及一些基本依赖包都需要在我们的 Dockerfile 中指定。

现在,我们需要编写创建微服务镜像所需的 Dockerfile,我们将使用该镜像创建微服务容器。每个 ABC-MSA 容器都应该有自己的开发周期,并可以使用我们讨论过的 CI/CD 循环部署,或者手动上传到团队仓库。

以下是为创建客户管理镜像所需的 Dockerfile 的示例:

# Docker File for "customer_management" microservice
FROM ubuntu
# Install some dependencies/packages
RUN apt-get install -y apt-transport-https
RUN apt-get update
RUN apt-get install -y net-tools mysql-server python3 pip git build-essential curl wget vim software-properties-common;
# Install OpenJDK
RUN apt-get update && \
    apt-get install -y default-jdk
ENV JAVA_HOME /usr/lib/jvm/java-11-openjdk-amd64/
# Install Flask to run our application and respond to API calls
RUN pip install -U flask
# Expose port TCP/8080 to listen the container's application/flask API calls
EXPOSE 8080
# Create the /app_data directory and make it the working directory in the container
RUN mkdir /app_data
WORKDIR /app_data
ENV PATH $PATH:/app_data
# Download the microservice app code from GitHub repo
ENV GIT_DISCOVERY_ACROSS_FILESYSTEM 1
RUN git config --global init.defaultBranch main
RUN git init
RUN git remote add origin https://github.com/mohameosam/abc_msa.git
RUN git config core.sparseCheckout true
RUN echo "/microservices/customer_management/" > /app_data/.git/info/sparse-checkout
RUN git pull origin main
# Initialize the flask app
ENV FLASK_APP /app_data/microservices/customer_management/customer_management_ms.py
# Specify a mount point in the container
VOLUME /app_data
# Start mysql & flask services and available bash sheel
RUN chmod +x /app_data/microservices/customer_management/start_services
CMD /app_data/microservices/customer_management/start_services && bash

上述 Dockerfile 指定了客户管理 Docker 镜像应该是什么样子。以下是对文件中每一行将执行的操作的一些见解:

  1. 指定 Ubuntu 作为将在客户管理容器中使用的 Linux 操作系统。

  2. 安装一些必需的软件包:

    • MySQL(我们的应用程序所需的)

    • Python(我们的应用程序所需的)

    • pip(用于安装 Flask)

    • 其余的是一些用于故障排除的其他工具(可选)。

  3. 安装 Flask(我们的应用程序所需的)。

  4. 为 Flask 暴露 TCP/HTTP 端口 8080 以监听 API 调用。

  5. 在容器中创建一个工作目录,作为保存容器数据的挂载点。

  6. 从我们的 GitHub 仓库下载客户管理应用程序代码。

  7. 设置一个环境变量,让 Flask 知道在响应 API 调用时将使用哪个应用程序。

  8. 使用我们下载的start_servicesshell 脚本在容器中启动 Flask 和 MySQL。

start_servicesshell 脚本包含以下命令:

flask run -h 0.0.0.0 -p 8080 &
usermod -d /var/lib/mysql/ mysql
service mysql start

第一行使 Flask 能够监听所有主机网络接口上的端口8080。这在开发和测试环境中是可以的。然而,在生产环境中,Flask 应该只可在 localhost 127.0.0.1网络接口上可用,以限制 API 访问到本地环境。此外,为了更好的安全性,API 调用应使用 HTTPS/443 端口。

假设 Dockerfile 已放置在当前用户主目录中,我们现在需要从 Dockerfile 创建我们的客户管理微服务/容器:

$ docker build -t abc_msa_customer_management ~/

Docker 将花费几分钟时间来完成镜像的创建。一旦所有 Dockerfile 步骤都已完成,你应该会在docker build命令输出的最后一行看到以下命令:

Successfully tagged abc_msa_customer_management:latest

这表示操作已成功完成。现在,我们可以使用docker image ls命令来验证abc_msa_customer_management镜像是否已成功创建。

最后一步是创建容器。由于应用程序将配置和更新 MySQL 数据库,我们需要创建一个持久容器以保留所有更改。

与我们之前解释的类似,我们将使用docker run命令来创建客户管理容器,如下所示:

$ docker run -itd -p 8003:8080 --mount source=customer_management_volume,target=/app_data --name customer_management_container abc_msa_customer_management

p选项用于“发布”并将容器监听的端口与主机机器监听的端口进行映射。因此,主机机器将监听端口8003以处理容器的 HTTP/8080 请求。

我们选择了 8003 来标准化主机监听容器 API 调用请求的方式。

请记住,每个容器都有一个与主机 TCP 栈不同的 TCP 栈。因此,TCP HTTP/8080 端口仅在容器内部是本地的,但在特定容器的环境之外,该 TCP HTTP/8080 端口与任何其他容器或主机机器上可用的 TCP HTTP/8080 端口不同。

要从customer_management容器外部访问该端口,您需要将customer_management容器的 TCP HTTP/8080 端口映射到主机机器上的一个特定端口。

由于我们需要将之前识别出的 12 个容器中的每个容器的本地 TCP HTTP/8080 端口进行映射,我们决定遵循一个特定的模式。将主机机器上的 TCP/80nn 端口映射到每个容器的本地 TCP HTTP/8080 端口。在这里,nn 是容器的编号。

图 9**.6展示了 ABC-MSA 容器的一些 TCP HTTP/8080 端口如何在主机机器上进行映射。

我们不必在单个主机上运行所有容器。系统容器可以根据许多因素分散在不同的主机上,例如容器上运行的服务/应用程序的重要性、系统的设计、所需的总体冗余度等:

图 9.6:容器本地端口映射到主机机器的 TCP 栈

图 9.6:容器本地端口映射到主机机器的 TCP 栈

现在,使用以下命令验证容器是否正在运行:

$ docker container ls

以下命令将允许您使用 root 权限(命令中指定的用户 ID 为 0)连接到容器的 bash shell:

$ docker exec -u 0 -it customer_management_container bash

对于客户管理微服务,这就是全部内容。以同样的方式,我们可以创建 ABC-MSA 的其余容器。我们只需确保为其他微服务的容器和卷使用适当的对应名称,并将它们映射到主机机器上的正确 TCP/80nn 端口号。

前端 Web 仪表板界面

仪表板是用户界面(UI)交互的主要组件,并与提供给用户的所有服务进行交互。在我们的 ABC-MSA 示例中,我们创建了一个简单的购物车应用程序,用户可以在购物车中放置产品并下订单。

仪表板容器的构建方式与 customer_management 容器相同,如前节所示。两者之间的主要区别是我们需要在仪表板微服务上添加额外的 Web 服务器,以及容器上需要暴露的端口。仪表板的 Dockerfile 应相应地进行更改。

就像我们构建的所有容器一样,该容器的本地 TCP 端口用于监听 API 调用是 TCP HTTP/8080,而在 dashboard 容器的情况下,主机映射的 TCP 端口应该是 TCP/8002。

仪表板容器仍然需要监听 HTTP/80 以处理用户 Web UI 请求。除非主机机器在 HTTP/80 端口上运行另一个应用程序或网页,否则我们应该可以使用该端口。

现在,我们需要将主机机器上的 HTTP/80 端口映射,如下面的 docker run 命令所示:

$ docker run -itd -p 8002:8080 -p 80:80 \
--mount source=dashboard_volume,target=/app_data \
--name dashboard_container abc_msa_dashboard

此命令具有一个额外的 p 选项,用于将容器上的 HTTP/80 端口映射到主机机器上的 HTTP/80 端口。abc_msa_dashboard 是仪表板微服务镜像。

管理您的系统容器

如您在先前的示例中所见,docker run 命令可能会变得冗长且混乱。Docker Compose 帮助我们管理容器的部署。使用 Docker Compose,管理容器的部署、更改部署参数、将所有系统容器包含在一个 YAML 文件中,以及指定容器的部署顺序和依赖关系都变得容易得多。

以下是一个示例 YAML 文件,用于初始化三个 ABC-MSA 容器,就像我们之前使用 docker run 命令所做的那样,但以更组织化和结构化的 YAML 方式:

# Docker Compose File abc_msa.yaml
version: "3.9"
services:
  customer_management_container:
    image: abc_msa_customer_management
    ports:
      - "8003:8080"
    volumes:
      - customer_management_volume:/app_data
  product_management_container:
    image: abc_msa_product_management
    ports:
      - "8004:8080"
    volumes:
      - product_management_volume:/app_data
  dashboard:
    image: abc_msa_dashboard
    ports:
      - "8002:8080"
      - "80:80"
    volumes:
      - dashboard_volume:/app_data
    depends_on:
      - customer_management_container
      - product_management_container
volumes:
  customer_management_volume:
  product_management_volume:
  dashboard_volume:

以下命令运行 Docker Compose .yaml 文件:

$ docker-compose -f abc_msa.yaml up &

f 选项用于指定 YAML 文件的名称,而 & 选项用于在 shell 的后台运行容器。

在本节中,我们向您展示了如何创建一些 ABC-MSA 图像和容器。ABC-MSA 容器现在可以直接相互通信,或者,正如我们将在本书后面展示的,通过 API 网关进行通信。

在下一节中,我们将学习如何使用我们创建的容器,如何向它们发出 API 调用,以及我们应该期待什么响应。

ABC-MSA 微服务间通信

在本节中,我们将学习如何从容器中暴露 API 以及容器如何与 API 消费者进行通信。

每个容器的微服务应用程序代码都可在 GitHub 上的 ABC-MSA 项目中找到。我们建议您将代码下载到本地测试环境中,以便在遵循本节中将要介绍的步骤时获得一些实践经验。

容器之间相互通信主要有两种方式。一种是通过在 Docker 网络 中使用容器的名称,另一种是通过使用容器的 IP 地址和 TCP 端口。以下是一些您需要了解的细节,以便能够配置您的容器以相互通信。

Docker 网络

当我们在同一主机上运行容器时,容器可以使用容器名称在同一主机上进行通信,而无需指定容器的 IP 地址或监听端口。

仅使用容器名称的概念在程序上非常有用,尤其是在这些 IP 地址动态变化的情况下。名称通常是确定的,并且仅通过指定 Docker 的容器名称,您就可以避免在开始与目标容器通信之前,必须应用不同层级的系统操作来了解容器的 TCP/IP 详细信息。

然而,要仅通过名称启用容器通信,有一些先决条件:

  • 与其他容器进行通信的容器都需要在同一主机上。

  • 我们需要在主机上创建一个 Docker 网络。

  • 当使用 docker run 命令运行容器或通过在 docker-compose YAML 文件中指定容器的实例化详细信息时,我们需要将容器附加到创建的 Docker 网络上。

以下命令在主机机器上创建一个 Docker 网络,可用于我们的 ABC-MSA 系统的微服务间通信:

$ docker network create abc_msa_network

以下命令列出了主机机器上配置的 Docker 网络:

$ docker network ls

现在,使用 docker run 命令中的 --network 选项将 ABC-MSA 容器附加到 abc_msa_network 网络,如下例所示:

$ docker run -itd -p 8003:8080 \
--network abc_msa_network --mount \
source=customer_management_volume,target=/app_data \
--name customer_management_container abc_msa_customer_management

在许多情况下,使用 Docker 网络非常有用。然而,由于我们正在设计 ABC-MSA 系统,使得容器可以独立于其主机位置运行,我们将使用容器的 IP/TCP 通信。

在下一节中,我们将解释 ABC-MSA 微服务如何使用 TCP/IP 进行通信,并介绍一些测试通信和数据交换的示例。

容器/微服务之间的 TCP/IP 通信

到目前为止,我们已经安装了 Docker,构建了我们的 Docker 镜像和卷,并启动了所有微服务的容器。现在,是时候了解这些容器如何相互交互了。

在容器的主机上运行 Docker 后,主机会自动创建一个虚拟 IP 网络并为该主机上运行的每个 Docker 容器分配一个 IP 地址。这个虚拟 IP 网络仅限于运行容器的宿主机内部,并且无法从宿主机外部访问。

容器的主机至少携带两个 IP 地址。有一个内部 IP 地址是 Docker 网络内部的,并且只能在 Docker 网络内部识别。然后,还有一个外部 IP 地址,通常由组织网络中的 动态主机配置协议DHCP)服务器分配。外部 IP 地址对于容器主机与外部世界的通信是必要的。

容器主机的内部 Docker 网络对网络中的任何其他主机都是不可见的。因此,对于外部主机要与容器主机的特定容器通信,它需要使用容器主机的外部 IP 地址。

在众多其他信息中,要获取分配的 IP 地址,以及系统中特定容器的内部和外部监听端口,请使用 docker inspect 命令,后跟容器的名称。

我们的下图展示了我们的演示设置:

图 9.7:ABC-MSA 容器通信

图 9.7:ABC-MSA 容器通信

如您所见,主机机的内部 IP 地址是 172.17.0.100,外部 IP 地址是 192.168.1.100。容器的主机正在监听容器映射的端口(80018012),如前所述。

如果网络中的其他主机想要向 ABC-MSA 容器之一发送 API 调用,那么外部主机需要将请求发送到容器主机的外部 IP 地址,即 192.168.1.100,使用它想要通信的容器的映射端口。

为了进一步说明,前面的图和下面的示例展示了外部主机测试产品管理容器 API 响应的情况:

$ curl http://192.168.1.100:8004/
<!DOCTYPE html>
<head>
   <title>PRODUCT MANAGEMENT Microservice</title>
</head>
<body>
   <h3>Product Management Microservice Part of ABC-MSA System</h3>
</body>

ABC-MSA API 调用返回一个 JSON 变量,以便更容易地处理数据。我们为 ABC-MSA 微服务构建的一个 API 是 service_infoservice_info API 调用的一个示例如下:

$ curl http://192.168.1.100:8004/api?func=service_info
{"service_name": "product_management", "service_descr": "ABC-MSA Product Management"}

如果你是在 Docker 网络(172.17.0.0)内部进行内部通信,你可以直接与容器的 IP 和监听端口进行通信。从 API 网关 shell 对产品管理容器执行相同的curl测试将看起来像这样:

$ curl http://172.17.0.4:8080/api?func=service_info
{"service_name": "product_management", "service_descr": "ABC-MSA Product Management"}

了解如何发送 API 请求和处理 API 响应对于开发你的 MSA 系统至关重要。请参考本书 GitHub 仓库中的 ABC-MSA 代码,以了解 API 调用在整个系统中是如何发出和处理的示例。

摘要

在本章中,我们介绍了容器这一概念,它们是什么,以及它们与虚拟机有何不同。然后,我们使用 Docker 作为目前最受欢迎的容器平台之一进行了操作。我们向您展示了如何安装 Docker 和创建 Dockerfile、Docker 镜像、Docker 卷和 Docker 容器。

然后,我们通过构建一些 ABC-MSA 微服务并使用实际示例应用了所有这些概念。我们构建了容器,并展示了微服务之间是如何相互通信的。

在下一章中,我们将专注于在 MSA 系统中构建 AI 微服务。我们将讨论一些在 MSA 系统中应考虑和实施的最重要的人工智能/机器学习/深度学习算法,以及这些算法如何帮助系统的整体稳定性、性能和支持性。

第十章:构建智能 MSA 企业系统

在前面的章节中,我们逐步构建 ABC-MSA 来展示 MSA 系统的一些功能、技术和流量模式。

在本章中,我们将结合 MSA 概念和 AI 概念来构建 ABC-Intelligent-MSA 系统,这是 ABC-MSA 的增强版本。ABC-MSA 的智能版本将使用各种 AI 算法来增强原始 ABC-MSA 系统的性能和一般操作。

ABC-Intelligent-MSA 将能够检查不同的流量模式,并检测潜在的问题,然后自我纠正或自我调整,以尝试在问题实际发生之前防止其发生。

ABC-Intelligent-MSA 将能够自我学习流量行为、API 调用和响应模式,并在检测到任何原因的流量异常或问题模式时尝试自我修复

本章涵盖了以下主题:

  • 机器学习的优势

  • 构建你的第一个 AI 微服务

  • 智能 MSA 系统在实际中的应用

  • 分析 AI 服务操作

机器学习的优势

在我们的 MSA 中,有许多领域可以利用 AI 来增强系统的可靠性和可操作性。我们将重点关注两个主要的潜在增强领域。一个是增强系统对微服务故障或性能下降的响应。第二个增强领域是添加一个主动断路器角色。

如我们在第三章中讨论的,断路器模式用于防止系统在其中一个系统微服务未能及时响应 API 消费者请求时发生级联故障。如果一个微服务失败或表现不佳,我们的 AI 将尝试采取主动行动来解决问题,而不是等待手动修复问题以使系统恢复正常运行。

第七章中,我们详细讨论了在 MSA 中使用机器学习ML)和深度学习(DL)的优势。本章将专注于构建两个 AI 微服务来增强我们的 MSA 系统。

第一个 AI 微服务被称为性能基线看门狗PBW)服务。PBW 是一个机器学习微服务,它为 MSA 系统在特定系统负载下每个微服务的预期性能创建基线。如果测量的微服务的运行性能低于性能基线,并且低于可配置值x,系统应向运营支持系统OSS)或网络管理系统NMS)发送警告消息;如果性能下降y(这也是可配置的),系统应采取预定义的行动来尝试自我纠正和自我修复 MSA 系统。

本章我们将构建的第二个 AI 微服务是性能异常检测器PAD)服务。PAD 是一个 ML 微服务,它对整个 MSA 系统有一个全面的视角。PAD 学习 MSA 的性能模式,并试图检测任何异常行为。它识别“问题模式”,试图在问题发生之前自动检测到问题,并相应地采取主动措施来修复系统的故障区域。

构建你的第一个 AI 微服务

在我们开始构建我们的两个 AI 微服务之前,我们需要首先考虑我们的训练和测试数据——我们将如何收集训练数据,相应地构建模型,测试模型并衡量其可靠性,以及在需要时增强算法的可靠性。

重要提示

我们在 MSA 系统中构建的 AI 服务只是为了证明概念,以展示在 MSA 系统中实施 AI 的价值。相反,企业应考虑一个与他们的独特需求、业务流程以及部署的 MSA 系统相匹配的 AI 服务或模型。

我们还需要模拟使用案例本身。模拟系统微服务的故障或性能下降,模拟级联故障,我们还应该能够模拟一些系统的异常模式,以查看算法如何检测和响应模式异常。

要完成所有这些,让我们首先了解 PBW 和 PAD 微服务如何与整体系统操作相匹配,以及它们通常如何与不同的系统组件交互。

AI 增强的解剖结构

PBW 和 PAD 的主要作用是增强我们 MSA 系统的稳定性和可靠性。因此,对于这两个服务来说,持续监控单个微服务和整体系统性能,并在检测到性能问题时采取必要的行动是至关重要的。

训练数据首先在一个受控环境中收集,用于特定的训练期间,其中模拟了正常、稳定的系统操作和平均用户负载。这可以通过我们构建的一些模拟工具实现,这些工具将在本节稍后讨论。

这个训练期间创建了一个理想的第一基线,这将成为 AI 服务在实际生产时间使用时的主要参考。收集到的训练数据将被用于构建算法。为了获得更好、更准确的结果,可以在收集到更多关于实时生产流量的信息后,定期调整训练数据和算法。

模拟负载和系统操作通过多个模拟参数进行调整。这些参数会定期调整,以模拟实际可接受的运行性能。随着 AI 算法的成熟,算法调整最终会停止(或变得非常微小)。将 AI 服务纳入 ABC-MSA 系统的周期在图 10.1中展示:

图 10.1:ABC-Intelligent-MSA 中的 AI 微服务实现

图 10.1:ABC-Intelligent-MSA 中的 AI 微服务实现

关于模拟工具和参数的更多信息将在下一节中提供。

一旦 AI 服务运行,它们将通过周期性 API 调用从系统中的每个微服务收集性能统计数据,然后比较这些性能统计数据与预期的性能或行为。

如果单个微服务或整体系统性能偏离 AI 期望看到的结果,系统将触发操作,要么警告系统管理员,要么尽可能进行自我修复。图 10.2显示了 PBW 和 PAD 服务的高级架构:

图 10.2:ABC-Intelligent-MSA 中的 PBW 和 PAD 服务

图 10.2:ABC-Intelligent-MSA 中的 PBW 和 PAD 服务

PBW 的算法根据收集的性能统计数据计算预期的性能指标。收集的性能统计数据包括 API 调用响应时间统计、单个微服务的失败或失败率、API 响应代码以及施加在微服务本身的负载。

根据微服务偏离计算性能指标的程度,将触发预定义的操作。根据 PBW 的配置,偏差越大,主动触发尝试自我修复的可能性就越大。然而,在轻微偏差的情况下,不应触发任何修复操作;系统警告通知系统管理员就足够了。

下表显示了在 ABC-Intelligent-MSA 系统操作过程中可能遇到的一些系统问题,以及 PBW 服务将采取哪些操作来尝试解决问题。

表格中显示的列表仅是潜在问题的样本,当然,随着更多用例的考虑,它也可以增长:

微服务问题 触发操作
响应缓慢 垂直/水平扩展微服务或重启微服务
间歇性超时 垂直/水平扩展或重启
API 调用 HTTP 响应错误 检查 Apache、Flask、JVM、Docker 卷、SQL 服务等。如有需要,重启服务重启微服务的容器
服务无响应(关闭) 重启微服务的容器

表 10.1 – ABC-Intelligent-MSA 操作中可能遇到的问题和 PBW 的自我修复操作

恢复机制可以通过使用多个 AI 服务应用于 MSA 系统,而不仅仅是使用我们在 ABC-Intelligent-MSA 中实施的 PBW 和 PAD。这只是一个例子。

自我修复过程

表 10.1中列出的所有 PBW 的修复操作不应孤立于 PAD 的操作,而应与 PAD 的修复操作仔细协调。一个微服务中的单个问题可能会(尽管不一定)同时触发 PBW 和 PAD 服务的操作,从而可能造成操作冲突。

在自我修复过程中,为了避免在触发自我修复操作时系统 AI 服务之间发生冲突,每当确定一个操作并在触发之前,AI 服务首先向其他 AI 服务发送 API 调用(无论是直接还是通过 API 网关),在受影响的微服务中声明自我修复锁定状态。因此,MSA 系统中的所有其他 AI 服务将推迟任何与该受影响微服务相关的计划中的操作。

在自我修复锁定状态下,唯一允许在受影响的微服务上工作的 AI 服务是修复 AI 服务,正是这个 AI 服务将其锁定。

一旦修复者修复了问题并检测到受影响的微服务中的正常操作,修复者随后向 MSA 系统中的其他 AI 服务发送另一个 API 调用,声明锁定状态结束。

如果修复者无法自我修复并放弃解决问题,它将向 NMS/OSS 发送警报,并将该微服务标记为无法修复,持续特定可配置的时间,称为无法修复的等待期(默认为 15 分钟)。

无法修复的等待期允许其他 AI 服务尝试修复该微服务,并给修复者一个喘息的机会,以调整其在 MSA 系统中的所有其他微服务上的操作。

为了防止修复者通过无限期的修复尝试消耗系统资源,修复者将尝试修复受影响的微服务特定数量的修复尝试,通过最大修复尝试次数值(默认为四次)进行配置,然后完全放弃尝试。如果最大修复尝试次数耗尽,则需要手动系统干预来修复受影响的微服务。

如果需要,系统管理员仍然可以配置无限期的修复尝试,但这可能会消耗系统资源,并且根据 MSA 系统或特定微服务遇到的问题的性质,可能不会有效。

如果另一个 AI 服务可以修复受影响的微服务或微服务被手动修复,在无法修复的等待期结束后,原始修复者将自动清除微服务的无法修复标志。

如果另一方面,没有其他 AI 服务可以修复问题,并且没有手动干预来修复微服务,那么当无法修复的等待期结束后,原始的治愈者——以及任何可能尝试修复微服务的其他治愈者——将再次尝试修复微服务,前提是有问题的微服务不在自愈锁定状态。

以下视觉图表总结了自愈过程,可能有助于更好地解释整个过程。

图 10.3:MSA 中的自愈过程

图 10.3:MSA 中的自愈过程

理解用于解释自愈过程的主要术语也很重要。以下表格展示了我们自愈过程主要组件术语的总结:

术语 描述
治愈者 一种尝试修复有问题的微服务的 AI 服务。
治愈动作 治愈者采取的行动,以尝试修复正在进行的系统操作问题。
自愈锁定状态 当治愈者尝试修复微服务时的微服务状态。在此状态下,只有一位治愈者(启动锁定状态的治愈者)被允许对有问题的微服务进行操作。微服务自愈锁定状态是整个 MSA 系统可见的状态,而不是特定于治愈者的状态。
重试等待期 治愈者在治愈动作失败后必须等待的时间,然后它才会重试。默认情况下,重试等待期为 2 分钟。
无法修复的状态 治愈者在其尝试修复有问题的微服务失败后,将该微服务标记为无法修复的状态。微服务的无法修复状态是特定于治愈者的状态,并且只有放弃修复该有问题的微服务的治愈者可见。其他治愈者仍然可以尝试修复有问题的微服务。
无法修复的等待期 治愈者必须等待的时间,然后它才会开始尝试修复有问题的微服务。默认情况下,无法修复的等待期为 15 分钟。
最大治愈尝试次数 治愈者在完全放弃有问题的微服务并停止尝试修复它之前,在每个无法修复的等待期后将会尝试的最大次数。默认情况下,PBW 尝试 4 次治愈尝试。

表 10.2:ABC-Intelligent-MSA 的操作问题与 PBW 的自纠正动作

到目前为止,我们已经解释了在 MSA 系统中部署 AI 服务的价值,并展示了几个实际应用示例来证明 AI 在 MSA 中的价值。

为了在 MSA 中构建、运行和调整 AI 服务,我们需要构建某些工具来收集和记录系统状态、操作动态和操作统计。在下一节中,我们将深入了解这些工具是什么以及如何使用它们。

构建必要的工具

创建项目工具的目的是首先能够构建 AI 模型,然后模拟整个 ABC-Intelligent-MSA 系统,然后收集统计数据并分析系统的操作。

虽然可能有一些在线工具可以帮助我们实现我们的目的,但我们将构建针对我们特定用例定制的简单工具。

我们创建了多个工具来帮助我们收集训练和测试数据,模拟系统和微服务负载,以及衡量微服务的性能。所有工具都可在我们的 GitHub 仓库中的 tools 目录中找到。

工具还帮助我们清理一些生成的日志和数据,以便进行分析和潜在的将来改进。

以下是我们 ABC-Intelligent-MSA 设置中需要的主要工具。

API 流量模拟器

API 流量生成器/模拟器 simulate_api_rqsts.py 帮助模拟系统的一个或多个微服务的 API 请求负载。

simulate_api_rqsts 在多个目标微服务之间创建多线程 API 请求。然后并行将 API HTTP 请求发送到每个微服务。

API 负载是通过每分钟的请求数来衡量的,API 请求可以是均匀的或随机的。

均匀速率的请求被安排得使得每个 API 调用之间的时间总是相同的,因此如果我们配置了 600 API 请求/分钟的均匀速率负载,simulate_api_rqsts 将每 T = 100 毫秒发送 1 个 API 调用。

在随机速率的情况下,每个 API 调用都是在从上一个调用发送的时间开始的随机时间段 TR 后发送的,但 TR 不能大于或小于 T 的 95%。因此,如果我们配置了 600 API 请求/分钟的随机速率负载,在这种情况下,TR 将等于一个大于 5 毫秒且小于 195 毫秒的值。

simulate_api_rqsts 将每:

(1-95%)T <= TR <= (1+95%)T(即对于 600 请求/分钟:5 毫秒 <= TR <= 195 毫秒

然而,所有 TR 的总和仍然大约等于配置的请求/分钟。在我们的例子中,负载是 600 API 请求/分钟。

当你手动分析特定微服务如何响应 API 负载时,均匀速率的请求更好,而随机速率的请求则更好地代表了实时生产 API 请求负载。

微服务性能监控器

微服务性能监控器 ms_perfmon.py 是另一个多线程工具,最初用于在理想条件下的模拟期间收集和构建 AI 训练数据。

ms_perfmon 向系统中的每个微服务发送并行 API 调用,然后记录 API 调用超链接、发送的日期和时间、接收微服务的响应时间和 HTTP 响应代码。以下是一个以逗号分隔的格式收集数据的示例日志条目:

http://payment_ms:8080,2022-12-28 15:48:57.271370, 0.010991334915161133,200

每个微服务的统计数据都收集在其 ms_perfmon 工作路径下的 perfmon_stats 目录中。

在实时操作中,PBW 和 PAD 都执行与ms_perfmon类似的工作。它们收集自己的统计数据,并将目标微服务的实时性能与基线和预期正常行为进行比较。

我们是否应该通过包括更多用于不同目的和用例的 AI 服务来扩展 MSA 系统的 AI 能力,这可能会要求每个 AI 服务进行自己的性能统计收集?

根据收集频率和数据收集类型,随着收集器的数量增加,可扩展性可能成为一个问题。在这种情况下,ms_perfmon函数可以扩展成为 MSA 系统中所有 AI 或非 AI 服务的主要 AI 收集器。这种设置可以帮助减轻系统的微服务负担,并允许 MSA 系统更好地扩展。

图 10.4:ABC-Intelligent-MSA 中的单次收集性能统计数据设置

图 10.4:ABC-Intelligent-MSA 中的单次收集性能统计数据设置

图 10.4显示了ms_perfmon如何代表 MSA 系统中的所有其他服务处理统计数据收集,然后作为代理并对请求特定 AI(或非 AI)服务所需任何统计数据的 API 调用做出响应。

响应延迟模拟器

为了模拟延迟响应或故障微服务,并且仅限于模拟和测试目的,我们在关键微服务中添加了一个功能,以模拟延迟的 API 调用响应。

当在微服务中启用延迟响应功能时,有两个可配置的值——最小延迟和最大延迟。当微服务接收到 API 调用时,它将自动分配一个介于配置的最小延迟和最大延迟之间的随机延迟值,然后等待这段时间后再对消费者的 API 调用做出响应。

此功能对于模拟级联系统故障非常有帮助。正如本章后面将要展示的,响应延迟功能还可以帮助展示使用 AI 服务增强 MSA 系统操作的价值,与之前解释的短路流量模式相比。

当最大延迟配置的值大于零时,响应延迟功能被启用。当最大延迟的值高于零时,会将一个延迟值分配给微服务 API 的调用响应,如下面的代码片段所示:

#Simulate a delay if received an API to do so
if delay_max_sec > 0:
  delay_seconds = round(delay_min_sec + random.random()*(delay_max_sec-delay_min_sec), 3)
  #print("Adding a delay %s ..." %delay_seconds)
  time.sleep(delay_seconds)

可以使用 API 调用配置最大和最小延迟值。以下是一个使用curl发送 API 调用以配置最大和最小延迟响应(以毫秒为单位)的示例:

curl http://inventory_ms:8080/api/simulatedelay?min=1500&max=3500

再次强调,此功能仅用于演示和测试目的。模拟延迟的更安全方式是使用受保护的配置文件或本地参数。

API 响应错误模拟器

与响应延迟模拟器类似,此功能仅用于演示目的。API 错误模拟器功能使用一个可配置的值——每小时的平均 HTTP 错误。当在微服务中启用此功能时,微服务将随机选择一个适用的服务器 500 错误,并以匹配配置错误率的随机速度响应 API 请求。

可以使用 API 调用配置错误率。以下是一个使用curl发送 API 调用以配置每小时5个 HTTP 错误的 API 错误响应率的示例:

curl http://inventory_ms:8080/api/response_err?rate=5

现在,我们知道了可用于我们进行训练、测试和模拟 MSA 系统生产的测试和模拟工具。

在下一节中,我们将讨论我们的 ABC-Intelligent-MSA 操作——如何初始化系统,如何构建和使用训练和测试数据,以及如何模拟系统的生产流量。

智能 MSA 系统正在运行中

在本章的前几节中,我们讨论了不同的系统组件之间是如何相互作用的,以及我们使用哪些工具来构建 AI 算法、测试系统和监控不同组件的操作。

在本节中,我们将对 ABC-Intelligent-MSA 进行测试。我们将运行所有系统微服务和工具,看看不同的系统组件实际上是如何相互作用的,我们会看到什么结果,以及我们如何调整系统以保持端到端操作的流畅。

ABC-Intelligent-MSA 将首先在一个理想的模拟环境中运行(没有错误模拟和延迟),以收集构建 AI 模型所需的训练数据。一旦收集到足够的数据,我们将训练模型并准备系统以应对实际生产流量。

因此,系统初始化步骤如下:

  1. 在理想操作情况下,不启动 AI 服务来收集必要的训练数据,并创建一个操作基线。

  2. 如有必要,对收集到的数据进行清理并移除异常值。

  3. 使用收集到的训练数据训练 AI 算法。

  4. 重新初始化系统,包括所有 AI 服务。

  5. 开始生产操作。在我们的例子中,我们将通过注入错误、数据延迟响应、服务故障等来模拟实际生产操作。

初始化 ABC-Intelligent-MSA 系统

我们首先使用系统的 Docker compose 文件abc_ msa.yamldocker-compose命令初始化我们的 MSA 系统,如下所示,

$ docker-compose -f abc_msa.yaml up &

如前所述,在第九章中,前面的docker-compose命令比使用多个docker run命令要方便得多。docker-compose将从abc _msa.yaml文件中读取系统的运行参数和配置,并据此初始化所有系统组件。

在我们的示例中,这将启动分析和监控工具,以及系统中的所有常规微服务。由于我们仍在收集训练数据,因此目前尚不需要初始化任何 AI 服务。

图 10**.2图 10**.4所示,当我们启动 AI 服务(PBW 和 PAD)时,它们需要能够远程控制(启动、停止和重启)系统的 Docker 容器。PBW 和 PAD 被设计为通过 API 调用控制 Docker 容器。因此,我们需要首先启用 Docker 引擎以响应 API 调用,并使 PBW 和 PAD 能够成功与 Docker 引擎通信。

启用 Docker 的 API 远程管理所需的步骤如下:

  1. 在您的 Ubuntu 系统上,使用vivim或任何其他类似工具编辑/lib/systemd/system/docker.service文件。

  2. 查找ExecStart条目,并对其进行必要的修改,使其如下所示:

    ExecStart=/usr/bin/dockerd -H=fd:// -H=tcp://0.0.0.0:2375
    
  3. 这将使 Docker 引擎能够监听 API 调用。确保在修改后保存文件。

  4. 使用以下命令重新加载 Docker 引擎:

    systemctl daemon-reload
    
  5. 为了确保 Docker 引擎正常工作并响应 API 调用,请使用以下命令:

    curl http://localhost:8080/version
    

现在,系统正在运行并收集训练数据。系统运行的时间越长,收集到的训练数据就越多,您的 AI 模型就会越准确。在我们的示例中,我们将让系统运行大约 48 小时。

在下一小节中,我们将介绍如何运行工具、构建训练数据、收集一些系统性能日志、模拟实时系统操作,以及分析收集到的性能数据。

构建和使用训练数据

ms_perfmon工具将在<ms_perfmon 的工作路径>/perfmon_stats目录为每个微服务创建一个单独的统计文件。保持工具运行并监控系统性能统计信息在最小负载条件下是非常重要的。

我们建议至少收集 48 小时的训练数据。理想情况下,数据应在适用的情况下收集季节性负载。例如,在某些环境中,系统负载可能在周末比工作日高,在购物季节等。这些情况应在训练数据中考虑,以便能够构建更准确的 AI 模型。

性能数据每 10 秒拉取一次,因此,在 48 小时的活跃监控下,ms_perfmon为每个微服务生成 17,280 条记录。

无论系统的训练期有多长,只要收集到足够的性能数据,就应该运行training_data_cleanup.py工具来检测任何异常值并在将其用于我们的 AI 服务之前清理性能数据。

training_data_cleanup 工具会清理 <ms_perfmon's working path>/perfmon_stats 目录下所有的性能数据文件,并自动创建一个包含所有清理数据的 scrubbed_stats 目录,每个微服务对应一个目录。这些清理后的文件是我们稍后用于训练 AI 服务的文件。

我们现在准备好编写用于训练 PBW 的 Python 代码:

  1. 我们将使用 numpy 库进行数组和科学数据处理,使用 pandas 读取我们的 CSV 训练数据文件和测试数据,以及使用 sklearn 构建我们的 AI 模型:

    import numpy as np
    import pandas as pd
    from sklearn.model_selection import train_test_split
    from sklearn.linear_model import LinearRegression
    
  2. 导入所需的库后,我们现在需要将所有性能数据复制到一个 DataFrame 对象中。以下是一个代码示例:

    payment_ms_stats_df = pd.read_csv('scrubbed_stats/ payment_ms_stats.csv')
    

PBW 的 AI 模型包括微服务响应时间、计算出的请求失败率和计算出的微服务负载。该模型应根据所有前面的参数计算预期的响应时间。

  1. 在我们的 Python 代码中,我们需要指向需要预测的数据列。在我们的示例中,那将是响应时间。以下是对 Payment 微服务的代码片段:

    payment_ms_rt = np.array(payment_ms_stats_df['response_time'])
    
  2. 我们现在需要构建我们的模型,但在这样做之前,我们需要将剩余的性能数据列加载到一个数组中,以便进行训练和测试处理。我们通过从创建的 DataFrame 中移除“响应时间”列(一个轴为 1)来实现这一点,然后将该 DataFrame 加载到数组中,用于我们的 sklearn 对象,如下所示:

    model_data = payment_ms_stats_df.drop('response_time', axis = 1)
    model_data = np.array(model_data)
    
  3. 需要将模型数据分为训练数据和测试数据。我们将模型数据分为 80% 的训练数据和 20% 的测试数据,如下所示:

    model_data_train, model_data_test, payment_ms_rt_train, payment_ms_rt_test = train_test_split(model_data, payment_ms_rt, test_size = 0.20, shuffle=True)
    
  4. 现在,我们从训练数据构建模型:

    lr_model = LinearRegression()
    lr_model.fit(model_data_train, payment_ms_rt_train);
    
  5. 将数据保存到 CSV 文件以备将来使用:

    trend_payment_ms_rt_predictions = lr_model.predict(payment_ms_rt)
    df = payment_ms_rt_df.assign(predicted_payment_ms_rt = trend_payment_ms_rt_predictions)
    df.to_csv("predicted_payment_ms_rt_trend.csv", mode = 'w', index=False)
    

现在,我们有了训练数据和训练好的模型。是时候使用该模型进行生产流量了。

在下一小节中,我们将模拟生产操作并描述如何将其应用于我们训练的 MSA 系统,即 ABC-Intelligent-MSA。

模拟 ABC-Intelligent-MSA 的操作

我们现在需要使用训练好的模型和生产流量重新初始化系统。由于我们的示例中没有应用实际的生产流量,我们需要模拟生产操作及其潜在的操作挑战,包括高流量负载、服务故障和潜在的网络安全问题。

我们首先使用 docker-compose 重新初始化 ABC-Intelligent-MSA 系统,如前所述,但使用 abc_intelligent_msa.yaml 文件:

$ docker-compose -f abc_intelligent_msa.yaml up &

abc_intelligent_msa.yamlabc_msa.yaml 之间的主要区别在于第一个文件包含了 AI 服务的初始化。

一旦系统运行,AI 工具将开始监控和收集微服务的性能,并在检测到系统问题且指标超过配置的性能阈值时触发修复操作。

现在可以使用simulate_api_rqsts API 流量模拟器和之前讨论的响应延迟模拟功能来模拟生产流量。

使用 API 响应错误模拟器,如果需要,也可以模拟偶尔的 HTTP 错误。更复杂的模拟将涉及注入 HTTP 500 错误代码,但我们为了简单起见,将坚持使用响应时间性能延迟。

ms_perfmon工具将始终运行,以便在需要时收集我们的离线分析数据。

现在,我们需要模拟特定的生产用例,并查看 AI 工具将如何响应并自愈整个系统。在下一节中,我们将讨论 PBW 和 PAD 的操作,并探讨这两个 AI 服务如何与系统性能读数和错误交互。

分析 AI 服务操作

在前面的章节中,我们首先构建了我们的第一个 AI 服务,并介绍了如何使用 AI 来增强 MSA 系统的操作和弹性、自愈过程以及我们构建的用于生成训练数据和模拟 ABC-Intelligent-MSA 系统操作的工具体。

在本节中,我们将检查系统日志,并检查 PBW 和 PAD 如何与系统交互以及实际上如何增强其操作。然后,我们将模拟级联系统故障,并检查自愈过程是如何被触发和处理,以将 MSA 系统恢复到正常操作。

PBW 正在行动

在训练期间,PBW 能够构建 AI 模型并计算 ABC-Intelligent-MSA 系统中每个微服务的预期响应时间。从下面的日志样本中可以看出,在正常系统负载下,库存微服务的平均响应时间约为 20 毫秒:

http://inventory_ms:8080,2022-11-23 15:48:25.094675, 0.01450204849243164,200
http://inventory_ms:8080,2022-11-23 15:48:35.816913, 0.0241086483001709,200
http://inventory_ms:8080,2022-11-23 15:48:46.543205, 0.02363872528076172,200
http://inventory_ms:8080,2022-11-23 15:48:57.271370, 0.010991334915161133,200
http://inventory_ms:8080,2022-11-23 15:49:07.983282, 0.021454334259033203,200
http://inventory_ms:8080,2022-11-23 15:49:18.645113, 0.012285232543945312,200
http://inventory_ms:8080,2022-11-23 15:49:29.310656, 0.0245664119720459,200
http://inventory_ms:8080,2022-11-23 15:49:40.010556, 0.013091325759887695,200
http://inventory_ms:8080,2022-11-23 15:49:50.744695, 0.021291017532348633,200
http://inventory_ms:8080,2022-11-23 15:50:01.715555, 0.024635791778564453,200

我们将 PBW 的警告阈值配置为 250 毫秒,操作阈值配置为 750 毫秒。现在,我们将开始通过使用simulate_api_rqsts向库存微服务引入 API 调用负载,并使用响应延迟模拟器功能引入延迟。然后,我们将从 PBW 操作日志中看到 PBW 的反应。

以下是大约 1.5 分钟的 PBW 性能读数。从读数中可以看出,响应时间始终高于 250 毫秒的警报阈值,但(除了一项读数外)仍然低于 750 毫秒的操作阈值:

http://inventory_ms:8080,2022-11-23 18:24:00.518005, 0.6386377334594727,200
http://inventory_ms:8080,2022-11-23 18:24:11.469172, 0.7164063453674316,200
http://inventory_ms:8080,2022-11-23 18:24:22.203452, 0.7233438491821289,200
http://inventory_ms:8080,2022-11-23 18:24:32.942619, 0.7101089954376221,200
http://inventory_ms:8080,2022-11-23 18:24:43.668907, 0.6982685089111328,200
http://inventory_ms:8080,2022-11-23 18:24:54.777383, 0.8207950115203857,200
http://inventory_ms:8080,2022-11-23 18:25:05.410204, 0.6812236309051514,200
http://inventory_ms:8080,2022-11-23 18:25:16.101344, 0.6544813632965088,200
http://inventory_ms:8080,2022-11-23 18:25:27.072040, 0.7446155548095703,200
http://inventory_ms:8080,2022-11-23 18:25:37.828189, 0.6969136238098145,200

为了触发 PBW 的自愈操作,读数必须持续高于 750 毫秒的操作阈值。一个超过 750 毫秒的读数不足以触发操作。然而,由于读数始终高于 250 毫秒的警报阈值,预计 PBW 将向 NMS/OSS 系统触发警报。

我们需要验证 PBW 在 NMS/OSS 系统或 PBW 操作日志中的行为。以下是从上一个示例中同一时期 PBW 操作日志的片段:

2022-11-23 18:24:00.518005: Alarming high response time (0.6386377334594727) detected in inventory_ms. No alarm triggered yet.
2022-11-23 18:24:11.469172: Alarming high response time (0.7164063453674316) detected in inventory_ms. No alarm triggered yet.
2022-11-23 18:24:22.203452: Alarming high response time (0.7233438491821289) detected in inventory_ms. No alarm triggered yet.
2022-11-23 18:24:32.942619: Alarming high response time (0.7101089954376221) detected in inventory_ms. No alarm triggered yet.
2022-11-23 18:24:43.668907: Alarming high response time (0.6982685089111328) detected in inventory_ms. No alarm triggered yet.
2022-11-23 18:24:54.777383: Actionable high response time (0.8207950115203857) detected in inventory_ms. No action triggered yet.
2022-11-23 18:25:05.410204: Alarming high response time (0.6812236309051514) detected in inventory_ms. No alarm triggered yet.
2022-11-23 18:25:16.101344: Alarming high response time (0.6544813632965088) detected in inventory_ms. No alarm triggered yet.
2022-11-23 18:25:27.072040: Alarming high response time (0.7446155548095703) detected in inventory_ms. No alarm triggered yet.
2022-11-23 18:25:37.828189: Alarming high response time (0.6969136238098145) detected in inventory_ms. Yellow alarm triggered and sent to NMS/OSS system.
2022-11-23 18:25:48.637317: Alarming high response time (0.6777710914611816) detected in inventory_ms. Yellow alarm triggered and sent to NMS/OSS system.
2022-11-23 18:25:59.327946: Alarming high response time (0.6758050918579102) detected in inventory_ms. Yellow alarm triggered and sent to NMS/OSS system.
2022-11-23 18:26:10.014319: Alarming high response time (0.6641242504119873) detected in inventory_ms. Yellow alarm triggered and sent to NMS/OSS system.

如您从前面的片段的最后四条日志记录中看到,在超过 250 毫秒的一致延迟后,触发了警报并发送到 NMS/OSS 系统。我们需要增加库存微服务的负载和响应时间,以查看 PBW 将如何反应。

以下是 PBW 性能日志的另一段。在一系列 10 次一致的响应延迟读数中,只有最后四条记录的响应时间超过 750 毫秒:

http://inventory_ms:8080,2022-11-23 18:29:31.852330, 1.326528787612915,200
http://inventory_ms:8080,2022-11-23 18:29:43.196200, 1.4279899597167969,200
http://inventory_ms:8080,2022-11-23 18:30:05.310226, 1.0108487606048584,200
http://inventory_ms:8080,2022-11-23 18:30:16.334608, 1.1380960941314697,200

通常,我们会配置表 10.1中显示的所有修复操作。然而,在我们的演示系统中,我们只配置了一个修复操作来演示系统自我修复操作的一般情况。我们只配置了微服务容器,如果微服务出现问题,则重启。因此,响应延迟模拟功能比我们之前提到的其他工具更相关的模拟工具。

如果由于 API 调用请求量高而导致性能缓慢,最合适的修复操作是首先尝试扩展微服务,并分配更多资源来响应高量的 API 请求。

在我们的模拟中,我们假设库存微服务的问题不一定是由 API 请求负载引起的,而是由于一些不可预见的问题导致库存服务变得不稳定,无法及时处理 API 调用,因此重启库存微服务可以修复问题。

现在,让我们看看 PBW 在同一时期内的操作日志。请注意,在响应时间达到可操作的高水平之前,已经检测到低于 750 毫秒的令人担忧的高响应时间。响应时间高于 250 毫秒且低于 750 毫秒:

2022-11-23 18:29:31.852330: Actionable high response time (1.326528787612915) detected in inventory_ms. No action triggered yet. Yellow alarm triggered and sent to NMS/OSS system.
2022-11-23 18:29:43.196200: Actionable high response time (1.4279899597167969) detected in inventory_ms. No action triggered yet. Yellow alarm triggered and sent to NMS/OSS system.
2022-11-23 18:30:05.310226: Actionable high response time (1.0108487606048584) detected in inventory_ms. No action triggered yet. Yellow alarm triggered and sent to NMS/OSS system.
2022-11-23 18:30:16.334608: Actionable high response time (1.1380960941314697) detected in inventory_ms. Red Alarm triggered and sent to NMS/OSS system.
2022-11-23 18:30:16.334608: Self-healing lock state declared for inventory_ms container.
2022-11-23 18:30:16.334608: Self-healing action triggered. Restarting inventory_ms container (inventory_management_container).
2022-11-23 18:30:21.359377: Verifying inventory_ms operations...
2022-11-23 18:30:22.945823: inventory_ms was successfully restarted
2022-11-23 18:30:23.089051: Self-healing lock state cleared for inventory_ms container.

如您从操作日志的最后四条记录中看到,PBW 检测到一致的响应时间(超过 750 毫秒),因此向 NMS/OSS 系统发送了红色警报,表明库存服务存在关键延迟,需要采取自我修复操作。然后 PBW 锁定库存微服务以避免与其他 AI 服务的修复操作冲突。然后 PBW 通过向 Docker 引擎发送重启 API 调用重启库存微服务,验证库存微服务已恢复在线,并最终解锁库存微服务。

要通过 API 重启 Docker 容器,您需要发送如下POST请求:

/containers/<container id or name>/restart

您还可以使用t参数指定在重启容器之前等待的秒数。以下是一个容器重启POST示例,在等待 10 秒后重启库存服务容器:

/v1.24/containers/inventory_management_container/restart?t=10

关于如何使用 API 调用控制 Docker 引擎的更多信息,请查看 Docker 引擎 API 文档docs.docker.com/engine/api/version-history/

然而,PBW 能否修复库存微服务问题?

现在,让我们回到 PBW 的性能日志,看看这个自我修复操作如何影响了库存服务的性能。以下是在修复操作触发之前的日志条目:

http://inventory_ms:8080,2022-11-23 18:30:16.334608, 0.1380960941314697,200
http://inventory_ms:8080,2022-11-23 18:30:27.629649, 0.1693825721740723,200
http://inventory_ms:8080,2022-11-23 18:30:38.486793, 0.1700718116760254,200

当然,响应时间从超过 1 秒降低到了最大 170 毫秒。虽然没有问题出现之前那么低,但库存微服务现在确实有了一些喘息的空间。如果不对根本问题进行关注并妥善修复,性能问题可能会再次出现。

在一个更高级的 AI 模型中,我们可以训练和配置系统,使其能够采取更复杂的操作,在需要时完全解决问题,但在这本书中,我们限于特定的范围,以便在原则上展示这个想法,并为您开发针对特定用例的 AI 模型和算法铺平道路。

在本节中,我们已经展示了 PBW 的工作原理以及当检测到微服务性能问题时如何触发操作。在下一节中,我们将介绍 PAD AI 服务以及 PAD 如何对整个系统采取更为全面的视角。

行动的 PAD

要展示 PAD 的操作,最好的方式是模拟级联故障,看看 PAD 如何将 MSA 系统恢复到正常操作。

为了模拟级联故障并确保 PAD 能够响应故障并尝试自动修复,我们首先需要禁用 PBW AI 服务。这将防止 PBW 触发修复操作,并防止它在 PAD 的修复操作启动之前尝试解决问题。

让我们快速回顾一下之前在第三章中讨论的内容,这是一个级联故障发生的例子。

如[图 10.5]所示,在重压下的 API 流量中,库存微服务的故障可能导致支付微服务在队列中积压过多的 API 调用,等待从库存服务得到响应。最终,这些 API 调用将消耗并耗尽支付微服务中可用的资源,导致其失败。支付微服务的故障将在订单微服务中产生类似的情况,并最终导致订单微服务也出现故障:

图 10.5:支付微服务已关闭

图 10.5:支付微服务已关闭

为了使 PAD 能够响应修复操作,必须为 PAD 检测到的每种异常类型定义相应的修复操作。

为了成功模拟级联故障,我们只为级联故障情况定义了一个操作。否则,PAD 会自动检测库存服务中的故障,并通过重启库存微服务容器来自我修复,从而从一开始就防止级联故障的发生。

我们将首先模拟大量订单请求给订单微服务,看看系统将如何应对这种情况,特别是 PAD 在这种情况下的反应。

要模拟高量的订单请求,请使用以下simulate_api_rqsts命令针对订单微服务进行固定均匀速率的每分钟 100,000 个订单请求:

simulate_api_rqsts 100000 http://order_ms:8080/place_order

我们现在将关闭库存微服务并检查 PAD 操作日志。以下是在 PAD 开始检测库存微服务故障大约一分钟后的日志片段。

请注意,我们已经将突发高流量引入了系统。这种突然的流量增加本身是一种由 PAD 检测到的流量模式异常,但 PAD 没有对这种特定的异常做出响应,因为没有为该异常定义特定的恢复操作:

2022-11-24 11:39:13.602130: Traffic pattern anomaly detected, (inventory_ms) is likely down. No action is defined. No action triggered yet. Yellow alarm triggered and sent to NMS/OSS system.
2022-11-24 11:39:23.469204: Traffic pattern anomaly detected, (payment_ms) slow API response detected. No action is defined. No action triggered yet.
:
:
2022-11-24 11:40:26.836405: Traffic pattern anomaly detected, (payment_ms) slow API response detected. No action is defined. No action triggered yet. Yellow alarm triggered and sent to NMS/OSS system.

在前面的 PAD 日志片段中,由于没有检测到来自该服务的响应流量,PAD 自动识别了库存服务故障。然而,由于没有为该特定异常定义恢复操作,PAD 没有采取任何行动。由于异常持续了超过 1 分钟,PAD 向 NMS/OSS 系统发送了警报,通知系统管理员问题。

由于库存微服务故障,支付微服务开始耗尽资源,并且鉴于 API 调用请求负载,PAD 检测到了支付微服务异常的异常缓慢流量。相应地,正如日志所示,大约 1 分钟后,PAD 开始向 NMS/OSS 生成警报。

如以下 PAD 日志所示,在支付微服务异常几分钟之后,订单微服务开始出现问题,相应地,PAD 能够关联所有这些异常并检测到潜在的级联故障:

2022-11-24 11:47:12.450897: Traffic pattern anomaly detected, (order_ms) slow API response detected. No action is defined. No action triggered yet. Yellow alarm triggered and sent to NMS/OSS system.
2022-11-24 11:47:12.450897: Traffic pattern anomaly detected, potential cascading failure detected. No action triggered yet. Yellow alarm triggered and sent to NMS/OSS system.

请注意,我们迄今为止唯一的微服务故障是我们手动关闭的库存微服务。支付和订单微服务目前仍在运行,但从日志来看,可能正遭受资源耗尽的问题。

到目前为止,系统仍在运行,如果库存服务恢复在线,系统将自动恢复。在重负载期间的用户体验将是订单过程中的性能缓慢,但尚未有订单被拒绝或失败。

通过检查所有之前提到的 PAD 操作日志,并且根据目前的情况,我们仍然处于正常状态。然而,如果不对库存微服务问题采取行动解决,系统最终会失败,用户订单将开始被拒绝。

第三章中讨论的短路流量模式有助于防止级联故障的发生,但它仍然不能解决根本问题。在传统的短路模式实施中,用户订单仍然会被拒绝,直到手动干预修复库存微服务。

这就是 PAD 的作用所在。查看以下 PAD 操作日志!

2022-11-24 11:48:13.638447: Traffic pattern anomaly detected, potential cascading failure detected. (inventory_ms) microservice is likely the root-cause. Red Alarm triggered and sent to NMS/OSS system.
2022-11-24 11:48:13.638447: Self-healing lock state declared for inventory_ms container.
2022-11-24 11:48:13.638447: Self-healing action triggered. Restarting inventory_ms container (inventory_management_container).
2022-11-24 11:48:18.663912: Verifying inventory_ms operations...
2022-11-24 11:48:20.325807: inventory_ms was successfully restarted
2022-11-24 11:48:20.474590: Self-healing lock state cleared for inventory_ms container.

PAD 能够在实际发生之前检测到级联故障,并能够确定问题的根本原因。PAD 向 NMS/OSS 系统发送了红色警报,在库存服务上声明了自愈锁定状态以尝试修复问题的根本原因,能够成功重启库存微服务容器,然后清除了库存服务上的自愈锁定。

让我们现在检查微服务的性能日志,确保问题已解决,ABC-Intelligent-MSA 系统和所有其微服务都在正常运行。

这是库存微服务的性能日志:

http://inventory_ms:8080,2022-11-24 11:51:33.132089, 0.033451717535487,200
http://inventory_ms:8080,2022-11-24 11:51:43.894705, 0.035784934718275,200
http://inventory_ms:8080,2022-11-24 11:51:54.809743, 0.027584526453594,200
http://inventory_ms:8080,2022-11-24 11:52:06.155834, 0.028615804809435,200

这是支付微服务的性能日志:

http://payment_ms:8080,2022-11-24 11:54:41.109835, 0.051435877463506,200
http://payment_ms:8080,2022-11-24 11:54:51.924508, 0.102346014326819,200
http://payment_ms:8080,2022-11-24 11:55:03.372841, 0.070163827689135,200
http://payment_ms:8080,2022-11-24 11:55:14.076832, 0.157682760576845,200

这是订单微服务的性能日志:

http://order_ms:8080,2022-11-24 11:58:37.135827, 0.209097164508914,200
http://order_ms:8080,2022-11-24 11:58:47.584731, 0.193851625041193,200
http://order_ms:8080,2022-11-24 11:58:58.243759, 0.150628069240741,200
http://order_ms:8080,2022-11-24 11:59:08.961412, 0.138192362340785,200

如前所述的库存、支付和订单微服务,所有这些微服务都已恢复正常,性能读数正常。系统现在恢复正常运行,应该能够无问题地处理生产负载。

摘要

本章逐步向我们介绍了如何构建 AI 模型以构建智能 MSA 系统。因此,我们相应地构建了两个主要 AI 服务——PBW 和 PAD,并利用这些 AI 服务来增强我们的 MSA 演示系统 ABC-MSA,构建了一个名为 ABC-Intelligent-MSA 的智能 MSA 系统。

我们详细解释了自愈过程的设计和动态,以及我们构建的用于开发 AI 训练数据的工具,如何模拟生产操作,以及如何衡量演示系统的性能。然后我们对 ABC-Intelligent-MSA 进行了测试,模拟了几个用例以展示 MSA 系统中的 AI 功能,并仔细检查了我们的演示 AI 服务的日志,以展示在 MSA 中使用 AI 的价值。

本章中解释的一切只是使用 AI 在 MSA 系统中的示例。企业应考虑使用适合其自身 MSA 系统和用例的特定 AI 服务。这些 AI 工具可能完全可以通过第三方提供或根据需要内部构建。

在下一章中,我们将讨论从传统 MSA 系统到智能 MSA 系统的转型过程——在绿色和棕色场实施中需要考虑的事项,以及如何避免集成挑战,使企业转型尽可能顺利。

第十一章:管理新系统的部署 – 绿色地带与棕色地带

在前面的章节中,我们讨论了构建 MSA 系统以及将 AI 算法集成以形成一个智能 MSA。我们涵盖了概念、技术和方法,并伴随着实例。

在本章中,我们将讨论不同的绿色地带棕色地带部署考虑因素,以及以最小的操作中断顺利部署新的智能 MSA 系统的方法,以保持整体系统稳定性和业务连续性。

我们还将探讨如何克服一般的部署挑战,特别是在现有系统正在生产的棕色地带部署中,并实施一个成功有效的迁移计划,以实现新的智能 MSA 系统的迁移。

本章将涵盖以下主题:

  • 部署策略

  • 绿色地带与棕色地带部署

  • 克服部署挑战

部署策略

组织在部署新系统时利用各种技术来最小化停机时间,并确保部署的顺利和成功。组织遵循的一些最常用的部署策略包括重建、斜坡、蓝/绿、金丝雀、A/B 测试和影子部署:

  • 重建部署是一种简单直接的方法,涉及一次性替换整个基础设施,类似于我们在第三章中讨论的大爆炸迁移。这种方法最适合小型和简单系统;然而,这也意味着在部署过程中系统将完全离线,这可能导致显著的中断时间。

  • 斜坡部署与我们在第三章中讨论的滴答式迁移类似。斜坡部署允许在部署过程中现有系统保持在线状态。新系统逐步上线,流量逐步路由到它,使得在整个部署过程中,两个系统都能对用户可用。尽管这种方法在小企业和简单系统中可能有效,但对于更大、更复杂的系统来说,这种方法是理想的,因为它可以最大限度地减少停机时间。

  • 蓝/绿部署是一种技术,涉及维护两个相同的生产环境,分别称为“蓝”和“绿”,并将流量路由到其中一个。这允许在新的部署版本中遇到任何操作问题时进行无缝切换。这种方法最适合关键任务系统,因为它确保在任何给定时间都有可用的系统供用户使用。

  • 金丝雀部署是一种技术,它涉及在新系统和现有系统并行部署,并将一小部分流量路由到新系统。这允许在完全推出之前,使用实际的生产流量对新系统进行测试。如果新系统中出现问题,可以根据遇到的问题类型重新评估推出;然后,在解决问题期间,可以恢复先前的系统。这种方法通常用于部署需要高可用性的关键系统更改。

  • A/B 测试部署是另一种方法,它涉及同时运行旧系统和新系统,但使用不同的用户子集进行测试,以确定哪个表现更好。这种方法最适合测试新系统功能或服务。

  • 影子部署中,新系统被部署以与现有系统并行运行。然后,旧系统的实时生产流量被重定向到新系统,以测试新发布的功能、系统在负载下的稳定性,或全面测试新系统。这种方法最适合在大型组织中部署的大型系统。

以下是对所有先前部署策略的比较总结:

持续在线 生产流量测试 成本 复杂性
重建
逐步
蓝绿 中等
金丝雀 中等
A/B 测试
影子

表 11.1:不同部署策略的比较

这些策略各有优缺点,没有哪一个适合所有情况。组织必须根据其具体需求和正在部署的更改的性质选择适当的策略。

正在被升级或替换的现有系统的复杂性、设计,以及其年龄、运行状况和所使用的技术堆栈,在确定部署策略方面发挥着重要作用。在下一节中,我们将讨论绿地和棕地部署及其对确定部署方法和计划的具体影响。

绿地与棕地部署

我们的智能 MSA 系统已准备好部署,我们现在需要详细考虑我们拥有的或需要获取的基础设施,以便在生产中部署系统。

我们需要解决的一些主要问题如下:

  • 运行智能 MSA 系统所需的哪些基础设施细节?

  • 我们是否有部署和高效运行系统所需的硬件和软件资源?

  • 我们能否利用现有的基础设施和应用来部署新系统?

  • 需要的基础设施与我们现有的基础设施之间的差异是什么,我们如何填补这个差距?

组织当前的基础设施设置和现有系统(如果有)在回答所有上述问题中起着至关重要的作用——也就是说,新系统是在绿色地带还是棕色地带环境中部署。

绿色地带部署是指从头开始构建和部署新的系统或基础设施,没有任何先前的系统或基础设施。因此,我们必须在没有重大限制、依赖性、集成工作或兼容性问题的前提下构建和运行新系统。

相反,棕色地带部署是指在新系统或基础设施已部署且现有系统在位的地点部署新系统或基础设施的过程。该地点可能具有现有的基础设施,如服务器、应用程序、网络组件等,这些可能被重用于部署新系统。

简而言之,绿色地带部署是从头开始的新起点,而棕色地带部署涉及在现有系统或基础设施之上构建,并可能处理一些集成问题、兼容性担忧和资源限制。

不论是绿色地带还是棕色地带部署,通常由组织的具体情况和组织的业务流程执行方式决定。尽管如此,了解每种部署类型的优缺点对于准确规划仍然很重要。

重要提示

如果成本节约是组织的主要关注点,我们应该尽可能多地利用现有的棕色地带基础设施组件。然而,我们需要以不会对已部署系统的效率、可靠性或功能产生负面影响的方式重用现有组件。

以下是在评估这两种部署类型时需要考虑的一些因素。

灵活性

因为在绿色地带(greenfield)部署中,我们是从头开始的,这给了我们设计、实施和优化新系统以适应组织特定需求的自由,而不受任何依赖组件或现有生产系统的限制。

相反,在棕色地带(brownfield)中,我们在设计或部署新系统的任何部分之前,必须始终考虑到已经运行的系统和它们的依赖性。这本身就可以限制新系统的部署、定制或优化。

可扩展性

与棕色地带相比,绿色地带的实施提供了更高的可扩展性,因为在绿色地带的实施中,我们部署新的基础设施而没有任何可能限制新系统设计或定制的现有约束。

这种缺乏限制给了架构师和系统设计师选择设计系统的自由,以便系统可以扩展,而无需考虑可能阻碍系统功能或扩展的底层技术或现有设备。

然而,在棕色场地的案例中,现有的基础设施可能包含遗留系统,这些系统在可能的情况下可能会部分或全部重用。重用遗留系统的不同部分可能会阻碍新系统的可扩展性。

此外,遗留系统通常比现代系统占用更多的物理空间,并且功耗更高,这给整个系统的可扩展性增加了更多限制。

话虽如此,随着我们部署新系统并逐步更新现有基础设施,我们将使用最新的技术和现代系统,这将释放物理空间并降低电力需求。这反过来将帮助我们最终更好地扩展系统。

技术栈

在绿色场地的部署中,我们有利用最新技术、应用程序和工具的自由,这可以在许多其他事情中增强性能、安全性和能力,并延长系统的整体使用寿命。

在棕色场地环境中,遗留系统使用较旧的技术、硬件、工具和系统,这可能会对系统的可支持性、功能、可扩展性以及未来的扩展和集成引入限制。

集成

如在其他比较方面的解释所述,在绿色场地环境中,系统的所有组件都是新的,并且是从一开始就设计和构建来无缝协同工作的。因此,集成根本不是问题。

然而,在棕色场地的部署中,将新系统与现有的 IT 基础设施集成可能会具有挑战性,因为这两个系统可能不完全兼容。可能需要集成努力,以便旧的和新的组件能够协同工作,即使如此,新的混合系统也可能后来提供运营挑战,这可能导致不可预见的技术故障。

成本

从采购和资本支出(CAPEX)的角度来看,从头开始构建新系统的成本高于从重用和新建组件的混合中构建系统。

要设置新系统,需要一定程度的专长,这可能不在内部可用。将系统启动并运行所需的努力和专长肯定会有相关的成本。然而,可以争辩说,这种成本可以通过整合新旧组件所需的努力和专长来轻松抵消。可能需要不同的努力和不同的专长,但成本相似。

当谈到运营支出(OPEX)时,在绿色场地的部署中,我们需要考虑新部署的技术和系统的培训成本,以及由于缺乏新系统实际操作经验可能导致的潜在运营错误。在棕色场地的部署中,这些培训成本通常较低。

在绿色场地的部署中,由于新系统和技术的通常目标是优化电力使用,因此电力消耗通常较低。

另一个重要的运营成本(OPEX)考虑因素是棕色地带实施中的潜在技术债务。技术债务是组织为了使系统上线而采取的捷径。换句话说,这涉及到在部署期间采取临时措施来解决集成或运营问题,并实现短期结果,这些结果在长期可能是灾难性的。

市场投放时间

市场投放时间是部署中的一个有趣方面,可以朝两个方向发展。一般来说,在绿地环境中部署一个系统比与已经存在的运行良好的系统集成要花费更长的时间,就像在棕色地带环境中那样。但这将高度依赖于现有系统的复杂程度。

如果我们在一个显著老旧或复杂且无序的基础设施上部署我们的新系统,我们可以认为从头开始部署一个全新的系统比尝试成功集成两个系统要简单得多,也能节省时间。

风险

这是部署中可以双方辩论的另一个方面。

由于缺乏对新基础设施、新技术、新系统、新工具和新应用的经验,系统操作失误的可能性更大,解决问题的耗时也可能更长。相比之下,如果没有备份系统,如果新系统未能按预期运行,将没有回退选项。

但再次强调,如果棕色地带环境中的旧系统过于复杂或无序,由于集成复杂性、潜在的技术债务、旧的不受支持的组件等因素,棕色地带部署的风险会更高。

员工入职

已经使用现有系统的公司对其工作原理有更好的理解,对其操作和潜在问题的洞察力也更强,这可以使部署过程和系统操作更加顺畅。

在绿地部署中,在员工开始熟悉系统细节之前,需要培训和积累经验。

用户接受度

新系统的用户接受度可能需要适应新的日常任务执行方式,内部和可能的外部业务运营方式的转变,以及组织如何处理内部和外部客户。这种转变可能需要改变组织文化,这可能会对系统的成功实施构成重大挑战,并在部署后揭示运营不足。

在棕色地带环境中,更新后的系统功能可能对用户来说是渐进的或相对透明的,这使得用户接受度比在绿地部署中部署一个全新的系统要容易得多和快得多。成功的逐步用户接受度有助于揭示潜在的设计、实施和运营缺陷,这些缺陷可以迅速解决和修复。

在任何情况下,都需要用户培训,但在绿地部署中,培训比棕地案例更为复杂和耗时。

下表总结了部署中绿地和棕地方面的比较:

绿地 棕地
灵活性
可扩展性
技术栈 灵活且优化 受限
集成 最小到无 需要大量努力
成本 初始投资成本高但运营成本较好 初始投资成本较低但运营成本较高
市场时间 通常更长 通常更短
风险 通常更高 通常更低
员工入职 过程更长 时间更短
用户接受度

表 11.2:绿地与棕地

在本节中,我们讨论了绿地和棕地部署之间的主要区别,每个环境的优缺点,需要考虑的因素以及原因。

在下一节中,我们将讨论如何克服两种环境中的部署挑战以及每种情况下的部署最佳实践。

克服部署挑战

我们现在已经了解了绿地和棕地环境中系统部署的不同方面,以及系统设计和实施过程中可能出现的几个挑战。在本节中,我们将介绍一些概念、策略和方法,以减轻这些挑战,确保系统部署顺利且成功。

我们应该以一个具备项目、部署和项目管理以及供应商管理方面多样技能和经验的稳固项目团队开始我们的部署项目。

在缺乏内部经验的情况下,可能需要通过第三方将一个或多个项目经验领域外包。合作伙伴可能包括系统集成商、设备供应商和增值经销商。

这个经验丰富的团队将帮助进行彻底的规划和研究,以便能够理解组织的具体需求、潜在风险、当地法规以及任何必要的合规需求。

遵守行业和当地法规是项目的一个基本部分。除了项目的技术方面和技术之外,例如处理信用卡的系统将需要具有 PCI 合规性和规则经验的团队成员。例如,在美国部署的医疗系统可能需要具有 HIPAA 合规性需求经验的成员,等等。

项目管理对于建立清晰的团队沟通和协作至关重要。项目经理帮助跟踪项目过程、变更和需求,并确保在整个项目周期内满足时间表和目标。项目经理还确保所有利益相关者在项目的不同阶段都得到适当的告知和参与。

项目管理风格或方法的类型取决于组织本身、时间表以及实施细节和技术部署。无论是瀑布、敏捷、Scrum 还是其他,项目经理必须与团队一起决定。

解决部署挑战是项目周期内追求的任务。我们在这里关注项目周期的这一方面,特别是关于绿色地带和棕色地带部署。

在任何部署活动之前,对部署风险的全面了解是必要的。因此,制定一个明确的部署风险计划至关重要,以便能够识别风险并减轻每个风险,以确保部署成功。

下图说明了我们部署周期的风险管理流程。该流程应从识别风险、确定避免或最小化风险的方法、制定缓解计划开始,并持续测试、监控和审查部署,以更新缓解计划,以应对实施过程中出现的新风险或挑战:

图 11.1:克服部署挑战的主要步骤

图 11.1:克服部署挑战的主要步骤

在以下小节中,我们将讨论风险管理计划的主要活动和每个阶段如何与我们的部署活动相关。

识别部署风险

为了解决部署挑战,我们首先识别我们在部署系统时可能遇到的风险。在绿色地带环境中,我们之前识别出的风险如下。绿色地带风险被称为GR

  • GR1:高资本支出

  • GR2:部署时间

  • GR3:由于缺乏培训和经验丰富的员工,系统故障

  • GR4:用户采用缓慢或不足

在棕色地带环境中,我们之前识别出的风险如下。棕色地带风险被称为BR

  • BR1:由于可能低灵活性和可扩展性以及一些重用的遗留技术,系统功能限制

  • BR2:高运营成本

既然我们已经识别出潜在的风险,现在是时候根据它们发生的可能性和对项目可能产生的影响来优先排序它们。这将帮助我们计算风险暴露并有效地规划和分配适当的资源。

优先排序风险

风险暴露是风险概率乘以该风险对部署项目的影响。风险暴露越高,减轻该风险的优先级应该越高。

图 11**.2 展示了之前识别出的风险的彩色编码风险暴露矩阵。请注意,由于各种因素,如项目复杂性和组织需求、现有系统的稳定性和复杂性、项目要求、预算、时间表等,组织之间的风险暴露水平可能会有很大差异:

图 11.2:风险暴露矩阵

图 11.2:风险暴露矩阵

我们总是从风险缓解图的右上角到左下角进行优先级排序。红色区域,其中风险和可能性为中等至高,是我们需要开始分配资源的地方。接下来是黄色区域,然后是绿色区域。

因此,我们应该从绿色地带风险#1(GR1)开始,即如何缓解高资本支出风险,然后是 GR3,我们缺乏部署新系统的经验人员,然后是 GR2,新系统部署时间可能成为问题,最后通过解决 GR4 来结束绿色地带风险的缓解,我们可能面临新系统缓慢的用户采用。

对于棕色地带风险缓解,我们首先从棕色地带风险#1(BR1)开始,因为它在矩阵的红色区域有更高的暴露度,然后进行 BR2。

风险并非总是可以避免的。风险往往无法避免。在风险消除不可能的情况下,缓解计划必须解决如何至少将风险降低到可管理的水平。

许多组织选择忽略低/非常低可能性和低/非常低影响的风险。在这种情况下缓解风险的过程可能非常昂贵,甚至比风险本身更具风险性。

制定和实施风险缓解计划

我们需要制定一个缓解计划来管理已确定的风险。该计划应包括为缓解或消除风险而采取的具体行动,涉及的资源,以及风险发生时的应急措施。

我们还需要根据每个风险的计算暴露度来确定我们的风险缓解策略。正如我们将在接下来的几个子节中看到的,这些策略可以包括实施操作保障措施,使用额外的系统组件采取额外措施,在部署前测试系统,培训系统用户和管理员以确保他们能够有效地使用新系统,以及在出现任何意外部署问题时制定回滚计划。

让我们将所有这些应用到我们的部署项目的已确定风险中。

GR1 - 高资本支出风险

当面对高资本支出风险时,我们需要关注几个方面——首先,项目预算,其次,系统和项目需求,第三,如何使用有效的谈判技巧来获取成功满足所有系统需求的必要基础设施。

当然,有时预算和时间限制成为获取所有需求清单项目的障碍。因此,优先考虑你的需求很重要,尤其是如果你有严格的预算限制。

目标是获取能够满足我们所有愿望清单要求的基础设施;然而,在某个时候,我们可能需要为了满足我们的预算而放弃系统的一些理想功能。这就是我们的谈判技能变得至关重要的地方。我们的谈判技能越强,成功部署新系统并在预算内满足所有要求的可能性就越大。

回顾一些重要的谈判技巧对我们实现目标是有用的。

首先,对可能参与提供基础设施资产的所有供应商进行彻底的研究。这包括每个潜在供应商的技术和业务优势与劣势、他们的标价、他们的部署和运营支持的质量水平、他们的产品路线图以及他们的未来业务展望。

然后,我们可以从我们的供应商研究中提出多个选项。拥有多个选项在我们与供应商的谈判阶段提供了更多的灵活性和议价能力。

在所有可用的选项中,拥有一个明确的谈判协议的最佳替代方案(BATNA)是至关重要的。BATNA 意味着如果谈判失败且与基础设施供应商没有达成协议,我们可以实现的最佳选择。这是在谈判没有按照我们最初与特定供应商的计划进行时,我们可以依赖的备选方案。

在谈判过程中拥有一个明确且定义良好的 BATNA 对于展示强大的议价能力至关重要。BATNA 让供应商感受到,如果他们不关闭基础设施收购交易,他们可能会错失多少。

BATNA 还有助于我们了解何时应该从谈判中退出,如果供应商不愿意用可用的预算满足我们的基础设施需求。这关乎我们可用的替代方案以及选择或未选择特定供应商的后果。

较大且声誉良好的组织可以利用杠杆。杠杆是指利用你的声誉、市场地位或市场规模来影响谈判结果的能力。杠杆在与供应商达成良好交易时可以非常有效。供应商通常努力在大组织中站稳脚跟,以创建客户参考或作为从该特定交易中创造更多业务的方式。

话虽如此,在谈判过程中展示一定程度的灵活性是至关重要的。灵活性将展示我们愿意妥协的意愿,并可能促成与供应商之间强大而健康的长期业务关系:

图 11.3:谈判策略

图 11.3:谈判策略

图 11**.3 总结了你可以遵循的谈判策略,以最小化因基础设施成本和整体项目资本支出(CAPEX)超支的风险。

另一种减轻 CAPEX 风险的有效方法是使用云服务提供商来部署您的新基础设施,尤其是在项目的初始测试和调整阶段。

使用容器化和虚拟化来构建新的工作负载是帮助减少资本支出风险(CAPEX)的另一种方式。然而,这样做可能会引入一些运营支出(OPEX)和其他类型的部署风险,尤其是如果工作人员在云环境部署方面的培训不足。

GR2 – 部署时间风险

在部署新项目时,尤其是涉及新技术、技术和概念的项目,不能忽视可能落后于进度表的风险。整个项目周期总是伴随着一个学习曲线。

此外,潜在的频繁变更和范围蔓延也需要考虑。这进一步强调了从一开始就组建一支熟练团队的重要性。

一支高技能和受过良好训练的项目团队将有助于最小化范围蔓延和时间延迟风险。在没有内部领域专家的情况下,聘请外部方作为系统集成商并为团队制定充分的培训计划,在管理时间延迟风险时变得至关重要。

除了团队技能之外,还可能有其他因素会影响项目时间表和范围蔓延风险。作为我们的风险缓解计划的一部分,我们需要考虑所有这些因素,并确保它们得到控制,从而控制它们相关的风险。

MSA 系统可能非常复杂且部署负担沉重。系统越复杂,预测和管理部署时间表就越困难。

在棕色地带,新系统的部署往往取决于现有系统的可用性和运行稳定性。此外,将新系统与现有系统集成可以增加另一个复杂层次和时间消耗的任务,所有这些都可能延长整体部署时间表。

为了减轻复杂性对整体风险的影响,一种方法是避免大规模和一次性变更。相反,通过将部署分解成多个简单阶段和阶段来逐步进行。遵循著名的爱因斯坦规则,“一切都应该尽可能简单,但不能过于简单”。尽可能简化,但不要以牺牲系统的功能或可靠性为代价。

虽然彻底测试对于确保新系统的可靠性和符合组织需求是必要的,但在复杂项目中,过度分析和过度测试经常发生。这个过程可能会显著延迟系统部署。

解决测试延迟因素的一种方法是在生产环境中以有限可用性LA)的方式运行系统,一个测试版,或者某种形式的预发布期。这种 LA 方法将帮助我们将真实用户流量应用于系统,同时我们专注于在将系统过渡到全面生产之前,根据需要监控和进行系统更改。

GR3 和 GR4 – 系统故障风险和用户采用风险

用户采用程度可能高度依赖于任何新的用户界面/用户体验UI/UX)变化或增加的复杂性。假设系统 UI/UX 设计良好,系统故障和缓慢的用户采用风险也取决于项目团队和运营团队的经验,以及用户对在该系统上工作的熟悉程度。

为了使经验和用户熟悉度风险正常化,重要的是首先在项目开始时就包括一个稳固的 UI/UX 设计团队,然后通过确保项目赞助者和高管的大力支持来采用自上而下的方法。

自上而下的方法将有助于采用组织在采用新系统进行业务运营时所需的过程和变化,以实现文化转变。这种承诺还可以帮助强制实施针对系统用户和项目团队的培训计划。这种培训可以显著帮助弥合项目团队现有经验与所需经验之间的差距。

BR1 – 系统能力限制

由于我们正在部署新系统,同时使用了一些在现有系统中使用的较旧组件,我们可能会遇到集成兼容性问题、较旧硬件和软件功能的限制,以及在流量、数据负载或存储方面的扩展限制。

此外,具有遗留组件的系统可能会比预期更快过时。这可能导致不再可用的软件和硬件组件,或者发布商停止支持的通知,这将缩短新系统的使用寿命。

当系统组件不再受支持时,供应商无法再提供部件更换或软件更新,甚至无法协助解决在不受支持的组件上出现的任何运营问题。这可能会严重影响系统的可靠性,并危及组织的业务连续性。

为了减轻系统能力限制的风险,我们必须对每个重用的遗留系统组件的供应商产品图有清晰的可见性和理解,以及对组件依赖性的清晰可见。这种理解将帮助我们评估该组件对部署的系统的可扩展性水平、未来运营可靠性和稳定性的影响。

BR2 – 高运营成本

正如我们之前解释的,整合新旧系统可能会引入许多部署和运营挑战,这会使系统变得更加复杂,并引入技术债务、高维护成本和高运营成本,以保持系统平稳运行。

安全性也是棕色地带部署中的一个主要关注点。技术债务,以及新旧组件的混合,可能会引入现有基础设施中不存在的安全漏洞,这可能导致昂贵的数据损坏、数据丢失、数据恢复、安全漏洞和不可逆转的声誉损害。

为了减轻这些风险,组织在部署之前应该彻底评估现有系统每个可重用组件的影响,并进行清晰的成本效益分析。

此外,在数据丢失或损坏的情况下,制定一个强大的灾难恢复和备份计划对于减轻一些运营成本风险至关重要。

所有上述风险示例及其缓解策略都应作为开发的风险缓解计划的一部分进行彻底讨论,包括逐步指南和文档。

如果发生任何这些风险,尤其是在棕色地带的情况下,即现有系统可能已经运行的情况下,应立即执行全面的回滚计划。回滚计划是克服部署挑战的下一步。以下小节将带我们了解回滚计划是什么以及它包含的内容。

回滚计划

记住墨菲定律,它指出“任何可能出错的事情都会出错”?

我们多久会创建一个坚实且精心制作的实施或迁移计划,期望系统平稳过渡,但在计划执行过程中却遭遇意外和奇怪的行为?

这里有一个经验法则,那就是在部署过程中任何事都可能出错。我们做了足够的规划和预防措施以确保不出错,但不幸的是,事情并不总是如我们所愿。我们可能仍然会忽略某些事情,系统错误可能会被触发,设备故障可能会发生,等等。

因此,制定回滚计划是维持业务连续性的必要条件。我们应该以包含明确的步骤和程序的方式制定计划,以便在更改之前将系统恢复到初始状态,并快速恢复正常运营。

如前所述,采用分阶段部署方法可以帮助快速回滚仅部分更改,这有助于我们避免浪费资源、宝贵的更改窗口时间和部署过程中投入的努力,以及开发部署计划。

测试、监控和调整

克服部署挑战的下一步是测试和验证系统需求与功能,以确保其满足性能、功能和操作要求。

当项目团队面临时间压力时,往往会将系统安全置于系统功能之上。数据安全和数据保护如果被忽视,可能会严重影响项目的整体部署和可靠性,尤其是对于处理关键用户数据并必须遵守某些法规和合规法案的系统,如 PCI 或 HIPAA。

因此,测试计划必须有一个专门针对系统安全和合规性测试的部分。聘请该领域的专业公司有助于最大限度地降低数据泄露或其他安全事件的风险。

随着我们逐渐将测试和生产负载应用到系统中,测试计划应该能够确保部署的系统具有可扩展性和足够的灵活性,以适应不断变化的需求和需求。这在棕色场部署期间的测试计划中是一个关键方面,因为与现有系统的集成可能会阻碍整体系统的能力。

然后,我们需要在整个项目期间持续和仔细地监控和审查已识别的风险,以确保风险缓解计划有效,并且任何新的风险都能得到及时识别和处理。

任何新识别的风险都必须通过项目变更管理流程纳入缓解计划。新识别的风险和缓解策略需要通知所有利益相关者,包括项目团队、组织的管理层和系统用户。

部署后和预生产审查

一旦部署完成,系统将投入运行,经过测试,并以预生产或 LA 模式运行。在关闭部署项目之前,我们需要评估我们风险管理计划的有效性,确定任何需要改进的领域,并记录我们的发现结果。然后,这些结果可以整合到同一项目部署工作中,或者结束现有部署,并为此启动一个新项目。

部署后评估将确保新系统的持续稳定性、性能和可靠性。

在棕色场中,我们可能会采用双模方法运行,其中两个系统同时运行并服务于用户,但以不同的方式和不同的级别。在这种情况下,我们需要考虑为每个系统建立特定的角色和责任矩阵。这有助于简化操作并提高系统的可支持性。

摘要

在本章中,我们介绍了绿色场和棕色场部署,以及它们之间的区别、优缺点、部署过程中的风险细节以及每个部署案例中每个风险的特定情况。

我们还提供了与绿色场和棕色场部署相关的风险示例,以及缓解这些风险的策略,以更好地理解成功部署新系统所涉及到的挑战。

本章讨论的主题作为对我们将在下一章学习内容的介绍。在下一章中,我们将应用本章所学的一些知识,并讨论测试、监控和更新我们新的 ABC-智能 MSA 系统的方法。

第十二章:部署、测试和运营智能 MSA 企业系统

在前面的章节中,我们详细讨论了微服务、单体架构、每种架构的优缺点、如何过渡到 MSA 以及如何使用 AI 服务使 MSA 系统更智能。我们还在 第十一章 中讨论了部署 MSA 系统的一些最佳实践。

在本章的最后,我们将整合整本书中涵盖的所有主题和概念,以了解我们如何通过实际和实用的示例应用我们所学的知识。

在我们深入细节之前,我们需要首先了解我们现有的系统是什么。

显然,每个组织都是不同的,并且有不同的部署需求、标准和依赖关系。一些组织将在绿地(全新环境)部署,而另一些组织将在棕地(现有环境)部署。为了向您详细介绍部署、测试和操作智能 MSA 系统的详细实际示例和步骤,我们将假设一个具有现有单体架构系统的棕地环境。

我们有时会以我们的 ABC-Monolith 作为现有系统的例子来阐述本章中涵盖的概念。在本章中,我们将涵盖以下主题:

  • 克服部署依赖关系

  • 部署 MSA 系统

  • 测试和调整 MSA 系统

  • 部署后的审查

克服系统依赖关系

在部署我们在 第十章 中构建的 ABC-Intelligent-MSA 系统之前,首先决定我们的部署策略是什么非常重要。根据需求、成本、复杂性和我们在 第十一章 中讨论的部署策略的优缺点,我们相信 ABC 系统的最佳部署策略将是渐增部署和金丝雀部署策略的混合。

这种部署策略将使我们能够在部署新的 ABC-Intelligent-MSA 系统的同时保持 ABC-Monolith 系统在线并服务用户,不间断地运行。我们将逐步用 ABC-Intelligent-MSA 系统中的相应微服务替换 ABC-Monolith 中的旧组件。这可以通过将流量从旧组件路由到 ABC-Intelligent-MSA 系统的微服务来实现。

虽然这种涓流式方法比其他部署方法具有更低的成本、更低的复杂性和更低的风险,但我们仍然需要仔细研究新旧组件之间的不兼容性、依赖关系和适当的集成。

此外,我们还需要评估我们的基础设施和现有系统 ABC-Monolith 的哪些组件可以在新的架构中重用,如果有的话。

可重用的 ABC-Monolith 组件和依赖关系

我们无法想到一个具体的 ABC-Monolith 代码库组件可以未经修改直接重用。所有的 ABC-Monolith 组件都必须从头开始重写或者进行不同程度的修改,以便与 ABC-Intelligent-MSA 系统兼容。

我们知道可以重用的 ABC-Monolith 和现有基础设施组件包括业务逻辑本身、服务器基础设施、操作系统、虚拟化基础设施、数据存储、网络基础设施、现有的监控和网络管理工具,以及一些软件和数据库许可。尽管如此,即使这些组件也可能需要更新或升级,以便执行新系统的功能。

在我们之前章节中列出的系统安装、命令行和代码示例中,我们使用了最新的 Ubuntu、Python 和数据库版本。然而,在实际情况下,情况可能并非如此;我们可能会在较旧的操作系统上运行单体应用程序,并且使用较旧的 Python 和/或数据库版本。

这些情况可能会导致旧组件和新组件之间产生一些不兼容性。例如,较旧的 Python 版本可能有一些已弃用的函数,在新 MSA 代码库中不再有效,因此,需要对现有系统进行一些更新或升级。此外,可能不同的技术栈也可能产生更多的依赖。

为了最小化这些依赖,我们宁愿在具有自己环境(包括自己的数据存储和自己的技术栈)的独立服务器或虚拟基础设施上部署新的系统组件。新的环境将有一个容器引擎,它将携带我们所有的 ABC-Intelligent-MSA 微服务。

重要的是要注意,每个系统都是不同的,可重用和非可重用的组件将根据现有的单体系统而有所不同。对现有系统组件进行彻底的分析和评估是必要的,以确定在迁移到微服务架构时可以和不能重用哪些组件。

缓解 ABC-Intelligent-MSA 部署风险

第十一章中讨论的一些风险与我们的场景相关。然而,我们仍然需要确定哪些资本支出风险是适用的,检查与部署时间、潜在的服务中断和运营成本相关的风险,并采取具体措施来缓解这些风险。

由于我们在实现中是在虚拟化基础设施之上使用容器,因此资本支出风险显著降低。只要现有基础设施有足够的存储和工作负载容量来吸收新的 ABC 系统,我们就是安全的。如果需要额外的基础设施资源,我们可能需要考虑一些容量规划和升级,以便在部署期间和部署后运行系统。

采用渐进式迁移方法给我们提供了快速赶上新部署技术中涉及的学习曲线的机会,这反过来有助于缓解系统故障风险和部署延迟。

逐步部署策略也有助于缓解其他 OPEX 风险。正如我们将在本章讨论的,在部署过程中,我们可以测试和监控新部署组件的性能,识别和解决任何问题,并在将所有流量重定向到这些新组件之前进行必要的调整。

另一种缓解 OPEX 风险的方法是通过建立一个结构化和透明的变更管理流程来建立一个强大的变更管理流程。这包括创建明确的指南,说明如何提出、评估、批准和实施变更,以及向相关利益相关者传达变更。

变更管理过程的一部分是回滚计划。如果特定的技术更改失败,回滚计划对于将系统恢复到操作状态至关重要。以下是我们需要考虑的步骤,以构建一个成功的回滚计划:

  1. 指定一些变更检查点,在这些检查点可能需要回滚。在我们的例子中,如果使用 ACL,ACL 将在将任何流量切换到新微服务之前部署。在变更期间(以及将一些测试流量切换到 ABC-Intelligent-MSA 之后),一些好的检查点示例如下:

    1. 测试 ABC-Monolith 和 ACL 之间的支付验证通信

    2. 测试 ACL 如何处理请求

    3. 测试 ACL 和 ABC-Intelligent-MSA 系统之间的通信

    4. 测试整体端到端请求的处理方式以及它们是否按预期处理

测试和排除 ACL、单体和 MSA 之间通信的常见 Docker 和 Linux 命令包括以下内容:

  • curl,用于模拟对 ACL 或特定微服务的 API 调用

  • netstat,用于检查特定服务是否正在积极监听连接,监听端口是什么,以及是否存在任何活动连接

  • docker inspect,用于返回特定微服务的配置、状态和网络设置的详细 JSON 信息

  • docker log,用于查看运行容器的日志

  1. 为每个先前指定的检查点制定一个逆转变化的计划。

  2. 在可能的情况下,在测试或预生产环境中测试回滚计划,以确保其可行性和完整性。

  3. 了解在每个指定的检查点,回滚计划需要完全执行的时间,并在您的变更中为其分配合理的时间。

  4. 在变更期间监控系统,并根据需要做出调整。

  5. 变更后进行事后分析,特别是在变更失败的情况下。

  6. 如果执行回滚计划,团队需要在事后分析变更失败的原因以及回滚计划的有效性。他们需要在安排下一次变更之前对部署和回滚计划进行必要的调整。

到目前为止,我们应该对部署依赖关系和风险有清晰的理解,并能够确定缓解它们的方法。我们现在准备好创建一个部署计划,并以最小化停机时间和保持业务连续性的方式执行它。

在下一节中,我们将构建在运行中的 ABC-Monolith 系统存在的情况下 ABC-Intelligent-MSA 系统的部署计划。

MSA 系统的部署

第九章第十章中,我们详细讨论了如何为 ABC-Intelligent-MSA 系统安装 Docker、容器和其他组件。这个安装主要是在实验室环境中完成的,没有特别考虑环境中的任何现有系统。我们基本上只是在模拟现实生活中的开发或预发布环境。

在本节中,我们将重点关注如何将我们构建的 ABC-Intelligent-MSA 系统逐步迁移到已经运行 ABC-Monolith 系统的 brownfield 生产环境中。目标是逐步提升 ABC-Intelligent-MSA 系统的运行能力,直到系统能够承载全部现有流量,然后完全淘汰旧的 ABC-Monolith 系统。所有操作都应尽量减少运营中断。

到目前为止,我们仍在生产中运行 ABC-Monolith,ABC-Intelligent-MSA 运行在预发布环境中。以下是与执行步骤详细分解的部署计划。

防腐层

我们的 ABC-Monolith 和 ABC-Intelligent-MSA 系统使用相同类型的 RESTful API 和相同的 JSON 数据格式。此外,我们的演示系统并不复杂,不足以证明 ACL 的必要性。因此,在我们的迁移中我们不需要 ACL。然而,我们还是在演示中开发了一个 ACL,以防你决定尝试它。

如果你感兴趣尝试 ACL,你需要做的第一步是启动 ACL。ACL 将充当缓冲区并处理 ABC-Monolith 和 ABC-Intelligent-MSA 系统之间的通信。

图 12.1:使用 ACL 进行部署

图 12.1:使用 ACL 进行部署

ACL 通常是针对组织特定情况、旧系统和新的 MSA 系统定制的特定代码。我们为 ABC 系统构建了abc_acl ACL。abc_acl代码可以在我们的 GitHub 仓库中找到。

在单独的主机或虚拟工作负载上部署所有新组件,包括abc_acl,会更有意义。然而,在我们的实验室示例中,为了简化,我们是在运行 ABC-Monolith 的主机上构建新系统的容器。

我们将门面适配器翻译器组件作为一个整体构建到 ACL 中,作为一个微服务的一部分。门面是为了与 ABC-Monolith 接口而创建的,适配器是为了与 ABC-Intelligent-MSA 接口,而翻译器用于输入/输出数据格式映射。由于我们在单体和 MSA 系统中使用相同的数据格式,翻译器代码不做任何处理,仅用作占位符。

我们可以像在第九章第十章中处理其他微服务一样设置并启动abc_acl微服务,使用docker build命令从 Dockerfile 构建abc_acl_image镜像,然后使用docker run命令创建abc_acl_container容器,如下所示:

$ docker build -t abc_msa_customer_management ~/

一旦镜像成功创建,使用以下命令运行容器,并开始在主机的 IP 上监听 TCP/8020 端口:

$ docker run -itd -p 8020:8080 --name abc_acl_container abc_acl_image

现在 ACL 正在运行,在将任何流量路由到它之前,是时候对其进行测试了。我们可以使用与之前章节中相同的方式使用 shell curl命令,或者我们可以使用一些 ACL 内置的 API 工具来验证连接。

以下是在主机机器上发出的curl命令,以确保 ACL 正在成功运行:

$ curl http://192.168.1.100:8020/
<!DOCTYPE html>
<head>
   <title>The Anti-Corruption Layer Microservice</title>
</head>
<body>
   <p>This is the ACL Microservice Part of ABC System. This ACL is used as part of the process of migrating ABC-Monolith to the new system, ABC-Intelligent-MSA </p>
</body>

以下是一个测试 ACL 的另一种方式的示例——更具体地说,是测试 ACL 与 ABC-Monolith 和 ABC-Intelligent-MSA 系统之间的通信:

$ curl http://192.168.1.100:8020/api?func=test_com
{"monolith_com_test": "Communication SUCCESSFUL", "msa_com_test": "Communication SUCCESSFUL"}

第一个curl命令确保 ACL 正在监听来自 ABC-Monolith 和 ABC-Intelligent-MSA 的 API 调用,而第二个curl命令确保 ACL 可以成功与这两个系统通信。

ACL 操作现在已验证;在下一小节中,我们将开始将 MSA 服务从预发布(或实验室)环境迁移到运行旧单体系统的实际生产环境。

集成 MSA 系统的服务

现在 ACL 已经启动并成功测试,我们准备开始将特定流量切换到 ABC-Intelligent-MSA 的特定部分。然而,由于在我们的演示示例中 ACL 实际上并不需要,我们将使用单体和 MSA 系统之间的直接交互。

请注意,根据现有的单体结构、设计和系统代码库,这个过程可能非常简单,也可能非常复杂。我们的部署策略需要在对单体系统进行一些代码更改后,才能将部分流量路由到新的 MSA。

因此,在某些系统中,我们完全可以在预发布环境中对 MSA 进行彻底测试,然后将 MSA 置于 LA 阶段,让部分生产流量通过系统进行更深入的测试。一旦我们对新的 MSA 系统的性能感到满意,我们就可以开始转发整个生产流量,并最终关闭旧的单体系统。

图 12**.2 展示了迁移前后的高级视图。在迁移过程中,我们将 ABC-Monolith 的一个特定功能路由到 ABC-Intelligent-MSA 中的一个微服务。该微服务应该能够替换 ABC-Monolith 系统中相应的功能。在测试该部分迁移的操作后,我们然后迁移另一个单体功能的流量,然后是另一个,以此类推,直到我们将所有 ABC-Monolith 功能迁移到 ABC-Intelligent-MSA 系统中。

图 12.2:我们的当前位置和目标位置的高级视图

图 12.2:我们的当前位置和目标位置的高级视图

我们可以从一个简单的微服务,如abc_msa_notify_user_container开始。我们可以通过将函数的代码替换为对abc_msa_notify_user_container的 API 调用,来路由目的地为 ABC-Monolith 中notify_user()函数的流量。所有用户流量仍然会通过 ABC-Monolith,但所有用户通知都将通过 ABC-Intelligent-MSA 处理。

同样地,abc_msa_customer_management_container应该替换 ABC-Monolith 中的register_customer()函数,以及place_order()order_status_update()函数等。

随着系统的稳定,我们逐渐迁移到其他 MSA 服务。该迁移周期在图 12**.3中显示。

通过遵循迁移周期,最终,所有 ABC-Monolith 功能都将被 ABC-Intelligent-MSA 系统中的微服务所取代。

图 12.3:微服务集成测试和调整周期

图 12.3:微服务集成测试和调整周期

图 12**.4 展示了在迁移过程中的系统状态快照。在图中,我们已经成功迁移了通知管理客户管理订单管理微服务,但其他任何微服务尚未迁移。

图 12.4:迁移过程中的系统快照

图 12.4:迁移过程中的系统快照

在单体功能迁移期间,密切监控系统,并在必要时使用回滚计划。一旦最后一个 ABC-Monolith 功能迁移到新系统,我们将在 ABC-Intelligent-MSA 系统上运行一个端到端测试,以确保系统正常运行且独立于 ABC-Monolith。

在测试过程中,测试所有微服务日志和统计信息是至关重要的。在迁移过程中,我们需要在每个步骤都建立正式的测试流程。测试过程将在下一节中更详细地描述。

保持两个系统运行一段时间,以防出现一些未预见的问题,并始终准备好应急计划。

最后一步是关闭单体。如果迁移步骤被正确遵循和测试,用户流量和系统操作不应受到影响。然而,复杂的系统可能有一个或多个组件仍在处理流量。为了避免这种情况下的业务中断,最好在维护窗口期间关闭单体,以便迁移团队能够分析任何意外问题并制定解决这些问题的计划。

在本节中,我们使用我们的 ABC 系统,通过 ACL 和直接单体到微服务的途径解释了 MSA 系统的部署过程。我们涵盖了需要采取的步骤、需要注意的事项以及如何以最小的系统中断尽可能使过渡到新系统变得顺利。

在下一节中,我们将介绍在每次微服务迁移后应计划并遵循的正式测试方法,以确保系统的可靠性和稳定性。

测试和调整 MSA 系统

在部署微服务之前,应该对每个微服务应用正式的测试或质量保证流程,以防止部署和生产过程中的错误。

在将 MSA 系统微服务部署到生产环境之前,需要对这些微服务执行一些测试。首先,测试微服务本身作为一个独立单元,在将其集成到 ABC 系统的任何部分之前——我们称之为单元测试。其次,测试该微服务集成到 ABC-Intelligent-MSA 系统中——我们称之为集成测试。第三,测试微服务在 ABC-Monolith 和 ABC-Intelligent-MSA 系统之间的操作混合期间的功能。

每次部署新的微服务时,测试 ABC 系统功能对于确保成功迁移和系统能够正确运行并承受应用流量负载和用户请求至关重要。

构建结构化的测试用例是测试过程中的重要部分。测试用例是一系列步骤,描述了如何测试系统的一个特定功能或功能。这些测试用例应该定义清晰、易于理解,并覆盖所有可能的场景。

创建测试用例应包括以下主要步骤:

  1. 确定系统的需求和我们要测试的功能或功能。

  2. 编写一个描述测试该功能或功能的步骤的测试用例。

  3. 在测试用例中,指定运行测试所需的任何先决条件。

  4. 根据测试用例的预期结果确定通过/失败标准。

  5. 运行测试用例,并将测试结果与预期结果进行比较。根据通过/失败标准,记录测试结果为简单的 PASS 或 FAIL。

以下是对通知管理微服务的简单测试用例示例。该测试用例验证微服务是否实际上向注册用户的手机号码发送短信通知。还应编写另一个测试用例来测试微服务的电子邮件发送功能。我们可以为每个单独的微服务编写所需数量的测试用例,以及针对系统功能整体的测试用例。

测试 用例详情
标题
ID
描述
需求
测试设置
流程
测试类型
通过/失败标准

表 12.1 – ABC-Intelligent-MSA 中通知管理的示例测试用例

测试 ABC-Intelligent-MSA 系统的 AI 服务可能更具挑战性,传统的测试用例方法可能不足以满足需求。测试系统的 AI 部分需要多层次的测试方法,包括将微服务本身单独测试(单元测试)、集成测试、功能测试、性能测试、数据验证测试以及人工在环测试。通过将这些方法结合起来构建测试用例,我们可以确保系统的 AI 组件按预期工作,并做出准确的预测和决策。

在本节中,我们讨论了构建结构化测试过程的重要性,并在我们的系统测试过程中构建了一个测试用例示例。我们讨论了如何创建测试用例以及识别需求和预期结果。

在下一节中,我们将讨论在 ABC-Intelligent-MSA 系统部署完成后进行部署后审查的重要性。本节还将涵盖不同的部署后审查类型,包括用户反馈审查。

部署后审查

ABC-Intelligent-MSA 系统目前正在运行,但它还没有运行足够长的时间来保证其在典型流量模式和负载下的稳定性和弹性。部署后审查对于确保 ABC 系统部署的成功和合规性,以及增强其功能性和整体用户满意度至关重要。

在部署后审查期间,我们需要密切监控系统,寻找可能发生的任何错误、缺陷或任何其他操作问题。然后,我们需要提出解决系统问题的建议,并对系统进行必要的改进,以确保系统满足其创建时的用户需求。

我们需要对系统中构建的 AI 服务进行特殊监控,以确保它们按预期运行,并持续改进自身和系统的整体操作。仔细查看我们在第十章中讨论的 AI 服务日志,对于确保系统的稳定性和性能提升至关重要。

以下是在进行部署后审查时需要考虑的一些方面和标准。

检查新系统的性能

我们首先定义性能指标,这将帮助我们为系统创建一个基线,以便了解系统在响应时间、用户交互、网络流量等方面的预期表现。我们可以使用互联网上可用的工具或我们在第十章中讨论过的ms_perfmon.py来衡量新系统的性能,并将其与单体性能进行比较。

旧系统和新系统性能之间的差异将高度依赖于两种情况下使用的设计、架构、操作标准和基础设施。

识别和修复系统缺陷

这与之前讨论的测试和调整过程有关,以及这个过程应该如何进行。在此需要指出的是,在部署后,识别系统缺陷还不是 QA 过程的一部分,直到它们首先在组织的缺陷跟踪系统中被记录。

我们在这里讨论的是监控系统操作方面,并确保系统的适当可支持性。支持过程可能会导致在系统部署后记录特定问题。稍后,需要对客户支持案例及其严重程度进行彻底调查,以解决和修复这些问题。

通过收集客户反馈,我们还可以识别系统问题,正如我们将在下一部分讨论的那样,以及从系统部署后进行的各种审计流程的结果中识别问题。

合规性

定期对系统进行维护和更新,以保持其平稳运行。如在第第八章中简要讨论的,相当一部分合规可以通过自动化或商业工具来完成。这些工具将帮助审计系统以符合不同类型的合规性,如 GDPR、PCI、HIPAA、SCSEM 等。组织必须遵守的具体合规性将取决于组织的业务本身、系统的性质以及它所服务的流程和用户。

首先确定适用于新系统的相关法规和标准。这可能包括数据隐私法规、行业特定标准以及网络安全标准。

按照我们在上一章中描述的方式,进行风险评估,以确定任何潜在的违规领域及其相关风险。这可能涉及审查系统设计、架构和数据处理的流程。然后,制定缓解计划以减轻确定的风险。

确保组织的员工充分了解合规性、其重要性以及在此方面的个人角色和责任。保持员工培训是确保组织遵守特定规则、法规和特定行业合规性的另一个方面。

合规流程不是一次性的,组织必须定期进行审计以维持合规性。审计可能包括定期运行特定的自动化审计工具,以及通过检查系统日志、数据检查、物理和数字安全检查等方式进行手动系统审计。

系统维护和更新

就像你的预防性汽车维护一样,定期进行系统维护和更新对于确保系统平稳运行,避免突然的意外故障至关重要。

通过规划、准备、测试、实施、监控、记录流程以及采取主动角色,我们可以确保我们新部署的系统按预期运行,并能够最大限度地减少运营中断。以下是在维护计划中需要考虑的几点:

  1. 制定一个定期的维护计划。这对于成功和可靠的运营是必不可少的。这包括确定系统哪些部分需要更新,需要执行哪些维护活动以及频率,优先考虑维护任务,并确定所需的资源。

  2. 确保您已经制定了定期的系统备份计划,并在任何维护工作之前进行更新。这在任何工作失误发生时恢复系统到原始状态非常重要。

  3. 在实际应用更新或更改之前,测试任何计划的工作。在实验室或预演环境中彻底测试更改,以确保其按预期运行。

  4. 在更新部署后监控系统。这包括监控性能指标、运行自动化检查、检查用户反馈和检查系统日志。

  5. 更新您的文档以反映变更,并记录维护和更新结果。这些文档将有助于确保维护和更新过程在未来可重复使用,并在未来可能发生任何问题时帮助解决问题。

在开始时,维护计划可能不如您期望的那么完美,但随着系统生命周期中的重复执行,该流程最终将得到非常精确的优化。

用户满意度

监控和提升用户满意度有时在部署任何新 IT 系统的成功中会被低估。通过收集系统内部和外部客户的反馈,分析这些反馈,优先考虑变更,实施变更,监控进度,并持续改进,我们可以确保系统满足客户需求。

以下是在部署后确保高客户满意度的四步周期:

图 12.5:四步客户满意度周期

图 12.5:四步客户满意度周期

  1. 监控我们客户满意度的第一步是收集系统用户的反馈。反馈可以通过调查、直接客户互动和访问、电话交谈等方式收集。

  2. 分析收集到的反馈,以识别常见的投诉、常见模式和可能在系统测试阶段未涵盖的具体用例。这将帮助我们了解系统的优势以及我们需要改进的领域。

  3. 优先考虑从收集到的反馈中决定的任何系统变更。这部分应对客户满意度产生最大影响。它将向客户表明您正在解决他们的关注点,回应他们的请求,有时甚至主动满足客户需求。

  4. 按优先级实施变更。从影响最大、努力最低的变更开始,类似于我们在第十一章“风险缓解”部分和图 11**.2中讨论的内容。

我们需要持续收集客户反馈,以定期监控客户满意度进展。这将确保正在实施的变化能够产生预期的客户满意度效果。

四步流程将有助于持续满足客户需求并相应地提高客户满意度。该流程还有助于不断改进系统功能、支持性、可靠性和稳定性,以增强整体用户体验。

在本节中,我们介绍了部署后审查过程,审查时需要考虑的不同方面和活动,以及这对于 ABC-Intelligent-MSA 系统整体成功运作的重要性。

摘要

在本章中,我们讨论了成功部署新系统所涉及的各个步骤。我们讨论了克服系统部署依赖性的重要性,构建结构化测试用例的重要性,以及测试和调整系统的步骤。

通过遵循本章中概述的步骤,组织可以确保其 MSA 系统的成功部署,包括克服依赖性,在过渡阶段与单体系统集成,测试和调整系统,以及进行部署后审查。本章最后强调了遵循客户满意度循环和让客户参与新系统操作适应过程的重要性。

posted @ 2025-09-04 14:14  绝不原创的飞龙  阅读(2)  评论(0)    收藏  举报