什么是云原生?

提示

此内容摘自电子书《为 Azure 构建云原生 .NET 应用程序》,可在 .NET 文档上获取,也可作为免费可下载的 PDF 脱机阅读。

《为 Azure 构建云原生 .NET 应用程序》电子书封面缩略图。

停止你正在执行的操作,让你的同事定义术语“云原生”。 你很有可能会得到几个不同的答案。

我们从一个简单的定义开始:

云原生体系结构和技术是一种方法,用于设计、构造和操作在云中构建并充分利用云计算模型的工作负载。

云原生计算基金会提供了官方定义

云原生技术使组织能够在新式动态环境(如公有云、私有云和混合云)中构建和运行可缩放的应用程序。 容器、服务网格、微服务、不可变基础结构和声明性 API 便是此方法的范例。

这些技术实现了可复原、可管理且可观察的松散耦合系统。 它们与强大的自动化相结合,使工程师能够在尽量减少工作量的情况下,以可预测的方式频繁地进行具有重大影响力的更改。

云原生是关于速度和敏捷性的。 业务系统正在从实现业务功能演变为加快业务速度和增长的战略转型武器。 必须立即将新想法推向市场。

同时,业务系统也变得越来越复杂,用户要求也越来越高。 他们希望实现快速响应、创新工作和零故障时间。 他们再也无法接受性能问题、反复出现的错误以及不能快速移动。 你的用户将访问你的竞争对手。 云原生系统旨在支持快速更改、大规模操作和复原能力。

下面是一些实现了云原生技术的公司。 请考虑其实现的速度、敏捷性和可伸缩性。

Company 体验
Netflix 在生产环境中有 600 多种服务。 每天部署 100 次。
Uber 在生产环境中有 1,000 多种服务。 每周部署数千次。
微信 在生产环境中有 3,000 多种服务。 每天部署 1,000 次。

如你所见,Netflix、Uber、微信公开了由许多独立服务组成的云原生系统。 这种体系结构样式使它们能够快速响应市场条件。 它们即时更新实时、复杂应用程序的小区域,而无需完全重新部署。 它们根据需要单独缩放服务。

云原生的支柱

云原生的速度和敏捷性源自许多因素。 最重要的是云基础结构。 不过还有其他内容:图 1-3 中显示的五个其他基础支柱也为云原生系统提供基础。

云原生基础支柱

图 1-3。 云原生基础支柱

让我们花一些时间来更好地了解每个支柱的重要性。

云原生系统充分利用了云服务模型。

这些系统设计为可在动态的虚拟化云环境中蓬勃发展,广泛使用平台即服务 (PaaS) 计算基础结构和托管服务。 它们将底层基础结构视为可处置:可在几分钟内进行预配,并根据需要重设大小、缩放或销毁(通过自动化)。

考虑我们如何对待宠物和商品之间的区别。 在传统的数据中心,服务器被视为宠物:物理计算机,起一个有意义的名字,还要照顾它。 你通过将更多资源添加到相同计算机(纵向扩展)来进行缩放。 如果服务器出现问题,你会进行修复,使它恢复正常运行状况。 如果服务器不可用,则每个人都会注意到。

商品服务模型则与之不同。 你会将每个实例预配为虚拟机或容器。 它们是相同的,并分配有系统标识符(如服务-01、服务-02 等等)。 通过创建更多实例进行扩展(横向扩展)。 没有人会注意到实例不可用的情况。

商品模型采用不可变的基础结构。 服务器不会进行修复或修改。 如果一台服务器发生故障或需要更新,则会销毁它并预配新服务器 – 所有操作都通过自动化完成。

云原生系统采用了商品服务模型。 它们会在基础结构横向缩减或扩展时持续运行,而不考虑运行它们的计算机。

Azure 云平台支持这种具有自动缩放、自我修复和监视功能的高弹性基础结构类型。

新式设计

如何设计云原生应用? 你的体系结构是什么样的? 你将遵守哪些原则、模式和最佳做法? 哪些基础结构和运营问题十分重要?

十二要素应用程序

