1. 前言
停下手头的工作,让你的同事定义“云原生”一词。你很可能会得到几个不同的答案。
1.1 让我们从一个简单的定义开始:
云原生架构和技术是一种设计、构建和操作在云中构建并充分利用云计算模型的工作负载的方法。
1.2 云原生计算基金会给出了官方定义:
云原生技术使组织能够在现代动态环境(例如公共云、私有云和混合云)中构建和运行可扩展的应用程序。容器、服务网格、微服务、不可变基础设施和声明性 API 就是这种方法的典型代表。
这些技术可实现松散耦合的系统,使其具有弹性、可管理性和可观察性。结合强大的自动化功能,工程师能够以最少的辛劳频繁且可预测地进行高影响的更改。
云原生关乎速度和敏捷性。业务系统正在从支持业务能力发展成为加速业务速度和增长的战略转型武器。将新想法立即推向市场势在必行。
与此同时,业务系统也变得越来越复杂,用户的要求也越来越高。他们期望快速响应、创新功能和零停机时间。性能问题、重复错误和无法快速行动已不再可接受。您的用户将访问您的竞争对手。云原生系统旨在适应快速变化、大规模和弹性。
以下是一些已实施云原生技术的公司。想想他们所实现的速度、敏捷性和可扩展性。
公司 | 经验 |
---|---|
Netflix | 有600个服务投入生产。每天部署100次。 |
优步 | 有 1,000 项服务投入生产。每周部署数千次。 |
微信 | 有3,000个作品服务投入生产。每天部署1,000次。 |
如您所见,Netflix、Uber 和微信都公开了由许多独立服务组成的云原生系统。这种架构风格使他们能够快速响应市场状况。它们可以即时更新实时复杂应用程序的小部分,而无需完全重新部署。它们可以根据需要单独扩展服务。
2. 云原生的支柱
云原生的速度和敏捷性源自多种因素。最重要的是云基础设施。但还有更多:图 1-3 所示的其他五个基础支柱也为云原生系统提供了基石。
图 1-3 . 云原生基础支柱
让我们花点时间来更好地理解每个支柱的意义。
2.1 云端(The cloud)
云原生系统充分利用云服务模型。
**这些系统专为在动态的虚拟化云环境中蓬勃发展而设计,广泛使用平台即服务 (PaaS)计算基础架构和托管服务。**它们将底层基础架构视为一次性用品- 只需几分钟即可配置,并可根据需要通过自动化调整大小、扩展或销毁。
考虑一下我们对待宠物和商品的方式之间的区别。在传统的数据中心,服务器被视为宠物:一台物理机器,被赋予一个有意义的名字,并受到照顾。您可以通过向同一台机器添加更多资源(扩展)来扩展。如果服务器生病了,您可以照顾它恢复健康。如果服务器不可用,每个人都会注意到。
商品服务模型有所不同。您将每个实例配置为虚拟机或容器。它们是相同的,并分配有系统标识符,例如 Service-01、Service-02 等。您可以通过创建更多实例(横向扩展)进行扩展。当实例不可用时,没有人会注意到。
商品模型包含不可变的基础设施。服务器无需维修或修改。如果一台服务器发生故障或需要更新,则会将其销毁并配置新的服务器 - 所有这些都通过自动化完成。
云原生系统采用商品服务模式。无论基础设施规模如何扩大或缩小,它们都能持续运行,与运行在哪台机器上无关。
Azure 云平台通过自动扩展、自我修复和监控功能支持这种高度弹性的基础设施。
2.2 现代设计(Modern design)
构建基于云的应用程序的一种广泛接受的方法是十二要素应用程序。它描述了开发人员遵循的一组原则和实践,以构建针对现代云环境优化的应用程序。特别关注跨环境的可移植性和声明性自动化。
虽然适用于任何基于 Web 的应用程序,但许多从业者认为十二要素是构建云原生应用的坚实基础。基于这些原则构建的系统可以快速部署和扩展,并添加功能以快速响应市场变化。
下表重点介绍了十二因素方法:
事件 | 解释 |
---|---|
1 - 代码库 | 每个微服务都有一个单独的代码库,存储在其自己的存储库中。通过版本控制进行跟踪,可以部署到多个环境(QA、Staging、Production)。 |
2 - 依赖项 | 每个微服务隔离并节省自己的依赖项,从而接受变化而不影响整个系统。 |
3 - 配置 | 配置信息从微服务中移出,并通过代码外部的配置管理工具进行外部化。只要应用了正确的配置,相同的配置就可以跨环境传播。 |
4 – 支持服务 | 辅助资源(数据存储、存储、消息代理)应通过可接收的URL公开。这样可以将资源与应用程序分离,可以相互分离。 |
5 - 构建、发布、运行 | 第二个发布都必须严格区分构建、发布和运行阶段。每个阶段都应标记唯一的ID并支持回滚功能。现代CI/CD系统有助于实现这一原则。 |
6 - 流程 | 每个微服务都应该在自己的进程中执行,与其他正在运行的服务隔离。将所需的外部状态化到全局存储或数据存储等支持服务。 |
7 - 端口绑定 | 每个微服务都应该是独立的,其接口和功能应该在自己的端口上公开。这样做可以与其他微服务隔离。 |
8 - 並發 | 需要增加容量时,跨相同进程(副本)水平扩展多个服务,而不是在功能最强大的机器上扩展单个大型实例。开发应用程序同时进行,从而在云环境中扩展。 |
9 - 可处置性 | 服务实例应该是瞬时的。支持快速启动以增加可扩展性的机会,并支持正常关闭以使系统处于正确的状态。Docker 容器以及编排器本身就满足了这个要求。 |
10. 开发/生产价格 | 使整个应用程序生命周期的环境问题相似,避免代价高昂的捷径。在这里,采用容器可以通过相同推广的执行环境做出巨大贡献。 |
11 - 日志记录 | 将微服务生成的日志视为事件流。使用事件聚合器处理它们。将日志数据传播到数据挖掘/日志管理工具(如Azure Monitor或Splunk),并最终传播到长期文档。 |
12 - 管理流程 | 将管理任务(例如数据清理或计算分析)作为一次性流程运行。使用独立工具从生产环境调用这些任务,但与应用程序分开。 |
在《超越十二要素应用》一书中 ,作者 Kevin Hoffman 详细介绍了最初的十二要素(撰写于 2011 年)。此外,他还讨论了反映当今现代云应用程序设计的三个额外要素。
新因素 | 解释 |
---|---|
13 - API 优先级 | 将一切变成服务。假设您的代码将被前端客户端、网关或其他服务使用。 |
14 - 遥测I 优先级 | 在工作站上,您可以深入了解应用程序及其行为。但在云中,您却无法做到这一点。确保您的设计包括收集监控、特定领域和健康/系统数据。 |
15 - 身份验证/授权I 优先级 | 从一开始就实现身份识别。考虑公共云中可用的RBAC(基于角色的访问控制)功能。 |
2.3 Azure 完善架构框架(Well-Architected Framework)
设计和部署基于云的工作负载可能具有挑战性,尤其是在实施云原生架构时。Microsoft 提供行业标准最佳实践,帮助您和您的团队提供强大的云解决方案。
Microsoft 完善的架构框架提供了一组指导原则,可用于提高云原生工作负载的质量。该框架由架构卓越的五个支柱组成:
果子 | 描述 |
---|---|
成本管理 | 着眼于近期创造增量价值。应用构建-测量-学习原则来加快时间,同时避免资本密集型解决方案。采用随用随付策略,在扩展时进行投资,而不是预先进行大量投资。 |
卓越运营 | 实现环境和操作自动化,提高速度并减少人员出错。快速回滚或转发问题更新。从一开始就实施监控和诊断。 |
性能效率 | 高效满足您的工作负载需求。支持水平扩展(横向扩展)将其设计放到您的系统中。持续进行性能和负载测试以识别潜在瓶颈。 |
可靠性 | 构建所有弹性又可用的工作负载。弹性使工作负载能够从故障中恢复并继续运行。可用性确保用户随时可以访问您的工作负载。设计应用程序以预期故障并恢复总量。 |
安全 | 在应用程序的整个生命周期内实施安全性,从设计和实施到部署和运营。密切关注身份管理、基础设施访问、应用程序安全以及数据主权和加密。 |
首先,Microsoft 提供了一组在线评估,帮助您根据五个精心设计的支柱评估当前的云工作负载。
2.4 微服务(Microservices)
云原生系统采用微服务,这是构建现代应用程序的流行架构风格。
微服务是一组通过共享结构进行交互的分布式小型独立服务,具有以下特点:
- 每个都在更大的领域环境中实现了特定的业务能力。
- 每个都是自主开发的,可以独立部署。
- 每个都是独立的,封装了自己的数据存储技术、依赖项和编程平台。
- 每个进程都在自己的进程中运行,并使用标准通信协议(如 HTTP/HTTPS、gRPC、WebSockets 或AMQP)与其他进程通信。
- 它们组合在一起形成一个应用程序。
图 1-4 对比了单体式应用程序方法与微服务方法。请注意单体式应用程序如何由分层架构组成,该架构在单个进程中执行。它通常使用关系数据库。然而,微服务方法将功能划分为独立的服务,每个服务都有自己的逻辑、状态和数据。每个微服务都托管自己的数据存储。
图 1-4。单片架构与微服务架构
请注意微服务如何促进本章前面讨论过的十二要素应用中的流程原则。
因素#6 指定“每个微服务应在自己的进程中执行,与其他正在运行的服务隔离。”
2.4.1 为何采用微服务?
微服务提供敏捷性。
在本章前面,我们比较了单体式电子商务应用程序和微服务电子商务应用程序。在这个例子中,我们看到了一些明显的好处:
每个微服务都有独立的生命周期,可以独立发展并频繁部署。您不必等待季度发布来部署新功能或更新。您可以更新实时应用程序的一小部分,而不必担心破坏整个系统。无需完全重新部署应用程序即可进行更新。
每个微服务都可以独立扩展。您无需将整个应用程序作为一个单元进行扩展,只需扩展那些需要更多处理能力才能达到所需性能水平和服务级别协议的服务即可。细粒度扩展可以更好地控制系统,并有助于降低总体成本,因为您扩展的是系统的一部分,而不是全部。
一本了解微服务的优秀参考指南是《.NET 微服务:容器化 .NET 应用程序的架构》。本书深入探讨了微服务的设计和架构。它是全栈微服务参考架构的配套读物,可从 Microsoft 免费下载。
2.4.2 开发微服务
微服务可以在任何现代开发平台上创建。
Microsoft .NET 平台是一个很好的选择。它是免费且开源的,具有许多简化微服务开发的内置功能。.NET 是跨平台的。应用程序可以在
Windows、macOS 和大多数 Linux 版本上构建和运行。
.NET 性能出色,与 Node.js 和其他竞争平台相比得分较高。有趣的是,TechEmpower在许多 Web 应用程序平台和框架上进行了广泛的性能基准测试。.NET 得分进入前 10 名 - 远高于 Node.js 和其他竞争平台。
.NET由 Microsoft 和 GitHub 上的 .NET 社区维护。
2.4.3 微服务挑战
虽然分布式云原生微服务可以提供极大的灵活性和速度,但它们也带来了许多挑战:
2.4.3.1 沟通
前端客户端应用程序将如何与后端核心微服务通信?您是否允许直接通信?或者,您是否可以使用提供灵活性、控制力和安全性的网关外观来抽象后端微服务?
后端核心微服务如何相互通信?您是否允许直接 HTTP 调用,因为这可能会增加耦合并影响性能和敏捷性?或者您是否会考虑使用队列和主题技术来解耦消息传递?
云原生通信模式章节涵盖了通信内容。
2.4.3.2 弹性
微服务架构将您的系统从进程内网络通信转移到进程外网络通信。在分布式架构中,当服务 B 未响应来自服务 A 的网络调用时会发生什么?或者,当服务 C 暂时不可用并且调用它的其他服务被阻止时会发生什么?
“弹性”一词已在“云原生弹性”一章中介绍。
2.4.3.3分布式数据
根据设计,每个微服务都封装了自己的数据,并通过其公共接口公开操作。如果是这样,您如何查询数据或实现跨多个服务的事务?
分布式数据在云原生数据模式章节中介绍。
2.4.3.4 密钥
您的微服务如何安全地存储和管理秘密和敏感配置数据?
密钥详细介绍了云原生安全。
2.4.3.5 使用 Dapr 管理复杂性
Dapr是一个分布式开源应用程序运行时。通过可插入组件的架构,它大大简化了分布式应用程序背后的管道。它提供了一种动态粘合剂,将您的应用程序与 Dapr 运行时中预构建的基础架构功能和组件绑定在一起。
图 1-5 从 20,000 英尺的高度显示了 Dapr。
图 1-5 . 20,000 英尺高的 Dapr。
在图的顶行中,请注意 Dapr 如何为流行的开发平台提供特定于语言的 SDK。Dapr v1 包括对 .NET、Go、Node.js、Python、PHP、Java 和 JavaScript 的支持。
虽然特定于语言的 SDK 可以增强开发人员的体验,但 Dapr 与平台无关。在底层,Dapr 的编程模型通过标准 HTTP/gRPC 通信协议公开功能。任何编程平台都可以通过其原生 HTTP 和 gRPC API 调用 Dapr。
图中央的蓝色方框代表 Dapr 构建块。每个构建块都公开了您的应用程序可以使用的分布式应用程序功能的预构建管道代码。
组件行代表您的应用程序可以使用的大量预定义基础架构组件。您可以将组件视为您无需编写的基础架构代码。
底行突出显示了 Dapr 的可移植性以及它可以运行的多样化环境。
展望未来,Dapr有可能对云原生应用程序开发产生深远的影响。
2.5 容器(Containers)
**在任何云原生对话中,听到“容器”一词都是很自然的事情。**在《云原生模式》一书中,作者 Cornelia Davis 指出,“容器是云原生软件的重要推动者。” 云原生计算基金会将微服务容器化作为其云原生路线图的第一步- 为开始云原生之旅的企业提供指导。
微服务的容器化既简单又直接。代码、依赖项和运行时都打包成一个二进制文件,称为容器镜像。镜像存储在容器注册表中,后者充当镜像的存储库或库。注册表可以位于您的开发计算机、数据中心或公共云中。Docker 本身通过Docker Hub维护一个公共注册表。Azure 云具有私有容器注册表,用于将容器镜像存储在将运行它们的云应用程序附近。
当应用程序启动或扩展时,您可以将容器映像转换为正在运行的容器实例。该实例可在安装了容器运行时引擎的任何计算机上运行。您可以根据需要拥有任意数量的容器化服务实例。
图 1-6 显示了三种不同的微服务,每个微服务都有自己的容器,全部在单个主机上运行。
图 1-6 . 在容器主机上运行多个容器
请注意,每个容器如何维护自己的一组依赖项和运行时,这些依赖项和运行时可能彼此不同。在这里,我们看到在同一主机上运行的不同版本的 Product 微服务。每个容器共享一部分底层主机操作系统、内存和处理器,但彼此隔离。
请注意容器模型如何很好地融合了十二要素应用中的依赖关系原则。
因素 #2 指定“每个微服务隔离并打包自己的依赖项,从而无需影响整个系统即可接受变化。”
容器支持 Linux 和 Windows 工作负载。Azure 云公开支持这两种工作负载。有趣的是,Linux 而不是 Windows Server 已成为 Azure 中更受欢迎的操作系统。
虽然有多家容器供应商,但Docker占据了市场的大部分份额。该公司一直在推动软件容器运动。它已成为打包、部署和运行云原生应用程序的事实标准。
2.5.1 为什么选择容器?
容器提供可移植性并保证跨环境的一致性。通过将所有内容封装到单个包中,您可以将微服务及其依赖项与底层基础架构隔离开来。
您可以在托管 Docker 运行时引擎的任何环境中部署容器。容器化工作负载还消除了使用框架、软件库和运行时引擎预先配置每个环境的费用。
通过共享底层操作系统和主机资源,容器的占用空间比完整虚拟机小得多。较小的尺寸增加了给定主机可以同时运行的密度或微服务数量。
2.5.2 容器编排
虽然 Docker 等工具可以创建映像并运行容器,但您还需要工具来管理它们。容器管理由一种称为容器编排器的特殊软件程序完成。当大规模操作许多独立运行的容器时,编排至关重要。
图 1-7 显示了容器编排器自动执行的管理任务。
图 1-7 . 容器编排器的作用
下表描述了常见的编排任务。
任务 | 解释 |
---|---|
调度 | 自动配置容器实例。 |
亲和性/反亲和性 | 将容器配置在各处附近或远处,有助于提高可用性和性能。 |
监测 | 自动检测并修复故障。 |
故障转移 | 自动将失败的实例重新配置到健康的机器上。 |
伸缩 | 自动添加或删除集装箱以满足需求。 |
网络 | 管理容器通信的网络覆盖。 |
服务发现 | 使容器能够相互定位。 |
滚动升级 | 协调增量升级,实现零配置。有问题的更改自动回滚。 |
请注意容器编排器如何采用十二要素应用中的可处置性和并发性原则。
因素 #9 指定“服务实例应该是一次性的,有利于快速启动以增加可扩展性机会,并优雅地关闭以使系统处于正确状态。” Docker
容器以及编排器本质上满足了这一要求。
因素#8指定“服务跨大量小型相同进程(副本)进行扩展,而不是在最强大的机器上扩展单个大型实例。”
虽然存在多种容器编排工具,但Kubernetes已成为云原生领域事实上的标准。它是一个可移植、可扩展的开源平台,用于管理容器化工作负载。
您可以托管自己的 Kubernetes 实例,但随后您将负责配置和管理其资源 - 这可能很复杂。 Azure 云将 Kubernetes 作为托管服务。 Azure Kubernetes 服务 (AKS)和Azure Red Hat OpenShift (ARO)都使您能够充分利用 Kubernetes 作为托管服务的功能和强大功能,而无需安装和维护它。
容器编排在“扩展云原生应用程序”中详细介绍。
2.6 支撑服务(Backing services)
云原生系统依赖于许多不同的辅助资源,例如数据存储、消息代理、监控和身份服务。这些服务称为支持服务。
图 1-8 显示了云原生系统使用的许多常见支持服务。
图 1-8 . 常见的支持服务
您可以托管自己的支持服务,但随后您将负责许可、配置和管理这些资源。
云提供商提供丰富的托管支持服务。您无需拥有服务,只需使用它即可。云提供商大规模运营资源,并负责性能、安全性和维护。监控、冗余和可用性内置于服务中。提供商保证服务级别性能并全力支持其托管服务 - 开具工单,他们就会解决您的问题。
云原生系统更青睐云供应商提供的托管支持服务。这样可以节省大量时间和人力。自行托管并遇到麻烦的运营风险很快就会变得昂贵。
最佳实践是将支持服务视为附加资源,动态绑定到微服务,并将配置信息(URL 和凭据)存储在外部配置中。本章前面讨论的十二要素应用程序中详细说明了此指导。
因素 #4指定支持服务“应通过可寻址 URL 公开。这样做可以将资源与应用程序分离,使其可以互换。”
因素#3指定“配置信息移出微服务并通过代码之外的配置管理工具进行外部化。”
使用此模式,无需更改代码即可连接和分离支持服务。您可以将微服务从 QA 提升到暂存环境。您可以更新微服务配置以指向暂存中的支持服务,并通过环境变量将设置注入容器中。
云供应商提供 API 供您与其专有的支持服务进行通信。这些库封装了专有管道和复杂性。但是,直接与这些 API 通信会将您的代码与特定的支持服务紧密耦合。隔离供应商 API 的实现细节是一种广泛接受的做法。引入一个中介层或中间 API,将通用操作暴露给您的服务代码,并将供应商代码包装在其中。这种松散耦合使您可以将一个支持服务换成另一个,或者将您的代码移动到不同的云环境,而无需更改主线服务代码。前面讨论过的 Dapr 遵循此模型,并带有一组预构建的构建块。
最后,支持服务还促进了本章前面讨论过的十二因素应用中的无状态原则。
因素 #6规定:“每个微服务应在其自己的进程中执行,与其他正在运行的服务隔离。将所需状态外部化到支持服务,例如分布式缓存或数据存储。”
云原生数据模式和云原生通信模式中讨论了支持服务。
2.7 自动化(Automation)
如您所见,云原生系统采用微服务、容器和现代系统设计来实现速度和敏捷性。但这只是故事的一部分。如何配置这些系统运行的云环境?如何快速部署应用功能和更新?如何完善整个图景?
输入被广泛接受的基础设施即代码(IaC)实践。
借助 IaC,您可以自动化平台配置和应用程序部署。您本质上是将软件工程实践(例如测试和版本控制)应用于 DevOps 实践。您的基础架构和部署是自动化、一致且可重复的。
2.7.1 自动化基础设施
Azure 资源管理器、Azure Bicep、 HashiCorp 的Terraform和Azure CLI等工具使您能够以声明方式编写所需的云基础架构脚本。资源名称、位置、容量和机密都是参数化的和动态的。脚本已进行版本控制并作为项目工件签入源代码管理。您可以调用脚本来跨系统环境(例如 QA、暂存和生产)配置一致且可重复的基础架构。
从本质上讲,IaC 是幂等的,这意味着您可以反复运行相同的脚本而不会产生副作用。如果团队需要进行更改,他们可以编辑并重新运行脚本。只有更新的资源会受到影响。
在文章《什么是基础设施即代码》中,作者 Sam Guckenheimer 描述了“实施 IaC 的团队如何快速、大规模地提供稳定的环境。他们避免手动配置环境,并通过代码表示其环境的期望状态来强制一致性。使用 IaC 的基础设施部署是可重复的,并可防止由配置漂移或缺少依赖项导致的运行时问题。DevOps 团队可以使用一套统一的实践和工具协同工作,以快速、可靠、大规模地交付应用程序及其支持基础设施。”
2.7.2 自动化部署
前面讨论过的十二要素应用程序在将完整的代码转换为可运行的应用程序时需要单独的步骤。
因素 #5规定“每次发布都必须严格区分构建、发布和运行阶段。每个阶段都应标记唯一的 ID,并支持回滚功能。”
现代 CI/CD 系统有助于实现这一原则。它们提供单独的构建和交付步骤,有助于确保用户随时可用的一致且高质量的代码。
图 1-9 显示了部署过程的分离。
图 1-9 . CI/CD 管道中的部署步骤
在上图中,要特别注意任务的分离:
- 开发人员在他们的开发环境中构建一个功能,通过所谓的代码“内循环”进行迭代,运行和调试。
- 完成后,该代码将被推送到代码存储库,例如 GitHub、Azure DevOps 或 BitBucket。
- 推送会触发构建阶段,将代码转换为二进制工件。该工作通过持续集成 (CI)管道实现。它会自动构建、测试和打包应用程序。
- 发布阶段获取二进制工件,应用外部应用程序和环境配置信息,并生成不可变的发布。发布将部署到指定环境。工作通过持续交付
(CD)管道实施。每个发布都应该是可识别的。您可以说,“此部署正在运行应用程序的 2.1.1 版。” - 最后,发布的功能在目标执行环境中运行。发布是不可变的,这意味着任何更改都必须创建新的发布。
通过应用这些实践,组织彻底改变了软件发布方式。许多组织已从每季度发布一次转变为按需更新。目标是在开发周期的早期发现问题,因为此时修复这些问题的成本较低。集成间隔越长,解决问题的成本就越高。通过集成过程的一致性,团队可以更频繁地提交代码更改,从而提高协作和软件质量。