十二要素应用程序是一种被广泛认可的用于构建基于云的应用程序的方法。 它介绍了一系列原则和做法,开发人员在构建针对新式云环境进行优化的应用程序时需要遵循这些内容。 其中特别关注了跨环境的可移植性和声明性自动化。

尽管适用于任何基于 Web 的应用程序,不过许多从业人员都将十二要素视为构建云原生应用的坚实基础。 基于这些原则构建的系统可以快速部署和缩放,并添加了功能以快速应对市场变化。

下表重点介绍了十二要素方法:

因素 说明
1 - 基本代码 每个微服务都有单个基本代码,存储在其自己的存储库中。 它通过版本控制进行跟踪,可以部署到多个环境(QA、暂存、生产)。
2 - 依赖项 每个微服务都隔离并打包其自己的依赖项,以在不影响整个系统的情况下进行更改。
3 - 配置 配置信息通过代码之外的配置管理工具移出微服务和实现外部化。 在应用了正确配置的情况下,相同部署可以在环境间传播。
4 - 支持服务 辅助资源(数据存储、缓存、消息中转站)应通过可寻址 URL 进行公开。 这样做可使资源与应用程序分离,使其可以互换。
5 - 生成、发布、运行 每个版本都必须在生成、发布和运行阶段执行严格的分离。 各自都应使用唯一 ID 进行标记,并支持回滚功能。 新式 CI/CD 系统有助于实现此原则。
6 - 进程 每个微服务应在其自己的进程中执行,与其他正在运行的服务隔离。 将所需状态外部化到支持服务,如分布式缓存或数据存储。
7 - 端口绑定 每个微服务都应是独立的,其接口和功能在自己的端口上公开。 这样做可与其他微服务隔离。
8 - 并发 当容量需要增加时,跨多个相同进程(副本)横向扩展服务,而不是在功能最强大的可用计算机上纵向扩展单个大型实例。 将应用程序开发为并发应用程序,从而无缝地在云环境中横向扩展。
9 - 可处置性 服务实例应是可处置的。 支持快速启动以增加可伸缩性机会,以及支持正常关闭以使系统保持正确状态。 Docker 容器以及业务流程协调程序本质上满足此要求。
10 - 开发/生产等同 使整个应用程序生命周期中的各个环境尽可能相似,避免使用成本高昂的快捷方式。 在这里,通过促进相同的执行环境,容器的采用可以做出很大贡献。
11 - 日志记录 将微服务生成的日志视为事件流。 使用事件聚合器处理它们。 将日志数据传播到数据挖掘/日志管理工具(如 Azure Monitor 或 Splunk)并最终传播到长期存档。
12 - 管理员进程 以一次性进程形式运行管理性/管理任务,例如数据清理或计算分析。 使用独立工具从生产环境调用这些任务,但独立于应用程序。

超越十二要素应用一书中,作者 Kevin Hoffman 详细介绍了原始的所有 12 个要素(于 2011 年撰写)。 此外,他讨论了反映当今新式云应用程序设计的三个额外要素。

新要素 说明
13 - API 优先 使一切成为服务。 假设前端客户端、网关或其他服务会使用你的代码。
14 - 遥测 在工作站上,你可深入了解应用程序及其行为。 在云中,你无法这样。 确保设计包括监视、特定于域和运行状况/系统数据的集合。
15 - 身份验证/授权 从开始便实现标识。 请考虑公有云中提供的 RBAC(基于角色的访问控制)功能。

我们将在本章和整本书中涉及超过 12 个要素中的许多要素。

Azure Well-Architected Framework

设计和部署基于云的工作负载可能具有挑战性,尤其是在实现云原生体系结构时。 Microsoft 提供了行业标准最佳做法,可帮助你和团队交付可靠的云解决方案。

Microsoft 架构良好的框架提供了一组指导原则,可用于提高云原生工作负载的质量。 该框架包含卓越体系结构的五个要素:

原则 说明
成本管理 专注于提前生成增量价值。 应用“生成-度量-学习”原则来加快上市时间,同时避免使用资本密集型解决方案。 使用即用即付策略,在横向扩展时投资,而不是预先提供大量投资。
卓越运营 实现环境和操作自动化,以加快速度并减少人为错误。 快速回滚或前滚问题更新。 从开始便实现监视和诊断。
性能效率 高效地满足对工作负载的需求。 支持水平缩放(横向扩展),并将其设计到系统中。 持续执行性能和负载测试,以识别潜在瓶颈。
可靠性 生成可复原且可用的工作负载。 复原能力使工作负载能够从故障中恢复并继续正常运行。 可用性可确保用户始终可访问工作负载。 设计应用程序以预测故障并从中恢复。
安全性 在应用程序的整个生命周期(从设计和实现到部署和操作)中实现安全性。 密切关注标识管理、基础结构访问、应用程序安全性以及数据主权和加密。

为了开始进行,Microsoft 提供了一组联机评估,可帮助你根据五个架构良好的支柱评估当前云工作负载。

微服务

云原生系统采用微服务,而微服务是一种用于构造新式应用程序的常用体系结构样式。

微服务构建为一组通过共享结构进行交互的分布式小型独立服务,共同具有以下特征:

  • 各自都在较大的域上下文中实现特定业务功能。

  • 各自都自主开发,可以独立部署。

  • 各自都是独立的,封装其自己的数据存储技术、依赖项和编程平台。

  • 各自都在自己的进程中运行,并使用 HTTP/HTTPS、gRPC、WebSocket 或 AMQP 等标准通信协议与其他微服务进行通信。

  • 它们组合在一起形成应用程序。

图 1-4 将整体式应用程序方法与微服务方法进行比较。 请注意整体结构如何由分层体系结构组成(在单个进程中执行)。 它通常使用关系数据库。 但是,微服务方法将功能分为独立服务,每个服务具有自己的逻辑、状态和数据。 每个微服务都承载其自己的数据存储。

整体部署与微服务方法

图 1-4。 整体式与微服务体系结构

请注意微服务如何促进本章前面所讨论的十二要素应用程序中的“进程”原则。

要素 #6 指定“每个微服务应在其自己的进程中执行,与其他正在运行的服务隔离。”

为何使用微服务?

微服务提供敏捷性。

在本章的前面部分,我们将构建为整体结构的电子商务应用程序与采用微服务的应用程序进行比较。 在示例中,我们看到了一些明显的好处:

  • 每个微服务都有自治生命周期,可以独立发展并频繁部署。 部署新功能或更新无需等待季度发布。 可以更新实时应用程序的小区域,降低中断整个系统的风险。 无需完全重新部署应用程序即可进行更新。

  • 每个微服务都可以独立缩放。 你可以仅横向扩展需要更多处理能力才能满足所需性能级别和服务级别协议的服务,而不是将整个应用程序作为单个单元进行缩放。 精细缩放可使你更好地控制系统,并且有助于降低总体成本,因为是缩放部分系统(而不是所有内容)。

了解微服务的极佳参考指南是 .NET 微服务:适用于容器化 .NET 应用程序的体系结构。 该书深入探讨了微服务设计和体系结构。 它是完整堆栈微服务参考体系结构的配套产品,可从 Microsoft 免费下载。

开发微服务

可以在任何新式开发平台上创建微服务。

Microsoft .NET 平台是一个很好的选择。 它免费且开放源代码,有许多可简化微服务开发的内置功能。 .NET 是跨平台的。 可以在 Windows、macOS 和大多数 Linux 风格上生成和运行应用程序。

.NET 具有高性能,与 Node.js 和其他竞争平台相比评分良好。 有趣的是,TechEmpower 在许多 Web 应用程序平台和框架间执行了一组广泛的性能基准。 .NET 在前 10 名平台中得分远远高于 Node.js 和其他竞争平台。

.NET 由 Microsoft 和 GitHub 上的 .NET 社区维护。

微服务挑战

尽管分布式云原生微服务可以提供巨大的敏捷性和速度,但它们带来了许多挑战:

通信

前端客户端应用程序如何与后端核心微服务通信? 是否允许直接通信? 或者,是否可以使用提供灵活性、控制和安全性的网关外观来抽象后端微服务?

后端核心微服务如何彼此通信? 是否允许进行可以提高耦合并影响性能和敏捷性的直接 HTTP 调用? 或者是否可以考虑将消息与队列和主题技术分离?

云原生通信模式一章中介绍了通信。

复原能力

微服务体系结构将系统从进程内迁移到进程外网络通信。 在分布式体系结构中,当服务 B 未响应来自服务 A 的网络调用时,会发生什么情况? 或者,当服务 C 暂时不可用,而调用它的其他服务被阻止时,会发生什么情况?

云原生复原能力一章中介绍了复原能力。

分布式数据

按照设计,每个微服务都封装自己的数据,通过其公共接口公开操作。 如果是这样,如何跨多个服务查询数据或实现事务?

云原生数据模式一章中介绍了分布式数据。

机密

微服务如何安全地存储并管理机密和敏感配置数据?

云原生安全性一章中详细介绍了机密。

使用 Dapr 管理复杂性

Dapr 是一种分布式开放源代码应用程序运行时。 通过可插入组件的体系结构,它大大简化了分布式应用程序背后的管道。 它提供了一种动态胶水,将应用程序与来自 Dapr 运行时的预生成基础结构功能和组件相结合。 图 1-5 显示了来自 20,000 英尺的 Dapr。

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 有可能对云原生应用程序开发产生深远影响。

容器

在任何云原生对话中,听到提及术语“容器”是很自然的事情。 在云原生模式一书中,作者 Cornelia Davis 观察到“容器是云原生软件的极佳推动者。”云原生计算基金会在其云原生轨迹图(企业云原生之旅启程指南)中将微服务容器化作为第一步。

容器化微服务非常简单直接。 代码、其依赖项和运行时会打包到称为容器映像的二进制文件中。 映像存储在容器注册表中,该注册表用作映像的存储库或库。 注册表可以位于开发计算机上、数据中心内或公有云中。 Docker 自己通过 Docker 中心维护公共注册表。 Azure 云采用专用容器注册表,用于存储与将运行容器映像的云应用程序接近的容器映像。

当应用程序启动或缩放时,将容器映像转换为正在运行的容器实例。 实例在安装了容器运行时引擎的任何计算机上运行。 可以根据需要创建容器化服务的任意多个实例。

图 1-6 显示三个不同的微服务,各自处于自己的容器中,全都在单一主机上运行。

在一个容器主机上运行多个容器

图 1-6. 在一个容器主机上运行多个容器

请注意每个容器如何维护其自己的依赖项集和运行时,它们可以彼此不同。 在这里,我们看到 Product 微服务的不同版本在同一主机上运行。 每个容器都共享底层主机操作系统、内存和处理器的一部分,但彼此隔离。

请注意,容器模型很好地支持了十二要素应用程序中的“依赖项”原则。

要素 #2 指定“每个微服务都隔离并打包其自己的依赖项,以在不影响整个系统的情况下进行更改。”

容器同时支持 Linux 和 Windows 工作负载。 Azure 云公开接受这两者。 有趣的是,是 Linux(而不是 Windows Server)成为 Azure 中更常用的操作系统。

虽然存在多个容器供应商,但 Docker 占据了最大的市场份额。 该公司一直在推动软件容器运动。 它已成为打包、部署和运行云原生应用程序的事实上的标准。

为什么要使用容器?

容器提供了可移植性,可保证环境间的一致性。 通过将所有内容封装到单个包中,可将微服务及其依赖项与底层基础结构隔离。

你可以在承载 Docker 运行时引擎的任何环境中部署容器。 容器化工作负载还消除了通过框架、软件库和运行时引擎预配置每个环境的支出。

通过共享底层操作系统和主机资源,容器的占用比完整虚拟机小得多。 较小的大小增加了给定主机一次可以运行的密度或微服务数。

容器业务流程

虽然 Docker 等工具会创建映像并运行容器,但你还需要工具来管理它们。 容器管理使用称为“容器业务流程协调程序”的特殊软件程序来完成。 当大规模操作许多正在运行的独立容器时,业务流程至关重要。

图 1-7 显示容器业务流程协调程序自动执行的管理任务。

容器业务流程协调程序执行的任务

图 1-7. 容器业务流程协调程序执行的任务

下表介绍了常见的业务流程任务。

任务 说明
计划 自动预配容器实例。
相关性/反相关性 预配彼此相近或远离的容器,帮助实现可用性和性能。
运行状况监视 自动检测并更正故障。
故障转移 自动将故障实例重新预配为正常计算机。
扩展 自动添加或删除容器实例以满足需求。
网络 管理网络覆盖以进行容器通信。
服务发现 使容器可以相互定位。
滚动升级 协调增量升级,部署停机时间为零。 自动回滚有问题的更改。

请注意容器业务流程协调程序如何支持十二要素应用程序中的“可处置性”和“并发”原则。

要素 #9 指定“服务实例应是可处置的,支持快速启动以增加可伸缩性机会,以及支持正常关闭以使系统保持正确状态。”Docker 容器以及业务流程协调程序本质上满足此要求。

要素 #8 指定“服务跨大量小型相同进程(副本)横向扩展,而不是在功能最强大的可用计算机上纵向扩展单个大型实例。”

尽管存在多个容器业务流程协调程序,但 Kubernetes 已成为云原生世界事实上的标准。 它是一种可移植、可扩展的开放源代码平台,可用于管理容器化工作负载。

你可以承载自己的 Kubernetes 实例,但随后需要负责预配和管理其资源,这可能十分复杂。 Azure 云功能采用 Kubernetes 作为一种托管服务。 Azure Kubernetes 服务 (AKS)Azure Red Hat OpenShift (ARO) 都使你能够以托管服务的形式充分利用 Kubernetes 的特性和强大功能,而无需安装和维护它。

缩放云原生应用程序中详细介绍了容器业务流程。

支持服务

云原生系统依赖于许多不同的辅助资源,如数据存储、消息中转站、监视和标识服务。 这些服务称为支持服务

图 1-8 显示云原生系统使用的许多常见支持服务。

常见支持服务

图 1-8. 常见支持服务

你可以承载自己的支持服务,但随后你需要负责对这些资源进行许可、预配和管理。

云提供商提供丰富的 托管支持服务。只需使用服务,而无需拥有服务。 云提供商大规模地运营资源,并负责性能、安全性和维护。 监视、冗余性和可用性内置在服务中。 提供商保证服务级别性能并完全支持其托管服务 - 开启票证,他们会解决问题。

云原生系统支持云供应商提供的托管支持服务。 在时间和劳动力方面的节省可能会十分巨大。 承载自己的服务和遇到问题的运营风险会迅速变得十分昂贵。

最佳做法是将支持服务视为附加资源,动态绑定到将配置信息(URL 和凭据)存储在外部配置中的微服务。 本章前面讨论的十二要素应用程序中详细说明了此指导。

要素 #4 指定支持服务“应通过可寻址 URL 进行公开。 这样做可使资源与应用程序分离,使其可以互换。”

要素 #3 指定“配置信息通过代码之外的配置管理工具移出微服务和实现外部化。”

借助此模式,支持服务可以进行附加和拆离,而无需更改代码。 可以将微服务从 QA 提升到暂存环境。 将微服务配置更新为指向暂存中的支持服务,并通过环境变量将设置注入到容器中。

云供应商提供了 API,使你能够与其专有支持服务进行通信。 这些库封装了专有管道和复杂性。 但是,直接与这些 API 进行通信会将你的代码紧密耦合到该特定支持服务。 隔离供应商 API 的实现细节是一种被广泛接受的做法。 引入中间层或中间 API,将泛型操作公开给你的服务代码并将供应商代码包装在其中。 这种松散耦合使你可以将一个支持服务交换为另一个,或是将代码移到不同的云环境,而无需更改主线服务代码。 前面讨论的 Dapr 通过预生成构建基块集来遵循此模型。

最后一点,支持服务还促进本章前面所讨论的十二要素应用程序中的“无状态”原则。

要素 #6 指定“每个微服务应在其自己的进程中执行,与其他正在运行的服务隔离。 将所需状态外部化到支持服务,如分布式缓存或数据存储。”

云原生数据模式云原生通信模式中讨论了支持服务。

自动化

如你所见,云原生系统采用微服务、容器和新式系统设计来实现一定的速度和敏捷性。 但这只是其中的一部分。 如何预配这些系统所运行的云环境? 如何快速部署应用功能和更新? 如何完成整张图片?

进入广泛接受的基础结构即代码 (IaC) 做法。

借助 IaC,你可以自动执行平台预配和应用程序部署。 实质上你是将软件工程做法(例如测试和版本控制)应用到 DevOps 做法。 基础结构和部署是自动执行的,具有一致性和可重复性。

自动完成基础结构

使用 Azure 资源管理器Azure Bicep、HashiCorp 提供的 TerraformAzure CLI 等工具,你能够以声明方式对所需的云基础结构编写脚本。 资源名称、位置、容量和机密都是参数化和动态的。 脚本会进行版本控制,并作为项目的生成工件签入到源代码管理中。 你调用脚本以在系统环境(如 QA、暂存和生产)间预配一致且可重复的基础结构。

在底层,IaC 是幂等的,这意味着可以反复运行相同脚本,而不会产生副作用。 如果团队需要进行更改,则会编辑并重新运行脚本。 只有更新的资源才会受到影响。

什么是基础结构即代码一文中,作者 Sam Guckenheimer 介绍了“实现 IaC 的团队能够快速、大规模地提供稳定的环境。 他们避免了手动配置环境,并通过代码来表示环境所需的状态,从而强制执行一致性。 采用 IaC 的基础结构部署是可重复的,可以防止因配置偏移或缺少依赖项而导致的运行时问题。 DevOps 团队可以结合使用一系列统一的做法和工具,迅速、大规模、可靠地提供应用程序及其支持的基础结构。”

自动执行部署

前面所讨论的十二要素应用程序会在将完成的代码转换为正在运行的应用程序时调用单独的步骤。

要素 #5 指定“每个版本都必须在生成、发布和运行阶段执行严格的分离。 各自都应使用唯一 ID 进行标记,并支持回滚功能。”

新式 CI/CD 系统有助于实现此原则。 它们提供单独的生成和交付步骤,可帮助确保提供一致且高质量的代码供用户随时使用。

图 1-9 显示整个部署过程中的分离。

CI/CD 管道中的部署步骤

图 1-9. CI/CD 管道中的部署步骤

在上图中,请特别注意任务的分离:

  1. 开发人员在其开发环境中构造一种功能,迭代执行所谓的“内部循环”(即代码、运行和调试)。
  2. 完成后,该代码会推送到代码存储库(如 GitHub、Azure DevOps 或 BitBucket)。
  3. 推送会触发将代码转换为二进制生成工件的生成阶段。 该工作使用持续集成 (CI) 管道来实现。 它会自动生成、测试并打包应用程序。
  4. 发布阶段选取二进制生成工件,应用外部应用程序和环境配置信息,然后生成不可变发布版。 发布版会部署到指定环境中。 该工作使用持续交付 (CD) 管道来实现。 每个发布版都应可识别。 你可以说“此部署正在运行应用程序的发布版 2.1.1。”
  5. 最后,已发布的功能会在目标执行环境中运行。 发布版是不可变的,这意味着任何更改都必须创建新发布版。

通过应用这些做法,组织彻底发展了其交付软件的方式。 许多组织都从每季度分布转变为按需更新。 目标是在开发周期中尽早捕获问题,因为此时修复这些问题的成本较低。 集成之间的持续时间越长,要解决的问题成本便越高昂。 借助集成过程中的一致性,团队可以更频繁地提交代码更改,从而改进协作和软件质量。

DevOps 中详细讨论了基础结构即代码和部署自动化以及 GitHub 和 Azure DevOps。