设计和实施微软-DevOps-解决方案-AZ-400-考试指南-全-

设计和实施微软 DevOps 解决方案:AZ-400 考试指南(全)

原文:annas-archive.org/md5/eb2d156388068d02b11a808e92183ccf

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

每位软件工程专业人员必须熟练掌握其对 DevOps 的理解,以及它为团队带来的积极影响。本书将使您了解一些最重要的 DevOps 实践,如配置管理、发布管理、持续集成、持续部署、基础设施即代码、持续测试以及应用程序监控和反馈。您还将学习使用 Azure DevOps、GitHub 以及其他相关工具来实施各种实践。

通过详细的案例研究和动手实验,本书作为如何实施各种实践的现成参考书。本书还涵盖了其他主题,将帮助您发展深厚的专业知识和面向 DevOps 的思维方式,这将对您的职业生涯产生重要影响。

本书适合人群

本书的目标读者包括以下人群:

  • 想要参加 AZ-400 考试的软件开发人员或运维专家

  • 想要学习如何使用 Azure DevOps 来实施 DevOps 实践的软件开发人员或运维专家

  • 想要提升其 DevOps 技能的技术爱好者

您应该熟悉软件开发以及更广泛的软件开发实践。您必须熟悉瀑布模型、Scrum、敏捷和 DevOps 等术语。任何有至少 2 年团队软件开发经验的严肃从业者都应该能够阅读本书。您还应该对 Azure DevOps 和 GitHub 有基本的了解。

本书涵盖内容

第一章Introduction to DevOps,概述了 DevOps 运动及其代表的文化。本章介绍了本书的重点实践和习惯。

第二章Site Reliability Engineering Fundamentals,向您介绍了Site Reliability EngineeringSRE)的原则和实践,以及采纳 SRE 思维方式在确保云应用成功方面的重要性。

第三章Getting the Best Out of DevOps Tools,提供了各种 DevOps 相关工具在整个应用程序开发生命周期中的高级概述。

第四章Everything Starts with Source Control,讨论了源代码控制的各种方法,以及如何正确进行源代码控制是持续交付的核心。

第五章Moving to Continuous Integration,涵盖了持续集成、自动化测试、质量控制、度量、代码覆盖率以及生产高质量产品所需的最低标准。

第六章, 实现持续部署和发布管理,讲解了如何使用 Azure DevOps 发布流水线,以及各种部署模式和策略以实现持续部署。

第七章, 依赖管理,讨论了 Azure Artifacts 如何帮助管理共享组件,并介绍了如何使用 Azure 流水线自动化这些组件的构建、发布,甚至使用。

第八章, 将基础设施和配置视为代码,展示了如何配置应用程序的基础设施,并如何将其创建或更新过程完全自动化,作为发布的一部分。

第九章, 在 DevOps 场景中处理数据库,讨论了将数据库视为应用程序代码、基础设施或配置的几种策略。

第十章, 集成持续测试,探讨了不同类型的测试以及它们如何通过测试金字塔相互关联,并展示了如何在 DevOps 流水线的不同阶段嵌入不同类型的测试。

第十一章, 管理安全性与合规性,展示了如何与安全专家合作,将他们的关注点自动化集成到流水线中,而不是通过后期的检查单手动验证。

第十二章, 应用监控,介绍了应用监控的过程,旨在对应用程序进行仪器化,以理解应用性能和使用模式,并提取关键性能指标。

第十三章, 收集用户反馈,讨论了如何通过假设驱动开发验证你的想法,在付诸实施之前先进行验证。

第十四章, 采纳持续改进文化,重点关注一些对业务成果产生直接影响的组织文化方面。

第十五章, 通过 DevOps 加速云端采用,提供了通过场景和指导来使用 DevOps 的概述,同时帮助制定企业云端采用计划。

第十六章, 容器,概述了容器技术,容器允许你将任何应用程序或工具(使用任何编程语言开发)打包,并在基本主机或集群上进行部署。

第十七章, 规划你的 Azure DevOps 组织,讨论了哪些做法适用于你的组织和团队,以及如何使用 Azure DevOps 实施实践和方法。

第十八章, AZ-400 模拟考试,包含了一份模拟测试,帮助你评估自己的学习成果。

为了最大程度地利用本书

本书涵盖的软件/硬件 操作系统要求
Visual Studio 2019/Visual Studio 2022/VS Code Windows、macOS 或 Linux
Azure 订阅 -
Azure DevOps 订阅 -

如果你正在使用本书的数字版本,我们建议你自己输入代码或从本书的 GitHub 仓库访问代码(下节中提供了链接)。这样做有助于避免与复制和粘贴代码相关的潜在错误。

下载示例代码文件

你可以从 GitHub 下载本书的示例代码文件,链接为 github.com/PacktPublishing/Designing-and-Implementing-Microsoft-DevOps-Solutions-AZ-400-Exam-Guide。如果代码有更新,它会在 GitHub 仓库中更新。

我们还提供了其他代码包,来自我们丰富的图书和视频目录,访问链接:github.com/PacktPublishing/。快来看看吧!

下载彩色图像

我们还提供了一份包含本书中截图和图表的彩色图像的 PDF 文件,你可以在这里下载:packt.link/OADjU

使用的约定

本书中使用了许多文本约定。

Code in text:表示文本中的代码单词、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟网址、用户输入和 Twitter 用户名。以下是一个示例:在 GitHub Flow 中,有一个 master 分支,始终应该保持可部署状态。不允许有未完成的更改进入该分支。

代码块的显示方式如下:

{
"appServiceName": {
"type": "string",
"metadata": {
"description": "a free to choose text"
}
}

当我们希望你注意代码块的某个特定部分时,相关的行或项会以粗体显示:

az aks update -n 'packtsbookaci' -g 'az400-dev'
--attach-acr 'packtbookacr'

任何命令行输入或输出如下所示:

git add NewFile.txt
git push

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

提示或重要注意事项

如此显示。

联系我们

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

常见反馈:如果你对本书的任何方面有疑问,请通过电子邮件联系 customercare@packtpub.com,并在邮件主题中提及书名。

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

盗版:如果你在互联网上发现任何非法复制的我们作品的形式,请提供该地址或网站名称。请通过 copyright@packt.com 联系我们,并附上材料的链接。

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

分享你的想法

阅读完 设计与实施 Microsoft DevOps 解决方案 AZ-400 考试指南 后,我们很想听听你的想法!请 点击此处直接前往亚马逊评论页面 为本书分享你的反馈。

你的评论对我们和技术社区都非常重要,并将帮助我们确保提供优质的内容。

第一部分 – 通过 DevOps 实现数字化转型

在这一部分中,你将学习 DevOps 的核心原则和实践,这些是实现软件开发目标的基础,能让你更快速、高效地完成任务。我们将探讨大多数组织如何通过整合 站点可靠性工程SRE)实践来构建安全可靠的系统。你还将了解可以提高团队生产力的重要 DevOps 工具。

本节为我们的工作奠定了必要的基础,之后可以按任意顺序阅读其余章节,以深入了解各个主题。

本书的这一部分包括以下章节:

  • 第一章DevOps 简介

  • 第二章站点可靠性工程 基础知识

  • 第三章充分利用 DevOps 工具

第一章:DevOps 介绍

DevOps 不是你可以购买或安装的产品或工具。DevOps 是关于文化以及你如何编写、发布和运营软件。DevOps 是关于缩短新想法与最终用户体验其带来的价值之间的时间。在本书中,你将学习如何利用工具和技术将这一理念应用于你的工作方式。本书的目的是帮助你准备 AZ-400 认证考试,因此核心概念和与 DevOps 相关的实践将通过 Azure DevOps 及其相关技术来阐述。

近年来,DevOps 在软件工程领域越来越受欢迎,几乎所有软件工程团队都在适应一个新的工作方式,使用一组新的工具来激发生产力,并在开发和运维之间实现更好的协作。DevOps 常被视为敏捷的延伸,但其范围要广得多,它补充了敏捷方法。

在本章中,你将更多地了解什么是 DevOps,以及如何识别一个成功的 DevOps 团队。到本章结束时,你将熟悉关键术语,并掌握 DevOps 的高层次原则和实践。你还将发现 DevOps 对软件工程团队的益处。

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

  • 什么是 DevOps?

  • 创建理想的 DevOps 组织

  • 探索 DevOps 实践和习惯

  • DevOps 发展历程的五个阶段

技术要求

本章没有技术要求。

什么是 DevOps?

如果你列出所有关于 DevOps 的不同定义和描述,会有很多。然而,尽管它们可能有所不同,它们最有可能共享几个概念。这些概念是协作、持续交付商业价值和打破孤岛。

在本书其余部分涉及到的技术讨论中,重要的是不要忽视采用 DevOps 的价值主张——即它将帮助你改进持续向最终用户交付价值的方式。为了实现这一点,你必须减少从开始开发新功能到第一个用户在生产环境中使用该功能的时间。这意味着你不仅要编写软件,还要交付并运营它。

在过去的十年里,我们编写软件的方式发生了根本性的变化。越来越多的公司现在采用敏捷工作方式,以提高软件开发的效率。越来越多的团队现在通过短期迭代或冲刺,快速创建产品的新增量。然而,快速创建潜在可发布的增量本身并不能创造任何价值。只有当每个新版本的软件被发布到生产环境并被最终用户使用时,它才开始提供价值。

在传统的组织中,开发人员和运维人员通常位于不同的部门,将软件投入生产往往需要交接,并且通常伴随有正式的仪式。在这样的组织中,加速将软件交付到生产环境,以及开发人员快速创建新版本的速度,可能会变得很困难。

此外,开发和运维部门往往存在目标冲突。开发部门的奖励是尽可能快速地创造许多变化,而运维部门则因减少停机时间和防止问题而获得奖励。后者通常通过尽可能少地做出更改来最佳实现。这里的冲突很明显——两个部门都有各自针对一个子目标的优化,如下图所示:

图 1.1 – 开发与运维之间的目标冲突

图 1.1 – 开发与运维之间的目标冲突

这违背了这些子目标的目的,这些子目标源于共同的、宏观的目标,即在保持稳定性的同时快速地引入新版本。正是开发和运维目标之间的冲突,应该成为 DevOps 文化中消失的一个问题。在这种文化中,开发和运维团队应该协同工作,以快速可靠的方式将新版本交付到生产环境,并共同承担这两个子目标的责任。

虽然知道 DevOps 是一个文化运动很重要,但工具和自动化在这种文化中扮演着重要角色。在本书中,我们将重点介绍这些工具以及如何使用它们来实现 DevOps 文化中的许多实践。换句话说,本书主要关注与 DevOps 相关的产品和流程。如果你想了解更多关于文化方面的内容和人群,还有许多其他书籍可以阅读。一本非常值得阅读的书是《凤凰项目:IT、DevOps 与帮助你的企业获胜的小说》,作者为 Gene Kim。

本节的其余部分将探讨 DevOps 与敏捷之间的关系,看看它们如何相辅相成。重点将放在敏捷技术和工作管理的价格上。我们还将讨论 DevOps 文化的目标和好处。

DevOps 与敏捷的关系

如果你看看敏捷开发,你可能会注意到它的一部分内容侧重于商业价值和缩短交付新商业价值的时间。从这个角度来看,采用 DevOps 是敏捷开发后的一个合乎逻辑的步骤。敏捷提倡软件开发团队的职责应向前延伸,通过与用户及其他利益相关者互动,更快地交付有价值且可能可以发布的产品。DevOps 不仅仅是指可能发布的东西,而是实际发布它。通过结合敏捷与 DevOps,你可以为用户创造一个端到端的、持续的价值流。

你需要为所有参与者提供一个共同的工作管理方法。在下一节中,你将找到一些关于如何将运维方面的考量纳入你管理工作方式的指引。

敏捷工作管理

当你开始增加开发与运维之间的协作时,你会很快注意到他们必须应对不同类型的工作。在开发中,大部分工作是计划好的:从待办事项中提取的用户故事和 bug。另一方面,对于运维来说,很多工作是未计划的。他们需要响应来自系统的警告和提醒,以及来自用户或开发人员的请求或工单。

将这两者整合在一起,尤其是当开发人员和运维人员位于同一团队时,可能会很具挑战性。为了学习如何应对这一点,我们可以探索以下方法:

  1. 首先,为开发人员切换到基于流的工作方式。

  2. 接下来,允许运维团队也通过同步将他们的工作列在与开发人员相同的工作管理系统中。你还可以选择实施快速通道(fastlaning),一种加速处理紧急工作的方式。

  3. 最后,如果可能的话,你可以选择停用现有的运维工单工具。

快速通道是一种组织工作的方式,它通过可视化两条独立的工作轨道,允许同时处理计划内和未计划的工作。为此,Scrum 看板上方扩展了一个类似 Kanban 的看板,这就是快速通道。在 Kanban 看板上,添加了紧急但未计划的工作。任何添加到此通道的工作将由优先级最高的团队来处理。只有当快速通道中的工作都处理完毕后,才会处理 Scrum 看板上的工作以及计划中的工作。每当有新的工作添加到快速通道时,它将再次优先处理。通常,团队会达成一致,所有正在进行中的工作在切换到快速通道工作之前必须完成。

重要提示

依赖管理也是敏捷工作计划中一个重要方面。因此,团队通常会利用优先级属性来确定短期内更重要的工作。

切换到基于流的工作方法

切换到基于流的工作方法时,首先需要考虑的是将开发人员的工作方式从批量处理转变为基于流的工作方式。批量工作方式的一个例子是 Scrum。如果你正在使用 Scrum 框架,你已经习惯了每 2 到 4 周拾取一批工作,并集中精力在这个时间窗口内完成所有工作。只有当这一批工作完成后,才会交付一个潜在可交付的产品。

在转向基于流程的方法时,你的目标是专注于一个任务,而不是一个批次。你专注于完成这个单一的工作项,直到它完成再开始下一个。这样,就不再有冲刺待办事项列表,只有产品待办事项列表。这种方法的优点在于,你不再需要预先决定执行哪些工作;只要有空,你就可以从待办事项列表中选择下一个任务。在优先级迅速变化的环境中,这让你能够快速应对变化。

开发人员组织工作方式的这些变化使得将操作工作纳入工作管理变得更容易,但也有另一个好处。当开发人员专注于完成单一的工作项而不是一次完成整个冲刺时,你还可以增加向用户交付小部分价值的频率。

将工作项同步到一个系统

一旦开发团队改变了工作组织的方式,开发人员应该能更容易地将他们计划中的工作列在共享的待办事项列表中,并在有空时从该列表中拉取工作。同时,他们现在也有一个地方可以列出他们的未计划工作。

然而,可能仍然存在一个现有的工单系统,其中用户会提交操作请求或监控工具会自动创建请求。虽然 Azure DevOps 有一个强大的 API 可以重新设计该集成,直接在 Azure DevOps 中创建工作项,但你可以首先选择在现有的工单工具和 Azure Boards 之间创建一个同步。这方面有很多集成选项,并且正在持续进行相关工作。通过这种方式,操作人员可以逐步从旧工具迁移到新工具,因为它们现在是同步的。当然,目标是让他们完全转移到与开发人员相同的工具。

快速通道

由于开发人员和操作人员都在使用同一个工作管理工具,你会发现系统中混合了计划和未计划的工作,且这些工作往往是紧急的。为了确保紧急工作能得到应有的关注和优先处理,你可以在冲刺看板中引入一个所谓的快速通道。下面的截图展示了一个 Azure 看板的例子,该看板已为生产问题设置了快速通道:

图 1.2 – 展示快速通道的 Azure 看板设置

图 1.2 – 展示快速通道的 Azure 看板设置

该看板中的水平分割线仅在快速通道中没有任务可选时,用于处理常规通道中的任务。

你可以在docs.microsoft.com/en-us/azure/devops/boards/boards/expedite-work?view=azure-devops找到如何在 Azure(看板)中配置游泳道以加速工作的说明。

停用其他工作管理工具

在创建了一个开发与运维之间的共享工作管理系统后,有机会增加它们之间的协作。当这种协作开始展开时,原本由运维使用的旧票务系统可能会随着时间的推移逐渐被淘汰。监控工具的集成可以迁移到新的共享工具中,开发人员与运维人员之间的票务数量应该会逐渐减少,因为他们找到了新的协作方式。

重要提示

Azure DevOps 允许你自定义工作项模板,并定义生命周期状态。通过这个功能,团队可以根据他们在现有工具中使用的任何现有分类法,轻松地建模他们的工作项模板类型。这大大减少了采用新共享工作管理工具时的学习曲线。欲了解更多信息,请访问docs.microsoft.com/en-us/azure/devops/boards/backlogs/work-item-template?view=azure-devops&tabs=browser#manage-work-item-templates

DevOps 文化的目标与好处

此时,你可能会对这一切的意义产生疑问。DevOps 的好处是什么?它对你、你的同事和你的组织有什么影响?采用 DevOps 的最常见目标是减少周期时间。周期时间是从开始开发一个新特性到第一个用户可以使用它的时间。这一目标通过自动化实现,同时也有助于降低更改失败率、降低平均修复时间MTTR)和减少计划停机时间。

除了这些,还有其他的好处,比如提高员工满意度、减少倦怠和压力、以及更好的员工留存率。这归因于消除了开发人员和运维人员之间的对立目标。

一段时间以来,大家对于 DevOps 是否有效、这些目标是否能实现、以及额外的好处是否能实现产生了疑问,因为这些成果仅通过案例研究来展示。这样做的缺点是,案例研究通常只针对成功的案例,而不包括失败的案例。直到 2018 年,《Accelerate》一书发布,情况才发生了变化。这本书基于多年的定量研究,展示了现代开发实践,如 DevOps,如何有助于实现 IT 目标和组织目标。

测量结果

为了衡量你目前作为一个团队或组织的现状,以及 DevOps 对你的影响,有几个指标可以开始记录。像往常一样,当处理指标或关键绩效指标KPIs)时,确保不要鼓励人们只看数字,从而操控系统。一些有趣的指标将在以下章节中详细说明,如果你查看这些指标,你会发现它们都是关于鼓励流程的。

周期时间和交付时间

周期时间交付时间是来自精益和看板的指标,用于衡量实现变更所需的时间。周期时间是从开始工作到用户能够在生产环境中使用某个特性之间的时间。周期时间越短,你对需求变化或洞察的反应就越快。交付时间是从请求一个特性到实现该特性之间的时间。它是从将工作添加到待办事项列表到你开始实施它之间的时间。

当你将周期时间和交付时间加在一起时,你是在计算另一个指标,称为上市时间。这是在开发软件时一个重要的商业指标。因此,最小化周期时间和交付时间会对业务产生影响。

在制工作量

你可以衡量的另一项内容是任何时刻在制工作量。DevOps 专注于价值流向用户。这意味着每个人应尽可能一次只做一件事,并在开始做其他事情之前完成当前任务。这能减少任务切换所花费的时间,以及未完成工作的时间。衡量一个团队并行处理的事务数量,并报告这些内容,可以作为激励的来源。

你甚至可以将实际的工作量限制设定为在制工作量。以下是图 1.2的一小部分,展示了这些在制工作量限制甚至可以在工具中显示:

图 1.3 – Azure Boards 显示每个阶段的限制

图 1.3 – Azure Boards 显示每个阶段的限制

目标是尽可能减少同时进行的工作量。

平均恢复时间

第三个指标是平均恢复时间。在发生(部分)故障时,恢复服务需要多长时间?过去,企业专注于减少平均故障间隔时间。这曾是衡量产品稳定性的主要指标。然而,这一指标鼓励限制生产环境中的变更数量。不良的结果往往是,尽管故障可能不常发生,但发生时却持续较长时间且难以修复。

衡量平均恢复时间将注意力转向你能多快修复故障。如果你能快速修复故障,你就能实现同样的目标——即在不牺牲变更速率的情况下,最小化停机时间。目标是最小化恢复时间。

变更率和变更失败率

最后,你可以衡量交付到生产环境的变更数量以及其中未成功的百分比。提高变更速率意味着你更频繁地为用户交付价值,从而实现价值流。同时,通过不仅仅衡量失败的次数,还要衡量失败的百分比,你鼓励许多小规模的成功变更,而不是仅仅鼓励总体变更次数的限制。

你的目标应该是增加变更的速率,同时降低变更失败率。除了本节列出的四个主要 KPI,许多其他度量标准可能对衡量 DevOps 成熟度也有帮助。所有这些度量标准都必须与期望的业务目标和关键结果OKRs)相关联。你可以在这里找到更多关于 OKR 的信息:docs.microsoft.com/en-us/azure/cloud-adoption-framework/strategy/business-outcomes/okr

为了说明,以下表格展示了一个典型的示例:

目标 关键结果
更快的市场上线时间
  • 部署频率:每周一次

  • 部署时间 <= 4 小时

  • 交付时间(重大版本):每季度一次

|

提高已实现的业务价值,同时维持或降低成本
  • CI/CD 流程:100%自动化

  • 资源利用率(95 百分位):80%

  • 用于监控健康状况和成本的仪表板

|

可预测的高质量交付和更快速的修正,缺陷更少
  • 高可用性:> 99.9%

  • RTO < 1 小时,RPO < 15 分钟

|

改进 IT、自动化、团队协作和文化的流程
  • MTTR < 1 小时

  • 交付时间(Bug):< 8 小时

  • 大规模敏捷:特性团队 > 5

  • 技术债务 < 1 周

|

提升客户参与度和快速响应市场需求的能力
  • CSAT:4 分或以上

  • 产品规划:50%的待办事项集中在客户反馈上

|

表 1.1 – 使用 OKR 方法衡量你的 DevOps 成熟度

在这一点上,你可能在想,如何帮助我的组织培养这种文化,并获得这些好处呢?接下来的章节将回答这个问题。

创建你理想的 DevOps 组织

好吧,也许你的组织结构根本不需要改变。DevOps 必须从文化变革开始:开放、同理心和协作是需要鼓励的价值观。但即便如此,改变组织结构可能有助于加速这一进程。

传统上,开发人员和运维人员通常被组织成不同的团队,甚至是不同的部门——这些团队成员拥有相似的技能和职责。组织结构的一个常见变化是通过调整,将团队围绕共同目标、单一产品或一组特性进行组织。

现在,你将需要具有不同技能和职责的团队,这些团队很可能包含开发人员和运维人员。重要的是要意识到,强行对这些人进行这样的变化可能并不是最好的前进方式。通常,最有效的做法是先改变文化,鼓励合作——然后,这种组织变革可能会自然而然地发生。

最后,在这一点上,重要的是要认识到一个反模式。一些公司试图通过招聘专门的 DevOps 工程师并将其置于开发与运维之间,与两者互动,从而实施 DevOps。虽然这开始时可能看起来是一个好主意,但这实际上违背了 DevOps 的价值观。如果你这样做,你并没有打破孤岛,而是增加了第三个孤岛。你没有减少交接次数,很可能是增加了交接次数。此外,将开发人员和运维人员分开,使用另一种组织结构通常并不能促进他们之间的合作,你可能完全看不到对最终用户的任何价值提升。

现在,你已经知道了什么是 DevOps,并且对如何组建 DevOps 团队有了清晰的认识,接下来是时候探索如何开始实现你的目标了。

探索 DevOps 实践和习惯

由于你们不是第一支进行这项旅程的团队,你们可以从前人经验中汲取教训。一个例子是构建 Azure DevOps 的微软团队。由于他们处于一个罕见的位置,可以将自己的产品用于开发另一个产品,因此他们学到了很多关于什么使 DevOps 成功的经验。从中,他们识别出了七个关键的 DevOps 实践和七个 DevOps 习惯,这些习惯是许多成功的 DevOps 团队所共有的:

DevOps 实践 DevOps 习惯
配置管理 团队自主权与企业对齐
发布管理 对技术债务的严格管理
持续集成 专注于客户价值流
持续部署 假设驱动的开发
基础设施即代码 在生产环境中收集的证据
测试自动化 现场文化
应用性能监控 将基础设施管理为灵活资源

表 1.2 – DevOps 实践和习惯

现在,重要的是要意识到,仅仅复制这里描述的动作并不能保证成功。就像敏捷方法一样,你需要花时间去理解这些实践和习惯,它们的来源,以及它们如何为最终用户提供持续的价值流。

重要提示

微软服务推出了一种更为全面的 DevOps Dojo 模型,旨在对你的 DevOps 实践的成熟度进行分类。它为你提供了一个很好的结构,帮助你在团队中优先考虑与 DevOps 相关的投资。你可以在这里阅读更多相关内容:docs.microsoft.com/en-us/learn/paths/devops-dojo-white-belt-foundation/

接下来的章节将更详细地探讨所有这些实践和习惯。在阅读本书的其余部分时,请将这些放在心底。虽然本书的其余部分大多数将专注于如何做事的技术手段,但请不要忘记,这些仅仅是手段。真正的价值来自于你的心态,以及创建一个专注于为客户创造持续价值流的文化。

DevOps 实践

本节将依次讨论七项 DevOps 实践。正如你很快会发现的,它们彼此高度相关,要单独实践其中一项几乎是不可能的。例如,测试自动化与持续集成和持续部署密切相关。

重要提示

如果你计划参加 AZ-400 考试,掌握所有这些实践并通过 Azure DevOps 执行它们将对你大有帮助。

配置管理

配置管理是关于对你的应用程序及其依赖的组件进行版本控制,包括你的应用程序本身。配置保存在源代码控制中,通常以 JSON 或 YAML 文件的形式存在,这些文件描述了应用程序所需的配置。这些文件是像 Ansible、Terraform、Puppet 或 PowerShell DSC 等工具的输入,帮助配置你的环境和应用程序。这些工具通常从持续部署流水线中被调用。

即使配置没有变化,所需的状态也可以在一定时间间隔后重新应用。这样,可以确保实际配置保持正确,并且手动更改会自动被撤销。我们称之为防止配置漂移。配置漂移是由于随着时间的推移服务器被添加或移除,或管理员进行手动临时干预而发生的。当然,这意味着预期的配置更新需要在源代码控制中完成,并且只能通过工具应用。

配置管理或配置即代码CaC)与基础设施即代码IaC)密切相关。这两者通常交织在一起,在某些平台上,它们之间的差异甚至可能显得人为。CaC 将在第八章中详细讨论,实施基础设施和配置即代码

发布管理

发布管理是关于控制你的软件的哪个版本被部署到哪个环境。版本通常通过持续集成和交付流水线创建。这些版本以及所需的所有配置将作为不可变的工件存储在一个仓库中。从这里开始,发布管理工具用于规划和控制如何将这些版本部署到一个或多个环境中。此类控制的例子包括手动审批和在允许部署到新环境之前自动查询开放工作项和质量检查。

发布管理与持续部署相关,并更多关注通过持续部署流水线的版本流程控制。第八章实施基础设施和配置即代码,将作为发布管理的一部分进行讨论。

持续集成

持续集成是一种实践,每个开发人员每天至少一次与团队其他开发人员集成他们的工作,最好更频繁。这意味着每个开发人员应该每天至少将他们的工作推送到代码库中。持续集成构建验证他们的工作是否编译,并且所有单元测试是否运行。重要的是要理解,这种验证不应仅仅运行在开发人员孤立工作的代码上。当工作与他人的工作集成时才能真正体现其价值。

当频繁快速地集成更改时,合并更改的问题较少,并且如果出现问题,解决起来通常较为简单。在第四章一切始于源代码控制中,你将学习如何设置你的源代码控制库以实现这一点。在第五章迁移到持续集成中,你将了解如何设置持续集成构建。

持续部署

持续部署是自动将每个新版本的足够质量部署到生产环境的实践。在实践持续部署时,你拥有一个完全自动化的流水线,接收每个应用程序的新版本(每个提交),生成一个新的发布,并开始将其部署到一个或多个环境中。第一个环境通常称为测试,最终环境称为生产

在这个流水线中,多个步骤会在软件进入下一个环境之前验证其质量。如果质量不足,发布将被中止,并且不会传播到下一个环境。这种方法背后的前提是,在流水线中,你试图证明当前版本无法进入下一个环境。如果你未能证明这一点,那么你就假定它已经准备好进一步进行。

只有当一个发布通过了流水线中的所有环境后,它才会部署到生产环境。每当一个发布无法继续到下一个环境时,该发布将完全取消。虽然你可能倾向于修复失败的原因,然后从失败点重新启动部署,但重要的是不要这样做。此时所做的更改尚未通过版本已经通过的所有控制进行验证。验证新版本作为一个整体的唯一方法是从头开始启动流水线。你可以在下图中清楚地看到这一点:

图 1.4 – 持续部署流程

图 1.4 – 持续部署流程

第六章《实现持续部署和发布管理》中,您将学习如何使用基础设施即代码设置持续部署。

重要提示

上面的图示可以在en.wikipedia.org/wiki/Continuous_delivery#/media/File:Continuous_Delivery_process_diagram.svg找到。该图片由 Grégoire Détrez 提供,原图由 Jez Humble 制作,采用 CC BY-SA 4.0 许可,详情见creativecommons.org/licenses/by-sa/4.0/

基础设施即代码

在编写应用程序时,您构建的二进制文件必须在某个应用程序主机上运行。此类应用程序主机的示例可以是像 IIS 或 Apache 这样的 Web 服务器。在应用程序主机旁边,我们可能需要一个数据库和一些消息传递解决方案。这就构成了我们应用程序的基础设施。在实践 IaC 时,您会将此基础设施的描述与应用程序代码一起保存在源代码库中。

当发布新版本的应用程序,并且需要对基础设施进行一项或多项更改时,您将使用 Chef、Puppet、Terraform、Azure Bicep、PowerShell DSC 或 Azure ARM 模板等工具执行对期望基础设施的描述。执行此类描述是幂等的,这意味着它可以执行多次,而结果始终相同。这是因为您对基础设施的描述是您希望基础设施处于的期望状态,而不是一系列需要执行的步骤。如果有任何需要执行的步骤,它们会由您选择的工具自动确定。期望状态的应用也可以在持续部署流水线中自动完成,并且通常在应用程序代码更新之前执行。

这样做的一个大优势是,您可以轻松地创建一个新的环境,其中基础设施保证与其他环境中的相同。此外,配置漂移的问题(即不同环境之间的基础设施逐渐发生变化)也不再可能,因为每次您将期望的状态重新应用到每个环境时,都会强制执行。

第八章《实现基础设施和配置即代码》将更详细地讨论 IaC。

测试自动化

为了不断向最终用户交付价值,您必须快速并频繁地发布。这对您测试应用程序的方式有一定影响。当您每隔几分钟就发布一次应用程序时,您无法再执行手动测试。这意味着您必须尽可能地自动化测试。

你很可能希望为不同阶段的应用创建多个测试套件,在你的交付管道中运行。快速的单元测试应该在几分钟内完成,并且每当有新的拉取请求时都会执行,这将为你的团队提供关于他们工作质量的快速反馈,通常也能捕捉到大多数错误。接下来,团队应该在管道后期运行一个或多个较慢的测试套件,以进一步提高你对应用版本质量的信心。

所有这些应该将手动测试的量限制到最少,并让你能够自动部署新版本的应用,且充满信心。

第十章持续测试集成,将详细讲解测试自动化。

应用性能监控

最后的这一项实践完全是关于了解你的应用在生产环境中的表现。收集诸如响应时间和请求数量等指标将告诉你系统的性能情况。捕获错误也是性能监控的一部分,它能让你在无需等待客户联系我们的情况下,开始解决问题。

除此之外,你还可以收集哪些应用部分被更频繁或不那么频繁使用的信息,以及用户是否在采纳新功能。了解使用模式将为你提供宝贵的见解,帮助你了解客户如何使用你的应用,以及他们常见的场景。

第十一章安全性与合规管理,以及 第十二章应用监控,将详细介绍你的应用以及用户在生产环境中的行为。

DevOps 习惯

成功的 DevOps 团队的七大习惯更关注的是文化和开发交付软件过程中的态度,而不是像 DevOps 实践那样关注技术手段。不过,了解和理解这些习惯仍然非常重要,因为它们将帮助你更轻松地进行 DevOps 的采用。

你会发现,培养这些习惯将强化前面列出的实践和你用来实施这些实践的工具。当然,反过来也适用。

团队自主性与企业对齐

在敏捷工作中,一个重要的部分是创建那些在很大程度上由团队自主管理并能够做出决策的团队,团队在做决策时(通常不需要太多)外部依赖。这类团队通常会包括多个角色,其中包括负责一个或多个功能并有权决定前进方向的产品负责人。

然而,这种自主性也意味着团队有责任将工作与整个产品的方向保持一致。开发将数十个或数百个团队的工作协同对齐的方式非常重要,确保每个团队可以按照自己的方向航行,但整体上船队能够保持一致。

最理想的情况是,团队能够主动自发地与更大愿景对齐,而不是时不时接受外部指示。

对技术债务的严格管理

另一个习惯是对技术债务进行严格管理。债务这个词本身就意味着处理问题的延迟会产生成本(利息)。为了保持持续的进展,并防止随着时间的推移逐渐失去速度,保持 bug 数量或架构问题最小化是至关重要的,且只能容忍一定的数量。在一些团队中,这甚至已被正式化为协议。例如,一个团队可以达成一致,规定未解决的 bug 数量永远不能超过团队成员的数量。这意味着,如果一个团队有四名成员并报告了第五个 bug,那么在至少修复一个 bug 之前,团队将不会承担任何新工作。

聚焦于客户价值的流动

重要的是要接受这样的事实:直到用户使用代码之前,他们无法从已编写的代码中获得任何价值。专注于为用户提供价值的流动意味着代码必须编写、测试、交付并且在生产环境中运行,直到完成。专注于这一习惯可以推动学科和团队之间的合作。

假设驱动的开发

在许多现代开发方法论中,通常有一个产品负责人,负责根据业务价值排序所有待办事项。作为专家,这个负责人负责通过根据业务价值(按工作量划分)来排序所有项目,最大化开发团队交付的价值。

然而,最近的研究表明,即使产品负责人是专家,他们也无法正确预测哪些功能将为用户带来最大价值。大约三分之一的团队工作为用户带来了价值,而另三分之一则减少了价值。因此,你可以将待办事项从功能或用户故事转向你希望证明或反驳的假设。你只需创建一个最小的实现,甚至仅仅是功能的一个提示,然后衡量用户是否会接受它。只有当这种情况发生时,你才能扩展该功能的实现。

在生产环境中收集的证据

性能测量应在生产环境中进行,而不是(仅仅)在人工负载测试环境中进行。如果负载测试在投入生产之前能够为你带来价值,那么进行负载测试是没有问题的。然而,真正的性能工作是在生产环境中完成的,应该在那里进行测量,并与之前的测量结果进行比较。

这同样适用于使用统计数据、模式和许多其他性能指标。它们都可以通过生产度量自动收集。

生产环境文化

现场文化倡导这样一种理念:生产环境中发生的任何事情优先于其他任何事情。接下来,任何威胁生产环境、即将进入生产环境,或者在任何时候妨碍生产环境的事情,都要优先处理。只有在这些问题都处理妥当后,才会将注意力转向未来的工作。

现场文化的一个组成部分是确保对任何扰乱服务运行的事件进行彻底分析——并非为了找出谁应该负责或被解雇,而是为了找出如何防止类似事件的再次发生。预防最好通过“左移”来实现;例如,通过在管道中更早地检测到重复事件的指示符。

将基础设施管理作为灵活的资源

最终,一个成功的 DevOps 团队将其服务器和基础设施视为可以持续更改、升级、调整,甚至停用的可变资产,以满足业务需求。实现这一点的能力得益于配置管理和基础设施即代码(IaC)。这甚至可能发展到为每个新部署创建一个新的生产环境,并在将所有流量从旧环境切换到新环境后删除旧生产环境。

除了牢记这些 DevOps 实践和习惯外,你还会经历一些阶段,尝试将 DevOps 文化引入组织中。接下来的部分将带你了解这些阶段。

DevOps 演化的五个阶段

当你尝试将 DevOps 文化引入组织时,这将需要时间。你需要经历一些过程,直到组织中的每个人都接受他们在工作方式上必须做出的改变。那些在你之前走过这条路的人,经历了以下五个步骤或阶段,这可能对你有所帮助。了解这些步骤可以帮助你加快进程。这些步骤首次发布在 2018 年 DevOps 状态报告 中,接下来的部分将对其进行讨论。

标准化技术栈

迈向 DevOps 文化的一个常见第一步是采纳。至少,有好的源代码控制工具,通常会推出公司标准并进行持续集成和交付。团队也会共同合作,规范他们开发软件的技术栈。例如,选择一到两个云供应商,逐步淘汰其他部署平台。其他目的的工具也是如此——它们在可能的情况下进行标准化。自制的解决方案会被行业标准所取代。

标准化并减少变异性

在这个阶段,团队致力于进一步减少应用程序之间以及开发与运维团队之间的差异,共同努力对齐操作系统、库和工具。此外,在这一阶段,部署过程也会被调整,以减少它们之间的差异,配置和基础设施通常会被移入源代码控制。

扩展 DevOps 实践

开发和运维之间的剩余问题被清除,确保开发团队的输出正是运维团队所期望的。同时,两者之间的协作开始增长,他们可以一起工作,不再依赖外部的变更创建和交付。

自动化基础设施交付

在这一阶段,开发人员和运维部门使用的基础设施完全对齐。所有内容都从源代码管理中部署,并且两个团队使用相同的脚本或解决方案。

提供自助服务功能

在 DevOps 之前,虚拟机或托管环境通常由开发人员通过手动方式或通过工单系统向运维部门请求。运维人员手动进行资源配置,这可能需要几天,甚至几周时间。

自助服务功能意味着环境不再由手动创建,而是通过运维团队提供给开发人员的自助 API 来创建。

这样,开发人员可以自行创建和销毁环境。他们可以自己创建并测试更改,然后将其提交或安排自动部署。

总结

本章中,你了解了什么是 DevOps(以及什么不是 DevOps),以及它与敏捷的关系。转向 DevOps 文化有助于打破开发人员和运维人员之间的目标冲突。这使得他们能够协同工作,持续地为最终用户提供价值,将工作组织在同一个待办事项列表中,并在同一个看板上进行操作,同时尊重他们各自的工作方式差异。将开发人员和运维人员组织成以产品为导向的团队,是创建志同道合、目标导向团队的下一个重要步骤。

转向 DevOps 可以带来许多好处,而你现在已经知道如何衡量这些好处,从而不断地进行改进。接下来,你了解了许多成功的 DevOps 团队所展示的 DevOps 习惯和实践。掌握这些习惯和实践,无论是你个人还是你的团队,都将帮助你通过 DevOps 评估。所有这些都能帮助你持续地为用户提供价值。

在下一章中,我们将讨论站点可靠性工程SRE),以及它如何补充 DevOps,帮助管理应用程序的可靠性和可扩展性。

自我练习

利用本章中介绍的概念完成以下活动:

  1. 识别对你的团队可能重要的 2-3 个 DevOps 相关指标。

  2. 对于每个指标,识别出适用的 DevOps 实践。

  3. 定义这些指标的当前基准,并列出改进领域。

问题

在我们结束时,这里有一份问题列表,帮助你测试自己对本章内容的理解。你可以在评估部分找到答案:

  1. 判断对错:开发和运维部门经常有冲突的目标。

  2. 真假:本章讨论的七种 DevOps 实践没有关联性,可以轻松单独实践。

  3. 以下哪项不属于 DevOps 进化的五个阶段?

    1. 规范化技术栈

    2. 自动化基础设施交付

    3. 标准化和减少变异性

    4. 招聘专门的自动化团队

  4. 什么是快速通道?

  5. 用你自己的话简述 DevOps 的核心是什么。

进一步阅读

还有许多其他资源可以帮助您更深入了解 DevOps 文化和 DevOps 思维方式。以下是其中一些:

第二章:网站可靠性工程基础

在上一章中,您学习了 DevOps 文化、目标和好处。DevOps 的实践和习惯促进了团队之间的协作,并缩短了从创意构思、设计到最终用户部署的交付周期。

网站可靠性工程SRE)和 DevOps 方法是互为补充而非竞争关系。SRE 并不是 DevOps 之后的下一步。技术上,采用 SRE 实践的团队在使用 DevOps 指标进行衡量时能够实现更好的客户成果。

传统上,组织将团队划分为不同的孤岛单元,如开发Dev)、质量保证QA)和运维Ops)团队。开发团队主要负责完成功能开发,而 QA 团队主要负责执行并完成已开发功能的质量检查。运维团队则主要负责应用程序在生产环境中的部署和维护。此外,他们还需要监控并采取必要的措施,以确保已部署应用程序的可靠性和可扩展性。

SRE 和 DevOps 都有由具备开发和运维专业知识的工程师组成的团队。因此,组织可以防止各团队之间的孤岛效应。

企业或产品的声誉和成功依赖于云解决方案在生产环境中的稳定性。为了提高应用程序的稳定性和可扩展性,SRE 将使用自动化来减少重复和手动的运维任务。将 SRE 实践融入软件开发生命周期,对于在当前 IT 解决方案构建的时代中生存至关重要。

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

  • SRE 简介

  • 关键原则与实践

技术要求

本章没有技术要求。

SRE 简介

SRE这一术语最早由 Ben Treynor Sloss 在 Google 提出(sre.google/sre-book/introduction/)。SRE 使 Google 能够以最有效、可靠、可扩展和可持续的方式管理大型复杂系统和庞大基础设施。

提示

SRE 主要关注服务的可靠性。

为什么可靠性如此重要?

可靠性被定义为在特定操作条件下,服务按预期执行的可能性。最可靠的系统将更易于访问,从而带来更好的客户体验。服务的可靠性是一个重要的质量指标。

可靠性和可用性是相互关联的;然而,它们的区别在于衡量方式。尽管可用性和可靠性是相辅相成的,但所采取的衡量方法可能会产生不同的结果。系统的可用性可以通过数学模型作为其可靠性的一个衡量标准。换句话说,可靠性可以视为可用性的一个子集。

什么是可用性?

系统的可用性可以通过系统可用或完全正常运行的时间百分比来衡量。一个软件系统由多个组件组成,所有这些组件都必须进行评估以确保其可用性。假设我们有一个包含数据库服务器、存储服务器和应用服务器的系统。系统的可用性将由这些组件的组合可用性来定义。

参考uptime.is/以了解更多关于基于可用性保证的服务停机时间,通常称为服务级别协议 (SLA)的信息。以下表格展示了某些可用性百分比和停机时限(以秒、分钟和小时为单位)。术语可用性正常运行时间通常以“九”表示(表示九的数量),如表中所示(相应地计算允许的停机时间):

可用性 允许停机时间
百分比 每日
99.999%(五个 9) 0 秒
99.99%(四个 9) 8 秒
99.95%(三个 9) 43 秒

表 2.1 – 可用性百分比和停机时间容许值

计算基于时间的可用性百分比的公式如下:

可用性百分比 = ((服务正常运行时间 - 服务停机时间) ÷ (服务正常运行时间)) * 100

例如,要计算单日生产服务的可用性,假设约定的服务预计会运行24365,可以使用以下公式*:

  • 一天内约定服务预计正常运行的总秒数 = 86,400 秒。

  • 特定日期的停机时间总和 = 部署时间 60 秒 + 因部署错误回滚时间 60 秒。

  • 使用上述公式计算可用性 = ((86,400 -120) ÷ (86,400)) *100。

  • 您当天服务的可用性为 99.86%。

SRE 提倡监控和评估对业务成功至关重要的重要服务的可用性。

您的 IT 部门的目标不应是全面提高服务的可用性。更高的可用性增加了业务成本,因为它需要更多的工作和资源。然而,由于更差的可用性可能导致显著的经济损失(通常,超过运营成本),因此 SRE 团队将采取务实的方法,根据业务需求定义可用性水平。

域名服务DNS),例如,是互联网的目录。域名通过该服务与 IP 地址匹配。DNS 应该全天候、每周 7 天都可用。因此,DNS 的可用性保持在 100%。

一般来说,追求任何服务或系统的 100%可靠性并不是一个明智的选择。用户很难识别出一个服务是否是 100%可用,和一个 99.999%可用的服务之间的区别。

根据表 2.1,99.999%的可用性意味着每月可以容忍 26 秒的停机时间,每周可以容忍 6 秒的停机时间,在大多数情况下,这个时间是相当少的。

然而,一些任务关键型系统,例如医疗设备和航空器,必须完全可靠,无法承受任何停机时间。

可靠性挑战与 SRE

云架构必须可靠、可扩展并具有良好的性能。云服务将使用自动扩展来动态调整服务规模,以满足变化的需求,并且可观察性将用于支持监控系统性能、可用性、成功率、错误率、依赖错误率、请求失败率、延迟、新鲜度和吞吐量的应用程序。

组织将使用服务的可用性和可靠性指标来确定保持业务活动顺利进行所需的服务水平。您的 SRE 战略的关键决策因素是可靠性和可用性,它们有不同的含义,并且测量方式也不同:

图 2.1 – 可靠性与可用性

图 2.1 – 可靠性与可用性

如前图所示,可靠性可以视为可用性的一个子集。组织已经意识到,在保持系统可靠性的同时,还需要保持开发速度、期望的可扩展性和操作稳定性,这是一项具有挑战性的任务。

传统的团队模型包括开发、质量保证(QA)和运维(Ops)团队,开发、QA 和运维任务被分开。这种分离导致了组织孤岛或推卸责任的心态,这影响了组织团队实现长期目标并培养学习和无责文化的能力:

图 2.2 – 可靠性挑战

图 2.2 – 可靠性挑战

开发团队他们的代码丢到墙那边,期望运维团队在生产环境中运行和管理它。开发团队希望以快速的节奏将功能发布到生产环境中,而不负责应用程序的稳定性。另一方面,运维团队希望尽量减少对生产服务的更改,以避免任何对业务的干扰,从而保持服务的稳定性。

SRE 帮助团队在交付新功能和确保系统适当有效运行之间找到平衡。这种协作方法弥合了开发责任与日常系统运营、客户服务和支持任务之间的鸿沟。日常的客户支持和运营活动形成了一个反馈循环,这对于提高系统质量至关重要。

尽管客户越来越期望更短的交付时间,但仅仅关注交付速度或更高的产品速度是不够的。今天现代开发中最关键的属性之一就是可靠性。因此,在提高产品速度的同时保持可靠性,是任何 IT 组织的基本前提。SRE 团队负责其服务的可用性、延迟、性能优化、变更管理、监控、警报、应急响应和容量规划。

SRE 的基本目标是提高系统的可靠性和稳定性。团队理解可靠性指标是由业务需求驱动的,并非每个业务功能都需要最高水平的可靠性,因为这会涉及成本权衡。

适当的可靠性

服务的可靠性水平必须与其业务需求相匹配。在线电子商务企业与传统零售商店相比,会有不同的需求。例如,它们需要比传统零售商店更高的可用性,而后者通常只在设定的时间段内运营。

可靠性通过服务级目标SLOs)来定义和衡量。SRE 实践可以根据需要调整,以实现适当的可靠性水平,SLO 通常被定义为在一段时间内的百分比达成率。SLO 是由关键业务目标驱动的,而服务级指标SLIs)则是由在实现服务时可以衡量的内容驱动的。

一致的可靠性

产品建立在可靠的系统、服务和人员的基础上。SRE 认为,建立一种文化和实践是至关重要的,这将带来一致和可预测的可靠性。我们与同事建立的联系和信任、可持续的操作程序、以及我们培养的学习文化,为团队提供一个心理安全的工作环境,以实现环境可持续性,这些都是可靠性的一部分。

下一节将介绍 SRE 团队的关键原则和实践。

关键原则和实践

SRE 团队的日常活动包括开发和维护大型分布式服务。成功地运营一个健康的服务需要一系列广泛的活动,例如构建监控系统、规划容量、响应事故、解决故障根本原因等。

本节介绍了影响 SRE 团队日常活动的关键原则和实践。以下图表展示了从最基本到最先进的服务可靠性所需的元素:

图 2.3 – 根据 Google SRE 书籍的服务可靠性层级

图 2.3 – 根据 Google SRE 书籍的服务可靠性层级

从最基本的需求到推出产品或服务的巅峰步骤,Google 描述了为了提高系统可靠性和保持服务健康所需的可靠性层级。每个层级将在下文简要讨论:

  • 监控:监控是维持系统可用性最重要的策略,它位于服务可靠性层级的最底层。有效的监控应简单且具有弹性,并且应该提供针对服务中关键故障的警报。这些警报应简单明了,易于理解。

没有监控的情况下,无法判断一个服务是否正常运行、是否离线或是否遇到间歇性的故障。要建立一个可靠的系统,你必须在用户注意到问题之前,就能察觉到服务中的问题和错误。一旦这些问题被发现,SRE 团队应优先处理并管理事故响应。为了减轻影响或恢复服务到先前状态,团队应以最有效的方式协调工作,并保持积极沟通。

Microsoft Azure 是一个非常强大的云平台,具备处理解决方案各个方面的丰富功能。该平台提供管理和部署应用程序的工具。在 Azure 云上开发解决方案时,必须选择适当的服务来满足你的 SRE 需求。

为了监控你的应用程序,你可以使用以下服务:

  • Azure Monitor 提供了一个全面的解决方案,用于收集、分析和处理来自云环境和本地环境的遥测数据,并支持大规模操作,配备了智能警报自动化操作

  • Azure Application Insights 是 Azure Monitor 的一个功能,提供强大的应用性能管理APM)工具,能够轻松地与应用程序集成,发送遥测数据并分析应用程序特定的指标。它还提供现成的仪表板和指标浏览器,可以用来分析数据并探索业务需求。

  • 事件响应:一旦你围绕服务建立了有效的监控,你需要配置通知系统,例如 SMS 和事件管理系统(EMS),用于处理未计划的、关键的和紧急的事件。事件和故障在复杂的分布式系统中是不可避免的。需要适当的人为干预来识别根本原因并修复这些故障。

为了最小化业务影响并平稳运行服务,你需要建立一个结构化的过程来缓解和响应这些事件。事件得到缓解后,SRE 团队应按照事件管理响应过程尽快恢复服务。

事件响应框架有三个共同目标,广泛称为事件管理的三大 C3Cs):

  • 协调响应工作。

  • 沟通:在事件响应者、组织内部以及外部之间进行沟通。

  • 保持对事件响应的控制

尽管每个组织的事件响应过程可能会根据组织结构、技能和先前的经验有所不同,但可以参考以下一套建议和最佳实践来响应事件:

  • 优先级:修复频繁发生的问题,尽快恢复服务,并保留根本原因分析的证据。

  • 准备:提前与事件参与者共同制定并记录你的事件管理程序。

  • 信任:为所有事件参与者在其分配的角色和责任范围内提供完全的自主权。

在事件发生期间,你必须平衡以下关键点:

  • 速度

    • 平衡快速行动以满足利益相关者需求与匆忙决策带来的风险。
  • 信息共享

    • 通知调查人员、利益相关者和客户,以减少责任,避免不切实际的期望。
  • 事后分析和根本原因分析/无责事后分析:一旦事件得到缓解并处理完毕,SRE 团队会执行事后分析程序。这个事后分析程序为培养无责事后分析文化提供了机会。无责事后分析(或回顾)是一个事后文件,帮助团队弄清楚事件发生的原因、哪些做得好、哪些做得不好,并集思广益,探讨如何防止类似问题的再发生。

无责事后分析是 SRE 文化的一个基本原则。为了建立可持续的文化,我们需要假设所有参与事件的人都抱有良好的意图,并且在他们掌握的信息基础上做出了正确的决定,以最小化业务影响。将责任归咎于个人或团队会打击团队士气,并引发对惩罚的恐惧,这会使得问题更难暴露出来。

无责事后分析是团队从失败或错误中学习的机会。

  • 测试和发布流程/可靠性测试:SRE 团队负责建立对其构建和维护的生产系统可靠性的信任。SRE 团队将采用完全自动化的测试策略,并结合传统的软件测试技术,将其部署到生产环境并发布给用户,确保没有问题或停机时间。旨在支持软件可靠性的自动化测试套件可以增强信心,确保软件能够顺利部署到生产环境中。作为其质量保证活动的一部分,SRE 团队必须优先考虑并持续投资于自动化测试实践。客户会更满意,平台的采纳率也会提高,从而带来更高的投资回报率ROI)。

  • 容量规划:作为 SRE 团队的一员,你负责确定你的服务所需的资源,包括必要的硬件、软件和网络资源,并确保即使面对预料之外的需求,服务仍能表现良好。容量管理是确保服务拥有足够资源以实现可扩展性、容错性、高效性和可靠性的过程。例如,SRE 会估算在特定时间间隔内,你将需要多少存储、服务实例或内存。这些数据将帮助你为服务创建可扩展的架构。在基于云的模型中,你可能对所需的容量更为灵活,因为可以动态地增加或减少所需资源。

Azure 应用服务、Azure SQL 数据库、Azure Kubernetes 和 Azure Cache for Redis 是 Azure 云服务的示例,这些服务内置了自动缩放功能。

例如,在Azure 应用服务中,缩放设置在秒级别应用,并影响你应用服务计划中的所有应用。你不需要修改或重新部署应用。你可以扩展并获得更多的 CPU、内存、磁盘空间以及额外的功能,例如专用的虚拟机VMs)、自定义域名和证书、暂存槽、自动缩放等。你还可以利用自动缩放,依据设定的标准和时间表自动调整实例数量。缩放规则是一个更为可控的扩展或缩减的方法。

例如,每天 21:00,你可以将 Azure 应用服务的实例数缩减到两个,并设置规则,当 CPU 需求的平均值高于 50%时,自动扩展一个实例。扩展或缩减通常需要几分钟才能完成。在设计你的扩展计划时,请考虑这一点,以匹配性能需求并满足 SLA 要求。

  • 开发:虽然每个人都期望服务能够顺利运行,但可能会由于某些无法控制的事件而发生中断,如自然灾害、硬盘故障,甚至是系统进程崩溃,这些都可能对您的服务产生不利影响。自然灾害可能会严重损坏某个区域的许多数据中心。为了保持系统 正常运行,SRE 团队必须设计策略来缓解这些故障。这些解决方案可能会采用诸如 地理复制与故障转移地理冗余、以及 活动-活动活动-被动 高可用性 等部署模式/策略。

图 2.4 – 多区域架构

图 2.4 – 多区域架构

例如,如前图所示,多区域架构比单一区域部署提供更高的服务可用性。这里讨论的架构包括以下组件,仅举几例:

  • 活动区域与备用区域:两个区域用于实现更高的可用性。一个是主区域,另一个区域用于故障转移。

  • Azure Front Door:一种现代云端 内容分发网络CDN)服务,提供高性能、可扩展性和安全的用户体验,适用于您的内容和应用程序。该服务提供多种第七层负载均衡能力以及接近实时的故障转移。

  • Azure DNS:用于 DNS 域名托管服务,提供名称解析。

  • Azure 应用服务:Azure 的主要服务,用于 Web 应用程序和基于 Web 的 API,提供与 Azure ADAzure Key Vault 的集成安全性。支持自动扩展。

  • Azure Functions:一种无服务器计算选项,采用事件驱动模型。在此架构中,当新消息推送到队列时,函数会被调用。

  • Azure Redis 缓存:作为解决方案的一部分,应用缓存层服务,提供内存中的托管缓存,以减少延迟并提高客户端性能。

  • Azure 存储、Azure Cosmos DB 和 Azure SQL:可以存储结构化和非结构化内容。

  • 在灾难发生的情况下,如果区域中断影响了活动区域,Azure Front Door 将会切换到备用区域。此架构使用两个区域,活动区域和备用区域,以实现更高的可用性。在正常操作期间,网络流量会被路由到主区域。如果活动区域不可用,流量将路由到备用区域。

  • 活跃/被动与热备份意味着在备用/待命区域的资源将始终处于运行状态。这些备用区域的资源可以用于 A/B 测试,以提高性价比。

  • 产品:公司推出新产品的速度较慢。在云计算和分布式现代化世界中,推出和发布周期需要更快。

团队可以创建清单来记录行动项目和回滚计划。清单在以可复制的可靠性启动新服务中发挥着重要作用。清单需要根据公司的内部服务、流程和基础设施进行定制。

清单需要小心创建,否则它会膨胀到无法管理的规模。此清单可以并且应该被文档化和自动化,以最小化工作量。清单可以涵盖以下主题:

  • 架构和依赖关系

  • 集成

  • 容量规划

  • 故障模式

  • 流程与自动化

  • 开发过程

  • 发布规划

  • 渐进式和阶段性发布

在下一节中,我们将讨论影响 SRE 操作的模式和原则。

实施 SLOs 和 SLIs

SLOs 定义了您服务可靠性的目标水平。SLOs 是 SRE 实践的核心,因为它们对做出关于可靠性的基于数据的决策至关重要。SLOs 是帮助确定优先处理哪些工程工作的工具。

SLIs 是衡量您提供的服务水平的定量指标。SLOs 和 SLIs 总是并行存在,并通常是迭代定义的。SLOs 由关键业务目标驱动,而 SLIs 则由实施服务时可衡量的内容驱动。

您第一次尝试 SLI 和 SLO 时不必是正确的。最重要的目标是建立并进行测量,并设置反馈回路,以便您可以改进。您可以识别哪些度量指标最符合用户在您的服务中关心的内容。

SRE 团队建议根据关键服务使用有限的度量指标,以提高用户体验,而不是开发无穷无尽的监控指标。您可以从广泛的目标开始,然后随着时间的推移进行调整。这使您能够将警报集中在可以可靠地判断服务将失效并开始影响用户体验的实例上。

假设您正在构建一个外卖应用,用户可以执行以下操作:

  • 浏览餐厅和菜单。

  • 选择菜单项并下单。

  • 支付订单。

对于这个应用,用户能够下单并成功支付对于更好的用户体验和整体业务成功至关重要。在这里,首次尝试的下单并支付场景将成为定义 SLOs 的基础,因为这个服务对业务成功的优先级高于其他任何服务。

下一步是弄清楚哪些度量指标可以作为 SLIs,最准确地跟踪用户体验。您可以从各种指标中选择,例如可用性延迟、吞吐量、正确性和数据新鲜度,接下来会详细介绍。

大多数服务专注于以下四个关键 SLI 指标来进行监控。这些是监控的四个黄金信号:

  • 请求延迟:您的服务返回请求响应所需的时间

  • 服务可用性:服务可用的时间占比

  • 成功率:成功完成的请求数量

  • 吞吐量:每秒处理的请求数量

定义 SLO 的测量周期非常重要,可以在不同的时间间隔内进行定义。SLO 和 SLI 需要是能够在监控系统中准确测量和表示的内容。随着时间的推移,你将比较 SLO 目标和 SLI 与实际度量指标。现在,对于下单并支付服务,它是 SLO 的基础,你希望在可接受的时间窗口内收到订单确认。

在我们以外卖应用为例的情况下,你可以为下单并支付服务设置一个响应时间值,比如 700 毫秒,用于在指定的 1 个月时间窗口内成功请求的响应。这些 SLI 目标和时间框架应当是业务可以接受的。所以,如果在一个日历月内有 10,000 个 HTTP 请求,其中 9,990 个请求成功,这意味着该月的可用性为 9,990/10,000,或 99.9%。

如果你的服务未能满足 SLO,SRE 团队将努力在新功能开发/部署和提高服务可靠性之间找到平衡,以避免在给定的测量周期内发生 SLO 违规。

SLA 是商业和客户之间的法律协议,包括可靠性目标以及未能达到目标的后果,而 SLO 是一个衡量客户如何使用服务的内部目标。SLO 不会与外部利益相关者共享,也没有法律约束力或后果。如果服务的可用性违反了 SLO,SRE 团队必须立即响应,以避免组织因未能满足 SLA 而受到处罚。SLO 应始终比相应的 SLA 更严格。SRE 团队通常不参与构建 SLA,因为 SLA 与业务和产品决策密切相关。然而,SRE 团队可以帮助定义 SLI。

为了在可靠性和创新之间找到合适的平衡,最好创建一个衡量 SLO 违规的比率,以及一个可以容忍 SLO 失效的错误预算。在接下来的部分中,我们将深入了解错误预算。

建立错误预算政策

错误预算是指在给定周期内,服务无法正常运行的最大错误数或最大时间,超过此限度会对业务产生负面影响,用户将不满。错误预算适用于服务的多个方面,例如可用性、延迟等。SRE 团队利用错误预算在服务可靠性和创新速度之间找到平衡。

错误预算将帮助你判断自己是否达到了预期目标,并帮助你采取适当的措施来减少服务的可靠性故障。如果服务在四周内收到 1,000,000 个请求,99.9%的成功请求 SLO(服务水平目标)允许我们在这段时间内预算 1,000 个错误。

一个具有 99.95% SLO 的服务,其错误预算为 0.5%,意味着在一年内整体的停机时间为 4 小时 22 分钟 48 秒。SRE 团队应采取适当的行动来恢复服务的稳定性,特别是当服务未达到 SLO,或错误预算已用尽或接近用尽时。

当你的服务在预算范围内时,SRE 实践鼓励你战略性地消耗错误预算,无论是用于新功能还是架构修改。尽管任何新的发布都会在某种程度上使服务变得不那么可靠,例如,如果服务因为部署配置问题而宕机,服务仍然在预算内。错误预算通常是根据一定时间段设定的,例如一个月、一个季度或一年。

错误预算通常用于战略性地为Toil任务(如手动部署、环境设置、配置更改、事件响应等)应用自动化。在下一节中,我们将进一步了解如何减少 Toil。

减少 Toil

在 SRE(站点可靠性工程)上下文中,"Toil" 指的是那些没有长期经济价值并且不会显著推动服务进展的操作。这些操作通常是重复性的,并且大多是手动的(尽管它们可以自动化)。随着服务或系统的增长,系统的手动请求数量可能会按比例增长,进而需要更多的人工劳动。

SRE 工程师最多只能将 50%的时间花费在Ops工作上,如处理工单、值班和手动任务等。

SRE 工程师应该将他们的另一半时间花在以下活动上(这不是一个全面的列表):

  • 使用自动化来实现可扩展性

  • 发布部署

  • 测试套件自动化

  • 应用数据库更改

  • 自动化响应事件,如密码重置和用户创建

  • 审查非关键监控警报

  • 为了减少 Toil 并提高系统可靠性,进行工程工作以开发新功能。

为了有效地减少 Toil 工作负担,SRE 团队可以从小处开始,逐步推进。消除 Toil 需要自动化,SRE 团队必须不懈努力,减少或最小化 Toil。自动化工程工作,无论是部分还是完全,都是必要的,但它不应危及系统可靠性。Azure Automation 可以用来自动化人类响应,并诊断和解决问题。自动化将提升团队的动力,使团队能够专注于工程工作。

总结

在这一章中,我们了解了 SRE 的原则和实践。现在我们知道如何计算基于时间的可用性,以及如何根据业务期望和需求定义可用性。

我们还探讨了传统团队模型中常见的可靠性挑战,以及如何通过建立 SRE 团队来帮助你在系统可靠性和开发之间找到合适的平衡。我们强调了适当且一致的可靠性的重要性。

然后,我们学习了从构思到成功将服务部署到生产环境的所有必要方面。我们还重点介绍了关键技术,如应用 SLO 和 SLI、减少繁重工作、事后分析文化,以及高效利用错误预算来提高系统和云服务的可靠性。

在接下来的章节中,我们将探讨 DevOps 工具和能力,看看它们如何帮助你管理软件开发生命周期。

自我实践练习

  1. 与业务利益相关者和开发团队共同确定 SLO。

  2. 对你服务在生产环境中最近或假设的生产事件进行无责事后分析:

  3. 识别与在生产环境中运行服务相关的繁琐、重复性任务/负担。

  4. 在 Azure 门户中,首先配置应用服务日志记录与应用洞察,以监控应用程序,然后配置电子邮件警报。

问题

在我们总结 SRE 战略时,这里有一份问题清单供你测试你对本章内容的理解。你可以在书本末尾的评估部分找到答案:

  1. 正确还是错误?SRE 主要关注服务的可靠性。

  2. 正确还是错误?可用性是以五个 9 来表示的。

  3. 正确还是错误?SRE 采用自动化来实现应用程序管理。

  4. 对于一个可用性为 95%的服务,每周、每月和每年的停机容忍时间是多少?

  5. 最主要的三大可靠性挑战是什么?

    1. 开发速度

    2. 质量

    3. 稳定性

    4. DevOps 管理

    5. 缺陷跟踪

  6. 正确还是错误?服务的适当可靠性水平由关键利益相关者确定。

  7. 在 SRE 的背景下,什么是负担(toil)?

进一步阅读

第三章:最大化利用 DevOps 工具

DevOps 可以看作是一种旨在改进整体软件开发生命周期SDLC)过程的方法,从而增强参与团队之间的合作。因此,使用适当的工具集来支持 DevOps 流程和活动对于推动各个工程团队之间的一致性和可预测性至关重要。目标是通过自动化各个流程,建立创新文化,从而同步团队(或角色)的努力,在开发、测试和部署软件时,以尽可能高的质量实现更快的速度。

Microsoft 提供了诸如 Azure DevOpsGitHubAzure MonitorVisual Studio Code 等优秀产品,帮助简化在 DevOps 实施的各个生命周期阶段中采用重要实践的过程。这些工具功能丰富,并为开发者社区提供了业界领先的体验。通过其他独立软件供应商ISVs)和开源社区提供的市场扩展,这些工具的开箱即用体验可以进一步扩展。

到本章结束时,你将熟悉其中一些工具的功能,以及它们在各自的软件开发程序中的应用背景。

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

  • SDLC 和 DevOps 工具

  • Azure DevOps 和 GitHub

  • Azure DevTest Labs

  • Azure Monitor

  • Visual Studio Code

SDLC 和 DevOps 工具

无论是服务还是产品,SDLC 这个术语指的是在构建软件解决方案时所应用的一系列过程和实践,以确保质量并达到为工程团队设定的其他目标。

这些流程通常根据开发生命周期的各个阶段进行分组。随着技术的演进,这些流程的成熟度也得到了提升。由于分布式计算和云技术的使用,工程系统变得相对复杂,开发团队需要保持高度的严谨性和纪律性,同时利用自动化来支持他们的数字化转型之路。

根据你所遵循的软件开发方法论,无论是 瀑布模型敏捷Scrum看板,还是你自己定制的版本,你很可能会以某种形式使用 DevOps 相关的实践。然而,值得一提的是,如果你希望充分发挥 DevOps 投资的真正潜力,瀑布模型已经不再是推荐的方法论。

让我们看看 DevOps 生命周期中执行的一些关键活动。

DevOps 生命周期中的关键活动

从高层次来看,DevOps 相关的活动可以分为四个不同的重点领域,如下图所示:

图 3.1 – DevOps 生命周期中的重点领域和关键活动

图 3.1 – DevOps 生命周期中的关注领域和关键活动

这里列出的活动清单旨在作为基本的基础列表。根据您的 DevOps 成熟度,您可能会实施更多的实践来最大化业务成果。您必须利用第一章《DevOps 简介》中与您的上下文相关的 DevOps 度量指标,并识别出有助于实现这一目标的重要实践。

在接下来的几个部分中,我们将详细回顾这些关注点,帮助您更好地理解每个部分。

规划和跟踪

开发团队识别并组织产品积压,并发布工作任务给团队。工作将根据业务优先级和可用容量进行排期。积压梳理和完善是产品生命周期中的持续活动,且将跨多个版本进行。

开发和测试

开发团队开始以源代码或脚本的形式产出工作成果。所有对代码库的更改都会通过静态分析器、单元测试,甚至人工检查来检测质量问题。一旦确认无误,工件将被编译并构建,生成可部署的包,也称为解决方案。

部署和发布

解决方案最初部署在测试环境中,使用自动化测试验证其正确性,然后发布到生产环境中。之后,解决方案将正式上线,并供最终用户访问产品或服务。

监控和学习

一旦解决方案上线,这一阶段开始生效,从产品或服务的实际使用中捕获诊断和健康监控信息。监控工具中捕获的数据会定期分析故障和错误;任何识别出的问题要么通过自动化程序进行缓解,要么记录为待修复的缺陷。此时,客户反馈也可以通过渠道反馈给产品或服务,以提出改进建议。现在,您已经了解了 DevOps 生命周期中的工作内容,我们接下来将回顾一些常用的 DevOps 工具。

您的 DevOps 需求的工具

微软提供了多种工具来支持您的 DevOps 生命周期需求。此外,它还支持与其他第三方工具的集成,为您团队的开发人员提供一个协调一致的体验。因此,无论您是在构建新软件系统还是维护现有系统,您都可以轻松采用 Azure DevOps 服务和 GitHub,以大规模创新。

这里列出了您可以计划用于各种 SDLC 活动的工具:

实践/活动 工具选择
管理产品积压,规划并跟踪工作,准备仪表板和报告。 Azure Boards
本地开发代码,构建、调试并测试。 Visual Studio Code/Visual Studio
管理和跟踪源代码的更改。 Azure Repos,Git
定义持续集成CI)/持续部署CD)工作流,以集成更改并将最新发布部署到不同环境。 Azure PipelinesGitHub ActionsJenkins(第三方)
提供开发环境。 Azure DevTest Labs,Azure Virtual Desktop
部署和管理 Azure 云基础设施和服务(包括 IaaS 和 PaaS)。 Azure 资源管理器ARM)和 Azure 命令行界面CLI)Ansible 和 Terraform(第三方)
管理资源的配置。 Ansible,Terraform,Chef,Puppet 和 Azure Automation
高级分析和报告。 Power BI
捕获和分析日志(诊断、审计和健康)。 Azure Monitor 和 Azure Data Explorer
准备测试计划并管理执行。 Azure Test Plans
存储可重用的软件包和工件。 Azure Artifacts
通过使用第三方扩展和插件提高生产力。 Azure DevOps MarketplaceGitHub Marketplace

表 3.1 – 用于 SDLC 活动的 DevOps 工具

请访问 https://azure.microsoft.com/en-in/solutions/devops/#practices 了解更多详细信息。

在接下来的几节中,我们将介绍这些工具的一些功能和能力。这些工具的详细使用也将在后续章节中解释,并提供说明性示例,展示如何实现各自的 DevOps 实践。

Azure DevOps 和 GitHub

Azure DevOpsGitHub 是微软提供的两款强大的软件即服务SaaS)产品,构成了完整的 DevOps 工具集。从管理待办事项和团队流程,到基于 Git 的代码库,再到能够执行 CI/CD 流程并进行自动化测试,你会发现 GitHub 和 Azure DevOps 是完美的解决方案。

在接下来的子章节中,我们将探索这两款工具的独特功能。

Azure DevOps

最初,作为 Visual Studio 套件的一部分,现今的 Azure DevOps 可能是工程团队最常用的产品,用于管理他们的源代码和应用程序生命周期过程。它以前被称为Visual Studio Team Services,在此之前还叫做Visual Studio Online

Azure DevOps 是 Azure 云托管版的 Azure DevOps Server,之前在开发者社区中被称为Team Foundation ServerTFS)。该产品中提供的主要服务如以下图所示:

图 3.2 – Azure DevOps 中可用的服务

图 3.2 – Azure DevOps 中可用的服务

您对这些服务的访问权限将取决于您用户帐户的许可类型。有关个人和组织可用的完整许可选项列表,请访问 azure.microsoft.com/en-in/pricing/details/devops/azure-devops-services/

让我们简要了解每个服务。

Azure Boards

使用 Azure Boards 管理产品待办事项并跟踪工作变得更加容易。通过使用看板风格的板块,您可以创建自定义的看板来跟踪从想法到实现的所有内容:

图 3.3 – Azure Boards 的功能

图 3.3 – Azure Boards 的功能

您可以在此阅读更多有关 Azure Boards 的信息:docs.microsoft.com/en-in/azure/devops/boards/get-started/what-is-azure-boards?view=azure-devops

使用 Azure Boards,您可以执行以下操作:

  • 管理和组织您的待办事项列表。

  • 可视化各个阶段,并在每日站会和团队会议中使用它们来跟踪进度。

  • 为各利益相关者创建自定义的仪表板报告。

在下一节中,我们将介绍 Azure Repos,这是您将存储所有工作产品的地方。

Azure Repos

源代码管理SCM)和版本控制对管理代码库更改的工程团队至关重要。Azure Repos 提供了一个版本控制工具,用于管理各种软件开发项目。

Azure Repos 提供两种类型的版本控制系统:

  • 团队基金会版本控制TFVC):这是一个集中式版本控制系统。尽管近些年使用不广泛,但许多项目团队仍然在出于遗留目的的情况下继续使用这种方法。

  • Git:这是更受欢迎的分布式版本控制系统。如果您刚开始您的 DevOps 之旅,建议仅使用基于 Git 的版本控制系统:

图 3.4 – Azure Repos 的功能

图 3.4 – Azure Repos 的功能

您可以在此阅读更多有关 Azure Repos 的信息:docs.microsoft.com/en-us/azure/devops/repos/get-started/what-is-repos

使用 Azure Repos,您可以执行以下操作:

  • 使用 Git CLI 或基于 GUI 的工具管理您的源代码更改。

  • 配置分支策略和安全性以确保合规性。

  • 使用语义搜索快速找到您在代码库中查找的内容。

现在,让我们回顾一下 Azure Pipelines,您将使用它进行 CI/CD 过程。

Azure Pipelines

所有基于代码的工件都遵循某种形式的持续集成(CI)、持续交付(CD)和持续测试CT),以确保它们的质量,然后才能将其移至生产环境。Azure Pipelines 提供自动化来执行构建、测试和发布工作流。执行任务的顺序通过 YAML 文件进行配置。在构建管道时,您可以选择多个源代码库(如Azure ReposGitHubBitbucket等)。此外,通常会为不同的工作负载创建单独的管道,并根据需要进行门控审批检查:

图 3.5 – Azure Pipelines 中的功能

图 3.5 – Azure Pipelines 中的功能

您可以在此阅读更多关于 Azure Repos 的内容:docs.microsoft.com/en-us/azure/devops/pipelines/get-started/what-is-azure-pipelines?view=azure-devops

使用 Azure Pipelines,您可以执行以下操作:

  • 创建 CI/CD 管道(基于 YAML),执行各种任务,如构建解决方案、运行自动化测试,然后部署解决方案。

  • 管理不同环境的部署。您还可以实施门控检查和批准。

现在,让我们回顾一下如何使用 Azure 测试计划组织您的测试用例。

Azure 测试计划

测试是软件开发生命周期(SDLC)中的一个非常重要的步骤。测试活动有不同的类型,如功能测试(手动或自动化)、性能测试、用户验收测试和探索性测试。Azure 测试计划允许您为任何特定的迭代或版本创建执行计划,包含适用的测试并跟踪执行结果。执行测试计划的报告可以轻松生成,利益相关者可以根据报告推断软件产品或服务的质量:

图 3.6 – Azure 测试计划中的功能

图 3.6 – Azure 测试计划中的功能

您可以在此阅读更多关于 Azure 测试计划的内容:docs.microsoft.com/en-us/azure/devops/test/overview?view=azure-devops

使用 Azure 测试计划,您可以执行以下操作:

  • 为各种类型的测试活动创建执行计划。

  • 维护测试用例、缺陷和产品待办事项之间的可追溯性。

  • 使用现成的图表和小部件分析测试报告。

在下一部分中,我们将回顾您的团队如何利用 Azure Artifacts 作为所有可重用代码组件的存储库。

Azure Artifacts

Azure Artifacts 允许开发人员管理各种类型的可重用包,以满足他们的解决方案开发需求。这些包可以仅供内部使用,也可以分发给外部。实质上,它充当了一个包的仓库,如公开可用的注册表。

您可以在此处了解更多关于 Azure Artifacts 的信息:docs.microsoft.com/en-us/azure/devops/artifacts/start-using-azure-artifacts?view=azure-devops

使用 Azure Artifacts,您可以执行以下操作:

  • 将包发布到中央私有仓库。支持的包类型包括 NuGet、npm、Maven、Python 和通用包。

  • 消费并共享包以供其他解决方案组件重用和参考。

现在,让我们来看看微软的 GitHub 提供的服务。

GitHub

GitHub 于 2018 年 6 月左右被微软收购。在最初几年,GitHub 是开发者社区中非常流行的代码托管平台,主要用于开源项目的代码共享。然而,自从被收购以来,它增加了各种功能,使其在开发者中更受欢迎,帮助他们构建和共享代码,同时通过提供多种其他功能扩大了企业使用范围。

GitHub 是另一款非常流行的工具,用于实现您的 DevOps 实践。产品中提供的主要功能如下图所示:

图 3.7 – GitHub 上可用的功能

图 3.7 – GitHub 上可用的功能

您可以在 https://github.com/features 详细了解这些功能。

由于功能列表持续更新(您可以参考公共路线图:github.com/github/roadmap),请随时查阅产品文档以获取最新信息。

在下一部分中,我们将介绍Azure DevTest Labs,它允许您快速配置非生产类型的环境,以验证您的解决方案是否有效。

Azure DevTest Labs

这是微软 Azure 提供的一项服务,允许开发人员使用已发布的可重用模板和工件快速创建开发和测试环境。您可以在此处找到更多信息:azure.microsoft.com/en-in/services/devtest-lab/

虽然该服务可免费使用,但仍要求开发人员拥有付费的 Azure 订阅。不过,根据适用的定价计划,使用已配置的 Azure 资源将按大幅折扣价格收费。您可以在此处查阅与定价相关的详细信息:azure.microsoft.com/en-in/pricing/details/devtest-lab/

在下一部分中,我们将查看 Azure 中可用的监控工具。

Azure Monitor

Azure Monitor 提供丰富的仪表板功能,帮助你监控特定应用程序的指标、资源健康状况和利用率,以检测异常并提供及时干预。这是一个综合性服务,允许你收集、分析并处理来自各种来源的监控数据,包括应用程序、基础设施和其他自定义源。

你必须计划使用的 Azure Monitor 的一些关键功能如下:

  • 日志分析,深入挖掘监控数据并获取深刻洞察

  • Application Insights,用于端到端事务追踪、异常和性能指标

  • 容器洞察,用于微服务的使用情况和健康相关统计

  • 自定义仪表板,根据使用案例量身定制,用于跟踪使用情况和可靠性指标

  • 警报,用于检测异常并通知团队采取适当的行动,以及执行自动化的操作

要了解更多关于 Azure Monitor 的信息,请访问 azure.microsoft.com/en-us/services/monitor/

来自不同来源的监控数据可以分为两部分,即日志指标。它们是 Azure Monitor 服务中的两个独立的大数据存储。

Azure Monitor 由一系列产品和服务组成,提供所需的监控和仪表板功能。在以下小节中,我们将回顾三种最常用的服务——即 Azure Monitor 日志、Azure Monitor 指标和 Application Insights。

Azure Monitor 日志

可以收集来自多个来源的通用日志和性能数据,并将其整合到 Azure Monitor 日志的一个工作区中。

更多信息请参见:docs.microsoft.com/en-in/azure/azure-monitor/logs/data-platform-logs

Azure Monitor 指标

这是 Azure Monitor 的一项功能,将来自多个来源的数值数据捕获到一个时间序列数据库中。这些值对应于从系统收集的、按定期间隔采样的指标列表。

更多信息请参见:docs.microsoft.com/en-in/azure/azure-monitor/essentials/data-platform-metrics

Application Insights

你可以通过 Azure Application Insights 在所有应用程序中集成对丰富遥测数据的捕获支持。使用日志来获取洞察,检测性能问题,诊断常见错误,利用端到端事务流可视化 HTTP 请求,并收集指标以推导出其他各种指标。

你可以在这里了解更多关于 Azure Application Insights 的信息:docs.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview

在下一节中,我们将回顾开发人员常用的工具——Visual Studio Code。

Visual Studio Code

Visual Studio Code 是微软推出的一款免费代码编辑器,允许你快速开发、构建、部署和测试现代云应用。它可以在 Windows、Linux 和 macOS 上使用,并内置支持多种编程语言。此外,它还有一个社区支持的插件和小工具,可以极大提升你的工作效率。

如果你是开发人员,必须尝试 Visual Studio Code。欲了解更多信息,请访问 code.visualstudio.com/

总结

本章中,我们回顾了几种可以用于 DevOps 生命周期需求的重要工具。我们将在接下来的章节中通过实例和实践实验详细说明这些工具的具体使用。不论你是开发人员还是 IT 管理员(Ops),你都会在日常工作中使用这些工具中的一种或多种。

继续专注于构建自动化工作流,更多地采用这些工具将有利于你的组织实现 DevOps 目标,这些目标通过各种指标进行跟踪。研究表明,已经转向 DevOps 交付模式的企业,在执行各种数字化转型计划方面比竞争对手更成功。

因此,必须理解,技术的使用在推动 DevOps 成熟度指数方面也发挥着至关重要的作用。在企业中采用 Azure DevOps 和 GitHub,不仅能激发团队更大的生产力和潜力,还能提升你成为 DevOps 采纳者的顶尖表现者的机会。

在下一章中,我们将探讨在 API 平台生命周期流程中实施正确的 DevOps 实践的重要性。

自我练习

利用本章中介绍的概念来完成以下活动:

问题

在本章结束时,以下是一些问题,供你测试自己对本章内容的理解。你可以在附录中的 评估 部分找到答案:

  1. 判断题:你需要企业用户账户才能创建 Azure DevOps 项目。

  2. 判断对错:你可以使用 Azure Boards 可视化和跟踪团队的工作。

  3. 判断对错:Azure Application Insights 提供性能指标,例如所有与其集成的应用程序的响应时间。

  4. 判断对错:你可以在 Azure Pipelines 中使用 GitHub 存储库。

  5. 判断对错:GitHub 仅作为开源代码存储库使用。

进一步阅读

要了解本章涉及的主题,请查看以下资源:

第二部分 – 实现持续交付

在本部分中,你将学习源代码控制、持续集成和持续部署的实践。大多数组织首先采用这些 DevOps 实践,因为它使你能够以最有效、可预测的方式持续地生产高质量的解决方案。

本书的这一部分包含以下章节:

  • 第四章一切始于源代码控制

  • 第五章过渡到持续集成

  • 第六章实现持续部署和发布管理

第四章:一切从源代码控制开始

源代码控制是软件开发中最基本的工具之一。因此,可以合理假设你之前已经使用过源代码控制。鉴于此,本章将仅简要介绍源代码控制,并迅速进入更高级的话题,以帮助你设置源代码控制并支持 DevOps 实践。

多种 DevOps 实践依赖于源代码控制,因此,设置你的代码库以持续为用户提供价值是一个很好的起点,并且是后续章节中许多主题的前提条件。

本章将涵盖以下主题:

  • Azure DevOps 源代码控制系统中的源代码控制类型

  • 选择分支和合并策略

  • 使用分支策略保护源代码控制

  • 可用于源代码控制的其他工具

技术要求

为了实践本章所涉及的主题,你可能需要一个 Azure DevOps 组织。此外,请确保在本地计算机上安装了 Git 工具。你可以从这里下载 Git 工具:git-scm.com/downloads

Azure DevOps 中的源代码控制类型

虽然存在许多不同的源代码控制系统,但它们可以分为两类:集中式和去中心化,如下所示:

  • 集中式源代码控制系统中,只有服务器拥有完整的历史记录和构成代码库的所有分支。

  • 去中心化源代码控制系统中,每个与代码库协作的人都拥有代码库的完整副本,包括所有分支及其历史记录。

Azure Repos,作为 Azure DevOps 服务的一部分,通过团队基础版本控制TFVC)和 Git 提供两种类型的源代码控制。接下来的两节将更详细地讨论这两种源代码控制。

集中式源代码控制

在集中式源代码控制系统中,服务器是唯一存储完整代码库(包括所有历史记录)的地方。当你创建内容的本地版本时,你只会获得代码的最新版本。获取这个最新版本的过程称为检出代码库。除了这个最新版本,你的计算机上只有你本地所做的更改。

不检出完整历史记录显然节省了你本地计算机的空间。然而,如今磁盘空间几乎从来不是问题。这样做的缺点是,你需要持续连接到服务器,才能执行诸如查看文件历史记录、其他人最近提交的内容,或是某个文件中的某一行最后由谁修改等操作。

集中式源代码控制系统的一个优点是,它们通常提供对谁可以访问哪些分支、目录,甚至文件的精细控制选项。

去中心化源代码控制

使用去中心化源代码管理系统时,所有文件、历史记录和分支也会存储在服务器上。与集中式源代码管理的不同之处在于,当你克隆仓库时,能够在本地计算机上拥有一份副本。

由于你拥有仓库的完整克隆,现在可以在不需要再次连接到服务器的情况下查看文件的历史记录和其他分支。这显然减轻了服务器的负担,并允许你在断开连接时继续工作,这是去中心化源代码管理的两个优势。

缺点是,去中心化的源代码管理可能比集中式源代码管理更难学习。总体来说,去中心化源代码管理系统的学习曲线更陡峭。此外,基于单独目录和文件的访问控制通常也更为有限。

无论你使用哪种类型的源代码管理,都必须制定分支和合并策略,以便让开发人员能够并行工作在不同的功能上,同时始终保持 master 分支处于可交付的状态。

在 Azure DevOps 的一次更新中,仓库创建时默认创建的分支现在被命名为 main。Azure DevOps 还提供了将默认分支重命名为其他名称的功能。有关更多信息,请参考:docs.microsoft.com/en-us/azure/devops/repos/git/change-default-branch

在下一节中,我们将了解开发者社区中最常用的不同源代码管理系统。

源代码管理系统

目前有许多源代码管理系统被使用,但在本章中,我们将只关注当前最常用的三种,它们如下:

  • TFVC

  • Git

  • Subversion

在 Azure DevOps 中,只有 TFVC 和 Git 可用。Subversion 是由 Apache 基金会创建的集中式源代码管理系统。在接下来的小节中,我们将更详细地了解 TFVC 和 Git,并学习如何在它们之间迁移源代码。Subversion 将在本章最后的 其他源代码管理工具 部分进行讨论。

TFVC

TFVC 是由微软在 2013 年推出的集中式源代码管理系统,作为 Team Foundation Server (TFS) 的一部分,该产品已经发展成了现在的 Azure DevOps。TFVC 在 Azure DevOps 中仍然受到支持,但不建议用于新项目。如果你还没有使用 TFVC,那么学习它没有什么价值,因为微软很可能不会再为它发布新功能,但也不必因为没有其他原因就停止使用它。

在 Azure DevOps 中,每个团队项目最多只能有一个 TFVC 仓库。

Git

除了 TFVC,Azure DevOps 还支持托管 Git 代码库。Git 是一种去中心化的源代码控制方式,目前在开发者社区中是标准做法。Git 并非 Azure DevOps 特有,它是一种通用协议,许多提供源代码托管服务的平台都使用这个协议。Azure DevOps 之外的著名例子有 GitHub 和 GitLab。

要与 Git 代码库一起工作,您必须首先克隆它:

  1. 打开命令提示符并导航到您希望存储代码库的目录。

  2. 执行以下命令并将示例 URL 替换为您的 Git 代码库的 URL。示例 URL 展示了 Azure DevOps 中 Git 代码库位置的构建方式:

    git clone https://{organization}@dev.azure.com/{organization}/{teamProjec t}/_git/{repository}
    

现在,您可以开始处理您想要做的更改。在本示例中,添加了一个新文件NewFile.txt

  1. 接下来,必须暂存此文件以便提交。暂存文件是为了区分您想提交的文件和您希望保留的更改:

    git add NewFile.txt
    
  2. 在将所有希望归为一个提交的更改暂存后,创建实际的commit可以通过调用commit命令并指定更改描述来完成:

    git commit -m “Added a new file that contains an important text”
    
  3. 最后,您可以通过执行以下命令将您的更改推送回中央代码库(也称为远程库):

    git push
    

要进行更多更改,您可以根据需要随时暂存和提交更改。您可以一次提交一个提交,也可以一次推送多个提交。

您也可以通过Visual StudioVS)或 VS Code 接口来使用 Git。在这里,您执行完全相同的步骤,但可以使用图形界面代替熟悉的命令行界面。

大文件存储

Git 被设计和优化用于处理纯文本文件,并跟踪从版本到版本的变化。然而,您可能希望在源代码管理中存储除了文本文件以外的其他内容。比如,图像或二进制文件,这些文件应该在应用程序运行时与应用一起使用。虽然这些是有效的使用场景,但开箱即用时,它们与 Git 的兼容性并不好。为了解决这个问题,引入了大文件存储LFS)。

Git LFS 并不直接存储二进制文件本身,而是允许您存储一个小的文本文件,这个文本文件充当二进制文件的指针。该文本文件包含二进制文件的哈希值,以便客户端在克隆或获取更改时下载文件。之后,当您更新二进制文件时,文本文件中的哈希值也会更新。

要使用 Git LFS,您必须在 Git 客户端之外安装 LFS 客户端。这是一个独立的客户端,代码库的每个用户都必须下载。没有这个客户端,其他用户只能看到指针文件,而无法看到实际的二进制文件。安装客户端后,您必须为代码库准备 LFS 的使用。以下示例命令启用了对 MP4 文件的 LFS 支持:

git lfs install
git lfs track “*.mp4” 
git add .gitattributes

从现在开始,您可以像处理任何文件一样处理 MP4 文件,在幕后,它们将与您的文本文件更改分开存储。

在控制系统之间迁移

DevOps 旅程中的一步是工具的整合。这意味着在某个时刻,您可能会被要求将源代码从一个源代码控制系统迁移到另一个,并且公司可能决定将所有源代码从 GitLab 或 Subversion 迁移到 Azure Repos。您可以选择多种选项来执行此类迁移。

最可能的情况是您将收到将源移动到一个或多个 Azure Git 存储库的请求。可能的来源包括其他 Git 存储库、TFVC 或 Subversion。有工具和方法可用于在保留原始存储库中变更历史的同时执行此类迁移。

如果没有可用的程序或者必须从另一个系统导入源代码,您还可以回退到创建一个新的空存储库,并使用现有代码库进行初始化。这种方法的缺点是所有历史记录将丢失。

迁移现有 Git 存储库

在迁移源方面,与其他迁移相比,将 Git 存储库迁移到另一个托管位置非常简单。让我们学习如何做到这一点:

  1. 首先,将现有存储库克隆到您的本地计算机:

    git clone https://{organization}@dev.azure.com/{organization}/{teamProjec t}/_git/{repository} .
    
  2. 添加另一个引用新的空存储库的远程服务器,您希望将源移动到该存储库:

    git remote add migrationTarget https://{organization}@dev.azure.com/{organization}/{teamProjec t}/_git/{newRepository}
    
  3. 最后,将更改推送到这个新存储库。您必须对每个要移动到主干旁边的分支单独执行此操作:

    git push migrationTarget master
    

同时,其他开发人员可能会继续使用现有的存储库。

  1. 要将这些包含在新存储库中,您必须从原始存储库将它们获取到您的本地计算机,然后将它们推送到新存储库。再次为每个分支重复此操作:

    git fetch origin master
    git push migrationTarget master
    
  2. 指示所有开发人员开始使用新的远程存储库。随后,计划废弃原始存储库。

  3. 成功迁移后,最好删除旧存储库。这样可以防止任何人意外继续在那里工作。

前述步骤对任何 Git 到 Git 的迁移都适用。

现在,如果您特别想迁移到 Azure Git 存储库,您还可以使用 Azure DevOps 提供的导入功能。要执行此操作,请按照以下步骤操作:

  1. 转到存储库,并可选择创建一个新的 Git 存储库。

  2. 选择导入现有存储库。

  3. 提供所请求的信息。

  4. 单击导入以开始导入存储库。

以下截图展示了这些步骤:

图 4.1 – 导入存储库

图 4.1 – 导入存储库

这种方法的缺点是您不能继续将更改从源仓库推送到新仓库。这意味着您团队中的所有其他开发人员必须确保他们自己将更改迁移过来,或者在您迁移仓库时没有任何待处理工作。

从 TFVC 迁移到 Azure Git 仓库

要从 TFVC 迁移到 Git,您可以使用与将任何 Git 仓库迁移到 Azure 仓库相同的导入仓库。此向导可以在进行导入时迁移过去 180 天的变更历史。如果这不够,您需要将超过 180 天的历史迁移到新仓库,您可以使用其他方法,但这些方法更加复杂。更多详细建议的链接已包含在本章末尾。

从 Subversion 迁移到 Azure Git 仓库

您可能收到的最后一种请求是将 Subversion 仓库迁移到 Git 仓库。对此,微软没有提供现成的解决方案。但是,Atlassian 创建了一个工具,可以在保持变更历史的同时,将 Subversion 仓库迁移到本地 Git 仓库。

运行此工具后,剩下要做的就是向新的空托管仓库添加远程仓库,并推送所有分支。这些步骤与从 Git 迁移到 Git 的步骤相同,从添加新远程仓库的步骤开始。

不保留历史记录的迁移

如果您被要求进行不保留历史记录的迁移,您可以直接从本地计算机上的源文件夹创建一个新的空仓库,并将现有更改推送到该仓库。

从包含应进入 master 分支的文件的目录执行以下命令:

git init 
git add
git commit -m “Initial import of existing sources”
git remote add https://{organization}@dev.azure.com/{organization}/{teamProject}/_git/{repository}
git push

这些命令初始化一个新的仓库,创建所有目录中文件的第一个提交,添加对目标服务器位置的引用,并将新创建的仓库推送到该位置。

如果您想保留多个分支,必须为每个其他分支重复以下步骤:

  1. 首先,进入该分支的正确目录:

    Git checkout {branchName}
    
  2. 现在,将需要进入该分支的文件复制到您的工作目录中。然后,继续执行以下命令:

    git add . 
    git commit 
    git push
    

这完成了迁移,您本地计算机上的源文件的最新版本现在可以在 Git 中使用。您的团队其他成员现在可以克隆该仓库并与其合作。接下来,我们将继续学习关于分支和合并的内容。

选择分支和合并策略

源代码管理允许你保留所有文件更改的历史记录,还允许你与团队成员暂时分开工作(如果你愿意的话)。我们称之为分支。当你在源代码管理中进行分支时,你分叉了当前注册的更改路径。我们称这样的分叉为分支。分支使你可以暂时将某些工作与其他工作隔离开来。在任何时候,如果你想将一个分支的更改与另一个分支的更改合并,你可以合并这些更改。分支通常用于开发尚未完成的特性、概念验证或热修复。使用分支允许你稍后决定哪些更改应包含在下一个版本中,哪些不包含。

分支策略

目前有许多分支策略可供选择,但如今最常用的三种策略如下:

  • GitHub 流程

  • GitFlow

  • 发布流程

以下小节将更详细地讨论这些内容。

提示

作为分支的替代方法,基于主干的开发如今变得越来越流行。欲了解更多信息,请访问 trunkbaseddevelopment.com/

GitHub 流程

GitHub 流程是一种简单但通常足够的分支策略。在 GitHub 流程中,只有一个 master 分支。

如果你想开始开发一个新特性或修复 bug,你需要在 master 分支上创建一个新的主题分支,并在该分支上提交你的工作。只有在你完全完成工作后,才应将该分支合并回 master 分支。一个示例提交流程可能如下所示:

图 4.2 – GitHub 流程

图 4.2 – GitHub 流程

由于这是涉及最少分支的分支方案,它可能是一个很好的起始策略。

了解更多信息,请参见:www.geeksforgeeks.org/git-flow-vs-github-flow/

GitFlow

GitFlow 是另一种著名的、复杂的分支方案,几乎可以处理在软件开发过程中可能出现的任何情况。GitFlow 描述了每当你开始开发新版本时,如何从 master 分支创建一个 develop 分支。develop 是集成分支,用于合并新特性并进行集成测试。它应该只包含你认为已准备好发布的工作。

develop 分支,你可以创建一个或多个 feature 分支,开始开发新特性。只有当特性完成后,才应将该分支合并回 develop 分支。

当你想发布应用程序的新版本时,你会创建一个release分支,基于develop分支。你可以在此分支上进行最终测试,并根据需要进行一个或多个 bug 修复。当你对代码的质量满意时,你可以将此分支合并到master并标记版本。你还可以将这些 bug 修复合并回develop,以便它们也能被纳入新的开发中。这个流程可以在下图中看到:

图 4.3 – GitFlow 分支模型

图 4.3 – GitFlow 分支模型

如果有一个紧急的 bug 需要尽快修复,或者你想做一个热修复,也可以使用基于 GitFlow 的分支策略。在这种情况下,你可以创建一个master分支的新分支,在这个分支上修复 bug。测试完成后,你可以将该分支合并到masterdevelop中——就像处理release分支一样。

发布流程

master 分支。

区别在于,部署到生产环境的不是位于master分支上的代码。而是每当需要发布新版本时,会从master创建一个名为release-{version}的新分支。该分支中的代码随后被部署到生产环境。一旦新的release分支被部署,之前的分支就可以被忽略。这导致了以下流程:

图 4.4 – 发布流程分支模型

图 4.4 – 发布流程分支模型

该模型的优点在于,它允许获取master分支当前状态的快照并将其推向生产环境。如果生产环境中出现了一个 bug,需要在新的完整发布之前修复,那么必要的提交可以从master分支合并到当前的release分支中。

基于主干的开发

在许多公司中,分支和合并操作用于保持发布新版本软件时的灵活性,并且能够在最后一刻为特定版本选择性地挑选更改。这种灵活性以某些时候需要合并或整合你的更改为代价。

这种成本不仅仅是所需的时间,还有合并操作引入的风险。合并来自两个不同分支的变化,即使它们包含完美运行的软件,也可能仍然产生无法工作的代码。

因此,你可能考虑切换到master分支,仅为准备单一更改创建一个短期存在的分支,然后将其合并到master分支。

基于主干的开发的好处

主干开发有助于提高开发团队在发布功能到生产环境时的敏捷性。功能团队将使用临时且短生命周期的features分支来开发功能。更改将在开发环境中进行单元测试和验证,随后通过 Git 的PULL请求功能推送到master分支。这将在后续章节中进行详细说明。

你可以在这里阅读更多关于主干开发的信息:trunkbaseddevelopment.com/

采用此方法时,你需要另一种方式来确定发布新版本软件时哪些更改对用户可用,哪些不可用。你可以通过使用抽象分支来实现这一点。

抽象分支

在进行抽象分支时,你不会通过分支并排存放代码的两个版本,而是将它们并排保存在代码库中。例如,当你想要更改名为FoodClassifier的类的实现,而该类实现了IFoodClassifier接口时,你需要执行以下步骤:

  1. FoodClassifier类的名称重构为FoodClassifierToBeRemoved

  2. 创建一个完整的FoodClassifierToBeRemoved类的副本。

  3. 将这个副本的名称改回FoodClassifier

此时,你的更改应该是这样的:

public class FoodClassifier : IFoodClassifier
{
public FoodClassification Classify(Food food)
{
// Unchanged classification algorithm
}
}
public class FoodClassifierToBeRemoved : IFoodClassifer
{
public FoodClassification Classify(Food food)
{
// Unchanged classification algorithm
}
}

请注意,在运行时,你的应用程序行为与之前完全相同。你只是添加了一个新的、尚未使用的类,并且它具有行为变更。现在提交这些更改并将新二进制文件交付给用户是安全的。接下来,你可以开始修改新FoodClassifier类的实现,进行测试,并建立对其实现的信任。

与此同时,你可以继续提交并推送你的更改,甚至是推送到客户那里。切换到新实现可以通过依赖注入配置、布尔标志或环境变量来完成。只需根据你的场景选择合适的方式。

只有当你完全确认新实现正常工作时,才会移除FoodClassifierToBeRemoved类,并将任何引用更新回FoodClassifier

我们将在第六章《实现持续部署和发布管理》中,讨论特性开关时详细介绍抽象分支。虽然抽象分支是加速交付的推荐方法,但它也是一把双刃剑。如果没有有效的流程来控制并清理并行实现的数量,且在切换实现后进行清理,代码库的质量可能会下降。

合并策略

根据你使用的源代码管理系统,可能有多种方式将你的更改从一个分支合并到另一个分支。

TFVC

当你使用 TFVC 时,你可以通过选择源分支和目标分支,并选择你想要合并的更改列表来准备本地合并。TFVC 将执行合并并将合并的结果显示为本地更改。你可以审查、更正或更改这些更改,并解决任何冲突。之后,你可以像处理常规更改一样提交这些更改。

Git

使用 Git 进行合并时,可以切换到目标分支,然后合并源分支的所有更改。如果分支之间有冲突的更改,你必须像从服务器获取新更改时一样解决这些冲突。合并源分支的更改并解决冲突后,你可以提交这些更改。这将产生一个合并提交,你可以像其他更改一样将其推送到远程仓库。

合并提交可以通过 Visual Studio 或 VS Code 的可视化界面来完成,也可以使用以下命令序列:

git checkout targetBranch 
git merge sourceBranch

在合并过程中,如果有任何冲突,你必须在此时解决这些冲突,否则无法继续:

git commit -m “Merged changes from sourceBranch” 
git push

正如你将在保护仓库部分中看到的那样,可以通过禁止这种方式的合并来保护某些分支。当涉及到合并更改到master分支时,你可能希望使用另一种机制,即拉取请求。通过拉取请求,你可以请求其他人从你的本地分支拉取更改到目标分支。这样,其他团队成员可以首先审查你的更改,并在满足所有约定标准时进行合并。其他人可以对你的更改进行评论或请求更新,然后再进行合并。这是强制执行四眼原则的最常见方式,适用于 Git 的源代码管理。四眼原则规定,每个更改或操作都应该至少由两个人审查。

当你批准拉取请求时,你可以使用不同的策略来生成合并提交。最常用的策略有合并提交、压缩提交或变基提交。

合并提交

常规的合并提交是一种保留所有先前提交可见性的提交类型。它引用了两个父提交,展示了更改的两个来源,即源分支和目标分支。这与你可以手动使用 Git 合并执行的合并类型相同。此类型的提交的优点是,它清晰地显示了目标分支的新状态来自哪里。

压缩提交

当执行所谓的压缩提交时,你将源分支中的所有单独提交合并为一个新的提交。这对于源分支上的所有提交都与一个功能相关,并且你希望在目标分支上保持清晰、简洁的更改历史时非常有用。当源分支上有修复 bug 或清理操作的提交时,这种方法最为合适。缺点是,你可能会失去一些源分支上增量更改的理由。

变基

master 分支暂时被搁置。与此同时,master 分支超前于本地分支的所有提交现在会合并到本地分支。最后,所有被搁置的你自己的提交将被重新应用。以下图示展示了变基提交前后的分支状态:

图 4.5 – 变基

图 4.5 – 变基

在变基源分支后,它现在被合并到master分支中。这种合并的优点是,你可以在一个提交历史中保留所有单独的更改。

管理仓库

在使用 Azure Repos 时,每个团队项目最多只能有一个 TFVC 仓库。然而,在使用 Git 时,你可以在同一个团队项目中拥有多个仓库。最近越来越受到关注的讨论是,是否应该为所有应用程序使用单一仓库,还是每个应用程序使用一个仓库。管理仓库时,其他重要的话题包括创建和删除仓库、确保仓库安全以及为仓库设置策略。

单仓库或多仓库

你使用单体仓库monorepo)时,将所有项目和应用程序的代码存储在一个源代码控制仓库中。与此相对,你可能会使用多个仓库,其中每个应用程序、库或项目存储在自己的仓库中。这两种方法各有优缺点,且大小公司都会使用这两种方法。

单仓库的可能优势包括以下几点:

  • 更容易重用现有代码:如果所有代码都在一个仓库中,那么每个人都可以访问和查看它。这意味着重用的机会增加。

  • 将所有应用程序放在一个仓库中也意味着,任何影响多个应用程序的更改都可以在一个提交中进行,这样可以在一个仓库里完成。一个典型的例子是 API 更改。

  • 由于所有代码都可以由每个人访问和维护,因此开发人员或团队声称某个特定仓库是自己的可能性较小。这鼓励大家互相学习。

单仓库的缺点包括以下几点:

  • 单仓库可能会变得非常非常大,甚至到达开发人员只检出或克隆单个部分的程度。这实际上会消除单仓库的大部分优势。

  • 将所有代码集中在一个仓库中会促使组件或应用之间的紧耦合。如果你有多个仓库,可以更新 API 并以新版本发布,逐个升级客户端。在单一仓库中,你可能会被诱惑在一次提交中升级 API 并更改所有消费者,带来所有相关的风险。

哪种方法最适合你,受到的不仅是已讨论的优缺点的影响,还受到你团队和组织的背景及构成的影响。如果你有一个团队负责内部应用的所有开发,单一仓库可能更有意义。如果你有多个团队为不同客户开发不同的应用,多个仓库更合适。

创建和删除仓库

在 Azure DevOps 中,每个团队项目可以有多个 Git 仓库。试着执行以下操作:

  1. 首先,访问管理仓库界面。以下截图展示了如何访问此界面:

图 4.6 – 管理仓库

图 4.6 – 管理仓库

  1. 打开此界面后,新的界面(如下图所示)会弹出。在这里,你可以通过点击带有加号的添加...按钮(请参阅左侧导航菜单旁边的垂直部分)来添加新仓库,并填写仓库名称。

  2. 也可以通过点击仓库名称,然后选择删除仓库(标记为2;请参阅仓库名称的上下文菜单)来删除仓库:

图 4.7 – 删除仓库

图 4.7 – 删除仓库

删除仓库并不是常做的事。将不再使用的仓库设置为只读或移除其所有授权,可能更有意义。

现在,让我们学习如何保护我们创建的仓库。

保护仓库

虽然分布式源代码管理的安全选项通常没有集中式源代码管理那么广泛,但 Azure Repos 提供了一些设置仓库或服务器端分支授权的方式。在上一节的最后一张图中,你还可以看到如何在中间列中选择一个组或用户,然后更新仓库的授权。默认情况下,所有授权都是从项目默认设置继承的。

提示

建议尽量减少更改授权的次数,如果确实需要更改,最好通过组进行操作并授权。

你还可以通过在左侧下拉菜单中打开仓库分支,点击你希望覆盖授权的分支来更改特定分支的授权。在之前的截图中,这是标记为HenryBreen.RazorAnalysers的仓库。

分支策略

最后,您还可以强制对特定分支的拉取请求应用一项或多项策略。分支策略的界面如下所示,可以通过在管理仓库分支的授权时选择分支策略选项来访问:

图 4.8 – 分支策略

图 4.8 – 分支策略

前四个复选框与可以启用(或不启用)的默认策略相关。默认情况下,它们都处于禁用状态。

构建验证可以用于禁止合并任何拉取请求,如果选择的一个或多个构建未成功完成。如何设置这样的构建是你将在下一章中学习的内容。

除了构建外,你还可以调用外部服务来检查拉取请求,并允许或拒绝它。这里经常使用的集成是与代码质量工具的集成。你也可以在这里调用自己的 API,以强制执行团队在诸如拉取请求标题、与工作项的关联或更复杂的约束等方面的约定。

最后,你可以强制要求特定的用户或小组必须参与拉取请求的审查。这可能是为了确保特定的质量水平,但也可能成为限制你开发速度和流程的因素。

其他源代码管理工具

除了 Azure Repos 中可用的源代码管理系统外,还有一些其他著名的系统,你应该了解它们:

  • GitHub

  • GitLab

  • Subversion

我们将在接下来的子章节中逐一讲解这些内容。

GitHub

GitHub 是一个托管的源代码管理提供商,提供托管的 Git 仓库。GitHub 允许任何人创建任意数量的公开可见仓库。当你创建需要三名或更多贡献者的私有仓库时,必须切换到付费订阅。

如果是在公共开发中使用,该模型允许平台的无限制免费使用,这使得 GitHub 成为全球最大的开源软件托管平台。

GitHub 于 2018 年被微软收购,从那时起,微软和 GitHub 一起合作,创建了 GitHub 仓库与 Azure DevOps 之间的良好集成体验,特别是在 Azure Boards 和 Azure Pipelines 上。除此之外,微软表示 GitHub 和 Azure Repos 将继续并存,目前没有计划为了一个产品而终止另一个产品。

GitHub 还提供了一个企业版,称为 GitHub Enterprise,提供两种部署选项,即云托管和自托管(或本地部署)。

你可以在这里阅读更多关于各种 GitHub 产品和定价计划的内容:docs.github.com/en/get-started/learning-about-github/githubs-products

GitHub 的公共路线图可以在这里查看:github.com/orgs/github/projects/4247/views/1

GitLab

GitLab 是另一个提供托管 Git 仓库的平台。像 Azure DevOps 一样,源代码控制托管是它提供的服务之一。

Subversion

Subversion 是一种较早的源代码控制系统。Subversion 开发并首次使用于 2004 年,由 Apache 软件基金会维护。Subversion 是一种集中式源代码控制系统,支持你期望的所有功能。

关于为什么 Subversion 比 Git 更差的论点有很多是错误的;然而,大多数论点对新版 Subversion 并不适用。事实上,Subversion 是一种广泛使用的源代码控制系统,特别适用于非常大的仓库或具有特定授权需求的仓库。

虽然 Azure DevOps 无法托管 Subversion 仓库,但它可以连接并与存储在 Subversion 中的源代码进行协作。

总结

在本章中,你已经了解了源代码控制。你了解到源代码控制有两种类型:集中式和分散式,Azure DevOps 都支持这两种类型。TFVC 不再推荐用于新项目。你应该在开始新项目时使用 Git。

在使用 Git 时,你可以在团队项目中拥有多个仓库。对于每个仓库,你可以分配策略来锁定特定分支,并强制执行四眼原则。你还学习了访问控制,如何为用户提供对一个或多个仓库的访问权限。最后,你了解了替代工具,并学会了如何将源代码从一种工具迁移到另一种工具。

你可以利用所学知识来决定在你的产品中使用哪种类型的源代码控制系统。你现在可以专业地组织你所工作的一个或多个仓库。你现在能够使用不同的分支策略,并使用策略来强制执行安全性或质量要求。

下一章将基于你学到的源代码控制知识,使用这些知识来设置持续集成。

问题

在我们总结之前,这里有一份问题列表,供你测试自己对本章内容的掌握情况。你可以在附录评估部分找到答案:

  1. 集中式和分散式源代码控制有什么区别,在哪些情况下哪种方式更合适?

  2. 判断题:Git 是分散式源代码控制的一个例子,正确还是错误?

  3. 以下哪一项不是常见的分支策略?

    1. 发布流程

    2. 变基

    3. GitFlow

    4. GitHub Flow

  4. 许多公司希望在代码合并到master分支之前进行代码审查。使用 Git 时,应该使用什么方法来完成此操作?如何在 Azure DevOps 中强制执行这一流程?

  5. 以下哪些不是有效的合并策略?

    1. 变基

    2. 基于主干的开发

    3. 合并提交

    4. 压缩提交

练习

图 4.9 – 创建一个新的团队项目

图 4.9 – 创建一个新的团队项目

  • 一旦PacktBookLibrary团队项目创建完成,使用左侧导航,按照层次结构进入Product-Backlog,在Product-Backlog查询中:

图 4.10 – 查询编辑器

图 4.10 – 查询编辑器

  • 使用编辑器视图中的列选项部分,选择列(堆栈排名等)并在堆栈排名上进行排序图 4.11 – 添加列并应用排序

图 4.11 – 添加列并应用排序

  • 添加一个初始的产品待办事项(史诗和特性),如以下截图所示:

图 4.12 – DevOps 特性列表

图 4.12 – DevOps 特性列表

这里的目标是通过相同的产品待办事项跟踪重要的 DevOps 实践的实施,以便在工作规划和优先级设置过程中将其纳入其中。最终,实施这些实践的将是同一组团队成员。

在导入现有仓库时,以下截图中的对话框会显示出来:

图 4.13 – 导入 Git 仓库

图 4.13 – 导入 Git 仓库

  • 一旦代码被导入,你应该能够在仓库中查看到代码文件。

  • 在以下设置中,为仓库中的main分支配置分支策略:

    • 打开(使用切换按钮)要求最低数量的审阅者。将最低审阅者数量设置为1

    • 打开(使用切换按钮)检查关联工作项,设置为必需

  • 打开限制合并类型。从允许的合并类型列表中选择Squash 合并作为唯一选项。

  • PacktBookLibrary仓库(main分支)克隆到你工作站中的本地文件夹。

  • 通过从main分支创建一个分支来检出源代码,然后开始使用它进行提交。定期计划发起PULL请求,以将更改合并到main分支中。

完成练习列表中的步骤后,你的源代码控制仓库将被设置好。我们将在接下来的章节中通过练习在此基础上进行拓展。

进一步阅读

第五章:转向持续集成

在为你的组织设置了源代码管理并决定了支持并行工作的分支和合并策略后,你就可以继续进行持续集成了。持续集成是一种方法,每个开发人员将自己的工作与其他人的工作集成,然后验证组合后的工作质量。这样做的价值在于在管道早期提高质量。这减少了以后在合并代码更改时出现错误的风险,并减少了在生产中发现的缺陷数量,从而降低了成本并保护了你的声誉。

持续集成只有在你具备必要的工具和适当的配置时才有可能实现。在本章中,你将学习如何使用 Azure DevOps 管道来设置持续集成。

本章将涵盖以下主题:

  • 介绍持续集成

  • 创建构建定义

  • 运行构建

  • 使用 YAML 管道

  • 代理和代理队列

  • 使用 GitHub Actions 自动化持续集成CI)构建

  • 其他工具

技术要求

为了完成本章中涵盖的示例,你将需要以下内容:

  • 一个 Azure DevOps 组织

  • Git 命令行工具

  • 一个代码编辑器,如 Visual Studio Code

介绍持续集成

持续集成是一种方法,你将自己的更改与项目中所有其他开发人员的更改集成,并测试组合后的代码是否仍按预期工作。通过这种方式,你创建了一个快速的反馈循环,能够为你的工作提供反馈。

在处理用于隔离代码更改的大规模分支策略时,开发人员可能会在一个孤立的分支上工作数天、数周甚至数月。这对于确保他们的更改不会干扰其他人是很有好处的,但持续集成是确保以后不会出现合并问题的好方法。如果你曾经需要将数周或数月的工作合并回主分支,你会知道这涉及多少工作,并且常常会导致错误或其他问题。

为了防止这种情况,开发人员应养成每天至少一次将自己的更改与其他开发人员的更改集成的习惯。在这里,集成意味着至少合并、编译并运行单元测试。这样,开发人员的更改质量就会得到持续的反馈,并且由于这些反馈是综合的,它是防止以后出现合并问题的好方法。

持续集成还使你能够将其他关注点嵌入到管道中,以自动保持代码质量。静态代码分析、单元测试和安全扫描是三个典型的例子。这些主题将在后续章节中讨论,但一个好的持续集成管道是这些实践的基础。

在本章的其余部分,你将了解如何使用 Azure Pipelines 设置持续集成的技术手段。但首先,让我们看一个常见的误解以及持续集成的四大支柱。

重要提示

虽然自动化的持续集成构建是执行持续集成的重要组成部分,但持续集成不仅仅是拥有一个构建管道。需要记住的关键点是,持续集成是一个过程,每个开发者至少每天都会将自己的工作与同事的工作进行整合。然后,整合后的源代码会被编译并进行测试。其价值在于编译和测试整合后的工作,而非孤立的工作。

持续集成的四大支柱

有四个支柱支撑着持续集成的成功实施:

  • 版本控制系统:用于存储自系统创建以来所做的所有更改。版本控制系统在上一章中已讨论。

  • 包管理系统:用于存储你在自己应用中使用的二进制包以及你创建的包。这将在 第七章 中详细讨论,依赖管理

  • 持续集成系统:一个可以将所有开发者的更改汇总在一起——一天多次——并创建一个集成的源版本的系统。这可以通过 Azure DevOps 管道来实现。

  • 自动化构建过程:用于编译和测试合并后的源代码。我们将探讨如何使用 Azure DevOps 管道实现这一过程。

可以在 Azure DevOps 中设置持续集成和自动化构建。下一节将解释如何在 Azure DevOps 中设置这两者。

在 Azure DevOps 中创建构建定义

执行持续集成的主要方法是使用持续集成构建。在 Azure DevOps 中,构建可以作为 Azure Pipelines 的一部分进行配置。目前,创建构建定义有两种方法:

  • 通过可视化设计器(也称为经典构建和发布

  • 通过 另一种标记语言 (YAML) 文件(也称为 YAML 管道或多阶段管道)

本节的其余部分将重点讲解可视化设计器。接下来的部分,YAML 构建定义,将详细介绍 YAML 管道。两种方法大致支持相同的功能,尽管也有一些差异。某些在经典构建和发布中可用的功能在 YAML 构建定义中尚不可用。此外,只有 YAML 管道才提供某些新功能。

如果你没有管道经验,经典编辑器是一个很好的方式,可以在转向 YAML 管道之前,先熟悉持续集成/持续开发管道的工作原理。经典构建中的几乎所有概念都可以转化为 YAML 构建。

在接下来的各节中,我们将从构建一个经典的构建管道开始。

连接到源代码管理

要开始创建构建定义,请按照以下简单步骤操作:

  1. 打开管道菜单。

  2. 从此菜单中,点击构建。在这里,您将看到一个按钮,用于创建新的构建。点击此按钮后,将打开一个新的视图,用于创建构建,如下图所示:

图 5.1 – 用于管道的源代码仓库选项

图 5.1 – 用于管道的源代码仓库选项

  1. 然后,您将被引导到新的 YAML 体验,但您仍然可以选择通过选择经典编辑器返回。

选择经典编辑器后,您可以配置如何连接到源代码管理。经典编辑器是在以下各节的所有截图中可见的编辑器。

支持多种源代码管理系统。如果您正在使用托管的 Git 仓库,请选择您的具体产品(如果有的话),如果没有可用的产品,则选择其他 Git;目前,支持GitHubGitHub 企业服务器Bitbucket Cloud。这样做的原因是,使用其他 Git进行持续集成是通过轮询模型来工作的,所有具体产品使用它们已知的集成 Webhooks。以下示例适用于位于同一 Azure DevOps 组织中的 Git 仓库。

当您选择管道标题时,您可以设置构建定义的名称,并选择作业阶段将默认运行的代理池。代理负责实际执行您的任务,关于代理的更多细节将在本章的代理与代理队列部分中介绍。

管道标题下,您可以看到构建定义的时间轴布局。首先是下载源代码。在这里,您可以再次选择连接到源代码管理系统。您还可以指定更多与获取源代码相关的高级设置,如是否先清理构建目录、选择一个分支或添加标签。

配置作业

在源节点下,您可以添加一个或多个作业来执行您希望执行的大部分工作。可以通过点击管道标题上的省略号来添加作业。这里有两种类型的作业:

  • 无代理作业:无代理作业可以用于运行不需要代理的任务。这些任务在 Azure DevOps 服务器上运行。

  • 代理作业:代理作业用于运行需要代理的任务,绝大多数任务都需要代理来运行。

一些无代理任务的示例如下:

  • 等待手动批准后继续

  • 在继续之前插入延迟

  • 调用 REST API

  • 调用 Azure 函数

无代理作业的主要优点是它在运行时不会占用代理。这使得代理可以做其他工作,意味着您需要更少的代理,从而节省成本。此外,您可以并行使用的代理数量受限于您在 Azure DevOps 中购买的并行管道数量。限制代理作业的数量也可以在这里节省费用。

让我们来回顾一下配置作业的过程:

  1. 选择任何作业。您将看到以下截图中显示的视图。在此视图中,您可以更改作业的名称,对于代理作业,可以覆盖执行此作业的代理池:

图 5.2 – 为管道添加/更新作业

图 5.2 – 为管道添加/更新作业

  1. 接下来,指定用于执行作业的代理池。在这里,还指定了您对执行此作业的代理的需求。需求将在本章的代理和代理队列部分中讨论。

  2. 作为代理执行计划的一部分,您可以指定并行性并选择以下三种选项之一:

    • :这将仅在同一代理上依次执行您添加到代理作业中的所有任务。

    • 多配置:在这里,您可以指定一系列变量,这些变量决定要运行的构建的变体数量。如果您想从同一代码创建例如 x86 和 x64 构建,这会非常有用。

    • 多代理:在这里,您可以指定将并行运行相同任务的代理数量。

  3. 接下来,您可以指定一个或多个依赖项。这些是需要在所选作业运行之前完成的其他作业。

  4. 此外,对于任何作业,您可以通过告诉它继续或停止,来指定如何处理前一个作业中的错误。

作为步骤 3步骤 4的替代方法,您还可以指定一个自定义表达式来确定是否应执行某个作业。此表达式应评估为布尔值,并支持基本操作,如or()and()eq()。以下是一个示例条件:

and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/main'))

此条件指定,只有当所有前置作业成功并且构建未从主分支启动时,作业才会运行。本章末尾附有条件语法的详细描述链接。

无代理作业比代理作业可用的选项更少。例如,无法为多个变量值并行执行相同的构建。

向作业添加任务

添加一个或多个作业后,您可以向作业添加任务。任务定义了在构建执行过程中需要完成的实际工作。以下截图展示了如何添加任务并进行配置:

  1. 单击您想要添加任务的作业旁边的加号:

图 5.3 – 选择任务并指定作业的配置参数

图 5.3 – 选择任务并指定作业的配置参数

  1. 然后,您将看到一个任务选择器,您可以在其中找到与您的搜索输入匹配的任何任务,并通过点击添加按钮添加一个或多个任务。接着会打开一个新屏幕,您可以在其中配置单个任务。此处提供的选项对于每个任务有所不同。

  2. 一个任务可能有多个版本,您可以在其主要版本之间切换。这意味着维护者可以推送不破坏兼容性的更新,您会自动接收这些更新。重大或破坏性更新可以通过新的主要版本号推送,您可以根据自己的需要进行升级。

可以根据需要向管道作业中添加任意数量的任务。

发布构建工件

构建定义的重要部分是其输出。构建通常用于生成一个或多个工件,这些工件稍后会用于应用程序的部署。工件的示例包括可执行文件或安装程序文件。这些文件需要在构建管道执行完成后提供以供使用。

前面截图中显示的发布构建工件任务是专门设计用于此目的的任务。它允许您选择一个文件或目录,并以工件名称发布它。这样,选定路径中的文件会在每次管道执行时保留,以便稍后手动下载或在发布定义中使用。发布定义将在第六章中讨论,实现持续部署和发布管理

接下来,我们将学习如何将我们的管道与其他工具集成,并配置我们的服务连接。

调用其他工具

在构建管道时,我们通常需要将其与其他工具集成。对于源代码控制系统,这是创建管道时的一个环节,您仅限于使用内置选项。对于任务,您可以使用服务连接创建对任何工具或位置的引用。以下部分展示了一个使用指向 Azure 应用服务的服务连接的任务示例。

服务连接是指向外部系统的指针,具有名称和一系列属性,针对每种类型的服务连接有所不同。通常,您需要提供一个 URL 来定位其他服务,并提供一个认证机制。以下步骤将帮助您配置您的服务连接:

  1. 在定义一个或多个服务连接后,您可以从下拉菜单中选择要使用的服务连接:

图 5.4 – 在任务中使用服务连接

图 5.4 – 在任务中使用服务连接

  1. 服务连接在一个中央位置作为项目设置进行管理。你可以通过从当前配置的任务中直接访问管理视图来访问它们,如前面的截图所示。你也可以通过导航到项目设置,然后转到服务连接来访问,如下图所示(参见2):

图 5.5 – 服务连接对话框

图 5.5 – 服务连接对话框

  1. 在此视图中,你可以选择添加一个新的服务连接或更新现有的服务连接(参见前面截图中的23)。

默认情况下,服务连接的作用域是项目级别,这意味着它们并非对所有 Azure DevOps 组织中的人都可用。为了鼓励服务连接的重用,Azure 自 2019 年中期以来已经实现了在项目之间共享服务连接的功能。

任务市场

Azure Pipelines 内置了一组常用的任务;然而,使用 Azure DevOps 的 Visual Studio 市场,你还可以找到更多的任务。如果你是 DevOps 组织的管理员,你可以在此找到并安装添加任务的扩展。如果你是普通用户,你也可以在此找到任务;但是,你无法安装它们,只能请求它们。你的 Azure DevOps 管理员会收到通知,并在他们批准后代表你安装扩展。

当然,你也可以编写并分发带有你自己任务的扩展。

创建变量和变量组

在配置构建时,可能会有一些值需要多次使用。通常,提取这些值为变量,而不是在任务中重复使用它们,是明智的做法。

变量可用于记录那些你不希望存储在源代码控制中的值。例如,密码和许可证密钥可以安全地作为不可检索的值存储,当它们被锁定(参见下图中的1)时。保存构建定义后,这些值将被加密,并且只能被属于它们的构建使用。

要学习如何在 Azure Pipelines 中使用变量,请按照以下步骤操作:

  1. 在 Azure Pipelines 中,你可以通过转到变量 | 管道变量选项卡(参见下图中的3)来将变量添加到你的构建定义中。在这里,你可以输入变量的名称和值,如下图所示:

图 5.6 – 管道变量

图 5.6 – 管道变量

  1. 一旦定义,你就可以在同一构建的所有任务配置中使用这些变量。为此,你可以使用以下表示法:

    $(variableName)
    
  2. 最后,你可以将变量标记为system.debug内置变量。当此变量设置为true时,构建中将包含详细的调试日志。

除了你自己的变量外,系统变量也会被定义。这些是包含当前正在运行的构建的相关信息的变量,包括版本号、代理名称、构建定义详情和源版本。系统定义变量的完整列表链接将会在本章末尾提供。

变量组

除了为特定构建创建变量外,你还可以创建变量组。这些变量组可以与一个或多个构建关联。这是一种有效的在构建之间共享变量的方式;一些例子可能包括你的公司名称、商标文本和产品名称。我们来看看如何使用变量组:

  1. 通过点击Library)在管道Pipelines)菜单中,你可以通过菜单访问变量组(参见下图中的1)。这将显示你可以编辑的现有变量组列表,同时你也可以在这里添加一个新的变量组,如下图所示:

图 5.7 – 新的变量组

图 5.7 – 新的变量组

  1. 在这里,你可以像使用构建自带的变量一样使用变量。唯一的区别在于以下列表中突出显示的内容:

    • 你不能将变量标记为在队列时可设置。

    • 你可以允许或拒绝在所有管道中使用该组。如果你拒绝在所有管道中使用它,那么只有你可以使用该变量组。你可以通过安全性Security)选项授权其他用户或组(参见前述截图中的2)。

    • 你可以引用一个 Azure 密钥保管库,该变量组将作为一个占位符。在登录到 Azure 后,你可以选择一个密钥保管库,并指定希望通过变量组访问的存储在密钥保管库中的值。

Azure 密钥保管库Azure Key Vault)是 Azure 提供的一项服务,可用于安全存储密钥。密钥保管库中的机密会自动进行版本控制,因此旧的值不会被覆盖,而是被新版本替代。此外,你可以指定分隔的访问策略,按用户指定他们是否可以读取、写入、更新或删除值。所有这些操作都会在密钥保管库中进行审计,因此你也可以追溯到谁进行了哪些更改。如果你将 Azure DevOps 与密钥保管库链接,则将在你的活动目录中创建一个具有该密钥保管库访问权限的新服务主体。现在,每当 Azure DevOps 需要从变量组中提取变量时,实际的值将从密钥保管库中提取。

变量组可以通过变量组标签与构建的变量关联(参见图 5.7中的截图)。

除了与变量组一起工作外,你还可以与库中的文件一起工作。你可以上传其他用户无法访问的文件,但这些文件可以在构建中使用。这对于包含私钥、许可证密钥和其他机密的文件很有用。欲了解更多信息,请参阅此链接:docs.microsoft.com/en-us/azure/devops/pipelines/library/secure-files?view=azure-devops

与变量组一样,你可以指定每个安全文件是否可以被任何构建使用,或仅授权特定用户使用。

触发构建

构建定义中的下一个标签控制着应该启动或触发构建的内容。要实现持续集成,请按照以下步骤操作:

  1. 点击触发器标签页,选择左侧的第一个标题:

图 5.8 – 为管道定义触发器

图 5.8 – 为管道定义触发器

  1. 勾选启用持续集成框。这意味着 Azure DevOps 将监视你仓库中的变化,并在有新的变更时立即排队新的构建。

  2. 接下来,你可以选择是希望单独构建每个传入的更改,还是在构建某个更改时将多个更改批量处理。如果可行,建议每次都单独构建每个更改。

  3. 除了持续集成触发器外,还需要指定一个或多个分支和路径过滤器。在这里,你可以指定哪些分支和文件需要排队新构建。你可以根据需要指定包括或排除某些内容。一个常见的例子是将构建限制在主分支。如果你的仓库中有名为docsrc的文件夹,且所有源代码都在后者文件夹中,那么限制触发器仅适用于该路径可能更为合适。

  4. 除了选择持续集成触发器外,你还可以选择按计划定期执行构建,选择一个或多个工作日和时间。

  5. 你还可以安排在另一个构建完成时启动构建。这被称为链式构建

接下来,让我们学习如何更改构建定义的配置。

构建选项

你可以更改构建定义的高级配置选项。这些选项包括描述、构建编号的格式,以及在失败或超时时自动创建工作项。要进行设置,请按照以下步骤操作:

  1. 点击选项标签页。你应该会看到以下界面:

图 5.9 – 构建选项

图 5.9 – 构建选项

  1. 现在,创建您的构建号格式。如果该字段为空,则您的应用程序的构建号将设为一个不断增加的数字,每次构建都会增加 1。该数字在团队项目中是唯一的,并且跨所有构建定义进行计数。您也可以使用可用的变量来指定您自己的格式。一种常见的方法是手动指定一个主版本号和次版本号,然后使用变量添加一个递增的数字。以下示例指定了版本号为 4.1.xx,其中最后部分会被一个两位数递增的数字替代:

    4.1($Rev:.rr)
    
  2. 右侧有一些高级(但很少使用)选项,用于指定构建作业超时的授权范围,适用于构建定义中的每个作业。

  3. 还可以指定代理需求,要求每个代理在构建定义中的每个作业中都必须满足这些需求。我们将在本章的代理与代理队列部分进一步探讨需求。

左侧的其他选项允许您暂时暂停管道。

构建历史

最后一页标签,称为历史记录,显示了构建定义中每一次更改的列表。构建定义以 JSON 格式存储,您可以查看每次更改的并排对比。您在保存构建时所输入的评论也会存储在这里,并可以用来提供更改的理由。您还可以恢复到管道的旧版本。

由于构建是保持质量的重要手段,因此跟踪是谁更改了它们非常重要,以确保自动化质量度量不会被删除。

完成这些操作后,您现在可以运行您的第一个构建。您可以直接使用在本节大多数截图中可见的保存并排队按钮来运行它。本章的运行构建部分将教您如何处理您获得的结果。

任务组

在一个拥有多个管道的团队或组织中,往往不会过多久就会出现多个形态相同的管道。例如,在一些公司中,所有管道都包含安全扫描、运行测试和计算测试覆盖率的任务。

与其在各处重复这些任务,不如将它们从现有的管道中提取到一个任务组中。任务组可以像任务一样在多个管道中使用。这样做可以减少创建新管道或更新所有管道时的工作量。同时,这也确保了所有使用任务组的管道具有相同的任务配置。

要创建一个新的任务组,请打开任何现有的构建定义并按照以下步骤操作:

  1. 按住 Ctrl 键并单击一个或多个任务,或者使用鼠标悬停在任务上时出现的选择器来选择任务:

图 5.10 – 创建任务组

图 5.10 – 创建任务组

  1. 右键点击选项并选择创建任务组

  2. 在现在出现的弹窗中(截图未显示),为任务组选择一个名称、描述和类别。如果选定的任务中有指定变量值的任务,你现在可以为这些参数提供默认值和描述。这些参数将在任务组中使用,并且在使用任务组时需要进行配置。

  3. 在点击创建后(截图未显示),现有的构建定义将通过移除选定的任务并用新的任务组替换它们来进行更新。

将已有任务组添加到构建或发布定义中的方式与添加常规任务完全相同。任务组会出现在同一任务选择列表中。

所有现有任务组的列表可以通过导航到管道菜单,然后选择任务组找到。要编辑现有的任务组,在显示的列表中选择它,然后选择编辑选项。编辑任务组的方式与编辑构建定义完全相同。

本节主要讲解了如何创建构建定义以及如何描述应用程序的构建方式。下一节将介绍如何执行构建。

运行构建

在本节中,您将学习如何处理构建结果,并利用它们报告和生成构建。您还将学习如何在每次拉取请求时运行构建,并将更改的质量报告回该拉取请求,以帮助审阅者。

查看构建结果

在构建运行时,代理将执行所有配置的步骤。Azure Pipelines 会捕获所有这些步骤的详细信息和日志。如以下截图所示,构建会在左侧显示它已执行的所有步骤列表。点击任何一个步骤将打开详细视图,显示该步骤的日志:

图 5.11 – 构建结果

图 5.11 – 构建结果

每当构建过程中出现警告或错误时,它们会分别以橙色或红色显示。

构建拉取请求

设置好构建定义并运行第一次构建后,您可能还会看到第一次失败——例如,当有人不小心提交并推送了无法编译的更改或包含无法成功运行的单元测试时。您可以通过设置使构建定义在每次拉取请求到来时自动运行来防止这种情况。要配置此功能,请按照以下步骤操作:

  1. 项目设置下点击策略,将打开以下界面。点击添加构建策略

图 5.12 – 添加构建策略

图 5.12 – 添加构建策略

  1. 选择一个构建定义,用于验证拉取请求。

  2. 接下来,您可以配置以下三项内容:

    • 触发器:构建定义应该何时开始执行,可以是自动执行或手动执行。当然,真正的价值来自于自动运行验证构建。

    • 策略要求:这决定了当构建失败时,是否可以完成拉取请求。换句话说,这决定了你是否可以忽略失败的构建。建议如果可能,避免将此设置为可选

    • 构建过期时间:这决定了一个成功的构建结果有效的时间长度。默认值是12小时,但你应该考虑将其更改为当主分支更新时立即过期。这样做的好处是,必须先执行构建,以检查你将要合并的分支的当前状态和拟议更改的组合,才能进行合并。

你可以添加多个构建策略。如果你有很多可以自动验证的内容,并希望将自动验证的时间保持在最小值,那么这是一个很好的方法。

访问构建工件

除了编译、测试和验证源代码外,构建还可以用于生成所谓的工件。工件是构建的输出,可以是任何你想要从构建中保存并发布的内容,比如测试结果和应用程序包。

应用程序包旨在成为你应用程序版本的不可变构建。这个包稍后可以在发布中使用,并部署到一个或多个环境中:

图 5.13 – 查看应用程序包

图 5.13 – 查看应用程序包

在前面的截图中,你可以看到,作为已执行构建的总结的一部分,有两个工件被发布。你可以通过屏幕右上角的工件下拉菜单或摘要标签访问这些工件。你可以从此页面下载并查看工件,在下一章中,你将看到如何使用它们来设置持续交付。

很棒!通过这一步,你已经学会了如何使用可视化设计器创建定义。但等等——正如我们之前提到的,还有另一种方法可以做到这一点,那就是使用 YAML 文件。让我们在下一节看看它是如何工作的。

使用 YAML 管道

你已经看到如何使用可视化设计器创建构建定义。自 2019 年初以来,一种新的替代方法是使用 YAML 管道。在使用 YAML 管道时,你需要在 YAML 文件中指定完整的构建定义,并将其存储在源代码管理中,通常与构建所针对的源代码一起存储。

虽然两种管道系统并存,但现在定义管道的首选方法是使用 YAML 管道。这意味着新的功能很可能只会出现在 YAML 管道中。

使用构建定义作为代码的原因

当你第一次开始使用 YAML 构建定义时,你可能会发现学习曲线比使用可视化设计器时更陡峭。这可能会引发一个问题,那就是为什么要使用 YAML 定义的构建。YAML 构建定义相对于可视化设计定义有两个主要优势。

当你在 YAML 中编写定义时,它可以与代码一起托管在源代码控制中。这样做的结果是,所有针对源代码控制的更改策略现在自动应用于你的构建定义。这意味着任何更改都必须经过拉取请求,由同行审核,并且可以提前构建和验证。强制实施四眼原则于你的构建定义以及代码,有助于提高构建过程的稳定性。当然,它也有助于提高安全性和合规性,这些主题将在后面的章节中讨论。

除了增强的安全性外,将构建定义放在源代码控制中还意味着它在每个分支中都是可用的。这意味着它可以在每个分支中进行更改,以便在与主分支合并之前构建该特定分支。使用可视化设计的构建定义时,这个单一的定义不仅负责构建你的主分支,还负责构建你想通过拉取请求合并的所有分支。

这意味着你必须执行以下其中一项操作:

  • 更新构建定义以适应你将要合并的更改。然而,这将终止当前主分支的构建。

  • 合并更改,这也会导致构建失败,因为构建定义尚未更新。

这两种选项都有允许错误更改流入目标分支的风险,这会破坏持续集成构建的目的。通过每个分支都有一个构建定义,我们消除了这个问题。

编写一个基本的 YAML 管道

要开始使用 YAML 构建,你需要做两件事:

  1. 首先,你需要编写 YAML 文件。

  2. 然后,你需要从中创建一个构建定义。

那么,让我们开始吧。

编写 YAML 文件

以下代码示例包含一个用于构建 .NET Core 应用程序并运行单元测试的 YAML 定义。将文件以任何名称保存——例如 pipeline.yaml——并存储在 Azure DevOps 中的任何 Git 仓库中。然后,可以稍后使用它来创建一个管道:

trigger:
- main
pool:
  name: Azure Pipelines 
  vmImage: windows-2019
steps:
-  task: DotNetCoreCLI@2 
   displayName: 'dotnet build' 
   inputs:
       projects: '**/*.csproj'
- task: DotNetCoreCLI@2 
   displayName: 'dotnet test' 
   inputs:
       command: test
       projects: '**/*.csproj'

这个 YAML 示例定义了一个基本的管道。每个管道都需要以某种方式触发。就像经典的构建一样,可以通过将管道连接到源代码仓库中的更改来实现。此默认仓库是包含 YAML 定义的仓库。trigger 关键字用于指定哪些分支的推送应触发管道。一个好的起点是主分支。由于 trigger 关键字接受列表,可以指定多个分支,并且可以使用通配符。

触发器不是必需的,因为管道也可以手动启动。

提示

还有其他替代触发器关键字的选项,例如在存储库中包含或排除一个或多个分支、标签或路径。这些选项的详细说明可以参见 docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema#triggers

每个管道都包含一个或多个任务,和经典构建定义一样,除了触发器外。所有这些任务需要在代理池上执行——同样,和经典构建定义一样。pool 关键字用于指定一组键值对,通过指定池的名称来确定任务将在哪个池上运行。使用 Microsoft 提供的默认代理时,可以使用默认名称 Azure Pipelines。使用这个特定池时,必须指定虚拟机VM)镜像。这决定了将执行任务的代理上可用的操作系统和软件。

提示

可通过 docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted#use-a-microsoft-hosted-agent 查找到所有可用的 VM 镜像的最新列表。

最后,定义包含构成管道本身的步骤列表。这些步骤与您可以拖入经典构建管道中的任务一一对应。添加任务时,指定任务的名称和版本——由 @ 符号分隔——然后您可以选择性地为任务指定显示名称。此显示名称稍后将在显示已执行管道结果的视图中可见。最后,为任务指定一个或多个输入。这些输入与您在可视设计器中已经看到的任务特定配置有关。

创建 YAML 管道

在将 YAML 文件保存在存储库中之后,您可以从中创建构建定义。创建新构建定义时(请参阅本章的创建构建定义部分),您应当按照以下步骤进行操作:

  1. 当向导启动时,选择Azure Repos Git YAML选项。

  2. 从这里,按照向导的步骤选择并查看您希望构建的 YAML,如下图所示:

图 5.14 – 用于存储库的 YAML 管道

图 5.14 – 用于存储库的 YAML 管道

  1. 然后,找到包含您要用作管道的 YAML 文件的存储库。

  2. 接下来,通过选择一个示例 YAML 文件作为起点或引用已有的文件来配置管道。

  3. 最后,您可以查看所选的 YAML 文件并从中启动构建。

你的管道会自动保存。管道保存后,可以启动,并且你可以像操作经典构建管道一样与它交互。

多作业管道

你在前一节中看到的管道没有指定任何作业,正如你可能记得的经典构建部分。相反,它包含了一个 steps 关键字下的任务列表。这意味着它隐式地只包含一个作业。在 YAML 管道中,也可以创建包含多个作业的定义。为此,可以使用以下结构:

trigger:
- main
pool:
    name: Azure Pipelines 
    vmImage: windows-2019
jobs:
- job: job1
  displayName: A pretty name for job1 
  steps:
  - task: DotNetCoreCLI@2
     ...
- job: job2
  displayName: My second job 
  pool:
      name: Azure Pipelines 
      vmImage: ubuntu-18.04
      ...

不直接将 steps 关键字添加到管道中,而是先创建一个作业列表。在该列表中,添加一个或多个 job 关键字,后跟该作业的名称。在该技术名称旁,还可以为每个作业指定一个显示名称 (displayName)。

如前例中的第二个作业所示,也可以为每个作业指定使用的代理池。当没有为作业指定代理池时,将使用文件顶部指定的默认池。

提示

本节讨论的作业称为代理作业。除了代理作业,还有服务器作业、容器作业和部署作业。有关这些类型作业的更多信息,请访问 docs.microsoft.com/en-us/azure/devops/pipelines/process/phases#types-of-jobs

默认情况下,管道中的所有作业都是并行运行的,但有可用的控制选项可以更改这一点。

控制选项

要控制作业的顺序,可以在作业的定义中使用 dependsOn 关键字。这表明该作业只能在一个或多个作业完成后开始。除此之外,condition 关键字可以用于指定作业运行的条件。可以结合这两个关键字来实现更复杂的场景,如下所示:

jobs:
- job: compile 
  steps:
  ...
- job: test 
  dependsOn: compile 
  steps:
  ...
- job: build_schema 
  dependsOn: compile 
  steps:
  ..
- job: report 
  dependsOn:
  - test
  - build_schema
  condition: or(succeeded('test'), succeeded('build_schema')) 
  steps:
  ..

该管道将通过运行名为 compile 的作业开始。一旦该作业完成,接下来的两个作业 testbuild_schema 将并行运行,因为它们都依赖于 compile 任务。在这两个任务都完成后,report 任务将运行,因为它声明依赖于 testbuild_schema 作业。在该作业实际开始之前,会评估条件,以确定该作业是否应该运行或被跳过。

条件可以使用类似于许多编程语言的语法来构建。它通过 succeeded()failed() 函数检查作业是否成功完成。同时还支持布尔运算符,如 or()and()ne()

你可以根据需要以任何方式结合 dependsOncondition 关键字。唯一的要求是,至少应有一个作业不依赖于任何其他作业。

变量

与经典构建管道一样,YAML 管道支持使用变量。变量可以在 YAML 管道的每一层级(任务内部除外)使用以下语法进行定义:

variables: 
  name: value
  anotherName: otherValue

以后可以使用你从经典构建管道中熟悉的语法来检索变量——$(name)$(anotherName)

也可以在 YAML 管道中引用现有的变量组。这是通过使用group关键字来完成的,而不是指定变量的名称。若要从名为myVariableGroup的变量组中检索所有变量,可以按照以下方式扩展前面的 YAML:

variables: 
  name: value
  anotherName: otherValue 
  group: myVariableGroup

可以在 YAML 管道的每一层级中设置变量,但只有在根级别设置的变量才能在手动排队新执行时被覆盖。你可以在这里了解更多:docs.microsoft.com/en-us/azure/devops/pipelines/process/variables

管道工件

与经典构建一样,YAML 管道可以用于构建和发布工件。由于用于此目的的任务与其他任务一样,它可以直接添加到作业中的步骤列表中。

然而,随着 YAML 管道的引入,一种新的工件类型——所谓的管道工件——已经可用。这种工件带来了提高大规模工件上传和下载速度的优势。当使用经典发布时,管道工件不会自动下载,而构建工件则会。

要发布管道工件,可以在作业的steps关键字中使用以下 YAML:

steps:
- publish: folder/to/publish 
  artifact: artifactName

管道工件主要用于在多阶段 YAML 管道中下载,相关内容将在下一章中介绍。

写 YAML 管道的技巧

从头开始编写 YAML 管道可能会比较复杂,尤其是对于刚入门的人。有两个工具可以帮助你。

首先,有一个选项可以从可视化设计器导出 YAML。对于每个任务,都会有一个查看 YAML的链接。点击该链接会打开一个小弹出框,显示当前打开的任务和配置对应的 YAML。对于作业也可以执行相同操作,并且在特定条件下,可以对完整的构建定义进行相同操作。

写 YAML 的另一个工具是内置的 YAML 编辑器:

图 5.15 – YAML 编辑器

图 5.15 – YAML 编辑器

每次打开 YAML 构建定义时,有两个工具可供你帮助。首先,在 YAML 文件的每个位置都有自动完成功能。它会显示该位置可用的选项。此外,右侧的任务选择器中也有代码片段。当选择右侧的任何任务时,你可以通过可视化界面配置它们,然后点击添加按钮将生成的 YAML 添加到定义中。

这两种工具旨在将可视化设计器的便捷性引入到 YAML 构建体验中,结合了两者的优点。

请参考这些文档,获取完整的 YAML 模式参考:docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema/?view=azure-pipelines

代理和代理队列

到目前为止,你创建的构建定义可以包含代理作业,而代理作业又包含任务。这些任务不会直接在你的 Azure DevOps 组织中执行,而是由在虚拟机或容器中运行的代理来执行。代理又被分组到代理池中。你可以使用两种类型的代理池:

  • 内建代理池

  • 自托管代理池

让我们一一来了解它们。

内建代理池

内建代理池由微软管理,并作为 Azure DevOps 产品的一部分提供给你。根据你的需求,提供了不同的代理池。池运行不同版本的 Windows 和 Visual Studio,还有运行 Linux(Ubuntu)和 macOS 的池。

这些托管池的缺点是,如果你需要的话,你不能在托管代理的机器或容器上安装额外的软件。这意味着,在这种情况下,你必须创建自己的私有代理池。

创建私有代理池

私有池在你的 Azure DevOps 组织中定义,并从那里提供到一个或多个团队项目。不过,如果在一次操作中创建并配置这些池,你也可以在团队项目级别创建私有池。为此,请进入项目设置 | 代理池。你应该会看到以下添加代理池选项:

图 5.16 – 代理池设置

图 5.16 – 代理池设置

在为池命名并确定是否希望自动为所有管道提供访问权限后,你可以保存该池。创建池之后,你可以添加或移除代理。

添加和移除代理

添加代理分为两个步骤:

  1. 下载并提取代理运行时。你可以通过进入包含代理池概览的部分并打开任何私有代理池的详细信息来找到代理运行时。打开池的详细信息后,点击右上角的新建代理

图 5.17 – 添加新代理

图 5.17 – 添加新代理

  1. 在弹出的对话框中,你可以下载一个包含代理程序和提取及安装代理程序的说明的 ZIP 文件。

重要提示

在配置阶段,你将被提示使用你的 Azure DevOps 组织进行身份验证,并提供你希望安装代理的代理池的名称。虽然有 x86 和 x64 版本的代理可供选择,但建议你使用 x64 代理,除非有特殊原因不这么做。

要从代理池中移除代理,可以使用以下两种方法之一:

  • 你可以像安装时那样返回到 PowerShell 命令行,并使用以下命令:

    .\remove.cmd
    
  • 另外,你也可以通过代理标签从代理池概览中移除代理。前往项目设置 | 代理池(见下方截图中的1) | 代理(见下方截图中的2),然后选择你想要移除的代理旁边的选项按钮(见下方截图中的3)。接着,点击删除(见下方截图中的4):

图 5.18 – 删除代理

图 5.18 – 删除代理

在前面的截图中,你可以看到使用界面移除代理的步骤。请注意,这不会清理主机上的二进制文件和任何文件;然而,如果承载代理的机器故障或虚拟机被移除,这将是唯一可以移除代理的方法。

代理选择

每当构建任务开始运行时,都会从池中选择一个代理来执行你在管道中定义的任务。代理的选择分为两个步骤:

  1. 只有属于选定代理池的代理才有资格执行任务。这意味着,在使用私有代理池时,最好在池中拥有多个代理。如果你将某个代理下线进行维护,那么依赖该代理池的代理任务可以继续运行。

  2. 在代理任务运行之前,来自每个任务及其包含的任务的需求会被收集。如你在变量组部分中学到的那样,代理任务可以指定它对所使用的代理的需求。任务也是如此——它们也可以指定需求。要运行一个任务,只有满足所有这些需求的代理才会被使用。需求和能力是键/值对,其中值为整数。有关需求的示例,请参考以下文档:docs.microsoft.com/en-us/azure/devops/pipelines/process/demands

当没有符合条件的代理可用于构建定义时,构建会在超时后最终失败。

查找代理能力

要查找各个代理上可用的能力,请按照以下步骤操作:

  1. 导航到组织设置 | 代理池

图 5.19 – 查看代理设置

图 5.19 – 查看代理设置

  1. 导航到正确的代理池(托管或私有),然后选择代理,接着打开代理详细信息(在前面的截图中未显示)。

  2. 打开能力标签。

在这里,你可以使用顶部的块来为代理指定一个或多个自定义能力,称为用户定义的能力。对于自托管(私有)代理,安装代理时在机器上发现的所有能力也会显示出来。

自托管代理池的好处

使用微软(云)托管代理是一种相当常见的做法,可以节省大量时间来管理和配置构建基础设施。自托管代理在许多场合已被证明非常有益。由于有大量优秀的文档,管理员已能轻松地为其管道配置和设置自托管代理。

使用自托管代理的主要好处如下:

  • 优化构建代理的成本。

  • 根据需要安装特定构建任务所需的附加软件。

  • 通过缓存资源并启用增量构建来提高构建性能,使其运行更快。

  • 构建问题的调试和故障排除变得更容易。

  • 支持 虚拟网络Vnet)集成,您可以在防火墙后安全地运行构建。

  • 使用白名单 IP 地址安全地访问其他企业资源。

在下一节中,我们将探讨如何自动化托管在 GitHub 上的仓库的 CI 构建。

使用 GitHub Actions 自动化 CI 构建

如果您使用的是 GitHub 仓库,您还可以通过 GitHub Actions(作为仓库顶部导航菜单中的一个标签)自动化 CI 构建。您可以从现有的模板工作流中选择(提供超过 50 个工作流)或创建自己的自定义工作流:

图 5.20 – GitHub Actions 中的可用选项

图 5.20 – GitHub Actions 中的可用选项

编写工作流的体验类似于创建一个 YAML 文件并将其保存在您的 GitHub 仓库中:

图 5.21 – 在 GitHub 仓库中编辑 YAML 文件

图 5.21 – 在 GitHub 仓库中编辑 YAML 文件

在接下来的章节中,我们将介绍可用于管理 CI 流程的其他工具。

其他工具

除了 Azure DevOps 外,还有许多其他工具可供选择。两个其他知名工具是 GitLab CI 和 Jenkins。了解这些工具的一些基础知识有助于您理解如何与它们集成,万一需要时也能更好地应对。此外,对其他工具的有限了解将帮助您快速理解概念,并推广您如何与这些工具协作的知识。

为了突出这些工具如何在相同的概念下工作,本节中的两个示例与 编写 YAML 构建定义 一节中的 Azure DevOps YAML 管道是等效的。

GitLab CI

GitLab 提供了使用 GitLab CI 功能的构建管道。GitLab CI 通过将一个名为 .gitlab-ci.yml 的文件放置在仓库的根目录中进行配置。在此文件中,您可以定义一个或多个阶段和任务,并指定它们应该执行的任务。以下是 GitLab CI 示例 YAML 文件的样式:

stages:
   - build
   - test
build:
      stage: build
      script: dotnet build **/*.csproj
test:
      stage: test
      script: dotnet test **/*.csproj

就像 Azure DevOps 使用代理池与代理一样,GitLab CI 依赖于运行器来执行实际的工作。在 GitLab CI 中,目前不支持直观地创建或编辑管道。

Jenkins

Jenkins 是另一个用于运行构建管道的工具。可以使用 Jenkins 管道运行复杂的构建,Jenkins 管道从 Jenkinsfile 中获取工作。Jenkinsfile 使用 Jenkins 特定的符号编写,如下所示:

pipeline {
        agent any 
        stages {
              stage('build') {
              agent any
                    steps {
                          dotnet build **/*.csproj
                    }
              }
              stage('test') { 
                    agent any 
                    steps {
                          dotnet test **/*.csproj
                    }
              }
       }
}

Jenkins 对直观地创建和编辑管道的支持有限。这被称为自由风格项目。

总结

在本章中,我们探讨了持续集成,并了解它是开发团队使用的心态、过程和工具的结合。你学会了如何使用 Azure Pipelines 创建构建定义,既可以使用图形设计器,也可以使用 YAML,还学会了如何运行构建。你学到可以使用构建管道来编译和测试代码,并将结果反馈给拉取请求。

你学到了构建可以产生输出,这些输出称为工件(Artifacts)。工件存储并保留在 Azure 管道中,可以用于存储报告,但它们也是部署管道的起点,你将在下一章学习到这一点。你还了解了运行构建所需的基础设施——即代理和代理池。最后,你看到了两个简短的示例,展示了如何使用 GitLab CI 和 Jenkins 运行持续集成构建,它们是你可以用于构建管道的另外两个工具。

拥有这些知识后,你现在能够为你的项目创建构建管道。你可以连接到源代码管理,并生成将在下一章中用于部署应用程序的构建。通过深入了解任务、作业、阶段和管道的底层结构,你能够解决复杂的应用构建问题。

在下一章,你将继续学习管道,但这次是关于发布管道。你将学习如何获取构建并将其发布到一个或多个环境中。

问题

在我们总结时,以下是一些问题,供你测试对本章内容的掌握情况。你可以在附录的评估部分找到答案:

  1. 对还是错?如果你每天至少编译一次项目的所有分支,就能实现持续集成。

  2. 对还是错?经典构建定义始终与源代码库连接。

  3. 对还是错?YAML 管道定义始终与源代码库连接。

  4. 以下哪项是从 Azure 管道调用外部工具所需的?

    1. 一个外部服务定义

    2. 一个 Azure 服务连接

    3. 一个服务连接

    4. 一个服务定位器

  5. 使用自托管代理的常见原因有哪些?从以下选项中选择所有正确答案:

    1. 需要访问封闭网络。

    2. 需要将特定的扩展任务提供给代理。

    3. 并行管道执行的数量需要大于 10。

    4. 必须安装特定软件才能让代理使用它。

练习

  • PacktBookLibrary仓库的根目录下添加一个名为pipelines的文件夹。

  • pipelines目录下创建一个名为build的子文件夹。

  • 添加名为main-ci-build.yml的构建文件。

  • 将以下代码块插入到文件中:

    trigger:
    - main
    pool:
      vmImage: ubuntu-latest
    variables:
      buildConfiguration: 'Release'
    steps:
    - script: dotnet build --configuration $(buildConfiguration)
      displayName: 'dotnet build $(buildConfiguration)'
    
  • 保存文件并将更改提交到分支。推送更改后,发起拉取请求以将更改合并到主分支。

  • 在你的团队项目中创建一个新的管道。在配置管道步骤中,选择现有的 Azure Pipelines YAML 文件选项:

图 5.22 – 使用现有管道选项

图 5.22 – 使用现有管道选项

  • 在提示选择构建管道时,指定main-ci-build.yml文件。点击继续完成构建管道的创建。

  • 运行管道作业以验证构建管道是否正常工作。在管道状态页面上,检查是否有任何错误并确认管道作业成功执行。

重要提示

该管道由构建 .NET Core 项目的任务组成。要构建其他类型的代码组件,请使用适当的任务库。

启动包的源代码仓库可以在这里找到:github.com/PacktPublishing/Designing-and-Implementing-Microsoft-DevOps-Solutions-AZ-400-Exam-Guide

进一步阅读

第六章:实现持续部署和发布管理

在前一章中,你学习了如何使用 Azure DevOps 管道进行持续集成。因此,你现在知道如何获取源代码的一个版本,并创建你可以部署的工件。在本章中,你将学习如何扩展这些技术,结合持续交付和持续部署实践,从而自动将这些工件部署到你的代码运行的服务器或平台上。

为了实现这一点,我们将从介绍 Azure DevOps 发布定义开始,这样你就可以定义并运行你的应用程序发布。接下来,将介绍一系列策略,你可以使用这些策略以低风险的方式执行部署。这样做使得你能够自动化部署新版本的过程,且在事件发生的风险最小的情况下进行无人值守的部署。从这里,我们将转向自动化创建发布说明的过程。之后,我们将介绍 Visual Studio App Center,它用于构建、测试和发布移动和桌面应用程序。最后,还将介绍其他持续部署工具。

本章将涵盖以下主题:

  • 持续交付与持续部署

  • 使用 Azure DevOps 发布

  • 编写多阶段的另一种标记语言YAML)管道

  • 实现持续部署策略

  • 部署移动应用程序

  • 自动化发布说明

  • 其他工具

技术要求

为了尝试本章所描述的技术,你可能需要以下一项或多项:

  • 用于构建发布定义和多阶段 YAML 管道的 Azure DevOps 账户

  • 用于部署移动应用程序的 App Center 账户

这两者都提供免费试用选项。

持续交付与持续部署

持续交付和持续部署之间的区别是一个常见的混淆来源。一些人认为这些术语可以互换,并将它们视为同一个概念的两个同义词,但它们实际上有两个不同的含义。

持续交付是一种实践,团队确保他们构建的工件被持续验证并随时准备部署到生产环境中。通常,这通过将工件部署到类似生产环境的环境中(例如验收环境或甚至预发布环境),并应用一系列测试(如验证测试)来确保应用程序正常工作。

持续部署是一种实践,任何部署到类似生产环境并通过所有测试和验证的版本都会自动部署到生产环境中。

无论您的团队是否决定更频繁地部署,都建议计划进行持续交付。部署和升级取决于多个因素,这些因素可能随时间而变化。因此,持续交付将成为更快发布周期的先决条件。

在使用 Azure DevOps 时,Azure Pipelines 是实现持续交付和部署的首选工具。可以使用视觉经典编辑器或多阶段 YAML 管道来完成这一操作,这两者将在以下部分讨论。

使用 Azure DevOps 发布

可以通过使用发布来在 Azure DevOps 中实现持续交付和部署。创建新的发布定义时,将创建发布过程的大纲。此过程通常从触发新发布的工件开始。接下来,可以定义一个或多个阶段,可以将发布部署到这些阶段。通常,这些阶段对应于不同的应用程序环境,例如测试和生产,但这不是强制的。

让我们学习如何创建一个新的发布定义并探索我们拥有的各种选项。首先,导航到管道并从菜单中选择发布。从这里,可以开始创建一个新的发布管道,这将带我们到一个看起来类似以下截图的屏幕:

图 6.1 – 新发布管道

图 6.1 – 新发布管道

在上一个屏幕上,我们可以执行以下操作(在上述屏幕截图中编号):

  1. 首先,请注意,左侧可以看到发布管道的大纲和一个框。在这里,您可以选择一个或多个可用于发布管道的工件。

  2. 在右侧,可以看到一个框,其中显示了发布的不同阶段。默认情况下,已经创建了一个阶段。

  3. 可以选择一个模板作为此预创建阶段的部署管道的起点。在此视图中选择从空作业开始,可以从头开始创建自定义部署管道。

在选择作业模板或从空作业开始之后,右侧的窗格将关闭,并且可以从左到右开始编辑发布管道,从工件开始。

一旦看到一个基本的发布管道框架,您需要配置的第一件事是发布应该使用的工件。这是下一部分的主题。

创建工件和发布触发器

前一章描述了构建定义和 YAML 管道,这些管道创建了工件。这些工件在发布中被接收,并形成部署应用程序的基础。

要开始编辑发布管道,请按照以下步骤操作:

  1. 点击添加构件按钮以开始构建发布定义的起点。这将打开右侧窗格,如下图所示:

图 6.2 – 添加构件

图 6.2 – 添加构件

  1. 在项目选择器中,当前项目将默认被选中。

  2. 现在,指定发布管道应获取的构件。

  3. 此后,默认使用的版本和源别名将自动选择。默认版本可以在手动启动发布时覆盖,因此最新是一个合理的默认选项。

  4. 源别名是我们稍后在发布阶段添加任务时,构件所在文件夹的名称。默认设置通常可以使用。

  5. 通过点击添加完成构件的添加。

现在我们已经指定了要使用的构件,是时候指定何时创建新发布了。让我们学习如何做到这一点:

  1. 若要配置新构件的可用性以触发发布,点击构件旁边的闪电符号以打开配置窗格。这可以在以下截图中看到:

图 6.3 – 指定部署触发器

图 6.3 – 指定部署触发器

  1. 在此窗格中,可以使用顶部滑块创建新的发布,当发布可用时。这将展开一个新部分,您可以在其中定义一个或多个过滤器,以便指定在何种条件下新构件应触发发布。

  2. 点击添加按钮以开始添加条件。

  3. 一个常见的示例是仅包含来自主分支的构件,如图 6.3所示。

  4. 除了来自常规构建的构件外,还可以允许来自拉取请求构建的构件启动新发布。

  5. 最后,可以按固定计划创建一个新的发布。

如果没有指定计划和触发器,则新发布仅在有人手动操作时创建。

指定要部署发布的阶段

在指定了要发布的构件后,是时候指定一个或多个阶段来部署发布了。通常,每个环境(测试、验收和生产)将对应一个阶段,但如果情况需要,也可以有其他阶段。

让我们学习如何添加新阶段并探索各种选项。首先,点击管道,进入如下屏幕:

图 6.4 – 在新管道中配置阶段

图 6.4 – 在新管道中配置阶段

现在,完成以下步骤:

  1. 点击添加按钮以创建新阶段。一个阶段可以是新的,也可以是现有阶段的克隆。

  2. 在选择已存在的阶段后,可以通过右上角的删除按钮将其移除。

  3. 在此页面上,还可以执行其他操作,包括重命名阶段和指定阶段所有者。每当发布部署到环境中时,所有者将会收到通知。

  4. 在创建并命名一个阶段后,可以像在构建流水线中一样,向该阶段添加作业和任务。为此,请点击表示该阶段的框中的链接。

从这里开始,它与构建流水线的操作完全相同。唯一的区别是:除了代理作业和无代理作业,还可以使用部署组作业。

这些将在稍后的与部署组一起工作部分进行讨论。但首先,让我们了解一下我们需要哪些阶段。

我需要哪些阶段?

在处理发布时,常见的一个问题是,我在发布流水线中需要哪些阶段? 根据文档,阶段应该表示发布流水线中的主要分区。在开始处理发布时,这通常简化为每个环境在发布流水线中都有一个阶段。适当的阶段包括测试验收生产

在长时间处理发布的过程中,我们可能会在流水线中加入更多自动化,并希望为其添加额外的检查阶段。例如,可能会有一个叫做负载测试的阶段,与测试阶段并行执行。另一个例子可能是引入一个用于自动化 UI 测试的阶段。

无论添加了哪些阶段,发布到生产的传播方式始终应保持一致。当发布从一个阶段传播到另一个阶段,并且越来越接近生产时,这应表明对该发布有信心,证明它正在正确工作,并且可以推广到生产环境。

阶段触发器、审批和门控

在定义所需阶段并向其添加作业和任务之后,接下来是配置何时触发发布到特定阶段。此步骤可以在以下屏幕截图中看到:

图 6.5 – 部署后配置

图 6.5 – 部署后配置

请注意,以下步骤需要为每个阶段单独执行:

  1. 要触发发布到特定阶段,请点击位于表示该阶段的方块左侧,带有闪电符号和人形图标的按钮。

  2. 在这里配置的第一件事是何时将发布传播到此阶段。可以在发布可用时、在完成另一个阶段后,或仅在手动请求时进行选择。您在此处做出的选择也将反映在流水线的视觉表示中。

  3. 与触发器分开,您还可以定义一个或多个过滤器,以限制哪些工件将触发对该阶段的部署。每个工件可以有一个或多个包括或排除分支过滤器。

  4. 也可以按固定计划重新部署。

  5. 最后,如果为从拉取请求启动的构建指定了创建新发布,则可以使用拉取请求部署滑块允许该发布传播到当前阶段。

在这些触发器旁边,可以添加审批者和门控,以便你可以配置如何处理部署队列设置。这些设置可以通过下面的触发器选项卡访问,如下图所示:

图 6.6 – 部署前条件

图 6.6 – 部署前条件

一旦触发条件被配置,接下来的部分是关于审批者的。在这里,指定了组或用户。在发布到此阶段之前,他们必须给予批准。可以添加多个人,如果是这样,可以定义他们必须批准的顺序,或者可以指定一个批准就足够。向下滚动,你会看到以下选项:

图 6.7 – 部署门控

图 6.7 – 部署门控

第二个选项卡(左侧)允许你添加一个或多个门控。门控是必须成功的自动化检查,才能继续发布。目前,这显示了配置工作项查询和结果数量阈值的配置细节——例如,确保在发布继续之前没有未解决的错误。也有可用的门控,可以调用 Azure Monitor、Azure Functions 或 RESTful API。此套门控可以通过 Azure DevOps 扩展机制进行扩展。这些扩展中的一些还与常见的变更管理系统集成。

最后一部分(右侧)允许你配置如何处理不同版本的发布准备部署到同一阶段的情况。在这里,可以指定可以并行运行多少个发布。如果有更多的发布到来,你可以将它们排队并一个接一个地部署,或者只部署最新的版本。

使用部署组

另一个你可能会遇到的话题是将应用程序部署到本地服务器或位于防火墙后的服务器。你还可能会遇到需要在所有托管应用程序的机器上运行脚本的情况,或目标环境没有提供部署应用程序的机制的情况。

本章中“与 Azure DevOps 发布工作”部分展示的发布方式依赖于能够连接到将托管应用程序的目标机器或服务。我们称之为推送式部署,但并非总是可以做到这一点。

当部署到无法连接的目标机器时,需要采取另一种方法。这种方法称为基于代理的部署。在基于代理的部署中,Azure DevOps 代理将安装在每台需要部署应用程序的机器上。接下来,这些代理必须被分组到部署组中。一旦完成这一操作,就可以向发布中添加部署组作业

这与代理作业非常相似,只有一处不同。在代理作业中,作业中的任务将在其中一台代理上执行,目标机器上。 在部署组作业中,所有任务将在目标机器上的发布组中所有代理上执行。下图展示了两者方法之间的区别:

图 6.8 – 作业在代理上运行

图 6.8 – 作业在代理上运行

使用此方法时,必须在应用程序需要部署到的机器上安装代理。这些代理监听 Azure DevOps,并在有新的发布请求时,它们会获取工作并在本地机器上执行。

管理部署组

在您可以向发布流水线中添加部署组作业之前,您需要创建一个部署组。为此,请执行以下步骤:

  1. 导航到流水线菜单。

  2. 打开部署组菜单。

  3. 点击新建以添加新的部署组。

  4. 输入部署组名称和描述,然后点击创建

一旦创建了新的部署组,右侧会出现一个脚本,如下图所示:

图 6.9 – 创建新部署组

图 6.9 – 创建新部署组

在目标机器上执行此脚本将安装代理,并自动将该机器注册为新创建的部署组的一部分。

如果必须使用部署组将应用程序部署到三个阶段(测试、验收和生产),则需要为每个环境创建三个单独的部署组。

创建带有部署组的发布流水线

创建所需的部署组后,可以在任务视图中使用它们进行发布,如下图所示:

图 6.10 – 在流水线中指定部署组

图 6.10 – 在流水线中指定部署组

为此,请执行以下步骤:

  1. 向流水线中添加一个新的部署组。

  2. 通过从下拉菜单中选择,指定作业应在哪个部署组上运行。

  3. 添加一个或多个任务以执行作业。用户界面的功能与常规代理作业相同。

除了在一组中对所有代理执行作业的不同方法外,部署组作业与常规代理作业的行为相同。

编写多阶段 YAML 流水线

除了可视化设计器用于发布定义外,还可以使用 YAML 流水线实现持续部署。进行此操作时,仍建议区分流水线中的构建(持续集成 (CI))和发布(持续部署 (CD))阶段。阶段的概念用于使这一目标成为可能。一个 YAML 流水线可以分为一个或多个阶段。一个阶段可以代表一个环境,如测试、验收或生产,但这并不总是成立。如果在应用场景中,有必要增加额外的阶段,如预生产或暂存阶段,你可以根据需要添加额外的阶段。一个好的实践是,在早期阶段发布 流水线工件,并在后续阶段使用或 下载工件

多阶段 YAML 流水线是 Azure DevOps 中创建流水线的默认方式。由于使用 YAML 流水线的学习曲线可能比经典发布更陡峭,你可能会发现先使用经典发布再转向 YAML 流水线更容易。与构建一样,经典发布的许多概念也可以转化为多阶段 YAML 流水线。

向 YAML 流水线添加阶段

如果 YAML 流水线中没有定义任何阶段,则总会有一个隐式阶段来容纳所有作业。要将流水线转换为多阶段流水线,你需要添加 stages 关键字和阶段列表,如下代码所示:

stages:
 - stage: stage1
   displayName: My first stage 
   jobs:
   - job: job1 
     steps:
     - task: DotNetCoreCLI@2 
       displayName: ‘dotnet build’ 
       inputs:
       projects: ‘**/*.csproj’
 - stage: stage2 
   jobs:
...

上述语法显示了在 YAML 文件的顶部定义了一个阶段列表。每个阶段从定义一个名称开始。这个名称可以在后续使用,方便你引用该阶段。

虽然作业(除非另有说明)默认是并行运行的,但阶段默认是顺序运行的。与作业类似,阶段也接受 dependsOncondition 关键字,用于改变执行顺序和并行度,甚至(可能)跳过某些阶段。

下载工件

多阶段流水线的常见用途是将构建阶段与部署阶段分开。为了实现这一点,构建阶段通常会发布一个或多个流水线工件。在 第五章 中已经讨论过,转向持续集成

当前流水线中前一个阶段发布的所有工件,可以通过 download 任务下载:

steps:
 - download: current 
   artifact: artifactName

也可以从另一个流水线下载工件。为此,必须将 current 常量替换为该流水线的名称。流水线工件将被下载到 $(Pipeline.Workspace) 目录。

提示

如果你希望对下载管道工件有更精细的控制——例如,控制使用的工件版本或下载工件的位置——你还可以使用下载管道工件任务,相关文档可以参考docs.microsoft.com/bs-cyrl-ba/azure/devops/pipelines/tasks/utility/download-pipeline-artifact?view=azure-devops

在管道中发布和下载工件可以确保在第一阶段构建的代码也是在第二阶段部署的代码——即使各阶段之间间隔几天。本质上,每次管道运行都会构建与该特定运行相关的所有工件的本地阶段。

审批

在多阶段 YAML 管道中,无法像创建经典发布管道时那样定义审批者。原因在于,管道——构建和部署过程——被视为代码。代码只由开发人员和运维人员处理。而审批则是由例如产品负责人来处理。然而,这并不意味着无法为管道的推进到下一个阶段实现审批流程。

要控制管道是否允许继续到某个阶段,需要引入环境的概念。环境在我们为其指定名称和描述时定义。可以将一个或多个审批者附加到这些环境。一旦完成,作业就可以配置为针对该环境。如果一个阶段中至少有一个作业针对某个环境,那么该环境就被认为是该阶段使用的环境。如果该环境已配置审批,则在审批者给予许可之前,部署将无法继续到该阶段。

要开始使用环境,你需要访问环境列表。该列表可以在管道菜单中找到,如下图所示:

图 6.11 – 添加新环境

图 6.11 – 添加新环境

要添加新环境,请执行以下步骤:

  1. 打开管道菜单并选择环境

  2. 从右上角选择新建环境

  3. 指定名称和描述。

  4. 点击创建

可以将资源与环境关联。与环境耦合的资源可以在管道中使用,但前提是该管道也针对该环境。为了保护环境的资源,环境的所有者可以添加一个或多个审批者。下面的截图中展示了一个配置的审批者示例:

图 6.12 – 配置环境的审批者

图 6.12 – 配置环境的审批者

可以通过以下方式向环境添加审批者:

  1. 导航到环境概览窗格。

  2. 通过点击环境名称打开该环境。

  3. 点击右上角标有三个点的菜单,选择审批和检查

  4. 点击创建按钮。

  5. 从列表中选择一个用户或组,并在需要时添加额外的指令。

  6. 再次点击创建按钮。

审批使你能够控制管道是否可以进入下一个阶段,前提是该管道的目标环境正确。目标环境是通过指定特定类型的作业——部署作业来实现的。以下 YAML 展示了如何做到这一点:

jobs:
 - deployment: deploymentJobName 
   displayName: Friendly name 
   strategy:
   runOnce: 
     deploy:
       steps:
       …

部署作业不像代理作业那样直接包含执行步骤。相反,它们首先必须为在steps关键字下列出的任务指定执行策略。截止目前,唯一支持的策略是runOnce。未来预计会宣布其他策略。

截至目前,仅支持 Kubernetes 集群作为环境资源,但未来将支持更多类型的资源。

现在我们了解了创建发布定义和编写多阶段 YAML 管道的技术手段,是时候看看我们可以在实践中应用的不同策略了。这些 CD 策略旨在最小化自动部署新版本应用的风险。

实现 CD 策略

在我们持续部署应用之前,重要的是要思考我们应该使用哪种策略。仅仅一个接一个地进行部署,可能带来的风险超出了企业所能接受的范围。因此,考虑如何处理在部署新版本应用过程中或之后可能发生的问题是非常重要的。

有几种部署策略可以应用,以减少部署可能带来的风险,所有这些策略将在本节中讲解。请注意,可以将以下一种或多种模式结合使用。例如,完全可以在基于环的部署中为每个环使用蓝绿策略。此外,所有部署策略都可以与功能标志结合使用。

蓝绿部署

蓝绿部署是一种技术,其中应用的新版本不会直接部署到生产服务器上。相反,它会先部署到另一组服务器上。一旦成功完成这一步,用户将被引导到新的部署版本。

假设一个应用默认运行在三个主机上。蓝绿部署的典型配置是两组三台主机——蓝色组和绿色组。在这两组前面,有一个反向代理,充当负载均衡器,将传入的请求重定向到蓝色组。以下图表展示了这个过程的工作原理:

图 6.13 – 蓝绿部署

图 6.13 – 蓝绿部署

在这种情况下,要部署应用程序的新版本,需要将其部署到绿色服务器组。由于这些服务器不接收来自最终用户的任何流量,因此这种方式的服务器升级对用户完全没有影响。

部署完成后,可以验证新部署以确保其成功且应用程序正常运行。验证之后,负载均衡器会重新配置,将流量重定向到绿色服务器组。现在,应用程序的新版本已经开始提供服务。

如果突然出现任何意外问题,完全可以通过重新配置负载均衡器将流量切换回蓝色服务器组,从而轻松地回滚到先前的部署。如果部署成功且没有问题,则可以按照相同的程序开始下一版本的部署,但此时绿色组和蓝色组的角色已交换。

不可变服务器

蓝绿部署模式的一种变体是不可变服务器。使用不可变服务器时,不再在两个服务器组之间来回切换。相反,提供旧版本应用程序的服务器组将完全被忽略或移除。通常,这是在宽限期之后进行的。

其结果是,仍然可以通过保持旧服务器一段时间,几乎即时地回滚到先前的版本。另一个好处是,现在可以保证没有任何先前部署的残留物被带入到新的部署中。使用不可变服务器,随着时间的推移,活动服务器的变化可能如下所示:

图 6.14 – 不可变服务器部署

图 6.14 – 不可变服务器部署

当然,这种方法只有在使用诸如容器或虚拟机等技术时才可行。没人会期待在每次重新部署后都忽视物理服务器。

渐进式曝光

渐进式曝光是一种部署策略,其中可以访问新部署或新功能的用户数量随着时间的推移缓慢增加。该策略的目标是限制在功能发布出现问题时,体验问题的用户数量。

我们也可以从更积极的角度看待这一点,并与持续交付(CD)思维方式一致:最初只向少数用户暴露新功能,并随着时间的推移逐步增加该用户数,允许我们在将新版本或功能暴露给所有用户之前,增加对其的信任度。

金丝雀部署

渐进曝光的第一种策略是使用金丝雀部署。在金丝雀部署中,并不是所有用户都会立即被引导到新版本 – 只有一小部分用户能够访问该版本。这些用户就是“金丝雀”,并且会被密切监控。如果他们遇到任何问题,或者服务性能出现下降,新的部署会迅速回滚。

实现金丝雀部署的典型方法是将其与蓝绿部署结合使用。不同之处在于,不是一次性将所有用户切换到新版本,而是开始时只将少部分用户切换到新版本,然后逐渐增加切换到新版本的用户数量。其过程可能类似于以下方式:

图 6.15 – 金丝雀部署

图 6.15 – 金丝雀部署

如果由于出现错误而回滚了部署,这对于用户来说不是一种愉快的体验。为了防止同一小部分用户反复遇到问题,之后选择不同的金丝雀用户可能会有益处。

基于环形的部署

在基于环形的环境中,不仅仅只有一个生产环境 – 而是有多个生产环境。每个生产环境只服务一部分用户。它与金丝雀部署不同之处在于,不仅仅有两个环境,而是根据需要有多个环境。而且,每个新版本都会依次推广到所有的环。

因此,在基于环形的环境中,新版本不会将用户重定向,而是会传播到这些用户使用的服务器上。新版本会从一个环传播到下一个环,直到所有环的用户都完成部署:

图 6.16 – 基于环形的部署

图 6.16 – 基于环形的部署

基于环形的部署架构特别适合全球各地客户访问的产品。不同的环可以布置在世界各地,从而将部署的优势与为用户降低延迟的额外好处相结合。

蓝绿部署与金丝雀部署

尽管这两种方法都能有效最小化对生产工作负载的影响,但在选择其中一种方法时,仍然有一些微妙的差异需要记住:

  • 只有在生产环境有一个完全相同的冗余副本时,你才能选择蓝绿部署。基本上,你可以先升级副本环境,然后将流量切换到副本上。一旦确认副本环境运行正常,你就可以继续对主环境进行部署。这种设置类似于灾难恢复时的部署方式。

  • 如果你没有额外的第二实例,但能够以某种方式将主实例分区,使得一部分用户流量能够路由到独立的物理计算单元,那么你可以通过使用金丝雀发布策略来获益。金丝雀发布提供了一种相对简单的方法,可以根据特定的标准向一部分用户激活或停用某些功能。金丝雀发布适用于右移测试策略,在这种策略中,新的功能在全面推向整个用户群体之前,可以先在有限负载下验证其性能和可用性。

一般来说,这两种方法都需要一些初步的规划和投资,以理顺部署过程。

功能标志

逐步部署的第三种形式可以通过使用功能标志(也叫做功能开关)来实现。与金丝雀发布和基于环路的发布依赖于将新的二进制文件逐步暴露给越来越多的用户不同,功能标志用于将新功能逐步暴露给越来越多的用户。即使它们都向同一服务器发送请求,这也可以实现。功能标志通常用于在升级到包含新功能的应用程序二进制文件时,减轻发布新功能的风险。这些标志像一个开关,能够让系统管理员在运行时启用或禁用特定功能。

功能标志的最佳示例是显示或隐藏一个按钮,该按钮允许用户访问新功能。应用程序设置、数据库或外部服务用于跟踪哪些功能已为哪些用户启用。根据该设置,功能会被显示或隐藏。此类外部服务的示例包括 LaunchDarkly、split.io 和 Prefab.Cloud。

Microsoft Azure 还提供了一项名为 Azure App Configuration 的资源,可用于集中管理功能标志和其他应用程序设置。你可以在这里阅读更多相关信息:docs.microsoft.com/en-us/azure/azure-app-configuration/overview

其他功能标志可能会控制错误修复或性能改进的开启或关闭。这有助于逐步暴露这些增强功能或修复,以确保没有问题。在代码库中引入功能开关时,应有一个流程。这增加了复杂性,因为功能必须支持开关操作,而不会对最终用户产生任何影响。这个流程不仅应该描述如何添加功能开关,还应该描述如何尽快移除它们。这样的流程示例如下。

一旦业务需要独立发布特性,而不依赖开发团队所做的部署,或者对于开发团队认为高风险的变更,需要随时能够撤回而不重新部署时,开发人员会引入一个新的特性标志。引入特性标志意味着在应用设置中添加一个新的数据库条目或声明一个新的设置。

在引入特性开关后,新特性或变更已经开发和测试完毕。这意味着代码库中会有一个或多个if语句,根据特性标志的状态执行不同的代码路径。在此时,应用程序必须维持两个代码执行路径,直到再次移除特性标志。一个好的做法是尽量通过现有的工程实践(如依赖注入)将这两个代码路径分开。

在代码不断交付给用户的同时,该特性对任何人都不可用。只有当开发团队完全满意该变更,或产品负责人觉得时机成熟,才会启用特性标志。

不要停留在这里。打开特性标志后,应积极判断特性或变更是否正常工作,如果正常,特性标志应尽快移除。这样,两个代码路径需要维护的时间就会尽可能短。

另外,请注意,除了需要维护更多的执行路径外,现在还需要测试更多的路径。如果引入了特性标志之间的依赖关系或排斥关系,这个影响会迅速增长。特性标志只能根据另一个特性标志的状态开启或关闭,可能会带来较高的成本,建议避免这种情况。

如果实现得当并尽早移除,特性标志的额外成本通常是值得的。像所有工程实践一样,先从小处开始,在给定的上下文中评估什么有效,再根据需要将其规模化。

回滚或失败前进

无论使用哪种策略,都需要考虑回滚一个或多个版本的能力以及所需时间。例如,蓝绿部署允许我们几乎瞬间回滚一个版本,只要新版本尚未部署到非活动服务器上。另一方面,在基于环的部署中执行回滚将需要完全重新部署上一个版本,这可能会耗时更长,并且伴随部署风险。这可能甚至需要在多个环上进行,增加了挑战。

另一种可以采用的方法是“前进失败”。采用这种方法时,声明永远不会回滚到先前的版本。相反,当遇到问题时,会通过重新部署一个包含问题修复的新版本来解决问题。最近这种策略越来越受到青睐,因为它节省了时间,我们不需要准备、测试和实践回滚操作。然而,这个过程可能存在风险:

  • 无法保证修复一定正确。问题可能无法通过新部署的版本解决,或者更糟的是,新版本可能会导致从一个问题转到另一个问题。

  • 找出任何问题的详细根本原因需要时间,就像编写修复一样。其结果可能是修复所需的时间比回滚所需的时间还要长。

无论采取哪种方法,都要考虑后果并做好准备。

到目前为止,我们主要关注的是基于 Web 的应用程序。在接下来的部分,我们将把注意力转向移动应用程序。

部署移动应用程序

一种需要特殊部署方法的应用程序是移动应用程序。这些应用程序通常不会由最终用户直接下载和安装,而是通过他们移动设备上的应用商店进行消费。

Visual Studio App Center 是微软推出的一款产品,能够通过应用商店或私人分发列表将移动应用程序分发(部署)给最终用户。

你可以在这里了解更多关于 App Center 的信息:visualstudio.microsoft.com/app-center/

登录 App Center 后,您将看到以下屏幕:

图 6.17 – 添加新应用

图 6.17 – 添加新应用

在这里,您可以创建一个新的应用定义。每个目标操作系统都应该创建一个应用定义。如果同一个应用程序将同时部署到 Android 和 iOS,至少需要创建两个应用。

创建应用的步骤如下:

  1. 登录 App Center。

  2. 点击蓝色的添加新应用按钮。如果没有现有应用,这个按钮会出现在屏幕中央;否则,它会出现在右上角(被之前截图中显示的弹出窗口遮挡)。

  3. 输入应用名称。

  4. 选择发布类型。

  5. 选择操作系统。

  6. 选择要使用的平台。

  7. 点击添加新应用来创建应用。

一旦应用创建完成,它可以连接到正确的应用商店,并可以创建分发组。

连接到应用商店

应用商店是所有移动平台分发应用的主要机制。一旦构建交付到应用商店,用户就可以安装并使用该应用。当前连接到应用商店的列表可以通过App Center左侧的商店选项卡打开。从此列表中,可以打开单独的商店连接,进入一个类似于下图所示的屏幕:

图 6.18 – Visual Studio App Center 中的应用商店

图 6.18 – Visual Studio App Center 中的应用商店

此视图显示已发布到连接的商店账户的所有版本的应用列表。这里也是选择要发布到商店的新版本的地方。通过顶部的蓝色发布到 Google Play按钮来执行此操作。此时会弹出一个窗口,您可以选择正确的版本。只需确认一次即可发布此版本。

新的商店连接可以通过返回到所有商店连接的列表并点击添加按钮来创建。这将打开一个向导,需要输入两项信息:

  • 商店连接类型:此列表仅限于在创建应用定义时选择的类型所允许的商店。例如,对于 iOS,这仅限于 Apple App Store 和 Intune 公司门户。

  • 连接详情:通常包括 App Center 与应用商店之间的身份验证方式。

一旦新连接创建成功,它可以在前面显示的列表中找到,并可用于分发应用。

另一种分发方式是使用分发组,我们将在下一节中介绍。

使用分发组

分发组用于创建一个或多个用户的命名列表,通常是测试人员或早期用户,他们通过邀请而不是通过应用商店安装应用。分发组可以在左侧菜单的下找到:

图 6.19 – Visual Studio App Center 中的分发组

图 6.19 – Visual Studio App Center 中的分发组

在这里,可以添加一个新的组,如下所示:

  1. 使用左侧菜单导航到分发组

  2. 单击带有加号(+)符号的蓝色按钮(位于前述截图中的弹出窗口下方)。

  3. 选择组的名称。

  4. 添加一个或多个成员。

  5. 保存新组。

一旦创建了分发组,就可以用于发布版本,我们将在下一节中讨论此内容。

发布应用

要发布应用的首个或新版本,必须与 App Center 共享。可以使用左侧的发布标签来完成此操作。打开发布页面后,将显示以下视图,详细列出所有当前的发布版本。从这里,你可以选择任何版本并查看其详细信息:

图 6.20 – 查看应用的发布版本

图 6.20 – 查看应用的发布版本

在此视图中,最近的发布列表显示在中间列。选择单个发布后,该版本的详细信息将会显示。这包括其正式版本、已共享的商店和/或分发组,以及其他详细信息。

从这里,你可以通过右上角的分发按钮,直接将此特定版本分发到商店连接或分发组。

从这里,你还可以通过上传应用的新构建来创建一个新的版本。为此,请按以下步骤操作:

  1. 点击新发布按钮,该按钮可以在所有发布版本的列表中找到(可能需要先关闭某个特定发布的详细信息)。这将打开以下视图:

图 6.21 – 上传新构建

图 6.21 – 上传新构建

  1. 一个新的向导将会打开,需要上传一个构建文件。根据应用类型,会请求上传正确类型的文件。上传二进制文件后,点击下一步

  2. 现在,必须填写发布说明。在详细列出本次发布的更改后,再次点击下一步

  3. 现在,是时候指定将这个新构建分发到哪里了。必须选择至少一个目标位置——无论是分发组还是商店。选择一个或多个目标位置后,再次点击下一步

  4. 最后的向导标签将显示你至今所做的选择。检查细节后,点击分发以完成新版本的创建及其初步分发。

通常,同一版本或发布需要随着时间推移分发到其他组或商店。每次都创建新发布既不必要也没有意义。相反,进入新的目标商店连接或分发组的详情页面,你可以将现有的发布版本发布到该目标位置。

作为使用 App Center 执行发布管理的替代方法,还可以使用 Azure Pipelines 进行发布管理。

通过 Azure Pipelines 使用 App Center

App Center 还可以与 Azure Pipelines 集成。如果团队熟悉 Azure Pipelines 中的发布过程,将应用构建在 Azure Pipelines 中,并仅使用 App Center 进行商店和分发组的部署是一个合理的选择。

为了实现这一点,Azure Pipelines 中提供了任务,允许你上传版本并触发将版本部署到存储或分发组。这样,版本管理可以在 Azure Pipelines 中进行,而在适用的地方仍然可以利用 App Center 的特定功能。

本节特别关注移动应用程序,而下一节将适用于所有类型的发布。当版本创建自动化,并且新版本迅速跟随前一个版本时,开始自动化发布说明的创建和发布也非常有用。下一节将对此进行讨论。

自动化发布说明

在自动化构建、发布应用程序并努力增加价值流向最终用户之后,许多开发者发现,保持文档和发布说明的更新变得越来越困难。随着版本数量的增加,这项工作变得越来越繁重,最终团队会落后,甚至完全放弃。

为了解决这个问题,可以自动化发布说明的创建和发布。实现这一点的一种方式是使用Azure DevOps 发布说明生成器。有关更多详细信息,请参考:docs.microsoft.com/en-us/samples/azure-samples/azure-devops-release-notes/azure-devops-release-notes-generator/

该生成器是一个可在 GitHub 上获取的 Azure Functions 应用程序。要使用发布说明生成器,需要执行以下操作:

  1. 从 GitHub 下载或克隆该功能代码,链接为:github.com/Azure-Samples/azure-devops-release-notes

  2. 在 Azure 中创建一个 Azure App Service 计划、功能应用和存储账户。

  3. 在存储账户中创建一个名为releases的新 Blob 容器。

  4. 编译功能代码并将其部署到 Azure App Service。

  5. 创建一个新的 Azure DevOps WebHook,以便每当创建新版本时,调用已部署的功能。(有关更详细的说明,请参阅 GitHub 上的 Wiki 文档。)

设置完成后,每当创建新版本时,生成器将自动运行。它将执行以下操作:

  1. 查询已创建的版本,获取其名称、所有关联的工作项以及自上一个版本以来新增的所有提交

  2. 生成一个包含所有这些信息的 Markdown 文件

  3. 将该文件上传到 Blob 容器——即releases

当然,Azure DevOps 发布说明生成器只是自动化发布任务的一个示例,还有其他替代方案可供选择。此外,许多公司还为更新和发布文档以及其他任务创建了定制的内部自动化脚本。

其他工具

除了 Azure DevOps 和 App Center,还有其他工具可以用于部署和发布软件。在上一章中,我们讨论过的 GitLab CI/CD 和 Jenkins,可以用于执行构建,也可以用于发布。除此之外,Octopus Deploy 也是一个常用的工具,与 Azure DevOps 集成得很好。

Octopus Deploy

Octopus Deploy 是一个部署自动化工具,基于在一个或多个目标机器上运行一系列任务的概念。

Octopus 通过在这些机器上安装触角(代理)来访问这些机器。在 Octopus Deploy 中,可以定义应用程序和环境,并将一个或多个机器分配给每个环境。为了进行部署,可以在图形编辑器中定义执行步骤,这类似于 Azure DevOps 的可视化发布编辑器。

其中一个主要区别是这些步骤不是按环境定义的,而是每个管道只定义一次。接下来,可以指定每个任务应该在哪些环境中运行。通过这种方式,更容易看到不同环境的部署差异。

Azure DevOps 和 Octopus Deploy 之间有一个集成,形式为构建和发布任务。通过这个集成,可以从 Azure DevOps 的构建或发布管道启动使用 Octopus Deploy 的部署。

总结

在本章中,你了解了持续部署以及如何使用 Azure DevOps 实现它们。除了可视化发布编辑器,你还学习了多阶段 YAML 管道,你可以使用这些管道将软件发布到多个阶段,直到生产环境。接下来,我们讨论了一系列可以用于发布的策略。你现在了解了蓝绿部署、使用不可变服务器和不同的渐进式曝光策略。你还学会了如何在确保具有回滚能力和接受失败前进策略之间做出选择。

然后,你了解了如何自动化发布说明和文档,并学习了如何将它们作为管道的一部分自动生成。之后,你了解了移动应用程序的持续部署及其与 Web 应用程序交付的不同之处。最后,你了解了 Octopus Deploy 的存在,它是如何操作的,以及它如何与 Azure DevOps 集成。

在下一章中,你将学习使用 Azure Artifacts 进行主题依赖管理。这可以用来托管你自己的 NuGet 包,或者在使用其他产品进行构建或发布应用程序时,结合 Azure Pipelines 托管构建工件。

问题

在我们结束本章内容时,以下是一些问题,用于测试你对本章内容的掌握情况。你可以在附录中的评估部分找到答案:

  1. 判断题:Azure DevOps Classic 发布总是由新版本的工件的可用性触发。

  2. 以下哪个平台可以通过 App Center 发布应用?(你可以选择多个)

    1. Google Play Store

    2. Apple App Store

    3. Microsoft Intune

  3. 以下哪些技术使用渐进式曝光来最小化部署新版本的风险?(你可以选择多个)

    1. 功能切换

    2. 基于环的发布

    3. 灰度发布

  4. 对或错:当 Azure Pipelines 代理安装在将运行软件的机器上时,部署组可用于将软件部署到本地服务器。

  5. 如果你在 Azure Pipelines 中有一个发布定义触发 App Center 中的操作,将 App Center 与 Azure Pipelines 集成的好处是什么?

练习

  • 前提条件:本章中的练习依赖于第八章实施基础设施和配置即代码。请完成该章节中提到的练习,以便你已在 Azure 中配置好必要的资源,从而继续进行构建和部署相关的管道创建。

  • PacktBookLibrary 项目。这将作为服务账户用于将资源和构建部署到 Azure:

    • 转到 项目设置 | 服务连接(位于 管道 下),然后点击 新建服务连接 按钮。

    • 新建服务连接 对话框中,确保选中 Azure 资源管理器 选项,然后在下一步中,选择 服务主体(自动)

    • 将范围保持为 订阅 并指定服务连接名称为 Azure 服务连接

    • 安全性 下,选中授予所有管道访问权限的选项。

  • 创建 DEVTSTPRD。现在暂时不要添加任何资源:图 6.22 – 部署环境

图 6.22 – 部署环境

  • 对于每个环境条目,配置其审批人。审批人列表应包括那些可以批准其各自所拥有环境中任何活动的成员:

图 6.23 – 配置环境设置

图 6.23 – 配置环境设置

  • 创建 Environment:指的是环境的名称

  • azureSubscription:指的是要使用的服务连接名称

  • Webappname:指的是 Azure App Service 资源的名称

  • Resorucegroup:指的是 App Service 资源所在的资源组

  • Slotname:指的是要部署到的 App Service 的槽位

  • 现在你应该能看到三个不同的变量组:图 6.24 – 每个环境的变量组

图 6.24 – 每个环境的变量组

图 6.25 – 查看流水线运行状态

  • 运行 main-cd-pipeline 并验证构建部署是否成功。

深入阅读

第三部分 – 扩展你的 DevOps 管道

在这一部分,你将学习到其他一些 DevOps 实践,这些实践建立在之前第二部分:实现持续交付中讨论的持续集成CI)/持续交付CD)的坚实基础上。你将学习如何建立一个健壮的应用生命周期管理ALM)流程,以管理你在云中的基础设施,以及如何在不丢失任何信息的情况下,实现后端数据库的简化升级和迁移。

我们还将探讨持续测试实践,以更好地理解“左移质量”这一概念。这意味着从一开始就将质量问题嵌入到我们的管道中,并不断地验证这些问题,从而加快交付一个安全且高质量的产品。

本书的这一部分包括以下章节:

  • 第七章依赖管理

  • 第八章将基础设施和配置作为代码实现

  • 第九章在 DevOps 场景中处理数据库

  • 第十章集成持续测试

  • 第十一章管理 安全性和合规性

第七章:依赖管理

在本书的第一部分,你学习了如何持续部署你的应用程序。在现代企业软件开发中,应用程序是由跨职能团队开发的,涉及复杂的解决方案和项目。具有数百个模块和功能的复杂解决方案,更容易出现代码重复的情况——也就是说,在项目中同样的功能可能有相同的实现。重复的代码是编程中的严重错误之一。你可以通过复制粘贴解决问题,但这通常会在后期导致维护上的噩梦。不要重复自己DRY)是软件开发的基本原则,旨在减少信息的重复。

对于一个小型项目或单一项目,你可能能够自己处理依赖关系,但对于复杂的解决方案,团队将会陷入依赖地狱。解决这一问题的一种方法是引入软件包管理。开发人员需要识别在内部项目或开源项目中可复用的组件。重用库将提高开发效率和解决方案的质量。与其将代码从一个项目复制到另一个项目,不如使用它创建一个共享库。在本章中,你将学习如何识别共享组件,并如何使用 Azure Artifacts 使它们可复用。除此之外,你还将学习如何在异构架构中使用 Azure Artifacts 存储管道工件。在这里,你还将使用除了 Azure DevOps 之外的其他持续集成CI)/持续部署CD)工具。为此,你将学习如何使用 Azure Artifacts 进行通用软件包的管理。

本章将涵盖以下主题:

  • 识别共享组件

  • 创建发布软件包的源

  • 使用软件包

  • 使用通用软件包

  • 探索其他工具

技术要求

要实验本章提到的主题,需要一个 Azure DevOps 组织。

识别共享组件

采用如 CI/CD 等 DevOps 实践可以大大减少你在构建和测试应用程序时所需花费的时间。除了构建你的应用程序之外,你还可以在管道中解决许多其他问题。

当你开始在管道中添加越来越多的任务时,可能会遇到一个问题,即管道的单次执行时间过长。为了应对这一问题,你可能会希望将解决方案拆分为更小的构建,甚至可能是不同的仓库。将解决方案拆分为更小的构建对于单体应用程序而言是不可行的,因为这会破坏构建过程。微服务应用程序或解决方案,其中组件已解耦成独立的项目,可以采用这种方法来拆分构建过程。为此,你可以单独构建该应用程序的某些部分,然后将这些构建的结果作为现成的组件在主应用程序中使用。

组件化是一个将项目结构化为可重用组件的过程,这些组件是应用开发人员独立编写和部署的。

通过源组件化,你将解决方案拆分为多个部分,并作为共享项目使用。假设你有两个紧密协作的解决方案:一个是 REST API,另一个是你提供给客户使用该 API 的客户端包。这两个解决方案在其源代码中很可能共享至少一个项目,包含共同的可重用对象,如数据模型,它们将作为数据契约来在这两个解决方案之间交换数据。在这里,你还可以利用包组件化,创建一个仅包含共享项目的第三个解决方案,然后将其作为包在其他解决方案中使用。包通常不是作为独立单元功能齐全的。

或者,如果你在一个团队工作,负责维护一整系列的解决方案,并且发现这些解决方案之间有完整的命名空间被复制和粘贴?这并不是一个理想的情况,而且可能会带来许多问题。如果你能够只编写一次这些代码,构建、打包,然后在所有这些解决方案中重用它们,会怎么样?总的来说,开始使用包和工件源的三个原因如下:

  • 将共享组件提取到包中

  • 构建其他团队使用的包

  • 通过将较大的解决方案拆分为多个部分来减少构建和 CI 时间

提示

依赖管理的三个主要方面是标准化、包格式和源、以及版本控制。

在本章剩余部分中,你将学习如何通过将(部分)应用程序代码构建成包,托管在一个集中位置,并在一个或多个解决方案中重用它们。

在这三种情境中,你可能希望提高代码的可重用性,同时减少从检查更改到接收更改反馈(以自动化测试结果的形式)之间的时间。在开始拆分应用程序之前,请记住,将应用程序的某一部分移到独立组件中并不总是能实现这个目标。

如果你将应用程序拆分为三个组件和一个剩余的主要部分,请确保你能够完全在隔离环境中构建和测试这三个组件,或者至少接近 100% 的隔离。如果你无法在隔离环境中测试应用程序的某个组件,那么为该组件创建一个单独的代码库和构建过程实际上会增加你作为开发者检查变更和收到反馈之间的时间。两个单独的构建可能会更快运行,但现在你需要等待两个构建过程完成,才能收到任何反馈。

提示

如果你将应用程序拆分为单独的组件,请确保每个组件能够在高度隔离的环境中构建和测试。

此外,你还必须确保将应用程序的一部分制作成可重用组件在概念上是合理的。例如,处理跨领域关注点的组件,如日志库或数据库抽象层,是非常适合提取到共享库中的候选者。(顺便提一下,完成此操作后,你可能还想考虑用现成的替代品替换自己的通用库——例如,对于数据库抽象,使用 Entity Framework;对于日志提供程序,使用 Serilog、NLog 等,尽可能使用这些现成工具。)

然而,如果将解决方案拆分为组件是合理的,它将带来很大的好处。

信息源的类型

一旦解决方案被拆分成不同的组件,一些组件将被多个团队和项目重用。此时将需要一个信息源来存储、共享和管理这些可重用的组件/包。

在 Azure Artifacts 中,可以托管许多类型的包信息源。你将如何使用信息源取决于应用程序所使用的语言和生态系统。

以下生态系统在 Azure Artifacts 中得到支持:

  • .nupkg 扩展名,并且其内容符合某些约定。

  • npm:当使用 JavaScript 或 TypeScript 构建应用程序时,使用 npm 协议。

  • Maven 或 Gradle:Maven 和 Gradle 用于 Java 生态系统。

  • pip 和 Twine:在使用 Python 包时,可以通过这些 pip 和 Twine 工具获取它们。

  • 通用包:通用包与特定生态系统无关,而是一种通用的上传和检索包的方式。

每当创建一个新的信息源时,无需指定类型。实际上,每个信息源都可以通过任何协议访问,甚至随着时间的推移使用不同的协议。然而,通常情况下,这样做没有意义。

创建信息源

一旦确定了一个或多个你希望发布的包,你将需要一个存储它们的地方。你可以使用 Azure Artifacts。以下图表展示了 Azure Artifacts 的结构构成:

图 7.1 – Azure Artifacts 视图

图 7.1 – Azure Artifacts 视图

在 Azure Artifacts 中,你可以创建一个或多个信息流来存储你的包。对于每个包,你可以在一个信息流中拥有多个版本。信息流是你可以设置发布包权限的级别。在一个信息流中,你可以创建一个或多个视图,用于设置消费包的权限。任何特定版本的包可以同时存在于多个视图中。接下来的小节将更详细地讨论这些概念。

设置信息流

在 Azure Artifacts 中,信息流是存储包的位置。每个信息流都是一个独立且完全隔离的仓库。要创建新的信息流,请按照以下步骤操作:

  1. 首先,在左侧菜单中导航至 Azure Artifacts,然后点击创建信息流按钮(在以下截图中,按钮部分可见,在创建新信息流的面板后面):

图 7.2 – 创建新信息流

图 7.2 – 创建新信息流

  1. 为信息流指定一个名称。该名称不应包含空格,最好只包含字母和数字,因为它将成为 URL 的一部分。

  2. 接下来,可以指定初始的可见性设置。这决定了哪些用户可以查看该信息流。这个内容将在后续小节中更详细地讨论,管理信息流的视图

  3. 配置上游源的使用。此部分内容将在后续小节中更详细地讨论,配置上游源

  4. 在选择创建后几秒钟,你的信息流就会变得可用。

一旦信息流创建完成,你可以配置各种设置,如隐藏已删除的包、启用包批处理和配置保留策略。要了解如何操作,请按照以下步骤执行:

  1. 创建信息流后,通过点击右上角的齿轮图标访问信息流的设置。

  2. 在下图所示的视图中,选择信息流设置。在这个视图中,你可以配置更多设置:

图 7.3 – 信息流设置

图 7.3 – 信息流设置

  1. 除了更改名称和添加描述外,你还可以选择隐藏已删除的包。当你这样做时,已删除的包版本将不再对信息流的管理员可见。普通用户永远无法查看或使用已删除的包,但此设置使管理员与普通用户具有相同的视图逻辑。

  2. 另一个可以启用的设置是包徽章。包徽章是一个视觉元素,显示了包的名称和最新的可用版本。如果启用此选项,Azure DevOps Feed 管理将提供一个直接的 URL 链接指向该包徽章。通过这个链接,你可以随时引用该包的最新版本。这对于想要跟踪包的最新版本的人非常有用。

  3. 最后,你可以配置保留策略。在这里,你可以配置当某个包的版本数超过某个阈值时自动删除。虽然这有助于节省磁盘空间,从而减少成本,但这也可能导致下游用户的代码引用无法找到这些特定版本。因此,为了防止这种情况发生,你可以在最后一次下载该包后的x天内阻止删除该包。此外,请记住,当前属于某个 feed 的任何包版本将不会被删除。

  4. 完成后,点击保存按钮。

在你创建并配置了 feed 后,接下来是指定哪些用户可以访问该 feed 以及他们的权限。接下来我们来学习如何做。

安全访问

你可以为用户或组分配四个角色,其中每个后续角色的权限包括前一个角色的权限:

  • 读者可以列出 feed 中的所有包,并可以下载它们。

  • 协作者也可以使用来自上游源的包。

  • 贡献者也可以发布自己的包,并且可以将包取消列出或弃用。

  • 最后,所有者拥有对 feed 的完全控制权,并且可以更改权限、重命名 feed 或删除它。

若要更改用户的权限,请按照以下步骤操作:

  1. 导航到权限视图,你可以在下面的截图中看到。在此视图中,你可以看到已分配权限的每个用户或组的列表:

图 7.4 – Feed 设置 – 添加/移除权限

图 7.4 – Feed 设置 – 添加/移除权限

  1. 若要移除权限,选择该行并点击删除

  2. 若要添加新行,点击添加用户/组按钮。这将打开你右侧看到的视图。

除了将用户或组作为整个 feed 的读者添加外,你还可以在 feed 上创建一个或多个视图,并为每个视图设置访问权限。

管理 feed 上的视图

feed 是一个包的仓库,你可以在其中发布和下载包。然而,在许多情况下,你不希望每个上传的包都可以供下载。你可能会发现,你希望控制谁可以使用哪个版本的包——例如,在你实现共享库的持续交付时,但又只想与组织中的其他人共享稳定版本。

为此,你可以创建视图。视图是 feed 中包版本的一个子集。作为消费者,在使用视图时,它的行为就像一个 feed 一样。

视图可以按照以下方式进行管理:

  1. 导航并点击视图;你应该会看到类似以下截图的内容:

图 7.5 – Feed 设置 – 管理视图

图 7.5 – Feed 设置 – 管理视图

  1. 在这里,您可以看到当前所有视图的列表,并通过选择某行并点击删除来移除任何视图。

  2. 添加新视图可以通过点击添加视图按钮来完成,按钮会打开右侧的视图。

  3. 您还可以在此设置对视图的读取权限。您可以允许整个 Azure DevOps 组织读取权限,或指定特定用户。您在此添加的任何用户或组将仅对该视图获得读取权限。

  4. 编辑权限可以通过选择任意行并点击编辑来完成。

一旦有一个或多个视图可用,包就可以被提升到该视图中供使用。

配置上游源

您可以在源上配置的最后一项是上游源。Azure Artifacts 源为您提供了一个存储库,您可以在一个或多个位置发布您自己的包,以便重复使用。

然而,您可能还会使用一些公开可用的包,这些包托管在像NuGet.orgnpmjs.org这样的仓库中。在这种情况下,您可以将 Azure Artifacts 源和NuGet.org结合使用,或者您也可以将您的源配置为从NuGet.org提供包。如果您这么做,NuGet.org被称为上游源。

除了简化操作外,这还为您提供了一个额外的好处,即可以在一个中心位置查看您在解决方案中使用的所有包。这使得您可以快速检查正在使用的包和版本,这对合规性或安全检查很有帮助。通过设置阅读者和协作者角色之间的不同权限,您还可以配置哪些用户被授权从NuGet.org拉取包到您的源中,哪些用户则没有权限。

当然,您可以对任何可以通过互联网访问且实现了 Azure Artifacts 支持的协议的仓库执行此操作。要配置上游源,请按照以下步骤操作:

  1. 上游源可以在导航到以下屏幕后进行配置:

图 7.6 – 配置上游源

图 7.6 – 配置上游源

  1. 上游源的配置方式与权限和视图相同。您可以通过菜单栏中的删除按钮删除上游源。

  2. 添加上游源可以通过点击添加上游源按钮来完成,按钮会打开右侧的视图。

重要提示

另一个关于使用上游源的注意事项是,如果包的版本已经在上游源中存在,您将无法将该版本的包发布到您自己的源。

例如,当您启用NuGet.org上游源时,您无法发布Newtonsoft.Json 10.0.3 版本的包,因为该版本的包已经存在于NuGet.org上。

本节讨论了如何创建和连接源。现在这些已配置完成,我们将在下一节学习如何将包发布到这些源。

发布包

现在您知道如何创建和管理 feed,是时候学习如何将包发布到这些 feed 中了。如果您有将包发布到公共 feed 的经验,您会发现发布到 Azure Artifacts 的方式完全相同。有两种方法可以将包发布到 feed 中:

  • 从您自己的计算机手动发布

  • 通过使用 Azure 管道

这两个选项在以下部分中进行了探讨。

手动发布包

要手动上传包,需要执行以下步骤:

  1. 首先,您需要检索 feed 的 URL。要做到这一点,请单击任何 feed 的 连接到 feed,如以下截图所示:

图 7.7 – 连接到 feed

图 7.7 – 连接到 feed

  1. 在左侧列表中,选择用于访问 feed 的协议。

  2. 选择正确的视图使用。请记住,对于发布包,需要使用完整的 feed URL,因为视图是只读的。

  3. 在进行正确选择之后,使用 复制 按钮将正确的 URL 复制到剪贴板。

  4. 执行以下命令,从常规的 .csproj 文件创建一个 NuGet 包。如果您尚未拥有 NuGet.exe 工具,请使用本章末尾提供的链接下载它:

    nuget.exe pack DemoSolution\MyPackage.csproj -Version 1.1.0
    
  5. 执行最后一个命令将包上传到 NuGet:

    nuget.exe push
      - Source “{feedUrl}” “MyPackage.1.1.0.nupkg”
    

执行最后一个命令后,该包将被发布,并在您的 feed 中可用。

从管道发布包

如果您需要频繁生成和发布库的新版本,手动上传包不是一个方便的解决方案。在您希望频繁生成和发布库的情况下,您可以使用 Azure 管道。除了提供自动化外,这也是引入重复性和可靠性的一个好方法,因为现在您可以利用管道所提供的所有优势。

您可以找到一个可能的构建定义,用于创建和发布一个 npm 包,如下例所示。这个构建的源自于一个名为 tfs-cli 的开源 Microsoft GitHub 仓库。

在这个流水线中,内置的 npm 任务被使用了三次:

  • 第一次出现的是一个 npm install 命令。此命令用于为该包安装依赖项:

图 7.8 – 通过 Azure 管道发布 npm 包

图 7.8 – 通过 Azure 管道发布 npm 包

  • 第二次出现的是运行自定义命令,build。这个命令在源代码中使用 package.json 自身定义,并用于将 TypeScript 源文件转译为 JavaScript:

图 7.9 – 在 Azure 管道中构建 npm 包命令

图 7.9 – 在 Azure 管道中构建 npm 包命令

  • 最后的第三个任务是运行npm publish命令,将生成的软件包发布到npm源。在此实例中,没有选择外部源,而是选择了内置的目标注册表——Azure Artifacts 源:

图 7.10 – 在 Azure 管道中发布 npm 包

图 7.10 – 在 Azure 管道中发布 npm 包

运行此构建后,您的软件包将可用于您的源。

软件包版本控制

使用任务上传npm包(或大多数类型的包)时,有一件事不会自动完成,那就是管理版本号。当然,有许多方法可以确保您的软件包具有正确的版本,但常见的方法是在构建软件包时设置(部分)版本号。

提示

语义版本控制,简称 SemVer,是版本控制系统中最常用的做法。

版本基础

采用Major.Minor.Patch[-Suffix]形式的应用程序。SemVer 标准将版本构建为四个部分,含义如下:

  • Major:表示此版本包含破坏性变更或与以前版本不兼容的更改。

  • Minor:表示此版本包含新功能,但与先前版本兼容——即向后兼容。

  • Patch:表示此版本向后兼容,但仅用于修复较小的错误。

  • Suffix (可选):这是一个连字符后跟一个字符串,表示预发布版本。

推荐的做法是在package.json文件中更新版本号后,发布软件包的新版本。

在我们之前演示的npm包构建的基础上,可以对构建定义进行三处更改:

  1. 首先,构建定义的构建编号格式更新为以下格式:1.0$(Rev:.rrr)。这确保每个构建都会自动生成一个唯一编号。Ref:.rrr变量将生成一个三位数的编号,必要时以零填充。第一次时,这个编号将为000,每次其余构建编号不变时,这个编号将增加 1。

  2. 其次,添加一个任务,使用{#Build.BuildNumber#}令牌替换当前在源控制中指定的版本号。这是对名为Build.BuildNumber的构建变量的引用,它包含在步骤 1中指定的构建号。

  3. 最后,在所有其他任务之前,添加了一个替换令牌任务。以下是配置该任务以将固定版本号替换为自动版本号的可能配置:

图 7.11 – 替换令牌任务

图 7.11 – 替换令牌任务

该任务可以配置为替换一个或多个目标文件中的令牌({#,并以#}结束),取出这两个标记之间的文本,然后将整个文本替换为相应变量的值。

例如,如果我们将以下 json 文件作为输入,它将在标签内替换 variable1 管道变量的值:

{
  “property1”: “{#Variable1#}”
}

如果值被配置为 Variable1: “validToken”,则输出的 json 文件将如下所示:

{
  “property1”: “validToken”
}

设置好之后,每个使用定义构建的包将拥有一个唯一且不断增加的补丁版本号。每当需要更新主版本号或次版本号时,可以通过更新构建号格式来完成。

作为这种方法的替代方案,扩展市场中有许多任务可以帮助进行版本控制,包括更复杂的场景。

本节讨论了如何将包发布到源中。通过发布到源中的包,下一节将详细介绍如何在 Visual Studio 或 Azure 管道中使用这些包。

恢复包

将包上传到 Azure Artifacts 源或仓库使其在多种不同场景下可用。两个常见的场景是:在 Visual Studio 中使用您自己的包,或通过 Azure Pipelines 使用它们。接下来的部分将详细描述这两种场景。

从 Visual Studio 恢复包

一旦您的共享库作为 NuGet 包在 Azure Artifacts 源中可用,您就可以开始在 Visual Studio 中使用它们。在此之前,您需要在您的 Visual Studio 实例中注册您的源。

为此,您首先需要获取您源的 URL。为此,请参考手动发布包部分。准备好 URL 后,按常规方式进入管理 NuGet 文件的界面。如果您不熟悉在 Visual Studio 中操作 NuGet 包,您可以在解决方案资源管理器中找到该选项,位于解决方案和项目的标题处:

图 7.12 – 配置 NuGet 包源

图 7.12 – 配置 NuGet 包源

一旦您到达这里,按照以下步骤操作:

  1. 单击右上角的小齿轮按钮,打开对话框,您可以在其中配置使用哪些 NuGet 源。

  2. 添加一个新的源。

  3. 填写您的源的名称和来源。

  4. 完成后,不要忘记点击更新;否则,您的名称字段的更改将不会被保存,并且不会有任何提示您尚未保存更改的警告。

  5. 在您做出这些更改后,您现在可以在屏幕右上角选择您的源作为包源。

从此以后,您可以像使用 NuGet.org 的包一样,使用您自己源中的这些包。

从管道恢复包

一旦您开始在 Visual Studio 中使用您的包,很可能您也需要在 Azure Pipelines 中使用它们。这是为了在使用您包的依赖应用程序中执行 CI/CD。

幸运的是,这可以通过在你的 NuGet 恢复任务中做一个小的配置更改来实现,具体如下面的截图所示。以下截图与 NuGet 恢复任务相关,既可与 Visual Studio 构建任务一起使用,也可与 .NET Core 构建任务一起使用。两者包含相同的界面,可以以相同的方式使用:

图 7.13 – 恢复 NuGet 包管理源配置

图 7.13 – 恢复 NuGet 包管理源配置

默认情况下,仅勾选了使用来自 NuGet 的包的单选按钮;因此,如果你还需要包含来自自己源的包,你需要在下拉列表中选择正确的源。

如果你发现需要从多个源中包含包,你将被迫创建一个聚合源,并将其他源作为上游源使用。

本部分介绍了如何从 Visual Studio 中使用组件包。下一部分将深入探讨如何使用 Universal Packages 来共享通用的二进制包。

使用 Universal Packages

之前的部分都专注于使用 Azure Artifacts 作为重新分发应用程序包(如库或其他共享组件)的一种方式。然而,Azure Artifacts 还有另一个重要用途——用于维护 Universal Packages。

Universal Packages 源可以用于存储不同类型的包,而不仅限于广泛使用的包,例如用于 .NET 的 NuGet、用于 Node.js 的 npm、用于 Python 的 pypi 和用于 Java 应用程序开发的 Maven

你可以使用 Universal Packages 存储和提供最大为 4 TB 的构建工件。将构建工件打包成 Universal Package 可以快速回滚到所需版本。可以使用 Azure CLI 或 Azure Pipelines 将包发布和从 Artifacts 源中检索。

提示

Universal Packages 仅在 Azure DevOps 服务中可用。

为了在异构架构中使用 Universal Packages 来暂存构建工件,你需要理解四个基本操作:从 Azure pipeline 上传和下载 Universal Packages,以及使用 Azure CLI 上传和下载 Universal Packages。后者可以通过其他工具调用。

Universal Packages 是一种轻量级、易于使用且高效的文件传输方式,具有依赖性管理功能。Universal Packages 提供客户端和服务器端去重功能,可以显著减少在传输文件时使用的网络流量。这些 Universal Packages 作为包管理的一部分通过源进行管理,因此你可以轻松控制对它们的访问。

从 Azure Pipelines 上传和下载 Universal Packages

将构建工件上传到 Universal Packages 源的方式与上传常规构建工件相似。你需要考虑两个变化。

首先,您需要使用另一个任务来执行上传操作。您不能使用发布构建工件发布管道工件任务,而必须使用名为通用包的任务。在使用此任务时,您仍然可以为工件指定一个名称,并指定构建代理文件系统上的上传位置。接下来,您可以指定目标源和使用的版本。该版本可以在每次上传新包时自动递增,或者通过构建变量进行指定。

其次,您需要考虑到上传的包与生成它的构建没有直接关联——这与常规构建或管道工件的标准做法不同。这意味着,无论您在哪个位置使用已上传的包,您都必须找到其他方式来找到正确的版本进行下载。

要执行实际的下载,您可以再次使用通用包任务,如下截图所示:

图 7.14 – 通用包下载

图 7.14 – 通用包下载

请参考截图并按照以下步骤操作:

  1. 添加任务后,您可以在上传下载之间切换。

  2. 您还可以为上传命令指定一个作为工件上传的目录。或者,对于下载命令,您可以指定工件应下载到的地方。

  3. 此外,还需要指定源的名称。

  4. 还需要指定包的名称。

  5. 指定要上传或下载的版本。

重要提示

请注意,您还可以通过在步骤 5中选择使用其他源来使用不属于您自己组织的源。如果这样做,您需要创建一个服务端点来访问该源。

使用 Azure CLI 上传和下载通用包

当您想要处理来自 Azure Pipelines 以外产品的通用包时,您必须使用 Azure CLI。为此,请执行以下步骤:

  1. 要使用 Azure CLI 处理通用包,您需要首先安装 CLI 本身。CLI 的链接将在本章末尾提供。

  2. 接下来,您需要安装 Azure DevOps 扩展。可以使用以下命令完成此操作:

    az extension add –name azure-devops
    
  3. 在使 Azure DevOps 扩展可用后,您必须使用您在 Azure DevOps UI 中也使用的账户进行登录。您可以通过以下命令进行登录:

    az login
    
  4. 登录后,您可以使用以下命令将文件作为工件上传:

    az artifacts universal publish
      --feed {yourFeedName}
      --name {yourPackageName}
      --version {yourVersion}
      --organization https://dev.azure.com/{yourOrganizationName}
      --path {sourceFileName}
    
  5. 要重新下载某个版本的工件,您可以使用以下命令:

    az artifacts universal download
      --feed {yourFeedName}
      --name {yourPackageName}
      --version {yourVersion}
      --organization https://dev.azure.com/{yourOrganizationName}
      --path {targetFileName}
    

使用 CLI 和这些命令,您可以将 Azure Artifacts 作为在多个工具之间共享构建工件的手段。在同一项目中使用多个工具时,通用包是移动二进制文件的好工具。

在下一部分,将探讨用于包管理的其他工具。

探索其他工具

还有许多其他工具可用于二进制管理。四种常用的产品是 MyGet、Artifactory、GitHub Packages 和 Azure 容器注册表ACR)。它们提供的功能有所重叠,但也有各自独特的优势。

MyGet

MyGet 是一个托管 NuGet 包的替代位置,允许你创建由你管理的公共和私有源。它还支持定义上游源,并提供内置的依赖扫描功能,让你能够持续反馈依赖项的安全性水平。

由于 MyGet 是 NuGet 协议的实现,你可以使用 Azure Pipelines 中默认的 NuGet 任务发布和使用包。

Artifactory

Artifactory 是 JFrog 提供的一款产品,它是你可以用来托管包源的另一种工具。Artifactory 最初是一个本地部署的产品,但现在也作为 软件即服务SaaS)提供。与 Azure Artifacts 一样,它支持多种协议与包源进行交互。写作时,Artifactory 支持的仓库协议比 Azure Artifacts 更多。例如,PHP Composer 和 红帽包管理器RPM)等。

JFrog 发布了一个 Azure Pipelines 扩展,用于下载和上传包。

Azure 容器注册表

另一种用于存储可重用包的存储方式是 ACR。该存储专为容器镜像设计,且在开发时考虑到了容器的分层结构。这使得当镜像的新版本可用时,如果不是所有的层都发生了变化,它只会接收部分上传内容。这样,ACR 成为存储容器镜像的一个非常好的位置。上传速度更快,而且 ACR 存储比 Azure Artifacts 存储便宜。这是一个很大的优势,因为容器镜像可能非常大。

你可以通过 Docker 集成扩展从 Azure Pipelines 集成 ACR。

小结

在本章中,你学习了如何识别解决方案中的共享组件——这些组件不仅出现在多个位置,而且还是逻辑上可以重用的单元。你学会了如何使用 Azure Artifacts 源来托管包含这些库的包。此外,你还学习了如何使用这些托管的包来使用 Visual Studio 和 Azure Pipelines 构建依赖的解决方案。你还了解了如何使用 Universal Packages 在 Azure Pipelines 和其他可能用于 CI/CD 的工具之间共享构建工件。

有了这些知识,你现在将能够识别解决方案中的共享组件。一旦识别出这样的组件,你还将能够在源控制中隔离它,构建它,并将其发布到工件源中。从这里,你可以将它分发到一个或多个消费解决方案。最后,你现在也能够使用 Artifacts 源在不同的 CI/CD 产品之间共享构建工件。

在下一章,你将学习如何将基础设施和配置作为代码。这是 DevOps 的基本实践之一,允许你将基础设施定义存储在源代码控制中,并将其作为发布管道的一部分使用。

问题

总结时,这里有一份问题清单,帮助你测试关于本章内容的知识。你可以在评估部分的附录中找到答案:

  1. 判断正误:任何版本的包只能部署到源中的一个视图。

  2. 判断正误:管道工件可以用于将构建结果(包)从 Azure DevOps 分享到其他产品。

  3. 判断正误:Azure Artifacts 中带有通用包的源可以用于将构建结果(包)从 Azure DevOps 分享到其他产品。

  4. 判断正误:标准化包格式和源以及版本管理是有效的依赖管理策略的方面。

  5. 以下哪项是为了在 Visual Studio 中启用使用 Azure Artifacts 源中的包来构建解决方案所必需的?(你可以选择多个选项。)

    1. 将完整的包 URL 添加到项目依赖项中,而不仅仅是包名。

    2. 至少拥有读取者访问权限,或者该源中的某个视图的访问权限。

    3. 至少拥有消费者访问权限以查看该源。

    4. 配置源的所在位置,作为 Visual Studio 的包源。

  6. 将解决方案拆分成多个由 Azure Artifacts 源分隔的部分的原因有哪些?

练习

  • 让我们创建一个共享组件来维护模型类,并将其提取为一个包。由于这个包包含模型类,它可以与多个项目一起使用,如移动应用、API 或网站项目。

  • 添加packtbookslibrary.Shared.Models.

  • 添加一个新的类来表示Book模型,如下所示:

    namespace packtbookslibrary.Models
    {
        public class Book
        {
            public string? Id { get; set; }
            public string? ISBNNumber { get; set; }
            public string? Title { get; set; }
            public string? Description { get; set; }
            public string? Author { get; set; }
            public string? AuthorName { get; set; }
        }
    }
    
  • 在 Azure Artifacts 中,创建一个PacktBooksLibraryFeed源。

  • 在名为构建管理员的[PacktBookLibrary]组下。

  • 或者,将项目集合构建服务身份设置为该源的贡献者:图 7.15 – PacktBooksLibraryFeed 设置

图 7.15 – PacktBooksLibraryFeed 设置

要推送包,运行packtbookslibrary-Shared-Models-ci-pipeline.yaml

  • 一旦包成功执行,你将在packtbookslibrary.Shared.Models的列表中看到一个新条目:

图 7.16 – 在 Azure Artifacts 中发布的共享模型包

图 7.16 – 发布在 Azure Artifacts 中的共享模型包

  • 现在,导航到 管理 NuGet 包设置,将此 NuGet 源添加到你的项目中:

图 7.17 – 管理 packtbooklibrary-api 的 NuGet 包源

图 7.17 – 管理 packtbooklibrary-api 的 NuGet 包源

  • 选择包并将其安装到你的项目中:

图 7.18 – 在 packtbooklibrary-api 项目中安装包

图 7.18 – 在 packtbooklibrary-api 项目中安装包

  • 你可以在 BookController.cs 中按如下方式使用这个包:

图 7.19 – packtbooklibrary-api 中的共享模型包

图 7.19 – packtbooklibrary-api 中的共享模型包

深入阅读

第八章:实现基础设施和配置作为代码

在上一章中,重点是存储和构建应用程序代码并发布生成的二进制文件。你学习了如何创建一个管道,从源代码控制到目标环境,用于自动化、可重复的应用程序部署。

在本章中,你将学习如何将相同的原则应用于你的应用程序所运行的基础设施和应用程序的运行时配置。这样做将帮助你进一步提高将更改交付到生产环境的速度,增加价值流向最终用户。

本章将首先解释将一切(包括基础设施和配置)作为代码的价值。之后,将继续解释Azure 资源管理器ARM)模板。将解释其语法,以及如何部署 ARM 模板。接下来,介绍 Azure Automation 服务,它可在 Azure 云中使用。Azure Automation 可以用于按计划运行脚本或加载并应用 PowerShell DSC 模块。接下来是管理 PaaS 产品(如 Azure 应用服务)的应用程序设置。最后,讨论其他具有类似功能的工具。

本章将涵盖以下主题:

  • 将一切作为代码

  • 使用 ARM 模板

  • 部署 ARM 模板

  • 反向工程一个模板

  • 使用 Azure Automation

  • 管理应用程序设置

  • 其他工具

技术要求

要实验本章中描述的一个或多个技术,可能需要以下一项或多项:

将一切作为代码

如果你曾负责创建和维护应用程序的基础设施和配置,你很可能经历过所谓的配置漂移。配置漂移是指接收环境与生产环境中的服务器配置不同的现象。更糟糕的是,当生产环境中有多台服务器时,它们的配置可能并不总是相同。

配置漂移的最常见原因是手动更改。当手动更改时,可能在生产问题的压力下,始终存在将不同的设置应用于不同服务器或主机的风险。如果你需要扩展并添加另一个服务器到生产环境中,那么这个服务器与所有已有服务器具有相同配置的可能性非常小。

提示

声明式(函数式)和命令式(过程式)是实现基础设施即代码IaC)和配置即代码CaC)的两种主要方法。

使用 IaC 和 CaC,你不再手动更改应用程序配置和基础设施,而是通过自动化来进行。实现这一点的第一步是指定所需的配置和基础设施状态。然后,将所需状态输入到配置管理工具中,强制执行此配置到你的基础设施上。仅指定所需状态被称为声明式方法,它不同于命令式方法,在命令式方法中,你需要指定所有需要采取的步骤。

其中一些工具通常也能够定期检查你基础设施和配置的当前状态,并在检测到任何偏差时重新应用你所需的状态。这是由于声明式方法的缘故。这使得应用配置成为一个幂等操作。

提示

一个操作是幂等的,如果它可以重复执行一次或多次,而结果保持不变。

在采用 IaC 和 CaC 时,你甚至可以重建完整的基础设施,然后再部署应用程序,将应用程序部署到新的基础设施上,切换到新的部署后再丢弃旧的基础设施。这是一种极端的不可变服务器形式。这种方法的额外好处是,你现在可以确保不会留下任何来自之前部署的配置或二进制文件的痕迹。

在接下来的章节中,你将了解不同的基础设施即代码(IaC)技术以及如何使用它们。理解它们是互补的,并且常常一起使用非常重要。例如,ARM 模板可用于在 Azure 中创建虚拟机,一旦完成,PowerShell DSC 或 Ansible 可用于配置这些虚拟机。

使用 ARM 模板

在 Azure 平台上工作时,基础设施是通过 ARM 模板来描述的。ARM 模板是用 JSON 编写的,一个骨架模板如下所示:

{
    "$schema":
"https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate. json#",
    "contentVersion": "1.0.0.0", 
    "parameters": {
    },
    "variables": {
    },
    "resources": [
    ],
    "outputs": {
    }
}

模板本身在最高层次上是一个 JSON 对象。它有一个必填属性,$schema

$schema 是一个必需元素,值的版本号取决于部署的范围和 JSON 编辑器。contentVersion 属性也是必需的,可以指定以对内容进行版本控制。这个版本号可由作者在必要时对模板进行版本控制。

本章的其余部分将更详细地讨论构成 ARM 模板的不同部分。在本章的末尾,你会找到指向在线文档的链接。也会提供一个正式、详细的 ARM 模板结构和语法解析链接。

参数

参数部分通常位于模板的顶部。在开始部署活动之前,ARM 将解析参数值。每当在模板中找到该参数时,ARM 会引用解析后的值。

本节的形式是一个 JSON 对象,可以为空,但不能省略。此部分的作用是声明一个或多个在部署 ARM 模板之前由调用者指定的参数。使用参数部分的常见原因是将同一个模板用于测试和生产环境,但在两者之间变化资源的名称。一个参数部分的示例可能如下所示:

{
  "appServiceName": { 
    "type": "string", 
    "metadata": {
      "description": "a free to choose text"
    }
}

对于每个参数,都会指定一个新的键作为参数的名称。值是一个对象。这个对象有一个必填键 typetype 的允许值有 stringintboolobjectarraysecureStringsecureObjectsecureStringsecureObject 类型可以用来确保这些参数的运行时值不会出现在任何日志和输出中。它们用于存储密码、密钥或其他机密信息。

metadata 对象,包含 description 键,是可选的,可以用来为参数添加描述,以供将来参考。

可以在参数对象上指定的其他属性如下:

  • minValuemaxValue 用于指定整数值的范围。

  • minLengthmaxLength 用于指定字符串值的长度范围。

  • defaultValue 用于指定默认值,如果在应用模板时未指定值,则使用此默认值。

  • allowedValues 用于指定一个允许值的数组,从而限制有效的输入。

接下来,让我们了解什么是参数文件。

参数文件

你可以利用包含参数值的 JSON 文件,而不是在脚本中将它们作为内联值指定。在本节中,我们将讨论如何使用参数文件来配合模板使用。通常,一个模板会配有多个参数文件,例如,一个用于测试,一个用于生产。参数文件的 JSON 可能如下所示:

{
  "$schema":
"https://schema.management.azure.com/schemas/2019-04-01/deploymentParameter s.json#",
  "contentVersion": "1.0.0.0", 
  "parameters": {
    "exampleParameter": { 
      "value": "exampleValue"
    }
  }
}

与 ARM 模板类似,每个参数文件都是一个包含必填 $schemacontentVersion 属性的 JSON 对象。第三个属性参数用于指定一个或多个参数值。对于每个参数,指定其名称作为键,值为一个对象。该对象可以包含 value 键,用于提供参数的实际值。

尽管对于指定资源名称、扩展选项以及在不同环境之间必须变化的其他内容非常有用,但此解决方案对机密无用。

以下图表展示了参数文件如何引用密钥并将该值传递给模板:

图 8.1 – 在 Azure 模板中使用密钥

图 8.1 – 在 Azure 模板中使用密钥

密钥、密码和其他机密不应作为明文存储在源控制的参数文件中。对于机密,提供了另一种表示法:

{
  "$schema":
"https://schema.management.azure.com/schemas/2019-04-01/deploymentParameter s.json#",
  "contentVersion": "1.0.0.0",
  "parameters": { 
    "exampleSecretParameter": {
      "reference": {
        "keyvault": {
          "id": "/subscriptions/…/Microsoft.KeyVault/vaults/<vaultname>"
        },
        "secretName": "myKeyVaultSecretName"
      }
    }
  }
}

使用这种表示法时,代替直接指定值,会指向 Azure 密钥保管库中存储正确值的位置。在部署模板时,该密钥(在 Azure 内!)将从密钥保管库中提取并用于部署。仅当启动部署的用户或服务在密钥保管库中具有所有者或贡献者角色,并且该密钥保管库已启用模板部署时,才允许这样做。

重要说明

严格来说,任何包含 Microsoft.KeyVault/vaults/deploy/action 权限的角色都能正常工作。默认情况下,这些角色是所有者和贡献者角色,但你也可以创建自定义角色,其中包含此操作。

变量

变量部分用于指定一个或多个将在整个模板中使用的值。一种常见方法是在变量部分根据一个名为 environmentName 的参数构建所有资源的名称。这样可以确保资源在不同环境中具有相同的名称。变量还用于指定那些无法从模板外部指定但应被视为可配置的值。例如,可能如下所示:

"Variables": {
  "appServicePlanType": "B1", 
  "appServiceName": "[concat('myAppService-',
parameters('environmentName'))]"
}

请注意,appServiceName 示例中包含了函数,接下来会在下一节中讨论。

函数

函数用于在 ARM 模板中动态评估属性。调用函数使用的语法与许多编程语言非常相似:functionName(arg1, arg2, …)。函数可以返回一个值,例如 stringint,也可以返回一个对象或数组。

当返回一个对象时,可以使用 .propertyName 语法访问任何属性。访问数组中的元素可以使用 [index]。要指示字符串的哪些部分应作为函数进行评估,它们必须包含在括号中:

"myVariable": "[concat('myAppService-', parameters('environmentName'))]"

上一节展示了两个函数的示例。在第一个示例中,调用了 concat 函数来连接两个字符串值,一个是硬编码的,另一个是第二个函数调用的结果,用于获取模板参数的值。

有许多可用的函数。这些函数可以用于字符串操作、获取当前订阅、资源组或Azure Active DirectoryAAD)租户的详细信息,或者获取资源详细信息。

函数也可以用于检索帐户密钥或其他秘密。这通常用于自动将密钥直接从服务插入到应用设置或密钥保管库中。这样可以消除手动传输密钥的需求。变量和函数可以帮助简化模板的维护。

你还可以向模板中添加自己的函数。这些函数可以在模板中使用。用户定义的函数独立于常规模板函数。在大多数情况下,你定义了复杂的表达式,不希望在整个模板中重复这些表达式。

注释和元数据

一个 ARM 模板可以包含与 JSON 语言本身无关的部分。你有几种方法可以添加注释和元数据。

注释

要注释一行的其余部分,可以使用 //,或者要注释一个块,则使用 /* */ 注释方式。这样,以下代码片段在 ARM 模板中都是有效的:

{
  "appServiceName": {
    // this is a single line comment
    "type": "string",
      /*
        This is a multi-line comment
      */
      "metadata": {
          "description": " The name of the web app that you wish to create.",
      "author": "author Name"
    },
  "location": "[
      parameters('location')
      ]", //defaults to resource group location
}

另一个与 JSON 不同的地方是,ARM 模板允许使用多行字符串。你可以将字符串分成多行。请参见前面示例中的 location 属性。

在前面的示例中,你会注意到在模板中使用了注释和元数据。

重要说明

要部署包含多行字符串和注释的模板,可以使用 Azure PowerShell 或 Azure CLI。对于 CLI,请使用版本 2.3.0 或更高版本,并指定 --handle-extended-json-format 开关。

元数据

你添加到元数据描述中的文本会自动用作该参数的提示。ARM 会忽略 metadata 对象,并且可以在模板中的任何位置添加它。

资源

资源是模板的主要部分,在这里会指定要创建的所有资源。这个部分是唯一一个不是对象,而是数组的部分。在这个数组中,会指定一个或多个如下形式的对象:

{
    "type": "Microsoft.Sql/servers", 
    "apiVersion": "2021-02-01-preview", 
    "name": "mySqlServer",
    "location": "West Europe", 
    "properties": {
        "administratorLogin": "myUsername", 
        "administratorLoginPassword": "myPassword", 
        "version": "12.0"
    }
}

每个资源都是以对象的形式指定的。前四个属性是每种类型资源的必填项:

  • 需要指定要创建或更新的资源类型。这采用 resourceprovider 的名称,后跟一个斜杠和该 resourceprovider 下的资源类型名称。

  • 要使用此资源的 REST API 版本:可以从 docs.microsoft.com/en-us/azure/templates/microsoft.resources/allversions 获取支持的 API 版本列表。

  • 资源的名称:每种资源类型都有其自己的规则来确定有效名称。这些规则也可以在前述参考链接中获取。

  • 许多资源需要指定位置。如果资源需要位置,则必须为每个资源指定一个位置。该位置不需要与资源组的位置信息相同。该位置必须是有效的 Azure 区域。

对象上的所有其他属性因资源类型而异,且都在资源中指定。

依赖资源

一种特殊类型的资源是依赖资源。例如,SQL 数据库托管在 SQL Server 上,服务总线主题位于服务总线命名空间内。

对于嵌套资源类型,类型和名称反映了这种嵌套关系。以下示例展示了服务总线主题对服务总线命名空间的显式依赖:

{
    "apiVersion": "2021-11-01",
    "name": "myNamespaceName/myTopicName",
    "type": "Microsoft.ServiceBus/namespaces/topics", 
    "dependsOn": [
        "Microsoft.ServiceBus/namespaces/myNamespaceName"
    ]
}

除了嵌套类型和名称外,还需要额外的属性dependsOn,以指定该嵌套资源只能在包含资源存在之后创建。由于此属性会从包含资源继承,因此不需要location属性。

以下示例展示了 Azure SQL Server 与 Azure SQL Database 之间的逻辑依赖关系。当使用dependsOn属性时,子资源与父资源之间的显式部署依赖关系会自动建立。子资源会在父资源之后部署。在这里,你会注意到使用了内置的重要resourceID函数。resourceID函数返回资源的唯一标识符:

{
    "type": "Microsoft.Sql/servers",
    "apiVersion": "2020-02-02-preview",
    "name": "[parameters('serverName')]",
    "location": "[parameters('location')]",
    "resources": [
      {
        "type": "databases",
        "name": "[parameters('sqlDBName')]",
        "location": "[parameters('location')]",
        "dependsOn": [
          "[resourceId('Microsoft.Sql/servers', concat(parameters('serverName')))]"
        ]
      }
    ]
  }

重要说明

循环依赖是一个依赖顺序问题,导致部署在循环中运行,无法继续并完成部署。ARM 在模板验证过程中会识别出循环依赖。

嵌套模板

第二种特殊类型的资源是模板部署。通过这种方式,一个模板可以触发另一个模板的部署。以下是将模板部署定义为模板中的资源的示例:

{
    "type": "Microsoft.Resources/deployments", 
    "apiVersion": "2021-04-01",
    "name": "linkedTemplate", 
    "properties": {
        "mode": "Incremental", 
        "templateLink": {
            "uri":"https://.../myLinkedTemplate.json"
        },
        "parametersLink": { 
            "uri":"https://.../myParameters.json"
        }
    }
}

模板和参数文件的位置可以使用 HTTP 和 HTTPS 来指定,即使用有效的统一资源标识符URI),但必须是公开可访问的位置。尽管模板 URI 需要外部访问,但我们希望为这些模板启用安全性并限制访问。为了在部署期间获得访问权限,可以将 SAS 令牌附加到模板文件的 URI。作为替代方案,可以指定一个单独的属性模板,模板内容应为整个模板的 JSON 对象。然而,不能同时使用内联参数和链接到参数文件。

输出

模板的下一个部分是输出部分。这里是返回给模板调用者的键。调用者可以使用这些值来启动另一个任务或脚本,并使用模板创建或使用的一个或多个值。

这样做的主要目的是避免在下游自动化中硬编码名称和其他动态值,尤其是 IP 地址。输出部分是以下格式的 JSON 对象:

{
    "outputName":
    {
        "type": "string", 
        "value": "myValue"
    }
}

在指定输出时,可以使用与参数相同的类型。当然,硬编码这些值没有太大意义,因此通常使用函数从参数、变量或甚至创建的资源中获取值。

继续上一节中关于创建 SQL 服务器的Dependent resources部分的示例,输出样本如下:

"outputs": {
    "SqlServerURL": {
      "type": "string",
      "value": "[reference(parameters('serverName')).fullyQualifiedDomainName]"
    }
  }

输出如下所示:

 sqlServerURL String serverName.database.windows.net

到目前为止,我们已经学习了组成 ARM 模板的不同部分,您应该能够自己编写它们。现在是时候学习如何通过各种工具来部署它们了。

部署 ARM 模板

一旦编写了 ARM 模板及其相应的参数文件,它们可以应用于 Azure 环境。有 PowerShell cmdlet 和 Azure CLI 命令可用于从脚本环境应用 ARM 模板。当 ARM 模板用于应用程序的基础设施时,Azure Pipelines 可用于部署不仅仅是代码,还有 ARM 模板。在 Azure 中部署模板的其他替代方法包括 Azure 门户、Azure CLI、REST API、Azure Cloud Shell 或 ARM 模板规格。

无论使用哪种部署方法(REST API、Azure CLI 或 ARM 模板),都将有一个deployment mode。这可以是IncrementalComplete。在增量模式下,将在 Azure 中创建模板中指定的所有资源,或者如果资源已存在,则将更新其属性。在完全部署模式下,将删除在 ARM 模板中未定义的任何资源。此模式不会重新部署所有资源;相反,它验证了模板中声明的资源是否已创建,并删除那些未在模板中定义但已存在于 Azure 中的资源。

默认部署模式为增量。

在接下来的几节中,讨论了几个执行部署的工具,首先是 PowerShell。

PowerShell

用于在本地开发和测试 ARM 模板的 PowerShell 快速命令可应用 ARM 模板到资源组:

New-AzResourceGroupDeployment -ResourceGroupName myResourceGroup - TemplateFile "c:\my\template.json" ` -TemplateParameterFile "c:\my\parameters.json"

上述命令将获取指定的模板和参数文件,并将其应用于指定的资源组。此命令假定当前会话已登录到 Azure。

可用几种命令的变体:

  • 参数 -Mode 包含 CompleteIncremental 值可用。这可用于指定 deploymentmode

  • 如果未指定参数文件且模板需要参数,则命令行工具将提示输入这些值。

  • 作为替代方案,可以使用 -TemplateUri-TemplateParametersUri 选项指定模板和参数的位置,以从其他位置检索。

接下来我们将了解 Azure CLI。

Azure CLI

Azure CLI 是从命令行部署 ARM 模板的另一种方式。CLI 的好处是完全跨平台,并可在 Windows、macOS 和 Linux 上运行。部署 ARM 模板的 Azure CLI 命令如下:

az group deployment create –resource-group myResourceGroup –template-file "c:\my\template.json" –parameters "c:\my\parameters.json"

PowerShell 中提供的所有其他选项在 CLI 中也同样可用。

Azure Pipelines

部署 ARM 模板的第三种机制是通过 Azure 管道进行。这对于同时部署应用程序的基础设施、配置以及二进制文件非常有用。要通过管道部署 ARM 模板,至少需要配置一个 ARM 模板的服务连接。完成此配置后,可以配置管道,如下图所示:

图 8.2 – Azure 管道 ARM 模板部署

图 8.2 – Azure 管道 ARM 模板部署

在这个示例中,有两个 ARM 模板的部署,围绕应用程序代码的部署。第一次部署是增量模式,第二次部署是完整模式。

提示

ARM 模板测试工具包可通过github.com/Azure/arm-ttk访问,帮助你使 ARM 模板符合标准实践。

使用这种方法,第一次部署将创建新版本应用所需的所有新基础设施。该部署是增量模式,因此模板中不再存在但当前部署的应用版本仍在使用的基础设施将不会被移除。第二次部署将在新版本代码部署后处理移除这些元素。

ARM REST API

ARM 提供了 REST API 操作组,用于部署和管理 Azure 的基础设施。要获取订阅中的资源列表,请运行以下命令:

GET https://management.azure.com/subscriptions/{subscriptionId}/resources?api-version=2021-04-01

你可以使用 ARMClient,这是一个简单的命令行工具,用于向新的 ARM REST API 发送 HTTP 请求:

armclient GET /subscriptions/{subscriptionId}/resources?api-version=2021-04-01

上述命令获取订阅中的资源列表。请注意,ARM 客户端不是微软的官方工具,而是一个开源项目,由 GitHub 上的社区维护。

此外,您还可以使用az rest命令来运行这些命令。以下是一个示例:

az rest --method get --uri /subscriptions/{subscriptionId}/resources?api-version=2021-04-01

Azure Cloud Shell

Azure Cloud Shell 提供了一个 Bash 和 PowerShell 环境,允许从浏览器内管理和部署 Azure 资源。Azure Cloud Shell 托管在 Azure 中。

图 8.3 – Azure Cloud Shell

图 8.3 – Azure Cloud Shell

部署 ARM 模板到资源组的 Azure Cloud Shell 命令如下:

az deployment group create --resource-group testrg --name rollout01 --template-uri https://myresource/azuredeploy.json --parameters @myparameters.json

Azure CLI 中的所有其他选项也可以在 Azure Cloud Shell 中使用。

逆向工程模板

从头编写一个 ARM 模板可能是一项繁琐且耗时的任务。幸运的是,现有基础设施中有两种方法可以生成 ARM 模板:

  • 使用导出模板

  • 使用资源浏览器

让我们在接下来的子章节中讨论这两种方法。

使用导出模板

第一种方法是使用导出模板选项,该选项可以在 Azure 门户的每个资源和资源组中找到。这样会生成当前资源(组)状态的 ARM 模板,如下图所示:

图 8.4 – 导出 ARM 模板

图 8.4 – 导出 ARM 模板

请注意,并非所有服务目前都支持使用这种方法反向工程 ARM 模板。对于任何不受支持的服务,屏幕顶部将会显示一个警告。为了绕过这个限制并获取单个资源的 JSON 模板,还有另一种方法,这是我们接下来要讨论的话题。导出模板过程将创建一个可重复使用的 ARM 模板。然而,大多数导出的模板在使用之前需要进行一些修改才能部署 Azure 资源。

使用资源浏览器

要获取单个资源的 JSON 模板,我们可以使用 资源浏览器。此处显示了资源浏览器,并可以通过菜单(1)在 Azure 门户中找到:

图 8.5 – Azure 资源浏览器

图 8.5 – Azure 资源浏览器

打开资源浏览器后,会打开两个新的面板。左侧面板可以用来浏览订阅并深入到资源组,直到单个资源。每选择一个元素,右侧将显示相应的 JSON。在前面的示例中,显示了硬盘的 JSON。这个 JSON 与可以在 ARM 模板的资源数组中使用的 JSON 相同,唯一不同的是 ID 元素。请注意,由于使用了不同的 API 版本,JSON 输出和相关模板资源可能会有所不同。

订阅级别模板

到目前为止,关于 ARM 模板的讨论都是关于资源组部署的 ARM 模板。模板描述了一个或多个部署到资源组的资源。此外,还有订阅级别的模板。以下是一个资源组的示例 ARM 模板:

{
    "$schema": "https://schema.management.azure.com/schemas/2018-05-01
/subscriptionDeploymentTemplate.json#", 
    "contentVersion": "1.0.0.1", 
    "parameters": { },
    "variables": { }, 
    "resources": [
        {
            "type": "Microsoft.Resources/resourceGroups", 
            "apiVersion": "2021-04-01",
            "location": "West Europe", 
            "name": "myResourceGroup", 
            "properties": {}
        }
    ],
    "outputs": {}
}

订阅模板的格式与资源组模板完全相同。不同之处在于 $schema,它指向另一个架构位置,以及支持的资源类型。订阅模板不支持直接创建资源,只支持创建资源组、启动模板部署、创建和分配 Azure 策略以及创建角色分配。

Azure 蓝图

除了订阅级别模板外,还有另一种可用的服务:Azure 蓝图。蓝图可以用来描述 Azure 订阅的期望状态,并将其应用到现有订阅上。

现在,使用蓝图可以做的所有事情也可以通过 ARM 模板来完成。然而,反之则不成立。Azure 蓝图仅支持以下构造,称为工件:

  • 策略分配

  • 角色(RBAC)分配

  • 资源组创建

  • 订阅或资源组级别的嵌套 ARM 模板

这些是构建默认布局或 Azure 订阅蓝图所需的所有元素。

蓝图和 ARM 模板之间有许多关键差异:

  • 蓝图是你可以在门户中创建并导航的资源。编写体验也在门户中,而不是本地计算机上的文本文件。

  • 订阅与用于创建它的蓝图之间的关系在部署完成后依然存在。

  • 通过将蓝图分配给订阅,可以将分配标记为锁定。如果这样做,则通过蓝图部署的所有资源在蓝图应用期间无法被删除或编辑——即使是被分配的订阅的所有者也无法进行操作。

  • 有许多内置蓝图可以用来实施来自知名标准(如 ISO、NIST 或 HIPAA)的控制。

Azure 蓝图在写作时仍处于预览阶段。在使用蓝图时,你可以一次性安装 RBAC 角色、ARM 模板和 Azure 策略,并将它们分配到某个范围。删除分配不会删除或移除资源,因此这很快会变得繁琐,并且 Azure DevOps 没有任务或自动化来大规模管理蓝图。

Bicep

Bicep 是一种领域专用语言DSL),允许声明性地部署 Azure 资源。你可以使用 ARM 模板完成的一切,也可以通过 Bicep 来实现。

Bicep 提供了所有资源类型和 API 版本。Bicep 提供了更好的编写体验,因为它支持类型安全并且具有简洁的声明性语法。Bicep 文件是幂等的,一个文件将代表所需的状态。然后,你可以使用该文件以一致的方式反复部署基础设施。

Bicep 是对 ARM 模板 JSON 的透明抽象,并支持 JSON 模板功能。Bicep CLI 将 Bicep 文件转换为 ARM 模板 JSON。你可以使用 Bicep Playground (aka.ms/bicepdemo) 来并排查看 Bicep 和等效的 JSON。

要在 Azure CLI 中将 ARM 模板 JSON 反编译为 Bicep,请使用以下命令:

az bicep decompile --file deployment.json

该命令会创建一个名为 deployment.bicep 的文件。反编译 ARM 模板可以帮助你开始 Bicep 开发。

使用 Azure 自动化

Azure 自动化是 Azure 中的一项服务,旨在帮助用户创建、管理、部署和维护其 Azure 资源。Azure 自动化包含几个概念,可以去除这些操作中的一些复杂性和底层细节。Azure 自动化允许以 Runbook 形式制定工作流。这些 Runbook 可以代表用户对 Azure 资源进行执行。

自动化帐户资源

在一个 Azure 自动化帐户中,有几个资源使其不仅仅是一个脚本引擎。这些资源在自动化帐户级别共享,因此可以在多个 runbook 中重用。

Run As 帐户

这些构造中的第一个是 Run As 帐户。该帐户是一个服务主体,它将在与自动化帐户关联的 Azure 订阅所连接的 AAD 中创建。用于验证该服务主体的凭据将安全地存储在自动化帐户中。这些凭据无法直接从自动化帐户中检索。服务主体也会被添加为 Azure 订阅的贡献者。因此,现在可以设置 runbook 使用该帐户执行。创建自动化帐户时,可以自动创建 Run As 帐户。

Run As 帐户功能仍然适用于当前和新的自动化帐户。然而,Run As 帐户已被托管身份所取代。托管身份是验证 runbook 中身份的推荐方式,也是自动化帐户的默认验证方法。由于不保存凭据,托管身份更加安全且更易于使用。如果你在 runbook 代码中使用了 Run As,应该将其改为使用托管身份。

调度

自动化工作流的常见方式是将其安排在特定的日期和时间运行,或者在固定的时间间隔内运行。为了避免为每个工作流都指定一个调度,可以创建共享的调度并在 runbook 中重用。要创建新的调度,首先打开所有调度的列表。之后,可以添加一个新的调度,如下图所示:

图 8.6 – Azure 自动化工作流调度

图 8.6 – Azure 自动化工作流调度

一个调度有一个名称和描述。这些值仅供与调度交互的用户使用。接下来,可以配置一个开始日期和时间,以及一个可选的重复间隔,如果指定了重复间隔,还需要配置一个过期日期和时间。

一旦创建了调度,它就可以用于一个 runbook。

模块

在 Azure 自动化中使用的 runbook 是用 PowerShell 或 Python 编写的。PowerShell 拥有一个非常丰富的模块生态系统,提供了许多预定义的功能,可以直接使用。要从自动化帐户中使用 PowerShell 模块,只有那些已上传到模块部分的模块可以使用。这样做的一个主要好处是可以固定使用某个版本的模块。这可以确保脚本在依赖项更新时仍然能够正常工作,而不会中断。

用于与 Azure 交互的 PowerShell 模块默认会安装到每个自动化帐户中。此外,管理员还可以添加更多模块,升级现有模块或移除模块。

变量

在运行手册中,可能涉及到许多变量:资源组名称、虚拟机名称、启动或关闭时间等。将这些值硬编码到脚本中并不是一个好习惯,而将它们与运行手册一起存储也有缺点。例如,如果有三个运行手册用于同一虚拟机,这意味着一些变量值(例如资源组名称和虚拟机名称)至少会重复三次。为了避免这种情况,可以将变量值存储在自动化帐户级别,从而可以在该帐户中执行的每个运行手册中重复使用。

一旦设置了变量,可以使用以下命令从运行手册中访问该变量:

$exampleVar = Get-AutomationVariable -Name 'ExampleVar'

除了在运行手册内读取和使用变量外,还可以从运行手册中更新变量:

Set-AutomationVariable -name 'ExampleVar' -value 'ExampleValue'

虽然从运行手册中更新变量是一个非常强大的功能,但它可能带来意想不到的后果。如果在多个运行手册中使用的某个变量值被其中一个运行手册更新了,可能会导致其他运行手册出现问题。因此,跟踪哪些变量是只读的,哪些是可写的也非常重要。

凭据

一种特殊类型的变量是凭据。凭据包含的不仅仅是一个值,而是两个值:用户名和密码。凭据在使用的地方会被视为机密。这意味着它们不会出现在日志中,必须使用特定的 PowerShell 语法来检索:

$myCredential = Get-AutomationPSCredential -Name 'MyCredential'

执行此命令后,myCredential对象可以用来获取用户名和密码。

连接

在运行手册中连接一个或多个外部服务是非常常见的场景。一个常见的例子是用于管理 Azure 中所有资源的 ARM 模板。为了避免在运行手册中存储一系列变量并建立相应的连接,自动化帐户允许事先创建一个或多个连接。

提示

在大多数情况下,不需要手动创建连接,因为它们会随Run As帐户一起提供。

一旦所有共享资源就绪,就可以开始编写一个或多个运行手册,这是我们接下来要讨论的话题。

运行手册

Azure 自动化支持多种类型的运行手册:PowerShell、Python 2 和图形化运行手册。前两者允许用指定语言编写脚本。图形化运行手册允许通过拖放方式从所有上传的 PowerShell 模块、资产和现有运行手册中组成一个运行手册。

除了这三种基本类型的运行手册外,还有 PowerShell 工作流和图形工作流类型可供选择。常规运行手册与工作流运行手册的区别在于,工作流运行手册还支持并行处理。PowerShell 工作流的另一个好处是它支持使用检查点,这样如果脚本在执行过程中遇到异常,它可以从检查点恢复继续执行。

运行手册执行

一旦写好了运行手册,就有多种方式可以执行它:

  • 手动:任何运行手册都可以随时通过在 Azure 门户中打开并按下开始按钮来运行。当然,这些操作也可以通过 PowerShell 或 Azure CLI 执行。

  • 通过附加 webhook:一旦发布了运行手册,可以生成一个或多个 webhook 用于执行该运行手册。每个 webhook 都可以启用或禁用,并可以设置过期日期。这些工具允许为每个运行手册的用户生成一个新的 webhook,并在将来需要撤销某个用户的访问时,进行细粒度的控制。

  • 按计划:已发布的运行手册可以附加到一个或多个共享的计划中。能够附加到多个计划意味着,可以轻松预先创建一系列典型的重复计划,例如每小时、每天或每周一,并将这些计划重复使用和组合到适当的运行手册中。

当通过 webhook 或按计划执行运行手册时,手动执行的选项仍然可用。

作业

每次执行运行手册时,都会在作业日志中创建一个新条目。该日志会记录每次运行手册时的条目,无论执行是如何发起的。每个条目都会包含运行开始的日期和时间、是否有错误,以及完整的执行日志。

运行手册画廊

运行手册是自动化常见任务的绝佳方式。当然,也有一些任务仅适用于特定客户,但也有许多任务适用于所有 Azure 客户。举例来说,自动化启动虚拟机,每周一上午 8 点,或者每天早晨扩展数据库并在晚上缩减。

对于这些常见场景,每个自动化帐户中都启用了运行手册画廊。在该画廊中,可以浏览和搜索数百个预先制作的运行手册。找到合适的运行手册后,可以直接将其导入帐户作为运行手册。

除了按设定的时间间隔或在调用 webhook 时执行脚本,Azure Automation 还可以作为 PowerShell DSC 拉取服务器使用。接下来我们将讨论这个功能。

在运行新创建或导入的运行手册之前,必须先发布它。每个 Azure Automation 运行手册都有草稿版和已发布版。只有已发布版可以运行,而草稿版可以进行修改。对草稿版的任何修改不会影响已发布版。当草稿版准备好后,可以将其发布,替换现有的已发布版。

PowerShell DSC

PowerShell DSC 是一种用于指定服务器配置的概念。该配置存储在拉取服务器上,虚拟机可以访问该服务器。虚拟机被配置为在指定的时间间隔检查该服务器,获取最新的 DSC 配置,并更新自己以遵循该配置。

PowerShell DSC 是 PowerShell 语言规范的扩展,用于编写期望的状态配置。配置使得可以指定一个或多个节点的期望状态。节点指定了要配置的服务器或服务器集合。节点的配置是通过一个或多个资源来编写的。以下是一个示例配置:

configuration ServerFarmConfig
{
    Node FrontEndServer
    {
        WindowsFeature IIS
        {
            Ensure = 'Present' 
            Name = 'Web-Server'
            IncludeAllSubFeature = $true
        }
        File LogDirectory
        {
            Type = 'Directory' 
            DestinationPath = 'C:\logs' 
            Ensure = "Present"

        }
    }
}

在本示例中,描述了一个仅包含单一类型服务器的服务器集群配置。该服务器包含两个资源。第一个资源是 WindowsFeature 类型,名为 IIS,它与所有子功能一起安装。第二个资源是 File 类型,用于确保目录 c:\logs 存在。IISFile 等资源类型内建于 PowerShell DSC 规范中。所有资源的完整参考可在线查阅,并且本章末尾附有链接。

编译并应用 PowerShell DSC

PowerShell DSC 文件以纯文本格式保存,通常为 .ps1 文件。这些文件可以编译成 管理对象格式MOF)文件。然后,这些 MOF 文件可以被推送到一个或多个服务器,以将服务器的状态更新为 MOF 文件中描述的状态。这称为 推送模式

除了推送模式,还有一种部署 MOF 文件的模式,称为 拉取模式。在拉取模式下,MOF 文件不会直接推送到单个服务器,而是存储在一个中央服务器上,该服务器称为 拉取服务器。这样,拉取服务器就有了所有配置和节点定义的完整记录。

一旦拉取服务器启动并运行,单个服务器将被配置为在固定间隔时间内获取其 DSC 配置并应用该配置。应用配置意味着,对于每个定义的资源,将执行描述的状态。如果实际状态已经与期望状态匹配,则可以什么都不做;如果不匹配,则通过执行命令以达到期望状态。在此过程中,所有之前的更改——即使是管理员的更改——如果有需要,都会被撤销。

在 Azure Automation 中使用 PowerShell DSC

Azure Automation 具有内建的 PowerShell DSC 功能,可以充当一个或多个虚拟机的拉取服务器。

要开始使用内建的拉取服务器功能,请将一个或多个配置文件上传到 Automation 账户。这可以从下图所示的 状态配置 视图中完成。现在,按照以下步骤操作:

  1. 点击左侧的菜单选项打开。

  2. 在顶部的标签栏中选择 配置

图 8.7 – Azure Automation 状态配置

图 8.7 – Azure Automation 状态配置

  1. 一旦打开所有配置的概览,可以使用 topHere 添加新配置,可以选择本地 ps1 文件,并将其添加到列表中。列表中的任何有效配置都可以点击并在当前位置进行编译。

  2. 现在,配置也会显示在已编译配置的标签页中,并可以应用于一个或多个虚拟机。

  3. 一旦有了已编译的配置,可以使用节点标签将一个或多个虚拟机从订阅中添加到配置节点。

  4. 在此标签页显示时点击添加按钮,打开此处显示的视图:

图 8.8 – Azure 自动化 – 添加新虚拟机

图 8.8 – Azure 自动化 – 添加新虚拟机

  1. 在此视图中,可以选择一个虚拟机,选定的配置将应用于该虚拟机。

  2. 该机器上的本地配置管理器将被配置为在固定时间间隔内刷新配置。

  3. 每当配置刷新时,它将重新应用于服务器。

Azure 自动化使用户能够管理虚拟机,例如应用程序配置。在使用 PaaS 产品时,无法使用 PowerShell DSC 等技术来完成此任务;必须使用其他技术来管理应用设置。下一节将讨论这些技术。

管理应用设置

应用程序的另一个基础设施部分是应用配置。在本节中,讨论了存储和加载 Azure 应用服务应用配置的多种方法。包括以下内容:

  • 将配置存储在应用设置中

  • 使用托管身份和密钥保管库的组合

  • 使用 Azure 应用配置服务

第一种方法的缺点是,任何具有应用服务管理(读取)权限的用户都可以读取应用设置。接下来的两种方法没有这个缺点。

使用 ARM 模板中的 Azure 应用服务设置

配置应用设置为代码的第一种方式是通过在 ARM 模板中将应用设置指定为资源。这应该作为嵌套资源来指定。如下截图所示:

{
    "name": "[concat(variables('websiteName'), '/appsettings')]", 
    "type": "config",
    "apiVersion": "2021-03-01", 
    "dependsOn": [
        "[concat('Microsoft.Web/sites/', variables('webSiteName'))]"
    ],
    "properties": {
        "key1": " [listKeys(parameters('storagename'), 2021-03-01').keys[0].value]",
        "key2": "value2"
    }
}

在这些场景中,使用 listKeys 函数尤其有用。它允许将支持服务的密钥直接复制到应用设置中,而无需将其存储在中间解决方案中。对于不来自 Azure 源的密钥,应使用模板参数。

在 ARM 模板中指定的配置对应于在门户中可以找到的应用服务的配置。这些设置用于覆盖 appsettings.jsonappsettings.config 文件中的相应条目。更新此配置也会自动重新加载应用程序。

这种方法的缺点是,以这种方式存储的机密可以通过 Azure 门户查看。任何具有应用服务读取权限的用户都可以检索以这种方式存储的所有机密。

从密钥库在运行时加载设置

存储应用服务设置的下一个可能位置是在 Azure 密钥库中,应用程序在运行时加载它们。为了实现这一点,必须具备以下条件。

为了能够授权应用程序访问密钥库,应用程序首先必须能够在 AAD 中进行身份验证。当然,可以手动注册一个服务主体,但这将返回一个必须存储在某处的用户名和密码。用户名和密码是机密,但不能存储在密钥库中,因为它们需要用于访问密钥库。如何保持密钥的安全问题可以通过使用 Azure 提供的一项功能 托管身份 来解决。

重要提示

安全存储机密并通过访问它们获取另一个机密的问题通常被称为 一切都是乌龟背上的问题。这指的是一个古老的轶事,章节末尾提供了一个链接。

启用 Azure 托管身份的应用服务,Azure 会自动生成一个服务主体,并为其提供不可检索的用户名和密码。只有在运行时,通过特定的代码,应用程序才能将自己认证为该服务主体。Azure 会确保这一过程仅适用于运行在该托管身份所属应用服务中的代码。

现在应用程序可以拥有自己的身份,该身份必须被授予访问密钥库的权限。这可以在 ARM 模板中的密钥库描述中完成,参考以下语法:

{
    "type": "Microsoft.KeyVault/vaults", 
    "name": "[parameters('keyVaultName')]", 
    "apiVersion": " 2021-11-01-preview",
    "location": "[resourceGroup().location]", 
    "dependsOn": [
        "[resourceId('Microsoft.Web/sites/',
parameters('appServiceName'))]"
    ],
    "properties": { 
        "enabledForTemplateDeployment": false, 
        "tenantId": "[subscription().tenantId]", 
        "accessPolicies": [
          {
              "tenantId": "[subscription().tenantId]", "objectId":
              [reference(concat(resourceId('Microsoft.Web/sites',parameters('appServiceNa me')),  '/providers/Microsoft.ManagedIdentity/Idntities/default'), ' 2021-11-01-preview').principalId]", "permissions": {    "secrets": [ "get", "list" ]
               }
          }
        ],
        "sku": {
          "name": "standard",
          "family": "A"
        }
    }
}

在这个示例中,使用 reference() 函数来检索托管身份的信息,并利用它在密钥库上创建访问策略。

最后,在设置好密钥库并获得访问权限后,应用程序必须在启动时检索内容。为此,可以使用配置构建器。它们从 .NET Core 2.0(以及 .NET Framework 4.7.1)开始引入,并在 StartUp 类中使用,以下代码片段展示了这一点:

var tokenProvider = new AzureServiceTokenProvider();
var kvClient = new KeyVaultClient((authority, resource, scope) => tokenProvider.KeyVaultTokenCallback(authority, resource, scope));
var configurationBuilder = new ConfigurationBuilder().AddAzureKeyVault(
    $"https://{ Configuration["keyVaultName"]}.vault.azure.net/", kvClient,
    new DefaultKeyVaultSecretManager()); 
Configuration = configurationBuilder.Build();

此代码示例中的所有类型都可以在 NuGet Microsoft.Configuration.ConfigurationBuilders.Azure 包中找到。

Azure 应用配置

另一个存储应用程序配置的位置是 Azure 应用配置。这是一个新服务,写作时仍处于预览阶段。应用配置允许创建一个包含键值对的中央注册表,可以由该注册表及多个应用程序作为配置使用。

应用配置是另一种可以从门户创建的资源类型。其主要组件是 配置资源管理器(Configuration Explorer),如以下截图所示:

图 8.9 – Azure 应用配置

图 8.9 – Azure 应用配置

除了配置浏览器外,还有一个用于获取访问密钥的“密钥”部分,应用程序可以使用这些密钥读取配置。还可以查看配置的最近更改,恢复早期版本,并进行配置设置的导入或导出。

在创建了应用配置资源并添加了配置密钥后,可以通过使用IConfiguration框架类型的扩展方法从应用程序中检索这些密钥:

config.AddAzureAppConfiguration(settings["ConnectionStrings:AppConfig"]);

从应用程序配置加载设置的加载器是 NuGet Microsoft.Azure.AppConfiguration.AspNetCore包的一部分。

与将设置存储在 Azure Key Vault 中相比,应用配置有两个缺点:

  • 首先,应用程序需要配置一个连接字符串,连接到 Azure 应用配置,并在应用程序设置中存储至少一个新的密钥。

  • 其次,应用配置不像 Key Vault 那样具有严格的访问控制选项。因此,根据配置值的类型,可能需要在应用配置和 Key Vault 之间分配配置。

这就是我们关于 Azure 和 Azure DevOps 在基础设施即代码(IaC)方面功能的讨论。接下来的部分将讨论一系列其他工具,这些工具提供类似的功能。

其他工具

还有许多其他工具可以通过代码管理基础设施和配置。除了前面讨论的原生 Azure 和 Windows 选项外,还有许多广泛使用的替代工具,其中一些在本节中列出。了解哪些工具可以用于哪些场景以及如何与它们集成是很重要的。

CloudFormation

CloudFormation 是 AWS 云的基础设施即代码(IaC)语言。CloudFormation 模板可以使用 JSON 或 YAML 格式编写。以下是创建一个公共可读的 AWS S3 存储桶的示例:

Resources:
  HelloBucket:
  Type: AWS::S3::Bucket 
  Properties:
  AccessControl: PublicRead

有一个扩展可用,允许从 Azure DevOps 在 AWS 上执行 CloudFormation 模板。此扩展提供了创建、更新或删除 AWS 堆栈的任务。堆栈的功能类似于 Azure 中的资源组,任务也类似于应用 ARM 模板的任务。

Chef

Chef 是一个用于配置即代码(CaC)的工具,支持描述和强制执行服务器的配置。Chef 使用一个集中的服务器,Chef 服务器,所有服务器的配置都保存在这里。在这里,确定每个服务器的正确期望状态,然后由Chef 客户端,一个运行在被管理的节点上的代理,拉取这些配置。

定义服务器的期望状态是通过一系列构建块来完成的。最低级别的是食谱(recipe)。食谱包含一个或多个资源,这些资源是可以使用的内置功能。例如,execute 是一个资源,用于运行 Bash 命令。另一个示例资源是 apt_update,它提供与 apt 包管理器交互的手段。一个或多个食谱可以组合成烹饪书(cookbook),它描述了可以分配给节点的能力。一个或多个烹饪书的分配是通过运行列表(run list)完成的。运行列表包含必须应用到节点上的所有烹饪书。

与 Chef 服务器的交互是通过名为 knife 的命令行工具完成的。

尽管术语完全不同,但 PowerShell DSC 和 Chef 在概念上有许多相似之处。

Puppet

Puppet 是一种部署和配置管理工具,采用服务器-客户端模型。它有一个集中式的服务器,称为 Puppet master,负责接收所有期望状态描述并将它们编译成一个内部目录,目录中保存着每个被管理服务器的期望状态。所有被 Puppet 管理的服务器都需要在本地服务器上安装 Puppet 代理。代理连接到服务器,拉取它所管理服务器的状态,并在本地应用该状态。被管理的服务器称为 节点

Puppet 使用的基本构建块叫做 资源(resource)。资源通过指定资源类型和一系列属性来定义。有很多类型的资源可供使用,例如用于管理用户和已安装的应用程序。资源被分组到一个或多个 (class)中,这些类进一步被分配到一个或多个节点上。

Puppet 可以安装在 Azure 中的任何 Linux 或 Windows 虚拟机上。Azure Marketplace 中也有一个包含 Puppet Enterprise 的预构建镜像。

Puppet 可与 Chef 和 PowerShell DSC 相媲美。三者在描述期望状态方面有相似的模型,并且都具有相同的目的。

Ansible

Ansible 是另一种配置管理工具,主要用于 Linux,但也支持 Windows。Ansible 与其他工具的不同之处在于,它没有一个集中式服务器来托管所有的期望状态,也不使用代理。所有由 Ansible 执行的命令都是通过 SSH 或其他相关协议(如 HTTP(S)、WinRM 等)执行的。

任何服务器都可以启动一个 playbook,对一个或多个 itemsinventory 中进行操作。Ansible 清单包含所有可以由 Ansible 管理的服务器。这些服务器可以分组为一个或多个组,并且可以嵌套在其他组中。每个单独的服务器和每个组都是一个清单项。在 Ansible 中,期望的状态是写在 playbooks 中的。Playbook 是一系列需要在目标服务器上运行的任务或角色。角色是一组任务。角色旨在在多个 playbook 中重用,因此应足够通用,以便在多种情况下使用。角色还应该是幂等的。这意味着角色中的任务应确保无论运行 playbook 的次数多少,结果都是相同的。

Ansible 脚本可以使用命令行工具或封装此工具的 Azure DevOps 扩展进行执行。还有其他管理系统可用,例如 Ansible Tower,它在 Ansible 命令行工具的功能之上提供了图形用户界面。

Terraform

Terraform 是一种多云基础设施管理解决方案。它可以与 ARM 模板或 Bicep 相媲美,不同之处在于它还支持 Amazon Web Services、Google Cloud Platform 和其他支持的云服务。Terraform 使用一种自定义文件格式来指定一个或多个资源,这些资源通过一个或多个提供者进行创建。资源对应于云资源,而提供者负责了解如何与不同厂商的 API 进行交互。

你也可以选择使用 JSON 格式,而不是 Terraform 专有的 HashiCorp 配置语言HCL)。Terraform 还支持使用模块来创建可重用的组件包。

Terraform 配置文件通过 CLI 执行。

你可以参考 Terraform 基础知识 (learn.hashicorp.com/collections/terraform/cli) 来了解这些核心组件。

摘要

在本章中,你了解了 IaC 和 CaC 的概念、它们的价值以及如何在实践中使用它们。为了实现这些,你了解了 Azure 的 IaC 机制 —— ARM 模板。你还学习了如何使用 PowerShell DSC 来管理虚拟机的配置,以及如何使用不同的技术来管理应用程序的配置。最后,你了解了市场上其他可用的工具。你学会了在什么情况下使用哪种工具,以及这些工具是否可以与 Azure DevOps 集成。

有了这些知识,你现在可以开始使用你读到的一个或多个工具,在源控制中描述你的应用程序的基础设施和配置。你也可以设置交付基础设施的方式,使用自动化,既可以通过发布管道,也可以使用专用的基础设施管理工具。无论你选择哪种解决方案,你现在都具备了将基础设施融入 DevOps 流程的能力。

在下一章,你将学习到实施 DevOps 实践时可能遇到的另一个挑战,涉及数据库。当提高功能流向生产的速度时,你可能还需要改变管理数据库架构和应用更改的方式。下一章将讨论这个话题。

活动

  • 使用 Azure CLI 创建并部署空的 ARM 模板

  • 在你的 ARM 模板中添加资源以创建存储帐户

问题

在我们总结时,这里列出了一些问题,供你测试关于本章内容的知识。你可以在附录的评估部分找到答案:

  1. 正确还是错误:增量部署模式下的 ARM 模板可用于创建、更新和删除 Azure 资源。

  2. 以下哪项不是 Azure 自动化帐户资源?

    1. 模块

    2. 容器

    3. 托管标识

    4. 变量

  3. 正确还是错误:IaC 的一个缺点是你必须将敏感信息作为 ARM 模板参数文件放入源控制中。

  4. 正确还是错误:Azure 自动化帐户允许按预定义的时间表执行 PowerShell 运行簿。

  5. 使用 IaC 的一些好处是什么?

进一步阅读

第九章:在 DevOps 场景中处理数据库

在前几章中,您了解了软件的持续集成和持续部署。您还学习了如何将相同的原则应用于基础设施配置的交付。一旦您采纳了这些原则并开始提高价值交付的流量,您可能会遇到另一个挑战:管理数据库模式的变化。

将 DevOps 应用于数据库可能感觉就像是在一辆行驶的汽车上更换轮胎。您必须找到一种方法,在数据库模式和应用程序代码之间协调变化,而不需要停机维护。

本章中,您将学习如何做到这一点:在避免停机的情况下管理数据库模式的变化。通过适当的规划和严格的管理,这可以以一种良好管理风险的方式实现。您将看到如何将数据库模式视为代码,并了解实现这一目标的不同方法。您还将看到一种完全避免数据库模式的方法,即采用无模式(schema-less)方式。

本章将涵盖以下主题:

  • 将数据库模式作为代码进行管理

  • 应用数据库模式变化

  • 采用无模式方式

  • 其他方法和关注点

技术要求

为了实践本章中提出的思想,您需要安装以下工具:

  • 安装了 Entity Framework Core NuGet 包的应用程序

  • 配备 SQL Server 数据工具的 Visual Studio

  • 访问 Azure Pipelines

  • 一个 Azure 订阅,用于访问 Cosmos DB

将数据库模式作为代码进行管理

如果您熟悉从应用程序代码操作关系型数据库,那么很可能您已经在使用 对象关系映射器ORM)。

ORM 的引入是为了解决面向对象编程语言与关系型数据库模式(与表格一起工作)之间的阻抗不匹配。著名的例子有 Entity Framework 和 NHibernate。

ORM 提供了一层抽象,使得在存储和检索数据库中的对象时,无需关心底层的表结构。为了自动映射对象到表,或者反向操作,ORM 通常内建有用于描述数据库模式、对应对象模型及它们之间映射的标记语言功能。大多数情况下,这些内容无需手动编写。它们通常可以从对象模型或现有数据库生成,且它们之间的映射通常是通过约定生成或在可视化编辑器中绘制的。

尽管这些措施使当前的数据库架构可以作为代码进行定义,但仅此并不能帮助应对架构变化。要以代码处理架构变化,有两种常见的方式。第一种方法描述了每一次变化的代码;第二种方法则仅在代码中描述架构的最新版本。这两种方法分别被称为基于迁移和基于状态的方法。两者都可以依赖第三方工具将变更应用到数据库中。

迁移

第一种方法基于保持一组必须应用到数据库的有序变更。这些变更通常被称为迁移,它们可以通过如 Microsoft Entity Framework 或 Redgate SQL Change Automation 等工具生成,也可以手动编写。

工具可以根据当前数据库架构和源代码控制中新的架构定义的比较,自动生成迁移脚本。这被称为脚手架搭建。工具生成的脚本并不总是完美的,可以通过应用程序员的领域知识进行改进。一旦生成或编写了一个或多个新的迁移,它们可以通过选定的工具应用到数据库中。以下是一个展示该过程如何工作的图示:

图 9.1 – 迁移方法

图 9.1 – 迁移方法

在这里,我们看到一系列不断增长的迁移,从m1m4,它们用于描述数据库的增量变化。为了将数据库更新到最新版本,首先确定最新应用的迁移,然后依次添加其后的所有迁移。

当手动编辑迁移脚本时,以下几点需要注意:

  • 迁移脚本应该是有序的。迁移描述了需要执行的 SQL 语句,以便将数据库从版本x迁移到版本x+1。只有在完成当前步骤后,才能开始下一个迁移。

  • 迁移脚本不仅应迁移架构,还应迁移数据。这可能意味着在迁移之间需要进行一些步骤。例如,将两个列移动到另一个表通常意味着首先创建新列,然后将旧列的数据填充到新列中,最后再移除旧列。

  • 建议在迁移脚本中包含所有数据库对象。额外的索引和约束不仅要应用于生产数据库,还应应用于测试环境。通过迁移,已经有了从源代码控制传递这些变更的机制。将这些内容包含在相同的迁移脚本中,还能确保索引和约束按照相同的顺序应用,并且不会因仅存在于生产环境中而意外阻塞迁移。

  • 如果可能,迁移脚本应该具有幂等性。如果出现问题或怀疑有问题,能够重新运行最后一次迁移是确保迁移已完全应用的好方法。

这种方法的一个缺点是生成和应用迁移时强制要求的严格顺序。这使得将这种方法集成到依赖于分支使用的开发工作流中变得困难。

在不同分支中创建的迁移,在稍后合并时可能会打乱迁移的顺序,或者更糟糕的是,合并了迁移路径的分裂。例如,假设在现有迁移a之后,在两个不同分支中分别创建了迁移bc。这些迁移将如何合并?无论是顺序a, b, c,还是a, c, b,都不正确,因为bc都是在a之后直接执行的。修复此错误的唯一方法是执行以下步骤:

  1. 移除所有迁移,除了第一个新的迁移,在这种情况下是c

  2. 将所有其他迁移应用到尚未应用任何新迁移的数据库上,在这种情况下,如果a已经应用,则仅应用b,或者同时应用ab

  3. 为其他迁移生成新的迁移,在这种情况下,c的替代迁移。

这种方法的一个优点是,每个单独的架构变更都会以相同的方式部署到数据库中。无论是否同时将一个或多个迁移应用到生产数据库,它们仍然会按可预测的顺序逐个执行,并以与它们在测试环境中执行时相同的方式运行,即使它们是在测试环境中逐个应用的。

最终状态

管理架构变更的另一种方法是,不跟踪单独的变更(或迁移),而仅存储架构的最新版本在源代码控制中。然后使用外部工具,如 Microsoft Visual Studio 和 Redgate 的 SQL Data Compare 工具,来比较源代码控制中的当前架构与数据库的实际架构,生成迁移脚本,并在运行时应用这些迁移脚本。迁移脚本不被存储,只能使用一次。

与编写迁移不同,手动执行此类任务是不可行的。虽然手动在源代码控制中跟踪架构的最新版本是可以管理的,但对于最终状态方法来说,这是不可行的。生成迁移脚本时,需要比较现有架构与新架构,并应用此迁移脚本,这只能通过工具来完成。适用的工具示例有 Redgate SQL Source Control 和 SQL Server Data Tools。以下图表展示了这些工具的工作原理:

图 9.2 – 管理架构变更

图 9.2 – 管理架构变更

在这里,我们看到如何比较当前的实际数据库模式和期望的数据库模式的描述,以生成升级并直接应用一个脚本,从而进行必要的更改,使实际模式与期望的模式一致。

这种方法的一个优势是,生成的脚本不需要按特定顺序执行。因此,这种方法可以轻松地与广泛的分支模式结合使用,在这种模式中,更改随着时间的推移慢慢集成。此外,它还消除了在简单场景中手动编写迁移脚本的需求,例如添加或删除列、表或索引。

这种方法的缺点是,它使得处理需要数据操作的更改变得更加困难。再举个例子,假设需要将两列移到另一个表中。由于工具只执行新的模式,这样会导致数据丢失,如果没有进一步的干预。

为了规避这个问题,一个可能的干预方式是向模式包中添加部署前后脚本。在部署前脚本中,将当前数据暂存到临时表中。然后,在应用新模式后,数据将从临时表复制到部署后脚本中的新位置。

本节内容讲述了如何以一种可以存储在源代码管理中的格式管理数据库模式更改。下一节将讨论如何在部署时捕捉这些更改并应用到数据库。

应用数据库模式更改

在数据库模式和(可选的)在源代码管理中定义的一系列迁移准备好后,就可以开始考虑何时应用这些更改到数据库模式中。可以通过两种方法来实现。数据库模式更改可以在新应用版本部署之前应用,或者由应用程序代码本身进行。

作为发布的一部分进行升级

应用数据库更改的第一种方法是作为发布流水线的一部分。在这种情况下,负责读取和执行迁移脚本的工具是通过流水线中的一个步骤调用的。

这个调用可以通过 PowerShell 或其他脚本语言中的自定义脚本来完成。然而,这种方法容易出错,而且每次工具发生变化时,脚本可能需要更新。幸运的是,对于大多数基于迁移的工具,Azure Pipelines 任务已经准备好,可以直接从发布启动迁移。

例如,有一个 Azure Pipelines 扩展可用于直接从定义迁移的 dll 文件将 Entity Framework Core 迁移应用到数据库。可以将此任务添加到发布流水线中,以便在新应用代码部署之前更新数据库。在本章最后的进一步阅读部分提供了构建与发布工具扩展的链接。

另一种变体是将应用程序的构建阶段和发布阶段分开。在这种情况下,迁移脚本作为单独的构建产物导出,可以直接从源代码导出(如果是用 SQL 编写的),或者在执行生成必要 SQL 脚本的工具之后导出作为输出。然后,这个产物会在发布阶段再次下载,在该阶段,它通过 Azure Pipelines 任务来执行 SQL 并应用到数据库中。

与应用程序代码一起升级

不必从发布管道中应用模式更改,它们也可以由应用程序本身来应用。一些内建迁移支持的 ORM 具有自动检测数据库模式是否与最新迁移匹配的功能。如果不匹配,它们可以自动将数据库模式迁移到最新版本。

支持此功能的一个 ORM 示例是 Entity Framework。Entity Framework 的核心版本没有内建的自动迁移支持。在 Entity Framework Core 中,可以通过一行应用程序代码来启动一个适合应用程序的升级时机。执行该操作的代码如下所示:

using (var context = new MyContext(...))
{
    context.Database.Migrate();
}

这种方法的优点是启用非常简单。例如,在 Entity Framework 的配置中只需一个布尔开关就可以启用此工作流。然而,缺点是大多数支持此功能的 ORM 会对数据库施加全局锁——在迁移运行时会停止所有数据库事务。对于任何需要几秒钟以上时间的迁移或迁移集合,这种方法可能不切实际。

这种方法通常仅用于基于迁移的方法。使用最终状态方法的方式需要一个外部第三方工具,用来生成必要的迁移脚本并应用它们。这通常是在发布管道中完成的,而不是应用程序内部进行的。

添加一个过程

正如前一部分所说明的那样,考虑何时以及如何应用数据库模式的更改或使用该模式的应用程序(或多个应用程序)的更改非常重要。但是,无论模式更改和代码部署的部署计划如何,总会有一段时间,其中以下一种情况是成立的:

  • 新的应用程序代码已经在运行,而模式更改尚未应用或正在应用过程中。

  • 旧的应用程序代码仍然在运行,而模式更改已经应用或正在应用过程中。

  • 在应用模式更改时,应用程序代码不会运行。

第三种情况是极其不理想的。这通常适用于所有情况,但在进行 DevOps 实践时尤为重要。如果更改频繁且在工作时间内发布,每次进行模式更改时都停机是不可接受的。

为了防止在应用架构变更时需要停机,必须满足以下条件之一:

  • 架构变更向后兼容,使得旧版本的应用程序代码可以在架构变更已经应用或正在应用的数据库上无错误地运行。

  • 新的应用程序代码向后兼容,可以同时在旧版本和新版本的架构下运行。

满足第一个条件确保在应用架构变更时,旧版应用程序代码可以继续运行。满足第二个条件确保新版本的应用程序代码可以首先部署,并且一旦完成部署,数据库可以在代码运行时进行升级。虽然这两种方法都可行,但通常希望优先实现第一个条件。原因在于,架构变更通常会支持应用程序代码的更改。

这意味着以下是一个安全的架构变更部署过程,无需停机:

  1. 创建一个新的数据库。

  2. 应用数据库更改。

  3. 验证更改是否已正确应用,或者中止部署流程。

  4. 部署新的应用程序代码。

重要的是要意识到,这一过程假设是“向前失败”。这意味着,如果架构变更的部署出现问题,应在继续进行代码更改之前解决这些问题。

最后,满足架构变更的向后兼容性条件有时可能无法完成。如果是这种情况,通常可以将更改拆分成两个部分更改,这两个部分合起来实现相同的最终结果,同时都符合向后兼容性条件。例如,重命名一个属性,或者将存储距离的单位从英尺改为米,可以按以下方式执行:

  1. 生成一个迁移,将一个新列添加到数据库表中,存储以米为单位的距离。

  2. 添加应用程序代码,该代码从旧列读取数据,但同时写入两个列。

  3. 将这些更改部署到生产环境。

  4. 添加一个新的迁移,将数据从旧列迁移到新列,适用于所有新列尚未填充但旧列已填充的情况。

  5. 更新应用程序代码,仅读取和写入新列。

  6. 将这些更改部署到生产环境。

  7. 添加一个新的迁移,删除旧列。

使用正确的工具和适当的流程,可以有效且安全地执行架构变更的部署。在接下来的部分中,将介绍另一种使用无架构数据库的方法。

使用无架构方法

在前面的部分中,重点是关系数据库,其中对每个表都应用严格的模式。另一种完全不同的数据库模式管理方法是不使用数据库模式。这可以通过使用无模式或文档数据库来实现。一个著名的无模式数据库例子是 Azure Cosmos DB。这些数据库可以将不同形式的文档存储在同一个“表”中。这里的“表”是指这些数据库通常不使用“表”这个术语,而称其为数据库、容器或集合。

由于这些数据库可以在同一个集合中存储具有不同模式的文档,从数据库的角度来看,模式变化不再存在。但当然,随着时间的推移,应用程序代码中相应对象的结构会发生变化。要了解如何处理这一点,最好区分将对象存储到数据库和从数据库读取的操作。

写入对象到数据库

存储在无模式数据库中的文档通常是应用程序代码中对象的序列化。当使用关系数据库时,这些对象通常通过 ORM 存储,例如 Entity Framework、Dapper 或 NHibernate。当使用文档数据库时,这些对象通常会被序列化并存储到数据库中。序列化是将对象转换为字节流的过程,以便能够轻松保存或跨进程边界传输。反序列化是从字节流构建数据结构或对象的逆过程。这意味着代码对象定义的变化也会导致保存该对象时文档结构的变化。由于文档数据库的特性,这种方式完全适用。

作为示例,考虑以下 C# 类及其 JSON 表示。

这段代码使用了 JsonConstructor 特性来指示在反序列化过程中应该使用类的构造函数来创建类的实例:

public class Person
{
   [JsonConstructor] private Person() {}
   public Person(string name) {
      Name = name ?? throw new ArgumentNullException();
   }

以下代码显示了将 Person 类实例序列化为文档数据库后的 JSON 表示:

{
   "Name": "Mark Anderson"
}

在该代码在生产环境中运行一段时间后,成千上万的人得到了救助,出现了一个新需求。除了记录人的姓名外,还必须记录他们居住的城市。为此,Person 类扩展了一个新的属性。完成此更改并部署新代码后,每当保存一个人时,以下代码将被使用:

public class Person
{
   [JsonConstructor] private Person() {}
   public Person(string name, string city) { 
   Name = name ?? throw new ArgumentNullException();
   City = city ?? throw new ArgumentNullException();
   }
   [JsonProperty]
   public string Name { get; private set; } [JsonProperty]
   public string City { get; private set; }
}

Person 类的定义已发生变化;新实例的相应 JSON 表示如以下代码所示。两种文档变体可以保存在同一个集合中:

{
   "Name": "Mark Anderson",
   "City": "Amsterdam"
}

这表明,从写入信息到数据库的角度来看,无模式的方式非常方便,因为开发人员根本不需要考虑模式变更管理。

从数据库中读取对象

虽然无模式数据库使得将不同形式的文档轻松写入同一集合变得非常容易,但这在从该集合中读取文档并将其反序列化时可能会带来问题。实际上,模式管理的问题并没有被消除,而是推迟到了稍后的时间点。

继续上一节的示例,在新 C# Person类定义中反序列化第一个保存的人员时,城市属性将得到一个空值。这可能是意料之外的,因为 C#代码保证没有城市的人永远无法构造。这是无模式数据库所带来的挑战的一个明确示例。

在这个例子中,可以通过将Person类更新为以下形式来避免这个问题:

public class Person
{
   [JsonConstructor] 
   private Person() {}
   public Person(string name, string city) {
      Name = name ?? throw new ArgumentNullException(); 
      City = city ?? throw new ArgumentNullException();
   }
   [JsonProperty]
   public string Name { get; private set; }
   [JsonIgnore]
   private string _city;
   [JsonProperty] 
   public string City {
      get { return _city; }
      private set { _city = value ?? _city = string.Empty}
   }
}

除了这种添加属性的场景外,还有许多其他场景需要调整 C#类以处理反序列化场景。以下是一些示例:

  • 添加原始类型的属性

  • 添加一个复杂属性、另一个对象或数组

  • 重命名一个属性

  • 用复杂属性替换原始类型的属性

  • 将可为空属性转换为非空属性

在对象中添加处理这些情况的代码会增加代码库的大小和复杂性,并使主代码库充斥着应对过去情形的能力。特别是当这种情况频繁发生时,可能会导致代码库中出现不必要的复杂性。为了防止这种情况,可以在每次对象模式更改时,按照以下流程进行处理:

  1. 更改对象的模式,确保仅添加了属性。即使目标是删除属性,在这个阶段,也只是添加了具有新名称的属性。

  2. 在对象中实现逻辑,以应对旧版本对象的反序列化。

  3. 部署对象的新版本。

  4. 启动一个后台进程,从数据库中逐一加载该类型的所有对象,并将它们保存回数据库。

  5. 一旦后台进程处理完所有现有实体,就从代码库中删除负责应对反序列化期间模式变化的代码,以及任何不再使用的属性。

使用这种方法,所有更改将在一段时间内传播到对象的所有存储版本。此方法的缺点是对象结构的更改分布在两个必须分别部署的更改中。此外,第二次更改的部署必须等到数据库中的所有对象都已转换完成。

其他方法和注意事项

除了之前讨论的更常见的方法,以下提示和方法可能有助于减少处理数据库时的工作量,或帮助减少与数据库变更相关的风险。

最小化数据库的影响

处理数据库的第一步可以是减少需要进行数据库变更的可能性。在许多数据库中,可以编写存储过程——或者其他一些在数据库引擎中执行的代码或脚本。虽然存储过程带来了一些好处,但更改它们也可能算作数据库模式变更,或者至少会导致一些难以测试的变更。

一种简单的方法是将存储过程替换为应用程序代码,使用功能切换可以更轻松地进行并行变更。

完全并行部署

在高风险环境中,或与脆弱数据库一起工作时,还可以采取另一种数据库模式变更方法。此方法基于应用功能切换和蓝绿部署模式,步骤如下:

  1. 修改应用程序代码,使其不仅将更新写入一个数据库,而是写入两个数据库。

  2. 在生产环境中,创建现有数据库的完整副本,并配置应用程序代码将所有更改写入两个数据库。从现在开始,这两个数据库将分别称为数据库和数据库。

  3. 仅在写入新数据库的路径中引入所需的数据库模式和应用程序代码变更。

  4. 在所有读取数据的代码路径中引入必要的变更,确保所有查询都同时在两个数据库中运行。

  5. 更新应用程序代码,以检测新旧数据库之间查询结果的差异,并在发现任何不一致时记录错误。

  6. 如果变更没有问题,删除旧数据库和应用程序代码中的旧读写访问路径。

  7. 如果变更运行时出现错误,修复问题。然后,通过恢复预定新数据库的备份来重新启动,并从第 5 步继续。

这种方法的优点是非常轻量级。缺点是它涉及的工作量很大,需要很多努力,而且成本更高。此外,还应考虑额外的数据库费用以及备份和恢复操作的持续时间。

测试数据库变更

与应用程序代码一样,数据库模式变更的质量可以通过测试获得洞察。关于如何对数据库模式进行测试的链接可以在本章末尾找到。

在大多数情况下,为了充分覆盖数据库变更带来的风险,需要执行针对完整应用程序堆栈的系统测试。这种测试可以覆盖由错误模式、无效存储过程以及数据库和应用程序代码不匹配引起的大多数风险。

总结

在本章中,你已经学习了如何使用源代码控制来管理数据库模式及其变更。你了解了基于迁移的方法和基于状态的方法来存储变更,以及如何以安全的方式将它们应用到生产数据库中。

此外,您已经了解了无模式数据库如何减轻传统模式管理的负担。然而,这也意味着在从数据库中读取旧版本的对象时,必须应对模式差异的问题。

在下一章,您将学习关于持续测试的内容。您不仅会了解测试技术,还会了解在何时应用哪些技术,以及测试如何成为 DevOps 中至关重要的一部分,并且是持续向最终用户交付价值的关键推动力。

问题

在我们结束时,下面是一些问题供您测试自己对本章内容的理解。您可以在附录中的评估部分找到答案:

  1. 判断题:使用 Entity Framework 时,模式管理是通过基于迁移的支持内置的。

  2. 判断题:使用基于迁移的方法进行模式管理时,您不需要在数据库模式中添加额外的跟踪表。

  3. 判断题:使用基于最终状态的方法进行模式管理时,您不需要在数据库模式中添加额外的跟踪表。

  4. 完整并行的数据库模式变更方法有哪些好处?(选择多个答案):

    1. 风险几乎降到零。

    2. 您可以在类似生产环境中衡量变更的实际性能影响。

    3. 并行迁移可以减少周期时间。

  5. 判断题:无模式数据库完全消除了考虑模式变更的需要。

  6. 您可以选择什么技术来限制变更对数据库模式的影响?

进一步阅读

第十章:整合持续测试

在前面的章节中,你了解了不同的技术,这些技术有助于提高你将变更交付到生产环境的速度。如果你已经在日常工作中使用这些技术,你会很快发现,这只有在你的工作质量足够高时才可能实现。如果你的工作质量不够高,你将面临许多故障或问题,最终用户也不会满意。要取得成功,提高变更的速度和提高工作质量必须齐头并进。要评估并提高工作质量,你首先需要知道什么是质量。这就是测试的作用所在。测试是报告软件质量的学科。

为了引入测试的主题,本章将首先讨论如何衡量软件开发的质量。之后,将深入探讨功能测试的主题。在本章的进展过程中,我们将探讨不同的测试策略,以确定需要哪些类型的测试,以及每种测试应使用多少次。之后,我们将逐一讨论不同类型的测试。你将了解它们的工作原理、测试内容以及每种测试的优缺点。最后,最后一部分将重点讨论如何利用你的管道生成和收集的所有指标和测试结果,持续报告团队工作的质量,甚至防止质量不足的变更传播到用户。所有这些都将帮助你保持软件的高质量,并使你能够自信地快速频繁地交付软件。

本章将涵盖以下主题:

  • 定义质量

  • 理解测试类型

  • 在管道中执行测试

  • 维护质量

技术要求

要实验本章中描述的技术,你可能需要以下一项或多项:

  • 一个具有构建和发布管道及仪表板访问权限的 Azure DevOps 项目

  • Visual Studio Code、Visual Studio 2019 或 Visual Studio 2022

  • Azure DevOps 的 Basic + 测试计划许可

  • 一个 SonarCloud 订阅

所有这些都可以免费获得,或在有限的试用期内免费提供。

定义质量

第一章中讨论的 DevOps 思维模式的主要目标之一是增加价值流向最终用户。为了做到这一点,软件必须频繁部署,甚至可能一天多次部署。要使频繁部署成为可能,有两个重要因素:自动化和质量。自动化在前面的章节中已经讨论过了,因此现在是时候转向质量的主题了。

一旦自动构建和发布流水线就绪,且更改开始以越来越快的速度流向生产环境,就该开始衡量这些更改的质量了。更重要的是,这使我们能够中止质量不够高的更改。

实际上,什么构成足够的质量可能因项目而异。在创建游戏时,一些小错误可能对用户造成困扰,但不会造成其他影响。而在为飞机或医疗用途开发软件时,一个错误可能会危及生命。在软件开发中,较高的质量通常更昂贵且/或需要更多时间。因此,我们在交付功能的数量和能够保证的质量之间存在权衡。每个项目都有不同的最佳权衡点。

在质量得以衡量之前,首先要确定如何衡量软件的质量。监控软件质量的常见方法是收集一个或多个度量。例如,可以决定每周收集五个度量值。随着时间的推移,将这些度量值绘制成图表,可以深入了解软件质量如何变化。一个例子可能像这里显示的图表那样:

图 10.1 – 软件质量度量示例

图 10.1 – 软件质量度量示例

接下来的部分讨论了几个度量示例。

质量度量

度量是捕捉某些内容的手段,这些内容通过数字来表示。在软件开发中,度量通常用于表示一个特定的质量方面,这个方面本身可能很难量化。例如,一段软件的质量可能很难单独描述。这一点对于质量变化的描述尤为明显。出于这个原因,我们通常会收集多个数字,综合来看它们能反映软件的质量。

需要意识到,度量是一个很好的工具,但应该谨慎使用。一方面,可能有比正在测量的度量值更多的因素影响软件的(感知)质量。另外,一旦人们知道某个特定的度量值被记录下来,他们可能会优化工作方式以提高或降低该度量值。虽然这可能在报告中显示出期望的数字,但并不一定意味着软件质量真正得到了改善。为了解决这个问题,通常会记录多个度量值。

一个广为人知的例子是在敏捷工作环境中使用故事点速度。记录团队的迭代速度,以观察其是否随着时间变得更加高效,这听起来很有效;然而,如果团队规模在每次迭代中都不同,那么这个度量可能就没什么用处,因为团队成员的出席情况会影响速度。此外,团队可以轻松地通过每次迭代时将所有估算值乘以一个随机数来伪造这个度量。尽管这样每次迭代的数字会增加,但它与团队的生产力增长并无关系。

进入到衡量软件质量的指标时,客观地衡量编写代码的质量可能会很困难。开发人员通常对什么构成优质代码有许多看法,讨论越多,团队越难达成共识;然而,当我们将注意力转向使用这些代码后产生的结果时,就更容易识别出可以帮助提供代码质量洞察的指标。

下面是一些例子:

  • 集成构建失败的百分比:如果代码无法编译或无法通过自动化测试,这表明代码的质量不足。由于测试可以通过构建流水线在每次推送新变化时自动执行,它们是判断代码质量的绝佳工具。而且,由于它们可以在将变更部署到生产环境之前执行并收集结果,这些结果可以用来取消那些质量不足或可能影响某些系统功能的变更,从而在发布流水线的下一个阶段前阻止这些问题。这种方式保证了只有质量足够的变更才会传递到下一个阶段。

  • 自动化测试覆盖的代码百分比:如果大部分代码都通过单元测试进行测试,这将提高软件的质量。

  • 变更失败率:这是指部署新版本代码后导致问题的百分比。一个例子是应用程序的新版本部署后,Web 服务器内存耗尽的情况。

  • 计划外工作的数量:在任何一段时间内必须执行的计划外工作的数量是衡量质量的一个重要指标。如果团队正在创建一个同时在运行的 SaaS 产品,那么就会有时间花费在运营职责上。这通常被称为计划外工作。计划外工作的数量可以反映计划工作质量的情况。如果计划外工作的数量增加,可能是因为质量下降了。计划外工作的例子包括现场故障事件、跟进警报、热修复和补丁。

  • 用户报告的缺陷数量:如果用户报告的错误数量增加,这可能是质量下降的迹象。通常,这是一种滞后性指标,一旦这个数字开始上升,质量可能已经在一段时间内下滑了。当然,这个数字增加可能有很多其他原因,比如新的操作系统,用户数量的增加,或用户期望的变化。

  • 已知问题的数量:即使发现或报告的新缺陷很少,如果缺陷从未被修复,并且已知问题的数量一直在缓慢增加,那么软件的质量将随着时间的推移而逐渐下降。

  • 技术债务的量:技术债务是一个术语,用来描述为短期利益(例如快速交付代码)而牺牲代码质量的后果。技术债务将在下一节中详细讨论。

测试是一项活动,旨在发现并报告软件的质量。测试结果(对质量的洞察)可以用来决定是否允许或取消某个更改进入下一个发布阶段。

在下一部分,将探讨质量的另一个维度:代码库中的技术债务量。

技术债务

技术债务是一个术语,用来描述为追求其他目标而牺牲代码质量所带来的未来成本。例如,为了加快新特性的交付,开发者可能选择快速扩展现有类并添加一些新方法。如果由此产生的类不符合面向对象设计原则,或者变得过于庞大,那么这个类可能会变得难以理解、维护或以后进行更改。术语债务意味着某种东西(时间、质量、注意力或工作)在解决方案中仍然欠缺。只要这笔债务没有偿还,你就必须支付“利息”,即所有其他工作的进度都会稍微放慢。

技术债务有许多形式,以下是一些示例:

  • 没有任何单元测试覆盖的代码,其中对该代码的实现进行更改时无法使用原始测试来验证

  • 没有以自解释的方式编写的代码,即没有使用有意义的变量和方法名称

  • 不遵循编码原则的代码,例如 KISS、YAGNI、DRY 和/或 SOLID

  • 类因为变量和方法太多而变得过于复杂

  • 方法因为语句太多(尤其是流程控制语句)而变得过于复杂

  • 在应用程序的不同部分之间存在循环依赖的类或命名空间

  • 不遵循应用程序架构设计的类

技术债务有许多形式,监督所有这些形式可能会让人感到艰难。因此,市面上有很多工具可以自动衡量代码库中的技术债务并报告它。处理这些问题的工具将在维护质量一节中讨论。

虽然技术债务通常被认为是一件坏事,但有时故意制造技术债务也可能有正当理由。就像常规债务一样,管理债务的规模并确保能够支付利息并偿还债务是非常重要的。

公司在初创阶段通常会承担技术债务,这往往是为了快速创建一个可用的解决方案而做出的有意识决定。虽然这个初始版本用于验证商业提案并吸引资金,但开发者可以通过重新实现或重构(部分)应用程序来偿还这笔债务。

另一个原因可能是市场机会或已提前几个月计划的重要商业事件。为了按时交付并满足截止日期,承担一定的技术债务可能是值得的。

然而,如果从未偿还债务,而只是随着时间推移不断增加更多债务,这也会增加隐喻上的利息,每次开发者需要进行更改时都需要支付。结果是,任何更改都将比上一次花费更多时间。如果这种情况开始发生,最终会不可避免地出现某个时候任何更改都不再值得进行,因为成本总是大于收益。在这一点上,项目或产品就失败了。

在谈论测试时,理解存在哪些类型的测试是非常重要的。下一节将深入探讨这个话题。

理解测试类型

在传统的软件开发中,测试通常在开发完成应用程序被声明为开发完成功能集被冻结或类似的情况下执行。在宣布开发完成后进行测试,通常会进入一个长时间的测试与修复 bug 往返周期。结果通常是,在上线后仍然发现许多 bug。

向左移动(Shifting left)是一个测试原则,指的是应该在开发过程的早期进行自动化测试。如果将与软件开发相关的所有活动绘制成从初始到发布的时间线,那么向左移动意味着将自动化测试活动更靠近初始阶段。

为了做到这一点,已经认识到许多不同类型的测试——例如单元测试、集成测试和系统测试。不同的来源可能会建议不同类型的测试,但这些是一些比较知名的类型。不管测试的具体名称是什么,从高度抽象的层面来看,它们通常被分为以下两类:

  • 功能测试:功能测试用于测试应用程序是否实际实现了所需的功能。

  • 非功能测试:非功能测试用于验证应用程序的其他期望属性是否得以实现,并确保没有出现不期望的属性。

这些类型进一步细分为更小的子类别,如下图所示:

图 10.2 – 理解测试类型

图 10.2 – 理解测试类型

接下来的三节内容简要回顾了不同类型的功能性测试和非功能性测试。这是为了便于后续讨论在不同情况下选择哪种测试类型以及项目可能需要多少种测试。

自动化功能测试的类型

在谈论自动化功能测试时,三种最常用的类型是单元测试、集成测试和系统测试。这些测试类型可以从多个维度进行比较:创建测试所需的时间、执行测试所需的时间以及它们测试的范围:

  • 单元测试:单元测试是最快编写的,它们的执行速度非常快,通常在不到一毫秒的时间内完成。它们测试的是应用程序中最小的范围,通常是单个类或方法。这意味着,一旦编写完成,几乎不需要更改单元测试。对于许多系统来说,更有可能的是测试会被删除,而不是被修改。

  • 集成测试:集成测试需要更多的时间来编写,因为它们涉及多个必须设置以便协同工作的单元。这些测试的执行仍然应该很快,通常从不到一秒到几十秒不等。集成测试的测试范围更大,这意味着,作为回报,它们将覆盖更多的代码部分,更有可能检测到因更改而引入的缺陷。

  • 系统测试:系统测试测试的是一个完全组装并正在运行的应用程序。根据应用程序的类型,这些通常是 API 测试或自动化 UI 测试。由于这些测试依赖于已部署的系统运行,并且通常需要在数据库或其他持久存储中设置初始状态,所以创建这些测试需要大量时间。它们的执行时间很长,有时每个测试需要几分钟。与单元测试和集成测试相比,系统测试的可靠性较差且更脆弱。即使是接口的小改动,也可能导致一系列测试失败。另一方面,系统测试能够检测到单元测试和集成测试无法检测的错误,因为它们实际测试的是正在运行的系统。

重要说明

请注意,测试中拥有较大测试范围既有优点也有缺点。优点是它可以检测到许多错误。缺点是,当测试范围非常大时,测试失败只提供有限的关于错误发生原因的信息。此类测试失败通常需要比小范围测试失败更多的调查。

以下部分将更详细地探讨每种测试类型。

单元测试

单元测试用于测试单个单元的独立性。在面向对象编程语言中,这通常意味着为应用程序中的每个类编写一个测试类。为了实现完全的测试覆盖,测试类将为对应应用程序类的每个公共方法编写一个或多个测试。

单元测试应该运行得非常快——平均来说,应该在几毫秒内完成。为了实现这一点,每个类都是在没有其依赖项的情况下实例化的。这是通过使用接口来实现的,类依赖接口,而不是直接依赖其他类。在测试中,依赖项会被模拟类替代,如下图所示。左侧显示的是运行时配置,右侧显示的是测试期间的配置:

图 10.3 – 接口测试

图 10.3 – 接口测试

一个模拟类实现相同的接口,但默认没有任何行为。可以根据每个测试设置特定的行为。模拟还可以用于验证是否调用了依赖项上的某些操作或函数。举个例子,以下是一个 C#类:

public class WorkDivider
{
   private readonly IMessageSender _messageSender;
   public WorkDivider(IMessageSender messageSender)
   {
      _messageSender = messageSender;
   }
   public void DivideWork(IEnumerable<WorkOrder> workOrders)
   {
      foreach(var workOrder in workOrders)
      {
         _messageSender.SendMessage(workOrder.GetMessage());
      }
   }
}

要在自动化测试中实例化此类,需要实现IMessageSender接口。为了绕过这个依赖,可以使用像 Moq 这样的模拟框架来测试WorkDivider,如下所示。在这些示例中,NUnit作为测试框架:

[TestFixture]
public class WorkDividerTest
{
   private Mock<IMessageSender> _messageSender; 
   private WorkDivider _subject;
   [SetUp]
   public void SetUp()
   {
      _messageSender = new Mock<IMessageSender>();
      _subject = new WorkDivider(_messageSender.Object);
   }
   [Test] 
   public void
WhenSendingAnEnumerableOfWorkingOrders_EverOrderIsSendToTheMessageSender()
   {
      var workOrder = new WorkOrder();
      _subject.DivideWork(new[] { workOrder });
      _messageSender.Verify(x => x.SendMessage(workOrder), Times.Once);
   }
}

这意味着无法为与其他系统(如数据库、缓存或消息队列)交互的类编写单元测试。为了确保这不会使覆盖应用程序的大部分部分变得不可能,通常的做法是将与其他系统的集成隔离在单独的类中。这些类包含与远程系统的交互,但不包含业务逻辑,并且尽可能少的代码。然后,接受这些类不包含在单元测试中的事实。通常用于实现这一点的设计模式有外观模式(Facade)、适配器模式(Adapter)和仓库模式(Repository)。

提示

本章末尾附有更详细的关于编写单元测试和如何模拟类的指南链接。

单元测试应该能够在每个开发者克隆应用程序代码库的计算机上运行。它们不应要求在本地计算机上进行任何特殊配置或设置,并且应随时可以运行。这样,所有与代码库合作的人员都可以在本地计算机上运行单元测试。因此,开发者在推送更改到中央代码库之前,应该在自己的计算机上运行所有单元测试,这是一个很好的实践。

除了本地验证步骤,单元测试还应该成为持续集成构建的一部分。稍后你将学习如何在管道中执行测试的部分完成这一过程。只要拉取请求中有失败的单元测试,就最好不要将更改合并到主分支。这甚至可以通过使用 Git 仓库分支策略来实现,正如在第四章中讨论的,一切从源代码管理开始

在下一节中,自动化功能测试的讨论将继续进行,重点是集成测试。

集成测试

集成测试用于验证一组组件是否能正确地协同工作。这些测试有两个目的:

  • 增加应用程序中未被单元测试覆盖的部分的测试覆盖率,例如与其他系统交互的类。

  • 解决单元测试未覆盖的风险,并在各个组件与其他系统交互时,减少风险以验证正确的预期结果。

理解可能存在的集成风险可能比较困难。通常假设如果各个部分或组件按其规格工作,那么整个解决方案也必定能正常运作。然而,事实并非总是如此,很多问题在单元集成后才显现出来。为了更好地理解这个风险,假设有两个组件共同负责气候控制。一个组件通过摄氏度来测量温度,另一个则基于温度做出反应,期待输入的是华氏度。很快就会发现,尽管这两个组件按预期工作,交换数字并根据这些数字采取行动,但它们的组合并不会产生预期的结果。

集成测试,尤其是那些与其他系统交互的测试,将不仅比单元测试运行得更慢,而且通常需要更多的设置或配置。这甚至可能包括用户名、密码或证书等机密信息。为处理此类配置,可以在测试旁边创建一个设置文件,并在执行测试之前从中加载设置。每个开发人员可以创建自己的该文件副本,并使用自己的配置运行测试。

继续使用上一节的示例,假设实现了IMessageSender接口的MessageSender类需要一个连接字符串来执行工作。MessageSender的测试类可能如下所示:

[TestFixture]
public class MessageSenderTest
{
   private MessageSender _messageSender;
   [SetUp]
   public void SetUp()
   {
      var connectionString = TestContext.Parameters["MessageSenderConnectionString"];
      _messageSender = new MessageSender(connectionString);
   }
}

connectionString,用于构建MessageSender类,来自TestContext上的Parameters对象。这是可用的.runsettings文件。具体方法可能会因测试框架而异。一个示例的.runsettings文件如下所示:

<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
 <TestRunParameters>
 <Parameter name="MessageSenderConnectionString" value="secret-value" />
 </TestRunParameters>
</RunSettings>

将设置移到单独的文件中,确保机密信息不会被提交到源代码控制中。在在流水线中执行测试一节中,你将学习如何为在流水线中运行测试构建.runsettings文件。

这是因为,如果可能的话,集成测试也应该是持续集成构建的一部分。然而,这也存在一个风险,那就是可能会使持续集成构建变得过于缓慢。为了解决这个问题,可以实施以下解决方案之一:

  • 集成测试在与持续集成构建并行触发的单独构建中执行。通过这种方式,持续集成构建的时长保持较低,同时集成测试仍然会被持续执行,开发人员能够快速获得反馈。

  • 集成测试会在管道的后期执行,接近软件发布时执行——例如,在部署到测试环境之前或之后。

第一种方法的缺点是,以这种方式执行集成测试意味着测试将不再作为质量门控,在代码合并到主分支之前进行检查。当然,它们仍会继续作为质量报告机制工作。这意味着,虽然错误可能已经合并,但它们会被构建过程检测并报告。

第二种方法没有这种风险,因为执行测试仍然是从源代码管理到生产的管道的一部分;然而,在这种方法中,测试的执行可能会推迟到稍后的时间点,前提是并非每个构建都至少进入了部分发布管道。这意味着缺陷可能会稍后显现,从而延长了检测和修复问题之间的时间。

无论采取哪种方法,失败的集成测试将不再阻止合并更改,因此你必须找到另一种方式,确保开发人员会负责修复导致测试失败的缺陷。

这些权衡在系统测试中尤为明显,系统测试通常需要很长时间,因此无法将它们作为持续集成构建的一部分。

系统测试

第三种也是最后一种自动化功能测试类型是系统测试。这些测试是针对一个完全组装并正在运行的应用程序进行的。系统测试有两种类型,取决于应用程序的类型:API 测试或 UI 测试。系统测试可能需要很长时间来执行,而且常常有复杂的测试数据设置,导致测试时间超过一分钟。

提示

你可能会遇到一种叫做编码 UI 测试的东西。这是一个现在已经被废弃的微软解决方案,用于编写 UI 测试。这些测试可以从 Azure Pipelines 中执行。幸运的是,有许多替代方案,详见微软在devblogs.microsoft.com/devops/changes-to-coded-ui-test-in-visual-studio-2019中发布的弃用通知。

系统测试是在正在运行的应用程序上执行的,这意味着在运行之前需要进行配置和设置。应用程序需要在受控环境中运行,并且所有与数据存储的集成都需要完全正常运行。与其他系统的集成要么需要正常运行,要么需要用替代的模拟工具进行替换,以确保所有与这些系统集成的操作能够正常运行。

这些条件使得开发人员在本地机器上执行这些测试的可能性较低,特别是在他们修改应用程序时。只有在创建新测试或更改测试时,他们才可能执行这些测试。然而,即使在这种情况下,他们执行的测试可能并非针对本地运行的应用程序版本,而是针对已经部署到测试环境中的版本。这不一定是好事,但在大多数团队中往往是现实情况。

重要提示

创建 API 或 UI 测试的介绍不幸超出了本书的范围。市场上有许多产品可供选择,最佳的使用产品因项目而异。

在作为管道一部分执行系统测试时,它们通常是在代码至少部署到一个环境后进行的。这通常是测试环境。这意味着系统测试是从源代码更改到生产部署的关键路径的一部分。如果这个路径过长,它们也可以被移出管道。然后,它们按照计划运行——例如,每晚运行一次。就像集成测试一样,这加快了管道的速度,但也失去了将系统测试作为质量门的机会。

系统测试,特别是 UI 测试,通常是脆弱的,在进行微小更改后可能会停止工作。因此,建议将这些测试的数量保持在最低限度;然而,需记住,这些测试可以捕捉到特定错误,如配置错误、其他运行时错误、数据库与应用程序的不匹配或创建错误状态的操作序列。

除了自动化功能测试,还有手动功能测试,这在许多 DevOps 项目中具有价值。将在下一节讨论,但首先介绍一下不稳定的测试。

不稳定的测试

不稳定的测试是指在代码或配置没有明显变化的情况下失败,或者代码在本地机器上运行正常,但在持续集成时失败。经过多次重试后,测试最终会通过。

不稳定的测试是可靠性差的,会对构建质量信心产生负面影响。解决不稳定测试问题的一种方法是将这些测试静默处理,以便持续集成和发布不失败,同时将不可靠的测试结果从自动化测试报告中排除。

手动功能测试的类型

虽然自动化测试是一个快速且频繁获取开发反馈的好工具,但仍有一些内容需要手动测试。虽然自动化重复性测试是持续监控质量的最佳方式,但某些事情仍需要人工检查。

手动测试是向左 shift 的临界点。每当任何类型的测试或验证被 shift 到左侧时,意味着它是在执行手动测试之前进行的。这样做的好处是,这些自动化活动增加了我们对被测试版本的信心,提升了该版本也能通过手动测试的可能性。换句话说,当手动测试开始时,应该很不可能再发现任何新问题。

有两种类型的手动测试:

  • 脚本化测试

  • 探索性测试

这两种测试类型将在接下来的部分中讨论。

脚本化测试

脚本化测试是一种用于在确保全面覆盖所有相关测试用例的同时,最小化测试执行时间的技术。通过将测试分为两个不同的阶段:测试准备和测试执行,来实现这一目标。测试准备与待测试功能的开发并行进行,甚至在开发开始之前就已完成。在测试准备阶段,功能被分析并识别出正式的测试用例。

一旦必须执行的测试用例被识别出来,就会编写手动测试脚本,描述在后续测试执行阶段每个要执行的步骤。这些脚本的编写方式使得它们易于跟随,并且没有任何疑问或困惑的空间。它们的编写方式还确保执行步骤的数量尽可能少。虽然这可能需要更多的准备时间,但所有这些都是为了确保在测试执行过程中花费的时间尽可能少。

测试分析及如何识别测试用例的深入讨论超出了本书的范围。虽然你负责创建测试用例,但 Azure DevOps 会在这方面提供支持。通过使用测试计划服务,你可以创建测试计划并记录其中的测试用例,以便日后快速执行。

要创建新的测试计划,请执行以下步骤:

  1. 打开 Azure 测试计划菜单。

图 10.4 – Azure DevOps 测试计划菜单

图 10.4 – Azure DevOps 测试计划菜单

  1. 在此菜单中,点击测试计划。这里将展示你当前所有测试计划的概览。

  2. 点击新建测试计划按钮开始创建新的测试计划。这将打开一个新的对话框,如下所示:

图 10.5 – Azure DevOps 新测试计划

  1. 给测试计划命名一个有意义的名字,例如,能说明测试计划目的的名称。

  2. 将测试计划链接到正确的产品区域路径。

  3. 选择与此测试相关的正确迭代或冲刺。

  4. 点击创建以完成测试计划的创建。这将自动打开此测试计划,如下所示:

图 10.6 – Azure DevOps 测试套件

图 10.6 – Azure DevOps 测试套件

一个测试计划可以拆分为多个测试套件,而测试套件又可以拆分为子测试套件。从本质上讲,测试套件对于测试就像文件夹对于文件一样。通过点击测试套件上悬停时出现的省略号按钮,可以管理测试套件。这在前面的截图中有展示。

创建测试计划后,就该添加一个或多个测试用例到计划中。为此,请确保测试套件的定义选项卡已打开,然后点击新建测试用例按钮。一个新的弹出窗口将打开。

图 10.7 – Azure DevOps 新建测试用例

图 10.7 – Azure DevOps 新建测试用例

在这里,可以定义测试步骤和预期结果。要定义一个新的测试用例,请执行以下步骤:

  1. 为测试用例输入标题。

  2. 在对话框中,输入一个或多个操作步骤和预期结果,以详细描述测试用例。

  3. 一旦测试用例完全描述,点击保存并关闭按钮以保存测试用例并返回到上一个屏幕,在那里你可以管理测试套件。

一旦准备工作完成,功能准备好进行测试,所有测试就会执行。由于所有测试都有详细脚本,这可以快速而高效地完成。甚至可能有开发人员、业务分析师或其他部门的人员参与测试执行。这意味着测试执行本身将非常快速。

要开始执行测试套件或计划,请执行以下步骤:

  1. 导航到执行选项卡。

图 10.8 – Azure DevOps 执行测试用例

图 10.8 – Azure DevOps 执行测试用例

  1. 选择一个或多个测试用例。

  2. 选择右上角的一个运行选项。

选择针对网页应用程序运行测试时,会打开一个带有测试运行器的新浏览器窗口。此测试运行器可以用来逐个执行所有测试用例,对于每个测试用例,依次执行所有步骤,跟踪所有成功和错误,如下所示:

图 10.9 – Azure DevOps 测试用例步骤

图 10.9 – Azure DevOps 测试用例步骤

每个测试步骤后的勾选标记或叉号可用于跟踪每个步骤的结果。如果某个步骤标记为不正确,可以添加一个带缺陷的评论。要将测试用例标记为通过或标记,可以使用右上角的蓝色下拉菜单。选择测试结果后,运行器会自动继续执行下一个测试。一旦所有测试执行完毕,可以使用左上角的保存并关闭按钮保存结果。

要查看测试运行的结果,导航到测试计划,然后点击运行以获取以下仪表盘:

图 10.10 – Azure DevOps 测试运行

图 10.10 – Azure DevOps 测试运行

在这里,你可以选择你想查看结果的运行,以快速概览测试结果。在第二个标签测试结果中,可以查看所有测试用例的列表以及它们是否通过。

拥有详细脚本的一个主要好处是,可以多次执行相同的测试,从而降低每次执行的成本。如果一个测试计划执行多次,所有的运行历史记录都会被保存,并且可以通过前述截图中显示的视图访问。这对于将手动测试作为回归测试的一部分非常有用;然而,一旦情况变成这样,通常通过系统测试来自动化这些测试会更为有益,尤其是在可能的情况下。

提示

可以多次执行相同的测试,但针对不同的配置。当开发 Web 应用程序时,通常会使用不同的浏览器进行测试。对于桌面应用程序,这可能会用于测试不同的操作系统。关于配置的工作,微软文档中有详细说明,参见docs.microsoft.com/en-us/azure/devops/test/mtm/test-configurations-specifying-test-platforms?view=azure-devops

下一部分将讨论功能测试的最终形式,即探索性测试。

探索性测试

编写和执行详细的测试脚本可能会花费大量时间,既对测试工程师,也对测试执行者,因此这些测试通常会被自动化。一旦它们被自动化,就会归类为系统测试,特别是自动化 UI 测试。

这并不意味着手动测试完全没有价值或没有好的投资回报。有些东西是人眼能发现的,而计算机不能,例如不友好的用户界面、未对齐的界面元素,以及未完全显示但被其他元素遮挡的文本行或图像。

为了在不花费大量时间编写详细测试脚本的情况下捕捉这些错误,探索性测试可能是一个解决方案。在这种方法中,测试人员打开应用程序并开始调查他们认为在即将发布的版本中包含最大风险的应用程序部分。在探索应用程序的过程中,测试人员会记录他们访问了哪些应用程序部分,以及执行了哪些测试用例。与此同时,测试人员还会跟踪他们发现的新的风险或尚未执行的测试用例。通过这样做,他们在工作时创建了一个已覆盖和未覆盖的测试用例列表。这也使测试人员能够始终集中关注最重要的风险和测试用例。探索性测试完成后,测试人员可以报告哪些应用程序领域和测试用例已经覆盖,哪些没有,哪些风险完全没有被探索过。这份报告对产品经理来说可能是一个宝贵的输入,帮助他们决定是否继续发布。

一个常见的误解是,探索性测试意味着测试人员只是随便点击,以查看应用程序是否正常工作。事实并非如此,前面的段落已经说明,探索性测试是一项高度结构化的活动,且需要实践。如果操作得当,测试准备和测试执行将在探索性测试过程中交织进行。

当时间有限或可用的测试时间无法提前确定时,探索性测试是一项很好的工具。探索性测试可能会发现需要记录为缺陷的问题。接下来将讨论如何进行记录。

报告手动测试结果

测试的一项活动还包括报告发现的缺陷或其他问题。这通常是繁琐且耗时的工作。你必须尝试重新现现该问题,回忆问题是如何发生的,并记录下所有这些步骤。然后,还需要描述期望的和不期望的结果,拍摄截图,并将所有内容插入到缺陷追踪或工作管理工具中,例如 Azure DevOps。

为了简化这一过程,Azure DevOps 提供了一个Test & Feedback扩展。该扩展提供了按钮来录制截图或视频,并用文本或图形进行注释。一旦发现问题并通过录制或截图进行记录,它可以自动提交到 Azure Boards。

这个扩展可以免费从 Azure DevOps 市场下载,并可以在 Firefox、Chrome 和 Edge Chromium 中运行。该扩展的链接将在本章末尾提供。

重要说明

Test & Feedback 扩展可以在执行脚本化测试和进行探索性测试时使用。

这就结束了对不同类型功能测试的讨论。接下来的部分将帮助你决定在项目中使用哪种类型的测试。

决定需要哪些类型功能测试的策略

面对如此多不同类型的测试,哪种测试最适合你的项目?鉴于测试类型繁多且各自特性不同,答案正如你所料:它们的组合,因为它们各自有不同的特性。

以下图表显示了不同类型测试执行所需时间与它们提供的软件质量信心之间的关系。图表显示,尽管成功完成的手动测试最有可能识别缺陷,但它们也需要最长的执行时间。对于自动化测试,执行数万次单元测试所需的时间通常可以保持在几分钟内,而进行 10 到 100 次系统测试可能需要超过 30 分钟:

图 10.11 – 质量信心与测试执行时间的关系

图 10.11 – 质量信心与测试执行时间的关系

从这个权衡来看,通常可以理解为什么单元测试比集成测试更受青睐,集成测试比系统测试更受青睐,任何类型的手动测试都优于自动化单元测试。

提示

如果单元测试和集成测试的质量提高,那么这条线将进一步攀升到左上角。高质量的软件架构也有助于减少系统和集成测试的需求,并增加早期发现缺陷的机会,这正是单元测试的优势。自动化的单元测试和集成测试运行更快,结果一致,对整体构建质量产生积极影响。

对这种权衡的理解有助于理解两种可以用于决定测试策略的模型,即测试金字塔和测试奖杯,这将在接下来的两个部分中进行讨论。

测试金字塔

在许多旧项目中,自动化功能测试的数量并不多。通常,这些测试运行缓慢,测试范围大,难以维护,并且经常失败而没有明确的原因。这些测试所提供的价值通常非常有限。为了弥补缺乏良好的自动化测试,通常会有许多手动测试被用来在新版本部署之前进行完整的回归测试。这些自动化测试非常耗时,且很少执行。开发人员无法得到快速反馈,缺陷通常在较晚阶段被发现。在这种情况下,很难实践 DevOps,因为 DevOps 的重点是频繁且高频率地发布新版本。

这样的测试组通常被称为“冰淇淋锥形测试”,其中包括许多手动测试和少量自动化测试,而其中只有少数是单元测试。冰淇淋锥形测试是一种反模式,但在旧项目和/或长期运行的项目中经常出现。

图 10.12 – 手动与自动化测试的测试金字塔

图 10.12 – 手动与自动化测试的测试金字塔

为了应对这一点,引入了另一个相反的模型:测试金字塔。这个模型主张拥有许多单元测试,可以在几分钟内提供关于应用程序质量的反馈,快速指出大部分错误。在此基础上,还可以叠加其他类型的较慢测试,以捕获前面层次无法捕获的错误。使用这种方法,可以在测试覆盖率和测试持续时间之间取得良好的折衷。

重要提示

请注意,测试金字塔并不主张分层方法。不要先构建一层单元测试,然后只有在所有单元测试完成后才进行集成测试。相反,它主张比例。你应该在单元测试、集成测试和系统测试之间保持健康的比例。

在不同类型测试之间找到最佳比例的一般建议非常困难。但在大多数项目中,金字塔中每个步骤的比例为 1:5-15 可能是合理的。

测试奖杯

尽管测试金字塔是一个广为人知且经常使用的分类测试和决定创建哪些类型测试的方法,但这种方法也受到了批评。虽然远离手动和系统测试在 DevOps 团队中被广泛接受是必要的,但并不是所有人都接受单元测试的重点。一些人反对测试金字塔暗示创建的单元测试数量比集成测试多得多。

这一反对意见的原因如下:

  • 单元测试部分的 WorkDivider 中,我们可以看到它依赖于了解 DivideWork 方法的实现方式。测试验证了实际的实现:对 SendMessage() 的调用。许多单元测试具有这种特点,因此增加许多单元测试会增加更改解决方案类级设计实现所需的工作量。

  • 单元测试的变动率通常比集成测试高。单元测试类与它们测试的类密切相关。这意味着如果测试的类被替换,该类的单元测试也会失去所有的价值。因此,有人认为集成测试可能会有更高的投资回报率。

  • 真正的价值来自于组件集成,而不是单个组件。即使所有单元都在隔离状态下工作,系统可能也不会提供任何价值。软件的真正价值只有在集成并准备运行时才会体现出来。由于测试应该确认价值交付,有人认为应该专注于编写集成测试而不是单元测试。

为了应对这些反对意见,Kent C. Dodds 提出了测试奖杯模型。该模型采用了测试金字塔的理念,提倡尽可能少使用手动和系统测试,但与之不同的是,它并不强调单元测试高于集成测试,而是强调集成测试优于单元测试。测试奖杯这一名称来源于如果将其绘制出来,它将呈现出类似奖杯的形状。

不幸的是,世上没有灵丹妙药,最好的建议是了解这三种模型及其背后的推理,并将适当的推理应用于当前的情况。在测试方面,并没有一种适用于所有情况的最佳解决方案。

非功能性测试的类型

功能测试主要关注验证应用程序展示的行为是否符合预期;然而,在应用程序开发中存在更多的风险:应用程序是否能足够快速地执行操作,随着更多用户同时使用系统时性能是否会下降,系统是否易于最终用户使用。验证这些属性的测试被称为非功能性测试。

有许多类型的非功能性测试,其中在 DevOps 场景中重要的三种如下:

  • 性能测试

  • 负载测试

  • 可用性测试

让我们逐一来看。

性能测试

性能测试的执行是为了确定在给定一组资源的情况下,应用程序执行某个操作的速度。性能测试通常使用专门的工具执行,并在完全组装的系统上运行。如果用于自动化 API 或 UI 测试的工具记录了测试的持续时间,那么这些测试的持续时间也可以作为性能结果使用。

为了比较多次测试的结果,确保影响性能的所有因素在测试之间保持一致是非常重要的。测试对象和测试运行者的虚拟机设置应该保持一致。应用程序配置应保持不变,集成点应该尽可能处于相同的状态——例如,不是重复使用同一个数据库,而是在每次性能测试前从备份中恢复相同的数据库。这样可以确保结果具有可比性。

虽然性能测试和负载测试常常混淆,但它们是两种不同类型的测试。

负载测试

负载测试的目的是测量系统在崩溃前能够承受的负载量。这类测试有时也被称为压力测试。与性能测试不同,负载测试会并行执行大量请求。测量的是所有请求的平均性能,同时逐步增加系统的请求数量。在大多数情况下,这会确定一个临界点,即每秒请求数达到某个特定值时,性能会突然下降。这是系统最大能承载的每秒请求数。执行负载测试时,随着最大请求数量的增加,通常会得到如下图所示的图表:

图 10.13 – 负载测试图表示例

图 10.13 – 负载测试图表示例

该图表显示了了解应用程序临界点的重要性:过多的负载可能会因为响应时间突然变化而意外地让系统崩溃。知道这个临界点在哪里可以让操作人员在生产环境中到达此点之前采取措施。

本章末尾提供了一个链接,指向一个在线的 Microsoft 实验室,供开发人员练习负载测试。

Azure 现在提供完全托管的负载测试服务。Azure 负载测试服务目前处于公开预览阶段,允许你通过模拟高规模负载流量来进行应用程序负载测试。开发人员、测试人员和质量保证QA)工程师可以使用该服务来识别和修复应用程序的性能、可扩展性或容量问题。Azure 负载测试抽象了执行大规模负载测试所需的复杂性和基础设施。你可以在这里了解更多关于 Azure 负载测试服务的信息:azure.microsoft.com/en-us/services/load-testing/

可用性测试

另一种重要的测试类型是可用性测试。其他类型的测试侧重于验证实现是否具备产品团队所期望的行为,而可用性测试则侧重于验证用户的期望是否真正得到满足。这意味着测试范围更大,这些测试能够识别笨拙的用户界面,并帮助找到模糊不清的文本或被误解的用户请求。

可用性测试通过让用户在一个或多个任务上与最终应用程序进行互动,并观察或询问他们如何与应用程序互动来进行。结果通常比通过未通过更加详细,结果通常会反馈给产品负责人,以便编写新的用户故事或更改需求。

启用可用性测试的一个极好技术是使用功能标志。功能标志使我们能够逐步将新功能暴露给更多用户。这个能力还可以用于最初仅将新功能暴露给一小部分用户,这些用户是可用性研究的一部分。这允许研究人员或产品负责人密切观察这些用户使用新功能,而其他用户尚无法访问。

重要提示

功能标志在第六章《实现持续部署和发布管理》中被讨论,作为渐进式曝光的策略。新功能的渐进式曝光本身就是一种可用性或用户接受度测试。

这种方法可以扩展到执行 A/B 测试。在这类测试中,一半的用户暴露于新功能,而另一半则没有。然后收集所有用户的数据,以查看新功能是否为用户带来了预期的好处——例如,用户是否每天使用应用程序的时间增加了。这一主题将在第十三章《收集用户反馈》中进一步探讨,内容涉及如何收集用户反馈。

这样做将可用性测试推向了发布过程的右侧。它也可以通过在最终应用程序之前,使用原型进行可用性测试,向左移动。

这就结束了对不同类型测试的讨论。在接下来的部分中,将使用度量和测试来自动衡量质量并实现质量门控。

在管道中执行测试

开发人员应该在为代码打开合并请求之前,在本地机器上执行测试。这样,他们可以确信所做的任何更改不会破坏代码的先前行为。从理论上讲,这可以保证所有合并到主分支的代码都能编译并且所有测试都通过。实际上,存在许多原因导致情况并非如此。以下是一些原因:

  • 一些测试可能无法在本地运行。它们依赖于机密的配置值,或者被配置为在完全配置的系统上运行。这两种情况常见于系统测试。有许多情况是无法从本地系统运行系统测试的。这些情况并不一定都是理想的或无法克服的——但这通常是事实。

  • 开发人员也是人。他们可能会忘记在做最后一个调整后运行本地机器上的测试,或者确信他们的更改没有破坏任何现有行为。特别是在压力下交付错误修复时,为了加快速度,跳过测试执行可能是很有诱惑力的。

为了防止这些情况使未经过充分测试的代码在流水线中传播,建议在流水线内执行所有测试。接下来的部分将展示如何为单元测试、集成测试以及通过其他系统运行的测试执行此操作。首先是单元测试。

运行单元测试

对于许多语言,Azure DevOps 已经内建了支持从流水线运行单元测试的功能。可以为 C#、TypeScript、Python、Maven、C++、Go 等语言执行单元测试。

对于其中一些语言,提供了一个现成的任务。例如,用 C#编写的测试。在执行.NET 测试时——例如在 C#中——测试结果会自动以 XML 格式存储,构建代理可以理解该格式。

这允许流水线代理解释测试结果并在构建结果中可视化它们,如下所示:

图 10.14 – 显示自动化测试执行结果的构建结果页面示例

图 10.14 – 显示自动化测试执行结果的构建结果页面示例

对于某些语言,需要执行多个任务。例如,用 TypeScript 编写的测试通常通过 NPM 命令执行。以下 YAML 片段可以用来实现这一点:

- task: Npm@0
  displayName: 'Run unit tests - npm run tests' 
  inputs:
    cwd: src 
    command: run 
    arguments: test

这将执行package.json中指定的自定义 NPM 命令。不幸的是,这不会以流水线代理理解的格式存储测试结果。为了将结果转换为正确的格式,需要另一个任务。以下 YAML 片段可以用来实现这一点:

- task: PublishTestResults@2 
  displayName: 'Publish Test Results' 
  inputs:
    testResultsFiles: '**\reportTests\TEST-*.xml' mergeTestResults: true
  condition: succeededOrFailed()

测试结果是否可以直接获得或需要转换,因编程语言而异。除了发布测试结果外,还建议收集测试覆盖率结果。

记录单元测试代码覆盖率

最佳实践是,在构建过程中不仅运行所有单元测试,还要确定在这些测试中执行的代码基础的百分比。这被称为单元测试代码覆盖率,它表示测试的全面程度。构建也可以配置为发布单元测试获得的代码覆盖率。

要配置构建以发布.NET Core 单元测试的测试覆盖率,必须执行以下步骤:

  1. 在单元测试项目中安装coverlet.msbuild NuGet 包。

  2. 使用.NET Core 任务执行测试,并添加两个参数以生成覆盖率报告,/p:CollectCoverage=true/p:CoverletOutputFormat=cobertura

图 10.15 – 配置单元测试代码覆盖率

图 10.15 – 配置单元测试代码覆盖率

  1. 添加Cobertura

  2. 配置$(System.DefaultWorkingDirectory)/**/coverage.cobertura.xml作为汇总文件。

图 10.16 – 发布单元测试代码覆盖率

图 10.16 – 发布单元测试代码覆盖率

  1. 构建的运行详情现在将包含代码覆盖率报告。

这是生成详细代码覆盖率报告所需的所有配置。生成的报告包含已覆盖和未覆盖的代码块数量以及计算出的覆盖率百分比。这些报告是构建结果页面的一部分。

除了单元测试,集成测试也可以作为管道的一部分运行,并且它们通常面临处理配置设置管理的挑战。

运行集成测试

集成测试通常与单元测试使用相同的框架编写。不过,它们有自己独特的挑战。通常,它们需要一个或多个设置,指定如何与测试中涉及的一个或多个其他组件进行集成。回顾之前讨论的MessageSender类的集成测试,这是这个问题的一个例子。

记得这个测试有一个.runsettings文件,在其中你应该指定connectionString,该连接字符串是MessageSender类将使用的队列。这个connectionString设置不能提交到源代码控制中。相反,可以将占位符提交到源代码控制中,并在管道执行过程中用实际的密钥替换它。

在这种情况下,这意味着以下pipeline.runsettings文件将提交到源代码控制中:

<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
  <TestRunParameters>
    <Parameter name="MessageSenderConnectionString" value="#{MessageSenderConnectionString}#" />
 </TestRunParameters>
</RunSettings>

在开始实际测试执行之前,另一个任务将被运行,用实际值替换占位符。正如在第五章《迁移到持续集成》中讨论的那样,这些值可以从变量组、密钥保管库或管道变量中安全地获取。Azure DevOps 有多个扩展可以用于将占位符替换为实际值。以下 YAML 代码片段是如何操作的示例:

- task: qetza.replacetokens.replacetokens-task.replacetokens@3 
  displayName: 'Replace tokens in pipeline.runsettings' 
  inputs:
    targetFiles: $(System.DefaultWorkingDirectory)/integrationtests- location/pipeline.runsettings

在替换令牌任务执行后,可以像单元测试一样调用测试运行器。这个Replace Tokens扩展可以在 Azure DevOps Marketplace 上找到:marketplace.visualstudio.com/items?itemName=qetza.replacetokens

运行外部测试

除了单元测试和集成测试,你可能还希望使用其他系统执行测试。例如,Azure DevOps 没有内置的支持来执行负载测试或自动化 UI 测试。对于这些类型的测试,必须从管道中调用其他系统。许多系统可以通过这种方式集成。

如何操作因系统而异,但大多数情况下,以下步骤将适用:

  1. 配置外部系统中的测试。

  2. 为 Azure DevOps 安装一个扩展,使新任务能够从管道调用外部系统。

  3. 创建到外部系统的服务连接。

  4. 将任务添加到管道中。

要了解配置集成的详细信息,一个好的起点通常是第三方产品供应商的网站。

维护质量

前面的章节详细介绍了可以用来描述应用程序质量的各种测试和指标。有了这些考虑,接下来是时候开始思考可以用于维持高质量甚至提高质量的工具了。

代码审查

保护代码质量的最强大工具之一就是代码审查。当使用 Git 时,必须执行拉取请求(pull request)将开发者的更改合并回主分支。拉取请求允许一个或多个开发者审查所有更改并对此进行评论。发起拉取请求的开发者可以查看评论并据此进行修改,在继续工作的同时提高更改的质量。

为了使代码审查发挥最大作用,重要的是不要将其视为一个必须尽量轻松通过的关卡。采取一种更为开放的态度,假设每个人都在努力编写高质量的代码,并将代码审查视为有关代码质量的讨论的开始,这样会更加有益。改变视角,把代码审查从软件开发中令人烦恼的仪式转变为一个欢迎他人对你的代码提出意见并帮助你编写更高质量代码的机会。

一旦有了这样的心态,代码审查将成为学习的源泉。它们将促使同行之间展开讨论,寻找解决问题的最佳方式:不仅仅是现在的最佳方法,还要考虑未来,不留下技术债务,并确保与要合并的代码一起有足够的单元测试和集成测试。代码审查也是辅导初级开发者的绝佳工具,允许他们获得对其工作的反馈。让初级开发者审查高级开发者的代码甚至更有价值。通过这种方式,他们可以提出自己尚未掌握的问题,并且通常会指出那些可能随着时间推移成为技术债务的过于复杂的解决方案。

自动收集质量指标

除了手动审查外,还有许多工具可以自动评估代码库的质量。有些工具内置于 Azure Pipelines 中,但更复杂的功能来自于独立的代码扫描工具。通过数学方法来衡量技术债务,使用工具来进行此类衡量不仅能为应用的质量提供有价值的洞察,还能帮助了解代码随时间变化的情况。

衡量应用程序质量的一种可能工具是 SonarCloud。SonarCloud 是基于 SonarCube 的 SaaS 产品。此工具可以自动扫描代码库,查找潜在的漏洞、安全风险、技术债务和其他质量指标。这是一个付费的独立产品,能够与 Azure DevOps 管道集成。要使用 SonarCloud,你必须创建一个帐户并获取项目密钥,以便从 Azure DevOps 调用 SonarCloud 扫描。

为了调用 SonarCloud,使用一组三项任务,这些任务是 Azure DevOps 扩展的一部分。在安装扩展并配置 SonarCloud 服务连接后,三个任务会被添加到管道中,以设置分析、执行分析并(可选地)在质量下降时使构建失败。第一个任务是唯一需要配置的任务,配置过程如以下截图所示:

图 10.17 – 配置 SonarCloud – 静态代码分析器

图 10.17 – 配置 SonarCloud – 静态代码分析器

每次执行的构建都会自动扫描其代码,由 SonarCloud 提供关于质量的详细报告。在这些报告的基础上,还会生成一个仪表盘,快速概览一些关键的质量指标。

图 10.18 – SonarCloud 质量指标概览

图 10.18 – SonarCloud 质量指标概览

这是仪表盘的另一个视图,展示了质量指标:

图 10.19 – SonarCloud 质量门控仪表盘

图 10.19 – SonarCloud 质量门控仪表盘

代码扫描工具可以用于报告代码的质量,同时也可以作为质量门控,阻止合并更改或将更改部署到特定环境中,如果检测到质量不足。

可视化质量

持续衡量应用程序的质量是没有意义的,除非采取了相应的措施。仪表盘可以成为一个强大的工具,帮助持续洞察当前的质量水平以及质量随时间的变化。

大多数代码质量工具都内置了报告选项,它们对 QA 工程师非常有价值。它们提供了详细的洞察,帮助了解应用程序哪些部分的质量较高,哪些类型的问题最近发生得更频繁。

这种类型的仪表盘的缺点是,它们通常很难阅读和关联,因为它们可能存在于与开发人员执行大部分工作所在的 Azure DevOps 不同的其他工具中。因此,创建 Azure DevOps 中的仪表盘来报告质量可能会更有益。以下截图展示了这样的仪表盘示例:

图 10.20 – Azure DevOps 仪表盘质量概览

图 10.20 – Azure DevOps 仪表盘质量概览

该仪表盘显示了当前质量和应用程序代码的概览,以及一些最近的历史记录。在这里,你可以找到以下信息:

  • 在顶部显示最近更改的数量,以及最新的 SonarCloud 质量 gate 结果,当前显示为 通过。该项目中两次不同构建的结果显示在第二行。

  • 第三行和第四行显示项目中所有构建和发布的聚合结果。使用符号表示构建和发布的状态:成功、失败或仍在运行。

  • 在右侧,使用两个小部件显示过去 10 个构建中的失败测试的百分比和相应的失败测试数量。

  • 每个环境下最新发布运行的结果显示在下方。

可以使用内置小部件或扩展根据团队或项目创建这样的仪表板。Azure DevOps 市场中有许多扩展。例如,在前面的仪表板中,使用了 Team Project Health 扩展。此扩展可在 Azure DevOps 上找到:marketplace.visualstudio.com/items?itemName=ms-devlabs.TeamProjectHealth

Azure DevOps 仪表板可以配置为每 5 分钟自动刷新,从而也能作为墙板使用。

质量 gates

衡量、报告甚至可视化质量是重要且有价值的;然而,如果没有人基于这些指标采取行动,那么对开发团队来说这些指标就没有价值。为了防止这种情况,可以引入自动质量 gates 或检查。

实现质量 gates 的一种方法是,当测试失败、测试覆盖率过低或代码扫描工具设定的阈值不再满足时,使持续集成构建失败。这些都是之前讨论过的内容。另一个强制执行标准的选项是向管道中添加 gates 或检查点。通过这种方式,必须满足某些条件,管道才能继续执行。

如何实现这一点在经典发布和 YAML 多阶段管道之间有所不同。

经典发布

另一个选项是使用 Azure 发布管道中的 gates。在这里,可以指定一个或多个条件,必须满足这些条件才能允许将发布部署到特定环境中。gates 还可以是扩展的一部分,例如之前讨论过的 SonarCloud 扩展。

可以通过选择发布管道中的任何阶段并编辑预部署条件来添加 gates。启用 gates 后,可以添加一个或多个 gates。以下是发布管道的截图,展示了如何禁止将任何质量不合格的构建部署到环境中:

图 10.21 – Azure DevOps 配置部署 gates

图 10.21 – Azure DevOps 配置部署 gates

部署审批和 gates 的使用并不相互排斥,因此可以同时使用两者。

多阶段管道

Gates(如在经典发布中可用)也可以在多阶段的 YAML 流水线中使用。在 YAML 流水线中,还有另一种机制:检查。检查被配置为在允许流水线继续之前自动验证一个或多个条件是否满足。检查可以添加到在某个阶段使用的资源上。如果在某个阶段的一个或多个资源上发现一个或多个检查,则所有检查必须通过,流水线才可以继续到该阶段。检查可以添加到环境和服务连接中。

要向环境添加检查,请导航到该环境。

图 10.22 – 配置 Azure DevOps 环境

图 10.22 – 配置 Azure DevOps 环境

现在,执行以下步骤:

  1. 在右上角,展开菜单并选择审批和检查

  2. 在打开的新视图中,点击查看所有以查看所有可用的检查类型。

  3. 选择调用 Azure 函数

图 10.23 – 配置 Azure DevOps 环境审批和检查

图 10.23 – 配置 Azure DevOps 环境审批和检查

  1. 在弹出的窗口中,配置要调用的 Azure 函数。至少需要提供函数的 URL 和密钥。

  2. 点击创建

一旦检查创建完成,每个目标环境的部署任务(参见第六章实施持续部署和发布管理)都必须通过此检查。当被调用的函数返回成功响应代码时,检查通过。

支持以下类型的检查:

  • 评估工件:验证容器镜像类型的工件是否通过了自定义策略。这些策略是使用一种名为Rego的语言定义的。

  • 调用 REST API:将流水线的详细信息发布到 Azure 函数或 REST API,以执行自定义逻辑。如果 API 返回成功的 HTTP 状态码,则允许流水线继续。

  • 调用 Azure 函数:与调用 REST API检查相同,但具有一些 Azure 函数的默认设置。

  • 查询 Azure Monitor 警报:仅在指定的警报处于非活动状态时才继续。

  • 必需的模板:仅当当前的 YAML 流水线扩展了一个或多个配置的基础 YAML 模板时,才允许流水线继续。

检查可以是确保满足一个或多个条件后再允许流水线继续的强大机制。

总结

在这一章中,你学习了如何衡量和验证软件开发过程的质量。快速和频繁发布要求所编写的软件具有高质量。需要进行测试,以确保你编写的软件质量高,技术债务少。你了解了不同类型的测试,以及自动化和手动测试的优缺点。最后,你还学习了如何通过代码审查和工具帮助保持项目的高质量,通过报告质量并作为质量门控。

通过这些知识,你现在了解了测试和测试类型,帮助你决定哪些测试是应用程序所需的,哪些风险可以通过哪种测试来解决,以及哪些是你需要的,哪些可以省略。你现在还能够设置和配置代码扫描工具,确保低质量的更改不会合并到主分支。

在下一章,你将学习关于安全性和合规性这两个话题,它们在实践 DevOps 时同样重要。

问题

在我们总结时,这里有一份问题清单,供你测试自己对本章内容的理解。你将在附录评估部分找到答案:

  1. 判断对错:单元测试验证的是单个单元的独立工作情况。

  2. 判断对错:集成测试验证的是一个完全组装的系统的工作情况。

  3. 以下关于测试金字塔原则的说法哪个是正确的?

    1. 有很多集成测试,较少的单元测试,甚至更少的系统测试

    2. 有很多单元测试,较少的集成测试,甚至更少的系统测试

    3. 有很多系统测试,较少的集成测试,有很多单元测试

  4. 以下哪个不是非功能性测试类型?

    1. 负载测试

    2. 可用性测试

    3. 适用性测试

    4. 性能测试

  5. 测试是关于深入了解工作质量的。可以采用哪些技术来防止低质量的工作传播到生产环境?

自我练习

  • 打开我们之前在第六章《实施持续部署与发布管理,练习部分》中创建的packtbookslibrary-api解决方案,并添加一个新项目(选择NUnit 测试项目)。

图 10.24 – 自我练习 – 添加新测试项目

图 10.24 – 自我练习 – 添加新测试项目

  • 创建一个新测试项目。

图 10.25 – 自我练习 – 创建新测试项目

图 10.25 – 自我练习 – 创建新测试项目

  • 一旦创建了测试项目,在依赖项中添加对packtbookslibrary-api的引用。

图 10.26 – 自我练习 – 配置依赖关系

图 10.26 – 自我练习 – 配置依赖关系

  • 添加一个IBookService.cs接口和实现该接口的BookService.cs类。

图 10.27 – 自我练习 – 添加  接口

图 10.27 – 自我练习 – 添加 IBookService.cs 接口

  • IBookService.cs 将有如下代码:

    using packtbookslibrary.Shared.Models;
    using System.Text.Json;
    namespace packtbookslibrary_api.Services
    {
        public interface IBookService
        {
            List<Book> GetBooksList();
        }
    }
    
  • BookService.cs 将有如下实现:

    using packtbookslibrary.Shared.Models;
    using System.Text.Json;
    namespace packtbookslibrary_api.Services
    {
        public class BookService : IBookService
        {
            List<Book> IBookService.GetBooksList()
            {
                List<Book> books = new List<Book>();
                string jsonString = System.IO.File.ReadAllText("books-data.json");
                books = JsonSerializer.Deserialize<List<Book>>(jsonString);
                return books;
            }
        }
    }
    
  • 将以下代码添加到 BooksController.cs 中:

          private readonly IBookService bookService;
            List<Book> books = new List<Book>();
            public BooksController(IBookService bookService)
            {
                this.bookService = bookService;
                books = bookService.GetBooksList();
                //string jsonString = System.IO.File.ReadAllText("books-data.json");
                //books = JsonSerializer.Deserialize<List<Book>>(jsonString); 
            }
            // GET: api/<BooksController>
            [HttpGet]
            public IEnumerable<Book> Get()
            {
                return books;
     }
    
  • 添加 Moq 包引用:

图 10.28 – 自我练习 – 添加包引用

图 10.28 – 自我练习 – 添加包引用

你会注意到所有的测试用例都已成功通过。

图 10.29 – 自我练习 – 执行 BookControllerUnitTest

图 10.29 – 自我练习 – 执行 BookControllerUnitTest

  • 更新 nugget.config 以添加 Sources 包:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
        <packageSources>
            <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
             <!-- add an Azure Artifacts feed -->
               <add key="PacktBooksLibraryFeed" value="https://pkgs.dev.azure.com/aurigadev/PacktBookLibrary/_packaging/PacktBooksLibraryFeed/nuget/v3/index.json" />
        </packageSources>
    </configuration>
    
  • 向 YAML 管道中添加测试用例步骤,以便在构建过程中执行测试用例:

    # Run your tests
       task: DotNetCoreCLI@2
       displayName: 'Run Tests'
      inputs:
      command: test
      projects: '**/*Tests/*.csproj'
      arguments: '--configuration ${{ parameters.buildConfiguration }}'
    
  • 执行 main-ci-pipeline 以运行管道,并检查 Test 选项卡查看测试执行结果。

图 10.30 – 自我练习 – 执行持续集成管道以查看测试结果

图 10.30 – 自我练习 – 执行持续集成管道以查看测试结果

进一步阅读

第十一章:管理安全性和合规性

确保你的应用程序执行所需功能同样重要的是,确保它不做任何不该做的事情。在上一章中,你了解了质量和测试,目的是不断衡量你的应用程序是否在按预期工作。在本章中,你将学习如何防止任何不必要的行为。这涉及到安全性和合规性问题。虽然通过更快的部署和缩短交付周期可以增加向最终用户提供的价值流,但你仍然需要确保交付的是安全和合规的软件。在本章中,你将学习如何在你的 DevOps 流程中解决这些问题。

为此,本章将从讨论速度与安全之间的感知权衡开始,并解释如何在拥抱 DevOps 时,安全性不会降低,反而可能会增加。接下来,讨论安全性的一个特定维度:如何安全地处理你的管道和应用程序所需的秘密,如密钥和密码。之后,介绍用于自动识别应用程序代码和依赖项中可能存在的安全风险的代码扫描工具。本章最后讨论如何保持你的基础设施和配置部署的合规性,以及如何使用 Azure 安全中心(现称为 Microsoft Defender for Cloud)检测运行时安全风险和威胁。

本章将涵盖以下主题:

  • 将 DevOps 原则应用于安全性和合规性

  • 处理秘密

  • 检测应用程序代码漏洞

  • 与依赖项一起工作

  • 确保基础设施合规性

  • 监控和检测运行时安全风险和威胁

  • 你可以使用的其他工具

技术要求

为了实验本章中描述的技术,你需要以下一种或多种工具:

  • 拥有访问构建和发布管道权限的 Azure DevOps 项目,并有权安装扩展

  • 一个 Azure 订阅(若你尚未拥有账户,可前往 portal.azure.com 并按照指南进行注册)

  • 安装了 Azure Az PowerShell 模块的 PowerShell(如何安装 PowerShell Azure 模块的说明可以在 docs.microsoft.com/en-us/powershell/azure/install-az-ps?view=azps-7.3.0 查阅)

  • 可选的 WhiteSource Bolt、SonarCloud 或类似产品的订阅

上述所有内容都可以免费或试用,以便学习或评估用途。

将 DevOps 原则应用于安全性和合规性

关于安全性和合规性的担忧,可能是公司不愿接受完全的 DevOps 思维方式的原因之一,因为这会妨碍它们频繁发布的能力。在过去,它们通常会减少发布频率,每次发布前都会进行安全或渗透测试。这给了它们信心,确保不会发布包含安全漏洞的软件。

这种减少发布频率、在最终发布之前进行一次大规模的安全测试的做法与 DevOps 的思维方式相冲突,这也是一些公司面临的难题。它们在寻找确保每次发布都能为用户提供业务价值的方法,但又不愿在此过程中妥协安全性。问题是,这样的取舍是否公平。难道不能在保证速度的同时保持安全性吗?或许实际上更频繁和更快速的发布,结合严格的自动化,反而能帮助提高软件开发中的安全性?为了回答这个问题,我们可以先探讨在非 DevOps 环境中安全性通常是如何实践的,并了解在采用 DevOps 时需要做出哪些改变。

将开发人员和安全工程师聚集在一起

在许多公司中,安全工程师与开发人员属于不同的部门。这样分开的思路是认为将编写代码的人(即开发人员)与检查代码的人分开会更加有利。

过去,软件开发人员与软件测试人员之间也常常存在相同的分隔。然而,最近的研究表明,将开发人员和测试人员放得更近并不会导致诸如“群体思维”、仅测试已知正常工作的内容,或通过仅为已知测试用例开发来作弊测试等不良行为。经验和研究表明,事实恰恰相反。将开发人员和测试人员放在一起可以产生更高质量的产品。正因如此,像敏捷开发这样的运动建议开发团队在开发过程中加入包括测试在内的各项纪律。

正是基于这一思路,越来越多的人呼吁将安全工程整合进 DevOps 开发团队。这一运动通常被称为DevSecOps强固的 DevOps。这两种运动都主张,运用 DevOps 原则,如向左推进(shift left)并尽可能自动化,可以帮助提高安全性。它们提倡,渗透测试或应用程序的漏洞审查不再由人工完成,而是完全自动化,并成为交付管道的一部分。这使得自动化、快速反馈循环以及持续交付和部署成为可能。

还有人提倡,更频繁地发布软件也有助于进一步提高安全性,原因如下:

  • 当可用的自动化软件发布机制可靠时,任何解决安全风险的更改都可以迅速部署。能够快速响应新的发现是一个重大的安全改进。

  • 速度本身也可以是一种安全措施。如果系统的工作状态每天变化多次,那么要弄清楚其内部工作原理并加以滥用就变得更加困难。应用不可变部署原则并使用基础设施即代码确保运行应用程序的基础设施经常得到刷新。这是有效减缓高级持续性威胁的良好措施。

本章将探讨的一个内容是如何配置交付管道以加入安全扫描。请注意,从管道中运行这些工具是一项不同的任务,它确保这些工具被正确配置并且应用正确的政策和要求。对于这些活动,拥有安全背景并与安全工程师密切合作仍然至关重要。这只是另一个密切合作能够带来变化的领域。特别是在安全问题上,需要与其他学科的协作——以自动化所有安全检查并避免(或最小化)任何手动验证过程。

安全问题

本章其余部分将介绍一些安全问题,但认识到一些前面的章节已经介绍了一些安全问题会有所帮助。正如你从软件开发中已经知道的,安全不仅仅是你在某一个地方添加的东西。安全应该无处不在。以下图表显示了与软件创建和交付相关的不同活动。在每个活动旁边,显示了适用的安全问题:

图 11.1 – 软件开发活动与安全问题

图 11.1 – 软件开发活动与安全问题

让我们回顾一下这些阶段中的安全问题:

  • 分支-主分支合并:在这个阶段,通过拉取请求应用四眼原则。拉取请求允许其他工程师在合并到主分支之前审查更改。分支策略用于强制要求使用拉取请求,确保代码能够编译并且单元测试能够运行。这在第四章《一切从源代码管理开始》和第五章《迁移到持续集成》中已有讨论。

  • 构建:在这个阶段,通过向构建管道添加额外任务,执行所有源代码和第三方依赖的安全扫描。这可以防止安全风险在未经检查的情况下传播。我们将在本章的《处理机密》部分讨论如何做到这一点。

  • 发布:在发布过程中,可以配置批准人。批准人是指必须在部署到特定阶段之前提供批准的用户。此外,使用自动化发布门控确保(并进一步强制执行)在发布可以继续之前满足某些标准。我们在 第六章实施持续部署和发布管理 中讨论了如何做到这一点。

  • 部署环境(目标系统):所有应用程序都会在目标环境中运行。可以是本地部署;然而,本书重点介绍的是 Azure。对于运行时的安全性和合规性问题,本章将介绍 Azure Policy 和 Microsoft Defender for Cloud(前身为 Azure Security Center)。

  • 横向影响:前述所有要点只有在 Azure DevOps 环境中有足够的访问控制时才有用。尽管这不在本书的范围之内,但它是一个重要的角度。用户应该拥有足够的权限来完成他们的工作,但不应能够对政策、构建和部署过程做出未经授权的更改。此外,在整个交付过程中,需要进行适当的机密管理,以确保证书、密钥和密码等机密的安全。我们如何做到这一点,本章也会进行讨论。

现在,了解了软件和安全工程师如何协作开发应用程序,接下来是时候在以下各节中讨论这项工作的不同方面了。下一节将讨论如何处理机密。

与机密一起工作

一个重要的安全元素是机密的处理。在部署应用程序时,总是涉及到机密。尤其是在部署到云端时——即通过互联网——以安全的方式处理这些访问密钥至关重要。除了部署所需的机密外,还有一些机密需要插入到应用程序的运行时配置中。一个常见的例子是访问数据库时所需的机密。

第八章实现 基础设施和配置即代码 中,讨论了多种交付应用程序配置的机制,包括 Azure Resource ManagerARM)模板。然而,模板需要输入外部机密,因为它们不能存储在源代码控制中的参数文件中。

重要说明

不应将机密存储在源代码控制中。

如果机密不能存储在源代码控制中,那么应该将其存储在哪里呢?常见的选项包括将机密存储在服务连接或变量组中。

将机密存储在服务连接中

部署任何应用程序所需的第一组机密是连接到目标系统所需的机密。任何个人都不应访问这些机密,因为它们仅在部署期间使用。这也是为什么 Azure Pipelines 允许你将它们安全地存储在服务连接中的原因。

服务连接是另一个系统的抽象,可以用于执行 Azure Pipelines 中的任务。服务连接有特定类型——即指定它们可以连接的系统类型。Azure、GitHub、Jira、npm、NuGet 和其他十多种系统都有现成的服务连接类型。也可以通过 Azure DevOps 扩展机制添加新的服务连接类型。

服务连接可以包含指向另一个系统位置的引用——通常是一个网址。在该位置旁边,它们可以包含授权令牌、用户名和/或密码,具体取决于服务连接的类型。存储在服务连接中的机密不能再被检索,甚至管理员也无法访问。此外,每当服务连接的任何细节发生更改时,必须重新输入机密。这是为了防止之前输入的机密被滥用以访问其他位置。这些细节表明,服务连接被设计为提供一个安全的位置来存储连接凭证。

服务连接可以在每个 Azure DevOps 项目的中心位置进行管理。你可以创建新的连接、编辑现有的连接、修改用户权限等等。按照以下步骤练习:

  1. 要打开此视图,请导航至项目设置。将打开一个包含各种设置选项的垂直列表。

  2. 从列表中点击服务连接。你将能够查看各种连接,如下图所示:

图 11.2 – 创建新的服务连接

图 11.2 – 创建新的服务连接

  1. 如果你希望创建新的服务连接,点击屏幕右上方的新建服务连接按钮。

  2. 要修改权限,请点击安全下的更多操作子菜单。这将带你到一个类似于下图的屏幕:

图 11.3 – 服务连接安全设置

图 11.3 – 服务连接安全设置

编辑和安全视图中,你现在可以执行以下操作:

  • 编辑服务连接详情。

  • 修改用户权限。

  • 限制权限。

  • 添加更多用户或组,并为每个用户或组指定是否可以使用或管理该端点。

  • 指定哪些管道可以使用此服务连接。

项目中的每个管道默认不应有权限使用服务连接。相反,每个希望使用服务连接的管道必须首先获得服务连接管理员的授权。

在变量组中存储机密

在应用开发中涉及的机密比仅用于连接其他系统的机密更多。例如,包括在应用编译过程中需要的许可证密钥,或在部署后需要传递给应用的数据库用户名和密码,或作为 ARM 模板部署的一部分传递的数据库信息。

这些机密可以存储在管道变量或变量组中,关于这一点我们在第三章,“优化 DevOps 工具使用效果”部分中,讲解了在Azure DevOps 中创建构建定义时的相关内容。微软会安全地存储所有标记为机密的变量,并使其无法通过用户界面进行检索。

然而,可能有一些原因不希望将机密存储在 Azure DevOps 中,而是存储在专门的密钥存储中,例如 Azure Key Vault。这样做将提供与 Key Vault 一起的额外保障,并能够使用Azure 基于角色的访问控制Azure RBAC)和密钥库访问策略进一步控制访问策略。

当将机密存储在 Azure 密钥库中时,它们仍然可以作为变量组使用,通过通过服务连接将空的变量组连接到密钥库,如下图所示:

图 11.4 – 将 Key Vault 用作变量组的存储

图 11.4 – 将 Key Vault 用作变量组的存储

要将密钥库用作变量组的存储,请执行以下操作:

  1. 启用从 Azure 密钥库链接机密作为变量的第二个滑块,以从密钥库加载机密。

  2. 从下拉菜单中选择一个已存在的 ARM 服务连接,或者通过从列表中选择一个 Azure 订阅,动态创建一个新的 Azure 管理身份服务连接。

  3. 输入要加载机密的密钥库名称。你也可以从下拉菜单中选择一个。在此情况下,仅显示可通过选定的服务连接访问的密钥库。

  4. 可以使用安全性选项卡配置特定用户的访问权限。

也可以自动创建正确的权限,以便访问 Azure 和密钥库的服务连接。请注意,这两个操作会对 Azure 安全设置进行更改,因此请确保这些设置(仍然)是正确的。

检测应用代码漏洞

在 DevOps 时代之前定期进行的安全评估,在转向 DevOps 文化时并不能完全省略。这意味着,必须以某种方式继续进行这些评估,而不是简单地放弃。可以通过两种方式来进行。

第一种方法是像以前一样,定期进行渗透测试、安全审查和其他安全检查。然而,代码不是等到测试通过后才进入生产环境,而是在与安全评估分开的情况下直接部署到生产环境。这意味着接受存在风险的假设,即可能会有一些漏洞在下次安全扫描时被发现,并会在下一个版本中解决。使用这种方法可以实现速度,但也需要接受某些漏洞可能会存在一段时间。

第二种方法是将应用程序安全扫描作为常规工作流程的一部分,纳入到将代码提交到源代码库的过程中。例如,安全代码审查不必每个增量或每两个月进行一次。它们也可以在每次拉取请求时进行——在代码合并之前。这样,你突然间不再只是检测漏洞,而是改为防止漏洞的出现。安全漏洞扫描也可以采用相同的方式。它们可以成为交付流水线的一部分,或者成为一个完整的每晚质量保证QA)构建,每天早晨报告开发质量。

当然,实际情况通常并不像黑白分明那样,很多公司会结合使用这些方法。它们使用自动化反馈机制来检测可以发现的问题,将安全代码审查作为拉取请求工作流程的一部分,然后结合定期的手动渗透测试。通过这种方式,交付速度提高的同时,安全风险没有增加,甚至可能减少,后者是漏洞修复速度提高的结果。

OWASP Top 10

在 Web 应用程序安全方面,有几种常见的安全问题,负责编制大部分安全问题。这些问题类型包括 OWASP Top 10。这是一个列出十大最常见安全问题的清单,由开放 Web 应用程序安全平台OWASP)发布。该清单每隔几年会进行一次审查,但在过去几年里保持了相对稳定。

OWASP Top 10 中的大部分错误都可以通过实施自动化安全测试来预防,方法可以是使用静态代码分析来检查安全漏洞,或者使用OWASP Zed 攻击代理OWASP ZAP)进行动态测试。

实施自动化漏洞扫描

在前一章中,讨论了持续测试,并介绍了 SonarCloud 作为用于技术债务和代码质量的代码扫描工具。除了评估应用程序代码的质量外,SonarCloud 还可以用于扫描安全漏洞。在第十章《集成持续测试》中,您学习了如何将 SonarCloud 扫描添加到您的管道中。还有其他更多专业化的工具可供使用,我们将在本章的最后一部分讨论这些工具。

这些工具通过静态测试评估应用程序。它们扫描代码以识别任何有风险的代码。这被称为白盒方法,因为它们可以看到、检查和扫描所有代码。换句话说,一切都是可见的。这与黑盒方法相反,黑盒方法将运行中的应用程序视为一个封闭的整体,只通过调用它并观察其响应来进行测试。OWASP ZAP 就是能够执行这种测试的工具之一。

OWASP ZAP

OWASP ZAP 是一款可以执行应用程序自动化渗透测试的工具。该工具可以在两种模式下运行:

  • 基准扫描:基准扫描只需几分钟,并且经过优化,可以在这几分钟内尽可能多地扫描安全风险。这使得基准扫描足够快速,能够在部署管道的早期阶段运行。甚至可以在每次部署到第一个测试环境后运行安全扫描,从而为开发人员提供快速反馈。

  • 完整主动扫描:完整的主动扫描需要更多时间。在这种类型的扫描中,代理会检查来自应用程序的每个响应,以识别应用程序的其他 URL,并对它们进行扫描。通过这种方式,完整的应用程序会在运行时被发现,采用蜘蛛抓取的方式。这种扫描方式更加全面,但也需要更多时间。因此,完整扫描通常会在间隔时间内进行,例如每晚一次。

OWASP ZAP 尝试识别任何可能的安全风险。最显著的风险包括 SQL 注入、JavaScript 反射和路径遍历。

OWASP ZAP 是一款可以安装在任何虚拟机上的应用程序。其缺点是,即使没有正在运行的扫描,虚拟机也会一直运行。这会增加成本,当然,虚拟机本身也需要进行补丁更新和安全加固。最近,还推出了该代理的容器化版本。该容器可以在 Azure 容器实例中运行,只在需要时启动代理,执行完毕后立即销毁。

这完成了我们对代码扫描工具及其实现的介绍。在这些工具的帮助下,您可以检测应用程序中的漏洞并防止任何安全问题。下一部分将探讨如何扫描应用程序的依赖项。

处理依赖项

除了应用程序代码本身可能带来的安全风险外,还存在与重用组件相关的风险。现代应用程序代码中有 50% 到 80% 不是内部开发的,而是来自其他方的包或依赖项。一些可能是开源的,但不一定如此。也可能是从其他开发公司购买的组件或从如 NuGet 等库中获取的二进制文件。

依赖项不仅带来安全风险,还可能带来许可风险。如果一个团队开始使用一个以 GPL 许可发布的组件作为封闭源组件,会发生什么?如果有人发现,他们可能被迫将产品开源,或者至少会因为未根据许可使用他人的工作而遭受公开的耻辱。

为了降低这些风险,可以使用多种工具来检测和扫描构建应用程序时使用的所有依赖项。其中一个可用的工具是 WhiteSource Bolt,它作为扩展可从 Azure DevOps 市场获取。

使用 WhiteSource Bolt

要开始使用 WhiteSource Bolt 执行扫描,请执行以下操作:

  1. 从 Azure DevOps 市场安装 WhiteSource Bolt 扩展。

  2. 转到Pipelines下的WhiteSource Bolt菜单。

  3. 注册并接受许可条款。

  4. 如下图所示,将WhiteSource Bolt扫描任务添加到构建或发布定义中:

图 11.5 – 一个 WhiteSource Bolt 管道任务

图 11.5 – 一个 WhiteSource Bolt 管道任务

  1. 一旦安装了 WhiteSource Bolt 任务的管道运行完成,构建结果页面将包含一个额外的标签,名为WhiteSource,显示类似的结果,如下图所示:

图 11.6 – WhiteSource 报告

图 11.6 – WhiteSource 报告

这完成了我们关于依赖项扫描的讨论。如前所述,您可以利用这些工具检测和扫描在构建应用程序时使用的所有依赖项。在下一节中,将介绍基础设施合规性。

确保基础设施合规性

另一个重要话题是合规性。在许多国家或市场中,创建软件时必须实施或遵守一系列规则和政策。其中很多政策与应用程序运行的基础设施相关。如果这些基础设施部署并管理在 Azure 平台上,Azure Policy 可以成为确保基础设施符合规定的强大工具。

第八章《将基础设施和配置实现为代码》中,讨论了 ARM 模板的相关内容。ARM 模板可以被视为一种技术,用于将完整的 Azure 环境描述为一个 JSON 数组,数组中的每个对象描述了应用程序基础设施中的一个资源。

Azure 策略允许您编写查询该文档以及通过任何 API 或 ARM 模板进行的更改的策略。每当找到一个与查询匹配的资源时,可以阻止其创建,或者将该匹配项添加到审核结果列表中。Azure 策略可以修复或修改不安全的配置,防止出现错误。

除了编写自定义策略外,还有许多现成的策略可供所有 Azure 用户使用。这些策略可以用来审核不符合最佳实践或通用建议的资源。还有一组组的策略,称为计划(Initiatives),它们描述了市场标准的适用部分。

分配 Azure 策略或计划

策略可以在 Azure 中的不同级别进行分配,具体可以是在资源组级别、订阅级别或管理组级别。可以通过门户、ARM 模板或蓝图,或者 PowerShell 来完成此操作。

要使用 PowerShell,可以使用以下一系列命令:

  1. 要检索资源组和策略的引用,请使用以下命令:

    $rg = Get-AzResourceGroup -Name myResourceGroupName
    $definition = Get-AzPolicyDefinition | Where-Object {
    $_.Properties.DisplayName -eq 'Audit VMs that do not use managed disks' }
    

这里选择的策略是一个内置策略,将审核所有未使用托管磁盘但在存储账户中有自定义磁盘的虚拟机。此策略定义将在以下任务分配的命令中使用。

  1. 要将策略分配给资源组,请使用以下命令:

    New-AzPolicyAssignment -Name 'audit-vm-manageddisks' - DisplayName 'Audit VMs without managed disks Assignment' -Scope
    $rg.ResourceId -PolicyDefinition $definition
    

在该任务分配后的 30 分钟内,新策略将生效。此时,会启动策略评估周期,所有在任务范围内的资源将会依据该策略进行评估。在撰写时,尚未发布有关此类评估周期所需时间的服务水平协议(SLA)。经验表明,评估周期的时间可以从 15 分钟到几个小时不等,具体取决于任务范围的大小。

编写 Azure 策略

虽然有许多内置的策略可供使用,但在许多用例中,仍然需要创建自定义策略。与任何其他 Azure 资源一样,策略是以 JSON 文档的形式编写的。适当的 ARM 资源类型为 policyDefinitions,其结构如下:

{
  "name": "string",
  "type": "Microsoft.Authorization/policyDefinitions", 
  "apiVersion": "2019-01-01",
   "properties": { 
    "parameters": {
     "location": { …}
   },
     "displayName": "…",
     "description": "…", 
     "policyRule": {
     "if": {
      "field": "location",
      "equals": "[parameters('location')]",
     },
     "then": {
     "effect": "<audit|deny >"
    }
  }
 }
}

parameters 对象可以用来指定在稍后分配策略时需要指定的一个或多个参数。这些参数遵循与 ARM 模板参数相同的语法,并且工作方式也相同。

displayNamedescription 属性可以用来为策略定义提供有意义的名称和描述,方便后续参考。

定义的主体包含以下两个元素:

  • if 语句用于指定一个查询,选择此策略应适用的 Azure 资源。JSON 中编写复杂查询的具体语法已在 ARM 模板参考中详细说明,链接在本章末尾。

  • then 语句用于描述需要对符合条件的任何资源采取的操作。这可以是拒绝——即自动拒绝任何不符合要求的资源的创建。另一种方法是并非拒绝不合规的部署,而是对其进行审计。虽然在理论上拒绝不合规的部署非常直接,但暂时允许不合规部署也有其合理的原因。在这种情况下,审计策略可以帮助跟踪这些资源。所有不合规的部署将在其 Azure 活动日志中记录审计记录,并可以在 Azure 门户的Azure 策略下的合规性标签页中查看。具体如下:

图 11.7 – Azure 策略合规性详情

图 11.7 – Azure 策略合规性详情

在编写策略定义之后,我们需要在 Azure 订阅中创建它,以便使其可用。这可以通过 ARM 模板完成,或者在门户中手动完成。从 DevOps 角度来看,建议将策略编写在源代码管理中,并通过管道作为 ARM 模板的一部分进行交付。这样,Azure 策略与应用程序的编写方式相同,可以进行审查并作为 DevOps 管道的一部分自动部署到 Azure。

倡议

在使用 Azure 策略时,许多公司发现需要创建大量策略来定义他们希望软件开发人员遵循的所有规则。为此,将策略进行分组可能会带来好处。这样的分组被称为倡议,这些也是通过 JSON 定义的:

{
  "name": "string",
  "type": "Microsoft.Authorization/policySetDefinitions",  
  "apiVersion": "2019-01-01",
  "properties": { 
   "displayName": "string", 
   "description":  "string", 
   "parameters": { … }, 
   "policyDefinitions": [
   {
     "policyDefinitionId": "string", 
     "parameters": {}
   }
  ]
 }
}

一项倡议的主体是一个对象数组。每个对象必须包含一个 policyDefinitionId 属性,并且可以选择性地包含一个带有 parameters 的对象。policyDefinitionId 属性必须通过其 Azure 资源 ID 引用有效的 policyDefinitions 条件。parameters 数组应指定策略所需的所有参数。通常,这通过让倡议指定所有策略的所有参数的组合集作为倡议参数来实现。然后,个别策略的参数通过引用倡议参数来指定。

获取审计结果

在分配了具有审计效果的策略后,该策略将在激活后自动评估分配范围内的所有资源。无法保证这个过程需要多长时间。对于新资源,策略评估结果通常会在 15 分钟内显示,但通常会更快。

一旦结果出来后,可以在门户中查看每个政策或计划的合规性状态,从而获得概览,如下所示的截图所示:

图 11.8 – Azure 政策合规状态

图 11.8 – Azure 政策合规状态

这份报告与其他手动审计报告的不同之处在于,这份概览会持续更新,反映出实际的当前合规状态——它不是某一特定时间点的合规快照。

这种合规性类型的一个重要好处是规则或政策持续应用于所有现有资源和任何即将发生的变化。这意味着可以确保应用环境始终符合要求,并始终遵守适用的规则和政策。

将其与通常每两个月进行一次的安全和合规审计的做法进行对比。通常,这会导致环境在审计前才符合合规要求,且合规性在审计后逐渐下降——直到下次审计时间到来,此时合规性又会接近 100%。在许多公司中,这会导致如下的合规性图表:

图 11.9 – 合规性水平随时间变化的成熟度

图 11.9 – 合规性水平随时间变化的成熟度

到此为止,我们已经讨论了 DevOps 实践如何通过确保基础设施合规性来帮助提高安全性和合规性的另一个例子。在下一部分,我们将讨论本章之前提到的几种替代工具,如 Defender for Cloud、Sonar Cloud 和 WhiteSource。

监控和检测运行时安全风险和威胁

到目前为止,我们讨论的所有安全工具都集中在防止将有漏洞的代码推送到生产环境。然而,一个完整的、已部署的软件解决方案,包括其所有支持基础设施,远不仅仅是代码。除此之外,解决方案的许多交互可能是意料之外或未经计划的。持续在生产环境中监控所有这些内容是必要的,不仅是为了防止安全问题的发生,还为了检测任何出现的安全问题。在 Azure 中,可用于执行此操作的工具之一是 Azure 安全中心。这个工具和 Azure Defender 现在被称为 Microsoft Defender for Cloud。

Defender for Cloud 工具提供安全态势管理和威胁防护,保护在 Azure、混合云和其他云平台上运行的工作负载。

Defender for Cloud 在管理资源和工作负载的安全性时满足以下三个需求:

图 11.10 – Defender for Cloud

图 11.10 – Defender for Cloud

  • 持续评估 – 该解决方案将提供当前安全态势的简要概览。

  • Security recommendation – 该解决方案将使用 Azure 安全基准加强资源和服务的安全性,并推荐优先级较高的加固任务以及详细的修复步骤,以改善安全态势。

  • Defend – 该解决方案将检测并解决对资源、工作负载和服务的威胁。这些警报会出现在 Azure 门户中,也可以通过电子邮件发送。

以下示例展示了针对资源加强安全性并提高整体安全态势的推荐:

图 11.11 – Microsoft Defender 推荐

图 11.11 – Microsoft Defender 推荐

Microsoft Defender for Cloud 具有更多功能,并且不断在增加新功能。在 Azure 中部署时,这是识别和管理安全风险的地方。

这部分讨论了用于监控运行时环境中的安全风险的各种技术。下一部分将介绍几种替代工具,用于执行前面提到的一些扫描任务。

其他工具

市场上有许多工具可以用于对应用程序代码和依赖项进行安全扫描。一些示例包括 WhiteSource、Black Duck、Veracode 和 Checkmarx:

  • WhiteSource 是 WhiteSource Bolt 的付费版本。它提供相同的服务以及更多功能。例如,它不仅在依赖项扫描时报告风险,还会在依赖项的最新扫描中出现新风险时发出警报。

  • Black Duck 是一款帮助团队管理使用开源软件相关风险的产品。它提供的服务与 WhiteSource 类似。

  • VeracodeCheckmarx 是代码扫描工具,用于识别易受攻击的代码。而 SonarQube 检查代码质量和安全风险,这两款产品则专注于安全风险。通常来说,它们在安全扫描方面更为出色,但缺点是价格较高。

  • Sonar Cloud 是 SonarQube 的托管环境,提供与 SonarQube 类似的功能。

  • CodeQL 是一种安全扫描工具,用于自动化安全检查。CodeQL 将代码视为可以查询的数据,并执行变种分析。变种分析是一种利用已知的安全漏洞作为种子,查找代码中类似问题的过程。Code scanning 是一种功能,允许您扫描 GitHub 仓库中的代码,以检测安全漏洞和编程错误。如果代码扫描检测到代码中的潜在漏洞或错误,GitHub 会通知开发者,并禁止他们提交额外的问题。

摘要

在本章中,您学习到 DevOps 和安全并不是两个相互冲突的目标,且 DevOps 实践可以帮助您加强安全性。首先,您学习了如何在持续部署流水线中处理密码和其他机密信息。接着,您学习了如何通过代码和依赖扫描工具增强流水线,同时将安全的“左移”原则应用其中。最后,您学习了如何使用 Azure Policy 来定义基础设施的约束和规则,以及如何自动应用这些规则,或对不合规的部署进行审核或自动拒绝。

通过您所获得的知识,您现在可以在公司内部与团队讨论如何解决 DevOps 团队中的安全问题。您可以与安全工程师合作,配置所使用的工具,并收到关于工作安全影响的自动反馈。

在下一章中,您将学习应用程序监控。此外,您将了解如何监控您的应用程序是否顺利运行,并收集运行时指标。

问题

以下是一些问题,供您测试对本章内容的理解。您可以在附录中的评估部分找到答案:

  1. 对还是错——确保软件交付的安全仅仅是部署流水线中的一个步骤。

  2. 哪种工具可以用于安全测试,通过代理识别有效的应用程序 URL,然后对应用程序进行各种攻击,例如注入?

  3. 对还是错——在大多数现代应用程序中,超过 50% 的代码库来自开源库。

  4. 在部署过程中或运行应用程序时,存储所需的机密信息的安全位置有哪些?(您可以选择多个答案。)

    1. 标记为机密的 Azure Pipelines 变量

    2. Azure Key Vault

    3. Azure DevOps Key Vault

    4. Azure 变量组

    5. Azure DevOps 安全变量

    6. Azure DevOps 服务连接

  5. 哪两种 Azure 产品可以用来在运行时检测安全风险?

进一步阅读

第四部分 – 闭环

DevOps 不仅仅是加速将优质产品交付到生产环境的能力。另一个非常重要的方面是观察和衡量使用情况以及其他关键性能指标。来自分析的洞察提供了至关重要的反馈,以塑造产品的未来,甚至帮助优先考虑其他质量举措,这些举措对于提高服务的整体可靠性至关重要。

本部分将介绍有效地为应用程序添加监控,以便收集可以更好地理解应用程序使用情况和用户行为的度量数据。持续学习的另一种方法是明确地从产品内请求反馈,以便用户在使用软件时可以分享宝贵的建议。通过这种方式,可以规划未来对最终用户体验的改进。

本书的这一部分包括以下章节:

  • 第十二章应用程序监控

  • 第十三章收集用户反馈

第十二章:应用程序监控

在前几章中,你了解了如何将 DevOps 原则应用于软件交付。你学会了如何从源代码管理到生产环境创建一条流水线。你还学会了如何确保交付符合要求并且安全,同时不牺牲速度或对业务价值交付的关注。本章中,你将学习如何将这条流水线转变为 DevOps 循环——一个持续交付新软件的过程,然后衡量你的应用程序的表现。这是一个持续的过程,因为你将评估应用程序在生产中的表现,并学习如何进行下一步。

为了实现这一目标,本章首先介绍了一种收集应用程序崩溃的方法。几乎所有应用程序都会在某个时刻抛出未处理的异常并崩溃。确保收集并报告应用程序崩溃,将帮助你调查其原因并进行修复。接下来,注意力将转向对应用程序进行仪器化。

仪器化是收集日志和指标的实践,帮助你了解应用程序在生产环境中的表现。你可以利用它们在出现问题时触发警报,或者希望在问题发生之前就能发现它们。本章最后探讨了与其他工具集成的几种选项。

本章涵盖的主题包括:

  • 调查应用程序崩溃

  • 仪器化 Web 应用程序

  • 与其他工具的集成

技术要求

为了实验本章中描述的技术,你将需要以下工具之一或多个:

  • 用于收集移动应用程序崩溃报告的 App Center 帐户

  • 用于收集桌面应用程序崩溃报告的 Raygun 订阅

  • 用于仪器化 Web 应用程序的 Azure 订阅

所有这些工具都提供免费试用选项。

调查应用程序崩溃

无论一个应用程序设计得多么完美,它最终都会因为某种意外情况而崩溃。为了从这些崩溃中吸取教训并尽量避免将来的崩溃,向应用程序添加代码以收集崩溃报告并将其发送到一个中心位置会有帮助。在那里,这些报告可以被分析和归类,以识别应用程序的改进方向。如何做到这一点取决于应用程序的类型。

以下章节讨论了移动和桌面应用程序如何工作。关于 Web 应用程序,收集崩溃报告可以使用与仪器化相同的工具;我们将在后面的仪器化 Web 应用程序部分讨论这一点。

收集移动应用程序的崩溃报告

用于收集移动应用程序崩溃报告和错误的众多工具之一是 Visual Studio App Center。除了分发移动应用程序,App Center还允许应用程序提交其崩溃和错误以供分析。

要开始使用 App Center 进行崩溃报告,首先需要定义应用程序。这被称为应用程序定义,如何处理它在第六章实现持续部署和发布管理中已经讨论过。通过这个应用程序定义,会创建一个应用密钥,用于配置应用程序发送崩溃报告。除了崩溃报告,还可以跟踪开发者感兴趣的其他错误和异常。要开始发送崩溃报告,需要执行以下步骤:

  1. 在项目中安装 Microsoft.AppCenter.Crashes NuGet 包。

  2. 将以下代码添加到应用程序初始化中:

AppCenter.Start("ios={appSecret};android={appSecret
};uwp={appSecret}", typeof(Crashes));

除了崩溃之外,还可以跟踪开发者感兴趣的其他错误。可以使用以下代码来实现:

Crashes.TrackError(ex);

所有未处理的异常都会被自动捕获并发送回 App Center。在这里,它们会变得可供分析,如下图所示:

图 12.1 – App Center 诊断概览

图 12.1 – App Center 诊断概览

点击报告的错误或崩溃项以打开详细视图,如下所示:

图 12.2 – App Center 诊断详细视图

图 12.2 – App Center 诊断详细视图

每个崩溃或错误会显示一个包含最重要信息的仪表板。这些信息包括报告数量和受影响用户的数量,还会显示受影响的设备类型和操作系统。在页面顶部,显示堆栈跟踪,开发者可以使用这些信息来调查并希望能修复该问题。

App Center 与 Azure DevOps、Jira 和 GitHub 集成,供 bug 跟踪使用。有关更多信息,请参考此链接:docs.microsoft.com/en-us/appcenter/dashboard/bugtracker/

对于任何关键事件,App Center 可以直接创建一个 bug 并发送电子邮件通知。

这部分涵盖了从移动应用程序收集崩溃报告和错误的内容。接下来的部分将介绍桌面应用程序中的相同概念。

收集桌面应用程序的崩溃报告

桌面应用程序也可以进行崩溃报告。对于桌面应用程序,有许多可用的解决方案,而且大多数的工作方式大致相同。其中一个解决方案是 Raygun。Raygun 是一个商业产品,适用于 .NET 应用程序,但也可以用于其他许多语言和平台。

使用 Raygun 收集崩溃的步骤如下:

  1. 注册一个 Raygun 账户。

  2. 在解决方案中安装 Mindscape.Raygun4Net NuGet 包。

  3. 捕获未处理的异常并将其转发到 Raygun。

以下示例演示了如何捕获并转发未处理的异常到 Raygun:

class Program
  {
    private static readonly RaygunClient _raygunClient = new RaygunClient("myKey");
    static void Main(string[] args)
    {
      AppDomain.CurrentDomain.UnhandledException += HandleEx; throw new Exception("Boom!");
    }
    private static void HandleEx(object sender, UnhandledExceptionEventArgs e)
    {
      _raygunClient.Send(e.ExceptionObject as Exception);
    }
  }

所有异常都可以在 Raygun 的 Web 界面中进行探索。在这里,如果堆栈跟踪足够相似,异常会自动分组。它们也可以被分组并单独浏览,但在大多数情况下,专注于较大的异常组更有意义。

以下截图展示了如何在 Raygun 中浏览这些组:

图 12.3 – 桌面应用程序异常的 Raygun 视图

图 12.3 – 桌面应用程序异常的 Raygun 视图

在此界面中点击异常消息将显示完整的堆栈跟踪以及任何发生的异常实例的所有共享属性。

这完成了我们对从移动和桌面应用程序收集崩溃报告的讨论。这样做可以让你发现并调查客户在生产环境中遇到的问题。接下来的部分将介绍 Web 应用程序的监控,以进一步增强我们对应用程序在生产环境中行为的洞察。

Web 应用程序的监控

Web 应用程序与移动应用程序和桌面应用程序在许多方面不同,其中之一是大部分应用程序的运行是由服务器而非客户端执行的。这使得开发人员比其他类型的应用程序更容易收集 Web 应用程序的运行信息。这就是为应用程序添加监控的过程。

日志是系统保存的文本消息,用于描述服务器执行的路径。这有助于开发人员回溯并通过检查日志输出来探索发生过的事件。结构化日志正迅速成为跟踪日志的标准。结构化日志是一种技术,其中日志不再仅仅是文本消息,而是带有每个参数值集的参数化文本消息。这有两个优势——日志可以更好地压缩,而且可以更快速地进行搜索。

度量指标是为应用程序记录的值。它们通常由时间戳、指标名称和值组成。一个例子是每秒记录一次 CPU 使用率的百分比。

在为应用程序添加监控时,容易专注于许多服务器级别的日志和度量指标。例如,许多操作员默认会开始收集诸如 CPU 使用率、内存压力和 I/O 操作等度量指标。虽然这些度量指标本身没有问题,但它们不一定能从用户的角度反映应用程序的性能。其他度量指标,如响应时间或队列消息处理延迟,可能会更好地提供关于用户体验的见解。虽然测量系统指标没有错(它们通常是未来问题的良好指示),但你也应该尝试收集以用户为中心的度量指标。

Azure 提供了 Application Insights 服务,用于对应用程序进行监控,重点是 Web 应用程序。可以通过 Azure 门户创建一个 Application Insights 工作区,该工作区会打开一个工作空间,如下图所示。在 Azure 门户的概述部分,监控密钥字段清晰显示,建议将其视为应用程序机密:

图 12.4 – Azure Application Insights 概述

图 12.4 – Azure Application Insights 概述

以下小节将详细介绍日志记录、度量和单个请求的调查。

日志记录

最基本的监控方式之一就是在应用程序代码中添加日志语句。过去,这些日志会保存到运行应用程序的服务器磁盘中。因此,获取和调查这些日志需要大量的时间和精力。

在现代托管环境中,日志不再保存在本地文件系统上,而是远程存储。由于基础设施的临时性,服务器可以随时增加或移除,因此无法再将日志保存在服务器上,并确保以后可以检索到。因此,这些日志会通过 HTTP 传输到专用的日志存储中,如 Application Insights。

生成日志

要从 ASP.NET 应用程序将日志条目写入日志存储(例如 Application Insights),必须执行以下两项操作:

  1. 需要在应用程序代码中使用 ILogger 接口生成日志条目(如适用)。该接口可以通过 Microsoft.Extensions.Logging.Abstractions NuGet 包使用。

  2. 需要安装 Application Insights NuGet 包(Microsoft.ApplicationInsights.AspNetCore),并将 Application Insights 注册为 LoggingProvider。这样,所有发送到前述接口的日志都会转发到 Application Insights 代码中。反过来,这段代码会将所有日志转发到 Application Insights 服务。

以下示例代码展示了如何使用类中的 ILogger 接口来生成结构化日志条目:

public class Example
{
private readonly ILogger<Example> _logger;public Example(ILogger<Example> logger)
  {
    _logger = logger;
  }
  public void DoSomething(User user)
  {
    _logger.LogWarning(
      "Doing something for user with id '{userId}' and username '{username}'",
      user.Id, user.Username);
    }
}

重要提示

日志条目开头不应有美元符号($)。这里没有使用字符串插值,但有两个占位符被插入到文本消息中。结构化日志条目会识别这些占位符,并在显示条目时插入提供的值。

在生成日志条目后,应注册一个日志提供程序来捕获这些日志。这个过程是通过 .NET Core 内建的依赖注入实现的。

启动应用程序后,所有级别为警告及以上的日志条目会自动转发到 Application Insights。要更改转发哪些条目以及哪些不转发,可以配置过滤器。本章末尾提供了有关如何详细配置 Application Insights 的链接。

搜索日志

在将日志条目发送到 Application Insights 后的几分钟内,它将出现在界面上供查询。为此,请打开 Application Insights 实例,并在左侧菜单中导航至 日志 (1)。这将打开如下所示的视图:

图 12.5 – Application Insights 日志视图

图 12.5 – Application Insights 日志视图

在这里,可以编写查询 (2) 来搜索以 Kusto 查询语言 (KQL) 记录的日志。Application Insights 被优化用于处理大量数据,大多数查询能在一秒钟或更短时间内返回结果,即使是在搜索数百万条日志条目时。

针对日志设置警报

收集和搜索日志在排查特定情况或响应用户投诉时非常有用。然而,在某些情况下,当某个条件出现时,自动通知比手动搜索更为有效。这就是警报的作用。

在 Azure 中,可以创建警报规则,当满足某个条件时通知开发人员。警报功能由 Azure Monitor 提供,并与许多 Azure 服务(包括 Application Insights)集成。

要创建一个新的警报规则,请按照以下步骤操作:

  1. 使用门户导航到 Azure Monitor。

  2. 现在,选择 警报。这将打开如下所示的视图:

图 12.6 – Azure Monitor 警报视图

图 12.6 – Azure Monitor 警报视图

如果有需要注意的警报,它们将在此处显示:

图 12.7 – Azure Monitor 警报创建视图

图 12.7 – Azure Monitor 警报创建视图

使用屏幕左上角的 创建 按钮来添加新的警报规则。这样会打开另一个视图,如前面的截图所示。在这里,可以配置警报条件:

图 12.8 – Azure Monitor 警报审查 + 创建视图

图 12.8 – Azure Monitor 警报审查 + 创建视图

在前面的截图中,打开用于配置警报的视图——范围——如图所示。在这里,需要选择资源以创建警报。

  1. 这里的 (1) 是警报的资源主题。这可以是任何类型的资源,在这个实例中,警报将针对一个 Application Insights 工作区。

  2. 这些 (404 代码结果:

    requests
    | where success == False and resultCode == 404
    
  3. 400 代码结果。

  4. 评估警报条件的时间间隔 (2c):当指定一个匹配特定数字的查询时,这将决定必须满足该数量的时间间隔。

  5. 评估警报条件的频率 (2d):过于频繁地评估警报条件可能会导致警报频繁开闭,产生快速的警报序列。过于不频繁地评估警报条件可能会导致警报反应过迟。实验将帮助你了解如何配置这个参数。

  6. 这是在满足警报条件时执行的操作。由于可能有许多警报必须调用相同的动作组,因此可以将操作分组,并在此处引用这些操作组。一些操作的示例包括调用 Webhook 或发送短信或电子邮件。

  7. 警报配置通过输入名称和描述(3)来完成。

  8. 最后,警报可以保存。

在按前面的截图回顾并创建警报后,激活会自动完成,几分钟内,警报就可以开始检查应用程序日志并在满足警报条件时发出信号。

日志记录是深入了解请求发生了什么以及错误如何发生的绝佳方法。另一种了解应用程序行为的技术是使用度量。

度量

除了日志外,应用程序还可以发出一个或多个度量。度量是随时间变化的一系列值,描述系统的一个或多个方面。以下是一些度量的示例:

  • 当前登录的用户数量

  • 用户查看的产品数量

  • 数据库事务的数量

收集此类度量可以提供有关系统使用情况和当前操作方式的洞察。度量通常用于创建仪表板和警报。

发出度量

要开始使用度量,首先必须由应用程序发出度量并将其存储在集中位置。除了日志记录外,还可以使用 Application Insights 进行度量。

使用 Application Insights 进行度量时,需要采取以下步骤:

  1. 度量需要在应用程序代码中发出,使用TelemetryClient类。在Microsoft.Extensions.Logging.Abstractions NuGet 包中可以找到此接口。

  2. 安装Microsoft.ApplicationInsights.AspNetCore应用程序 Insights NuGet 包。

  3. 使用Dependency容器注册TelemetryClient。通过在容器构建器上使用扩展方法来完成此操作,如下代码片段所示:

    builder.RegisterType<TelemetryClient>().SingleInstance();
    
  4. 完成此操作后,应用程序即可开始发出度量。这是通过使用TelemetryClient类来完成的:

    public class Example
    {
    private readonly TelemetryClient _telemetryClient;
    public Example(TelemetryClient telemetryClient)
    {
    _telemetryClient = telemetryClient;
    }
    public void DoSomething()
    {
    _telemetryClient.GetMetric("doSomethingCalledCounter").TrackValue(1.0);
     }
    }
    

发出度量涉及两个步骤。首先,使用GetMetric()方法检索度量的引用。接下来,使用TrackValue方法提交一个值。提交的值应该是双精度浮点数或允许隐式转换为双精度浮点数的类型。

一旦度量被发出,它们可以用于创建图表和度量。然而,在继续这些话题之前,首先需要讨论另一种类型的度量——即 Azure 平台度量。

除了应用程序发出的度量外,还有许多可以从系统运行所在的 Azure 平台记录的度量。以下是一些示例:

  • CPU 使用百分比

  • 服务总线上的消息数量

  • 每秒数据库事务数量

  • 可用的磁盘空间量

这些指标通常与应用程序的性能密切相关,甚至可能是领先指标。例如,当可用磁盘空间达到 0 时,大多数 Web 服务器会停止工作。

在 Azure 中,每个服务默认会发出一系列指标,这些被称为平台指标。发出哪些指标因服务而异,且无法由用户影响。这些指标也会自动被 Azure Monitor 收集,并可以像应用程序发出的指标一样用于绘图和告警。

平台指标是集成的、免费的,并且大多数资源的指标会保留 93 天。

绘制指标图

所有收集到的指标,无论是在 Application Insights 还是 Azure Monitor 中,都可以用来构建图表和仪表板以可视化指标。可以使用每个 Azure 资源上都可以访问的指标选项卡来创建图表。也可以使用 Azure Monitor 来创建图表,这样就可以将多个资源的图表合并到一个画布中。操作步骤如下:

  1. 打开 Azure Monitor,它可以从左侧菜单中访问。

  2. 导航到指标菜单。这将打开如下所示的视图:

图 12.9 – Azure Monitor – 指标

图 12.9 – Azure Monitor – 指标

  1. 打开画布后,可以向其中添加一个或多个图表。图表是使用顶部的图表构建器构建的。在这里必须做出四个选择:

    • 需要为其绘制图表的资源。

    • TelemetryClient

    • 来自上一节的GetMetric()方法。

    • 将多个测量值合并为图表中的一个点的数学操作:可以选择最小值、最大值、平均值、总和或计数。

    • 要在同一个图表中添加多条指标线,请选择顶部的添加指标。重复前面的四个选择来配置新图表。

  2. 若要将此图表作为仪表板的一部分以便于重复使用,请点击顶部的固定到仪表板按钮。

  3. 然后可以通过右侧菜单直接访问仪表板:

图 12.10 – App Service Http400 指标图

图 12.10 – App Service Http400 指标图

拥有某个指标的图表,或者在仪表板中拥有多个图表,对于调查问题非常有帮助。然而,没人喜欢一直盯着仪表板看事态进展。因此,也可以配置指标告警。

告警指标

就像日志条目一样,当某个指标超过或低于某个阈值时,可以通过 Azure Monitor 设置告警。需要后续跟进的日志条目可能仅与遇到问题的某个用户或客户相关。而指标则有助于检测所有用户都受某个问题影响的情况,或检测基础设施停止工作或即将停止工作的情况。

对指标创建警报的工作方式与从日志创建警报非常相似。要创建新的警报规则,请使用门户导航到 Azure Monitor,然后选择警报

调查请求

在使用应用洞察进行日志记录和度量时,您可以使用应用洞察的许多内置功能。其中一项功能是从一个称为搜索的视图执行针对应用洞察收集的所有类型数据的搜索查询。

在此处,可以搜索应用洞察收集的所有信息,包括以下内容:

  • 应用程序代码发出的日志,其中包括 NuGet 包和 .NET Framework。

  • 所有依赖调用:这些是对数据库和其他系统的调用,由应用洞察自动检测到。应用洞察记录目标系统和持续时间。

  • 所有异常:应用程序中发生的所有异常都会被应用洞察记录,即使应用程序代码已正确处理。

  • 请求:所有通过 HTTP 进入的用户请求都会被记录。还包括重要属性,如 URL、持续时间和 HTTP 动词。

要使用搜索视图搜索特定事务,请在 Azure 门户中打开正确的应用洞察实例,然后导航到事务搜索菜单(1),以获取以下截图显示的视图:

图 12.11 – 应用洞察 – 事务搜索

图 12.11 – 应用洞察 – 事务搜索

事务搜索视图中,可以配置几个搜索参数(2):

  • 要搜索的时间间隔:默认为最近 24 小时。

  • 要搜索的事件类型:可以是请求、日志条目、页面查看、异常、依赖调用等。

  • 任何要搜索的文本。

几秒钟内,条形图显示所有匹配的结果。每个条形代表一个时间段,并显示在该时间段内有多少匹配项。在此图表下方显示所有单个匹配项。这些是所有可用事件类型的混合。

点击任何结果会打开一个新视图,显示选定的记录与所有其他类型的请求分组相关联的情况。这使您可以快速导航到应用洞察在单个用户请求执行期间收集的所有日志、依赖调用和异常。结果可以显示为列表和时间轴。

这使您能够快速查看服务器在执行用户请求时的操作:

图 12.12 – 应用洞察 – 端到端交易详细信息

图 12.12 – 应用洞察 – 端到端交易详细信息

在所有这些用于调查应用程序并接收事件通知的手段下,决定创建哪些告警、哪些不创建是非常重要的,这不仅是为了创建一个健康的工作环境,也是为了平衡监控与新工作的关系。这是下一节的主题。

优化告警

一旦团队开始为应用程序添加指标并设置告警,首批告警就很快会出现。在这一点上,重要的不是仅仅响应告警,还要调查它们并关闭它们。告警还应当被评估,并视为学习的机会。

优化告警

创建一系列告警后,重要的是定期重新评估它们。通过这种评估,可能得出的两点结论如下:

  • 告警阈值变化:定期评估告警涉及查看一段时间内的指标,并观察当前告警阈值的情况。这可能导致得出结论,阈值设得太低或太高。

  • 消除重复项:查看一个月(或几个月)内触发的告警,很可能你会发现一个或多个告警组总是同时触发。例如,针对特定 Web 服务器设置的一组告警,可能是如此相关,以至于它们总是同时触发。一个常见的例子是 CPU 使用率和 HTTP 请求的平均响应时间,这两个通常会同时上升。如果是这种情况,值得考虑删除其中一个告警,或者将其中一个降级为仅警告。重复的告警增加了需要立即响应的项目数量,导致团队在没有明确好处的情况下承受更大的压力。

不断优化告警集不仅有助于减少浪费,还能防止所谓的告警疲劳。

告警疲劳

如果告警规则不不断地审查和更新,它们可能会对团队产生负面影响,尤其是当告警规则触发过于容易或过于频繁时,人们将不再正确响应它们。如果告警过多,团队成员会感到疲劳,对告警的反应变得麻木。无论这些告警是虚假告警还是实际告警,单纯的告警数量足以让人们进入一种不再关心的状态。

如果在团队中观察到这种情况,便是时候彻底改变告警的生成和响应方式了。如果不这样做,团队成员可能会生病或完全离开公司。

防止这种情况发生的一种方式是实施健康的值班安排。

捕获哪些指标

在讨论指标时,常常会有一个问题:应该发送和监控哪些指标?有许多可能的指标,关于这个问题也有更多的看法。作为一个良好的起点,以下是通常会为 Web 应用程序收集的一些指标:

  • 每分钟请求数、每分钟事务数或类似的指标:这是一个旨在捕获 Web 应用程序当前负载或吞吐量的指标。

  • 平均响应时间:这个指标捕获时间窗口内所有请求的响应时间。

  • 400 及以上的状态码通常会被记录。

当这三个指标被捕获并一起绘制成一个图表时,它提供了理解应用程序行为的第一步。让我们探索几个例子:

  • 当平均响应时间上升,但吞吐量(每分钟请求数)保持不变时,这可能表明托管应用程序的基础设施出现了问题。

  • 当吞吐量和平均响应时间同时上升时,这可能表明流量增加,当前的基础设施无法在相同的响应时间下承受如此高的吞吐量。

  • 当错误率上升,而其他指标保持不变时,这可能表明部署出现了问题,或者某个特定的代码路径开始产生(更多)错误。

当然,这些仅仅是例子,还有许多可能的场景。其他指标可以帮助排除某些特定场景或避免它们。例如,开始监控数据库负载百分比也可以帮助检测这三种场景的具体实例。如果数据库负载接近 100%,可能是时候将数据库升级到更高性能的层级,以便在相同响应时间下支持更高的吞吐量。

总结这一部分时,有一个最终的建议——在开始监控时,通常有一种倾向是专注于托管应用程序的系统。作为替代方案,也可以考虑监控那些对业务有直接影响的指标,或者反映用户满意度的指标,这些指标与应用程序的可用性相关。与仅仅监控系统相比,这种方法更接近于衡量业务价值。

以下是一些例子:

  • 在一个在线商店中,每分钟销售的书籍数量可以是一个非常有价值的业务指标。试想一下,如果通过 Azure Monitor 和应用程序代码中的自定义指标,能够几乎实时地获取该指标,这对业务的影响将有多大。

  • 对于一个在线阅读平台,虚拟翻页次数可以是一个有价值的指标,能够反映用户是否愉快地使用该服务。一旦该数字出现急剧下降或快速增长,可能表明出现了问题。

要找出在特定场景下哪些指标是有意义的,可能需要与业务或领域专家进行交流。

制定待命排班表

一旦配置了警报并开始触发,就没有必要设置警报在早上 8 点之前或下午 5 点之后不触发。换句话说,必须确保一定严重程度的警报即使在非工作时间也能得到跟进。

在许多引入警报的新公司中,存在一种隐性预期,即某些人在正常工作时间以外(除了完成常规工作任务)需要处理这些警报。有时,如果警报每年只触发一次或两次,而且没有关于响应时间的协议,这可能根本不会成为问题。

然而,在许多组织中——尤其是随着时间的推移——存在一种期望,即这些警报需要在一定时间内得到响应。此外,随着系统规模的扩大和复杂性增加,或者系统数量的增加,警报的数量也可能会增加。

应对这一问题的方式是制定待命排班表,并与工程师达成正式协议,明确他们的期望以及组织如何奖励他们的努力。这使得组织能够设定清晰的期望,并且工程师可以根据这些协议来管理自己的空闲时间。足够的系统停机时间可以帮助工程师在高压期之间放松,从而确保他们在待命时保持警觉,随时准备响应。

有很多资料讨论什么构成健康的待命排班表,什么不构成,而这里的关键词是健康。一些常见的建议如下:

  • 在非工作时间待命的人员不应在工作时间也待命。

  • 为待命的工程师提供合理的补偿,以奖励他们保持电话畅通、不受干扰等。什么是合理的补偿因情况而异,但待命的要求越高,补偿就应该越高。

  • 为待命人员提供合适的工具。例如,当期望响应时间为 30 分钟或更短时,为待命人员提供一个背包,里面装有笔记本电脑、电话和上网工具。

  • 确保每个员工在至少 75% 的时间内不需要待命。

  • 允许员工在换休时休假,这样如果他们需要在晚上响应警报,第二天上班时可以迟到。

每次系统正常运行被打断后,无论是在工作时间还是工作时间外,都可以进行现场事故审查,了解发生了什么,并探讨如何降低再次发生的可能性。

现场审查

当警报触发后,团队响应并解决问题后,便是评估发生了什么的时候。这就是现场事故审查。在此,整个团队会聚集在一起,讨论以下内容:

  • 发生了什么——首先,应该从事件被发现的时间开始,构建一个时间线,直到正常操作恢复为止。接下来,时间线会扩展,加入导致触发事件的相关事件。

  • 接下来,将评估一系列事件,以了解响应中哪些方面做得很好。如果团队成员之一使用了新工具快速诊断了问题,这可以让团队的其他成员也受益。

  • 只有在此之后,才是审视可能改进的点,并将这些点转化为团队的高优先级任务的时候。可能的故障保护被识别并安排实施,或是识别出新的警报,这些警报会在类似问题再次发生之前发送预警。

  • 触发初步响应的警报或警报组会被评估,以确定它们是否足够充分或可能包含重复的警报。

最适合进行现场事件回顾的时机是事件发生后尽可能快的时间。实际上,这意味着给每个人足够的时间休息和恢复,并计划在下一个工作日召开会议。

这完成了我们对Application Insights和 Azure Monitor 在仪表化 Web 应用程序方面的概述。接下来的部分描述了几种将 Application Insights 和 Azure Monitor 与其他工具集成的方法。

与其他工具的集成

Azure Monitor 和 Application Insights 是收集应用日志和指标的出色工具,同时也能存储它们并使其可搜索。然而,开发团队或企业可能有理由更倾向于使用其他工具来可视化应用性能或响应警报。集成的一个重要驱动因素通常是某个人或团队主要使用的工具。如果一个团队主要在 ServiceNow 或 Grafana 中工作,通常将这些工具与 Azure Monitor 集成,而不是强迫这些团队使用多个工具,是一种更有用的做法。

存在许多可能的集成,以下子部分详细介绍了一些例子。

IT 服务管理应用程序

在上一部分中,我们讨论了如何对 Web 应用程序进行仪表化时引入了操作组。操作组是执行响应警报时需要进行的一组操作。

除了丰富的内置功能外,还可以在现有的IT 服务管理ITSM)解决方案中自动生成警报。如果公司内已有 ITSM 解决方案,那么使用 Azure Monitor 时不需要创建独立的警报渠道。相反,使用 Azure Monitor 的 ITSM 连接器可以让你通过一个解决方案来管理全公司范围的所有警报。

目前,已有与 ServiceNow、Provance、System Center Service Manager 等工具的集成。这些连接是通过 ITSM 连接器创建的。

Azure Boards

在许多开发团队中,Azure DevOps 是开发人员花费大部分时间使用的工具。它也是他们通过 Azure Boards 进行待办事项管理的地方。

与此同时,运维人员(希望开发人员也能参与)在 Application Insights 中进行调查工作,以确定用户错误的原因并深入分析故障原因。这项调查工作可能会产生新的任务,需要在 Azure DevOps 中加入待办事项。

为了便于操作,可以通过以下步骤在 Application Insights 中配置与 Azure DevOps 的集成:

  1. 在左侧菜单中导航到工作项选项(1)。这将打开以下截图中显示的视图。在这里,可以配置与 Azure Boards 的连接:

图 12.13 – Application Insights – 工作项集成

图 12.13 – Application Insights – 工作项集成

  1. 要配置连接,需要填写以下详细信息:

    • 输入 Azure DevOps 链接。在此,需附加组织的名称。

    • 选择要使用的 Azure DevOps 项目。可以从下拉菜单中选择。

    • 选择一个将创建新项的产品区域。默认情况下,这与项目名称相同,除非你更改它。

    • 提供用户名称作为新工作项的默认所有者。

配置此连接后,在 Application Insights 的相关页面上将显示一个新的+ 创建工作项按钮。此按钮允许你在待办事项中直接创建一个包含所有相关信息的缺陷。

Grafana

Azure Monitor 允许你构建简单、易于使用的仪表板。使用 Azure Monitor 仪表板的优势在于它们与所有其他 Azure 实践(如基于角色的访问控制RBAC)和 Azure 资源管理器模板)完美集成。

然而,团队可能已经采用了其他可视化工具,如 Grafana。Grafana 是一个著名的平台,适用于操作仪表板。Grafana 可以配置为使用 Azure Monitor 连接,并查询指标以进行图表绘制。Grafana 还具备告警功能。

要将 Grafana 连接到 Azure Monitor,需要执行以下步骤:

  1. 在你的 Azure 订阅所使用的 Azure Active Directory 帐户中创建一个新的应用程序注册。记下此应用程序注册的租户 ID客户端 ID订阅 ID客户端机密属性。

  2. 为应用程序注册创建一个新的 RBAC 角色分配,并至少在要监视的资源上设置Reader权限。

  3. 在 Grafana 中配置一个新的Azure Monitor类型的数据源。插入在步骤 1中收集的用于 Azure 身份验证的属性。

  4. 向仪表板添加一个新图表,选择Azure Monitor作为数据源。

通过执行前面的步骤,可以在几分钟内设置 Grafana 与 Azure Monitor 的连接。

总结

在本章中,您学习了如何开始完成 DevOps 循环。您还学会了如何处理崩溃报告,并从各种类型的应用程序中收集这些报告,以及如何为 Web 应用程序添加监控。您现在知道如何使用 Application Insights 集中日志和度量,并获取请求和依赖调用的洞察。您还学会了如何将 Azure Monitor 与其他工具集成,以进一步简化开发流程。

有了这些知识,您现在可以开始了解您的应用程序在生产环境中的运行情况。通过这样做,您不仅能更快地交付软件,还能从其使用情况中学习并开始改进。

在下一章中,您将学习如何收集用户反馈,以补充您从系统日志和度量中学到的内容。您还将学习如何衡量最终用户对您的应用程序和新功能的满意度。

问题

在我们总结时,这里有一份问题清单,供您测试自己对本章内容的理解。您可以在附录评估部分找到答案:

  1. 是否可以使用 Application Insights 从 Azure 平台服务中捕获自定义度量?——对还是错?

  2. 在 Azure Monitor 中,平台度量的保留时间是多少?

  3. 是否可以使用 Application Insights 从您自己的应用程序代码中捕获自定义度量?——对还是错?

  4. 当工程师因为收到过多警报而开始忽视这些警报时,这种情况被称为什么?

  5. 在 Azure 中,当警报触发时,是否可以调用 Webhook?——对还是错?

进一步阅读

第十三章:收集用户反馈

在前一章中,您学习了如何衡量应用程序在生产中的运行情况。您学会了如何收集崩溃报告和日志,以及如何对应用程序进行仪器化。然而,软件的目的不仅仅是交付完美运行的应用程序,而是创造业务价值。收集用户反馈是必要的,以确定您的应用程序是否也实现了这一更高的目标。在本章中,您将学习如何测量用户是否满意、他们使用哪些功能以及不使用哪些功能,以及如何利用这些信息来引导未来的开发。

为此,本章首先介绍了持续反馈的概念。接下来,它将介绍不同的方法来向用户请求反馈并记录他们的响应。这既可以是应用内的方式,也可以通过其他渠道进行。除了直接收集反馈之外,您还可以利用其他间接渠道。例如,在 Twitter 上对软件的反应和应用程序中功能的使用。最后,本章将介绍假设驱动开发,这是微软实践的一种软件开发方法。

本章将涵盖以下主题:

  • 理解持续反馈

  • 请求反馈

  • 收集间接反馈

  • 实施假设驱动开发

理解持续反馈

如在第一章中所解释的,DevOps 简介,DevOps 是一场文化运动,试图将开发人员和运维人员更紧密地联系在一起,以帮助他们更快速、更可靠地提供业务价值。反馈循环是实现这一目标的重要元素。在前一章中,我们看到了许多反馈循环:

  • 开发者可以在本地机器上运行单元测试,以验证他们的更改是否破坏了现有的行为。

  • 在源代码检入后,所有单元测试将再次运行,并启动更多测试的流水线。

  • 除了功能测试,还可以运行安全测试和依赖扫描。

  • 发布后,收集日志和指标以确定应用程序是否运行顺畅。

所有这些都提供了关于工作技术质量的反馈,现在是时候增加一个更多的反馈循环——一个旨在验证应用程序是否真正满足用户需求的循环。

尽管这听起来很明显,但比大多数开发者愿意承认的更容易被忽视。在许多公司中,对产品所有者或业务分析师有信心,他们被信任能预测用户需要哪些功能并按优先级排序。

我们知道软件开发是一项复杂的活动,其中变更的结果通常无法事先预测。在这种情况下,持续寻求用户反馈以确定功能是否提供了应有的价值是非常重要的。

持续寻求反馈将有助于做出如下决策:

  • 移除大多数用户未使用的功能;这样可以减少对这些功能的维护需求,从而降低成本,并释放开发时间。

  • 扩展用户最常使用的功能,使其在界面中更加突出。

  • 根据用户对应用程序的感知质量,增加或减少测试工作量。

进一步推理下去,我们可能会得出结论,无法预测一个功能是否会真正提供足够的商业价值来证明其存在的必要性。做此类工作的公司通常会采用假设驱动开发的做法,稍后将讨论这一点。

下一部分将介绍请求应用用户反馈的不同方法。

请求直接反馈

收集用户反馈的一个非常直接的方式就是直接向用户询问。近年来,越来越多的应用程序在其内部嵌入了反馈机制。其他常见的方法包括发布公开的产品路线图并直接与客户互动。

产品内反馈的优势

收集产品内反馈是获取直接用户反馈的好方法。产品内反馈的例子包括评分特定视图或操作,给出点赞或点踩,或发送开心或难过的表情符号。

收集产品内反馈具有以下优势:

  • 这是客户提供反馈最简单的方式,几乎不占用他们的时间。

  • 由于这种方法的非侵入性,许多终端用户可能会选择回应。

  • 记录的反馈可以是上下文感知的。

  • 在记录评分、表情符号或点赞和点踩反馈时,应用程序还可以记录当前状态和最近的用户活动,并将这些信息与用户反馈一起发送。这使得用户的一次点击比表面看起来更有价值。它能够快速洞察应用程序中最受欢迎和最不受欢迎的部分。

  • 最后,允许产品内反馈能让用户感到自己被听到和重视。

提示

当然,记录有关用户及其使用应用程序的数据需要获得他们的同意。你对收集用户信息的意图需要完全透明。同时,通常需要明确的内容选择加入(opt-in),并提供撤销先前同意的选项。具体要求因国家而异,是一个法律考虑因素。

这种反馈类型的缺点是,分析这些反馈可能会过于繁琐。此外,由于结果通常是匿名的,无法跟进反馈。这使得很难理解为什么用户对某个应用功能感到满意或不满意。有时,通过在反馈框下方添加一个复选框来反制这一点,复选框内容可能是我同意被联系讨论这个问题

如果你想理解用户反馈的原因,其他反馈机制,如访谈或焦点小组,可能更为合适。

拥有一个公开的路线图

另一种收集用户反馈的方法是公开分享当前待办事项中已完成的和未完成的任务。一个公开分享他们正在处理哪些功能的团队是 Azure DevOps 团队。当然,这个列表并不包含产品团队计划的所有功能。原因可能是为了保持竞争优势,或者为了保密一些新功能,直到重大公告发布。然而,他们的待办事项提供了一个关于当前正在开发的功能的良好洞察。

采用这种做法可以让产品的用户参与并评论这个公开的列表。用户可以请求将某些功能在优先级列表中上移或下移,并分享他们缺失哪些功能。

这能为公司带来以下好处:当用户参与反馈功能列表时,他们会被鼓励具体说明为什么提出某个请求。这可能提供关于客户需求的新见解,并可能导致优先级的调整。

这种方法也有一些缺点:

  • 并非所有用户都会参与并提供关于公开待办事项的反馈。这可能导致在提供反馈的群体中,偏向那些更为活跃或要求更多的客户。虽然这不一定是问题,但需要注意这一点。

  • 与用户就功能请求或他们希望在列表中上移或下移的功能进行互动可能非常耗时。尤其是与产品内反馈相比,这种方式需要更多的时间。

除了拥有公开的功能路线图外,还有其他方法可以让用户了解公司目前在做什么以及他们的计划。以下是一些例子:

  • UserVoice:UserVoice 是一个平台,允许用户提出新功能并对其他人提出的功能进行投票。它使得在不对用户开放实际待办事项的情况下收集用户的想法成为可能。

  • Bugtrackers:如果客户非常积极地报告应用程序中的错误和故障,开放一个 Bugtracker 可能会有所帮助。这允许用户查看哪些问题已知,并且是否以及何时可能被修复。

公开的待办事项和像 UserVoice 这样的反馈平台比开放的待办事项更为常见。公开的错误或问题列表更多地出现在开源开发中。

使用访谈或焦点小组

其他形式的用户反馈请求包括一对一面谈和焦点小组。尽管这些比开放式需求池和公开讨论更耗时,但它们也有利于选择更加平衡的用户。

例如,如果一个应用明显针对四个不同的市场段,那么有五个焦点小组可能是有利的——每个市场段一个,以及一个混合市场段的额外小组。前四个将使我们能够专注于每个群体的特定需求,而第五个将引发大量讨论,并揭示不同群体的不同愿望如何比较。

面谈和焦点小组还更适合不仅获取反馈,还了解用户的推理方式。面对面与用户交谈使我们能够探索他们的推理方式以及他们如何看待应用程序。

这里结束了对直接用户反馈的讨论。下一节将讨论间接用户反馈。

收集间接反馈

软件开发中一个众所周知的说法是用户不知道他们想要什么。虽然这听起来有些苛刻,但直接来自讨论、面谈和焦点小组的用户反馈并不一定导致良好的产品反馈的原因有几个:

  • 其中一个原因是每个人都想被喜欢。在进行面谈或与一群用户交谈时,他们可能只会说出他们认为面试官想听到的话。

  • 这需要很高的周转时间。安排面谈和焦点小组需要时间,找到每个人都能参加的时间可能需要数天甚至数周。

  • 每隔几周不断向同一组用户询问反馈是困难的。这在尝试确定功能质量是否随着最新更新而改善时尤为重要。

基于这些理由,减少请求反馈而是测量用户在功能级别上如何与应用程序互动,以及他们是否对应用程序所提供的价值感到满意可能更值得一试。

一种方法是通过测量应用程序中的用户行为,并基于此发出指标。在第十二章应用程序监控应用程序洞察引入了收集应用程序级指标的功能。虽然传统上用于发出有关应用程序性能的指标,但也可以用于发出有关应用程序使用情况的指标。以下是一些示例:

  • 每个页面访问频率有多高?

  • 特定操作执行多少次?

  • 完成某个视图需要多长时间?

  • 多少用户打开特定表单,却从未完成?

收集这些指标可以提供重要的见解,了解用户如何与应用程序互动以及他们使用或不使用的部分。

除了使用情况,用户满意度的另一个指标可以是 Twitter 情感或支持请求的数量。

情感分析

除了收集产品内的指标外,还有一些可以在产品外收集的指标。一个信息来源的例子是 Twitter。利用 Azure 云和机器学习算法,现在可以持续分析所有指向某个 Twitter 账号或话题标签的推文,并自动检测到突发的变化。

这甚至扩展到一个 Azure Pipelines 插件,它可以持续地衡量 Twitter 情感,并在情感过于负面时取消将发布进程推进到下一个阶段。这个插件作为一个管道门控实现,可以在Azure DevOps Marketplace中找到。

支持请求

就像 Twitter 情感一样,可能还有其他能够自动收集的用户满意度指标。持续收集每分钟的支持电话或电子邮件数量,并检测到某个突发的高峰,可能是用户出现问题的明确指示。利用机器学习和系统集成,这可以用于自动化响应或将用户引导到相关结果。

采纳这样的实践可以在发现生产问题时节省几分钟或几个小时。获取用户反馈并根据情感做出决策甚至可以走得更远。这就是假设驱动开发,接下来将进行讨论。

实施假设驱动开发

软件开发中的一个风险是,团队忙于创建越来越多的功能,以至于忘记了反思其业务价值,而大家都知道,并不是每个功能都是成功的。有些功能可能根本不会被使用,甚至可能会被用户讨厌。作为一个行业,我们已经意识到产品负责人很难预测哪些功能会受到用户喜欢,哪些不会。即使使用了之前讨论过的所有反馈机制,预测用户需求依然是困难的。

另一个需要认识到的重要问题是,产品中的每一个功能也都会带来未来的成本。每个功能都需要文档、支持和维护。这意味着不必要的功能也会推高成本。从这个角度来看,不仅要保留有价值的功能,甚至应尽早从产品中移除那些无价值的功能。

假设驱动开发是一种实践,它首先承认无法预测一个功能是否会增加价值、没有价值,甚至更糟的是,降低业务价值。接下来,它建议将待办功能转化为快速、轻量的实验,在产品中运行,以确定新功能是否会增加价值。

这样的实验可以以类似用户故事的形式写成,例如:我们相信用户希望通过一个新的单字段弹窗快速创建预约,而不是完整的对话框。我们确信这是事实,当我们看到超过 25%的预约是通过这个新对话框创建的,并且预约的平均批准率上升了 2 个百分点或更多时。第一部分被称为假设,第二部分是验证假设的确认标准。

一旦写下这些内容,就创建一个最小化实现的单字段弹窗,并使用指标监控它的使用情况以及原始表单的使用情况。根据测量结果,可能会发生以下情况:

  • 假设中提出的信念被确认是真实的,并且新特性增加了价值。更多与该特性相关的故事可以被添加到待办事项中,以增加产品提供的业务价值。

  • 假设中提出的信念尚未得到确认,且进一步的实验预计不会产生不同的结果。该特性已从待办事项中删除,当前的最小化实现甚至可能从产品中移除。

  • 假设中提出的信念尚未得到确认,但实验仍在继续。这种情况可能发生在有大量用户抱怨某个功能,而团队决心修复它时。如果一种方法不起作用,他们可能会尝试另一种方法。

采用之前提到的方法,团队可以通过减少在实验后确认不增加价值的特性上花费的时间,来提高他们对业务价值的影响,甚至将这些特性从产品中移除。

通常,假设驱动的开发会与分阶段发布机制如功能标志部署环结合使用。实验只会在一小部分用户中进行,这使得如果该功能未能增加足够的价值时,能够更容易地将其撤回。

这完成了关于收集和使用用户反馈以及用户反馈如何与 DevOps 目标(为最终用户提供业务价值)相关的讨论。

总结

在本章中,你学习了如何衡量软件开发活动的业务成果。首先,你了解了反馈的重要性,以及如何通过反馈帮助理解客户需求以及这些需求是否得到了满足。然后,介绍了多种请求反馈的方法,包括直接和间接的。

最后,你了解了假设驱动的开发以及实验心态如何帮助减少浪费。

通过这些知识,你现在可以选择并实施反馈机制,帮助你了解用户对你的应用程序的情感反馈。你现在能够采取基于实验的方法来创建软件,专注于增加价值的特性,忽略或甚至移除那些不增加价值的特性。

在接下来的章节中,你将学习容器的所有知识。容器正在迅速改变软件交付的方式,并且常常用于将 DevOps 原则应用于现有应用程序和新应用程序。

问题

在我们总结时,以下是一些问题,供你测试自己对本章内容的理解。你可以在附录中的评估部分找到答案:

  1. 判断题:公开共享路线图没有任何缺点。

  2. 在评估公共路线图上的用户反馈时,需牢记的一个重要问题是什么?

  3. 哪两个间接的用户满意度指标相对容易捕捉?

  4. 以下哪项不是假设的一部分,假设驱动开发中使用的假设?

    1. 假设

    2. 确认阈值

    3. 结论

  5. 面试或焦点小组相较于其他收集反馈的方式,具有哪两个优势?

进一步阅读

第五部分 – 高级话题

在本部分的最后,你将学习到在组织中采纳良好的 DevOps 文化的好处。你将探索一些可作为指导的情境,帮助你准备企业云采用计划,这可能会直接影响你的业务成果。在本书的最后,你可以通过参加模拟考试来测试自己是否为认证做好准备。

本书的这一部分包括以下章节:

  • 第十四章采纳持续改进文化

  • 第十五章通过 DevOps 加速云的采用

  • 第十六章容器

  • 第十七章规划你的 Azure DevOps 组织

  • 第十八章AZ-400 模拟考试

第十四章:采纳持续改进文化

团队部署的 DevOps 策略大多围绕着按照规定的一套实践不断地、重复地进行,每次发布迭代时都要执行这些实践。然而,高绩效团队以学习的心态来处理一切,并在过程中识别改进之处,以建立更好的纪律,并成为做事的高手,如有效的持续集成CI)/持续部署CD)实践以提高发布灵活性,监控部署以减少故障,并及时解决生产问题。

没有任何团队能够宣称他们在实施 DevOps 实践时是完美的。必须将持续学习的文化作为团队的必备要素。因此,聆听来自各方利益相关者的反馈是改善 DevOps 卫生状况的第一步。

此外,大多数 DevOps 工具供应商,包括微软,都会定期推出新的增强功能和产品创新。团队必须不断评估并规划采用这些最新的产品功能,以最大化生产力。采纳持续改进的文化对于在 DevOps 旅程中保持相关性至关重要。

本章重点介绍持续改进的概念,并讨论了组织可以利用的一些方法来不断学习。

本章将涵盖以下主要内容:

  • 衡量 DevOps 成功

  • 价值流映射

  • 数据驱动的方法

  • 将反馈循环落实为操作

衡量 DevOps 成功

如在第一章《DevOps 简介》中讨论的,定义有效的指标并根据这些指标衡量结果是确保 DevOps 投资成功的关键。这也为严格遵循 DevOps 实践并随着时间的推移改进提供了强有力的动力。

从这些指标中得出的见解,激励工程团队更加关注他们的不足之处。让我们通过一些例子来说明这一点:

  • 交付周期指标,如每次迭代交付的故事点数、整体生产力和每行代码交付的缺陷数量,为项目管理员提供了关于迭代进展和输出质量的见解。

  • 与自动化测试相关的指标,如测试通过率、代码覆盖率和未解决缺陷,为软件在发布和部署到生产环境之前的准备情况提供了洞察。

  • 在软件发布后,关于使用情况和用户活动的报告、健康监测指标以及错误数量会反映软件的采纳情况。

从分享的例子可以看出,仪表板和指标对团队的文化以及它所培养的习惯,如何实现更广泛的业务目标,具有重要影响。

关键绩效指标KPIs)分解为更小且更有意义的度量标准非常重要。以下是一些示例:

  • KPI – 交付时间:

    • 度量标准:冲刺速度,按严重程度划分的缺陷状态,静态代码分析结果等
  • KPI – 部署频率:

    • 度量标准:测试用例覆盖率,单元测试通过/失败率,构建自动化百分比,构建流水线数量,以及整体部署时间
  • KPI – 恢复服务的平均时间

    • 度量标准:事件解决时间,每日警报计数,服务响应时间等
  • KPI – 更改失败百分比

    • 度量标准:服务器异常,按严重程度统计的事件数量等

因此,我们可以得出结论:你计划衡量的 DevOps 指标越多,你将会在 DevOps 实践转型中观察到越显著的潜力和收益。

在下一部分,我们将讨论价值流映射的概念,以及它如何应用于减少你在 DevOps 生命周期中的浪费。

价值流映射

价值流映射VSM)是一个在制造业中非常流行的概念。它指的是创建精益流程的技术,即通过尽可能少的步骤为客户生产产品。这是一种改进任何业务工作流程或过程的重要方法,从而消除浪费和不必要的步骤,尤其是从客户的角度来看。

VSM 的主要目标是对任何给定的流程进行严格分析,减少不同团队成员之间所需的交接次数,从而提高整体效率和生产力。随着数字化转型的日益重视,组织正在重新定义其业务战略,并迅速调整现有的业务/IT 流程。整体目标是根据需要重新调整交付的价值的定义,以便更好地为客户服务。

在 DevOps 生命周期的背景下,这可以被视为识别一种精益方法,包含了仅为客户创造价值所需的步骤。DevOps 价值流作为优化驱动因素,进而影响人员、流程和产品。主要动机是最小化浪费,同时持续交付客户价值。

在开发生命周期的各个阶段中,工作流的可视化,包括产品构思、特性优先级排序、解决方案开发以及最终发布给客户,称为价值流图。

VSM 相关活动包括三个主要步骤:

  1. 首先,准备当前状态的基准。

  2. 其次,定义目标未来状态,并通过分析差距或改进区域重新设计流程。

  3. 最后,分析 VSM 带来的收益,以便进一步迭代。

受 VSM 活动影响的两个主要 KPI 如下:

  • 交付时间LT),也称为吞吐时间、响应时间和周转时间。基本上,这指的是从工作作为输入进入价值流的某个特定阶段开始,到完成并交付到价值流中的下一个阶段或团队的经过时间。

  • 处理时间PT),也称为加工时间、接触时间、工作时间和任务时间。基本上,这是一个人完成所有任务以将输入转化为输出所需的时间,适用于单个工作单位。

你可以在这里阅读更多关于如何将 VSM 应用于 DevOps 的内容:www.lucidchart.com/blog/value-stream-mapping-for-devops

在下一节中,我们将探讨管理团队如何通过将焦点集中在最需要关注的领域,显著受益于数据驱动的 DevOps。

数据驱动的方法

通过数据驱动的方法制定改进策略已被证明对大多数组织有效。通过采纳数据驱动的文化,提升 CI/CD 过程并加速软件交付和质量具有巨大的潜力。大多数高性能工程团队通过跟踪的各种仪表板和 KPI 获得见解,并利用这些见解识别改进的领域并进行相应的调整。

让我们来看一下数据驱动的 DevOps 的一些关键方面:

  • 客观地衡量重要事项:一切都从识别必须跟踪的重要指标和关键绩效指标(KPI)开始,所有的测量应该是定量的,而非定性的。有人说过,事实和数字不会说谎。测量项必须扩展到推导出具体可执行的项目。

  • 与基准对比报告:你使用的报告工具必须持续生成报告,以供各方利益相关者使用。必须定义一些初步的基准,并且报告应该跟踪与这些基准的偏差。任何偏差都必须突出显示以便采取措施。

报告和后续分析必须是一个定期的活动(至少每周一次,如果不是每天的话),以便尽早进行课程调整。

  • 自动化数据交付以便立即采取行动:及时获取数据以便采取纠正措施非常重要。因此,我们必须依赖现有的自动化工具,或探索更多机会以改进数据捕获和通知流程。如今大多数 DevOps 工具都具备丰富的自动化功能,使得决策更加容易。

例如,当拉取请求PR)提交时,构建运行会检查变更的质量,并且如果有任何自动化测试失败,它可以立即向开发者发送电子邮件。这样,开发者可以修复问题并重新提交他们的更改,而不是等待几个小时才能得到完整的结果。

  • 为决策者提供洞察:衡量数据驱动的 DevOps 流程的成功对于任何组织都至关重要。通常有两个关注领域——首先是提供有关工程生命周期健康状况的洞察的 KPI,其次是作为领导团队反馈的主要 DevOps 成熟度指标。

团队还必须定期进行数据审查和分析,以识别改进机会。使用因果分析与解决方案CAR)和鱼骨分析等技术可以帮助团队识别问题的根本原因,并采取相应的行动。你可以在文末的进一步阅读部分找到更多相关资源。

在下一节中,我们将探讨反馈循环如何有助于持续改进。

反馈循环的操作化

反馈循环的概念起源于系统思维。每个组织都可以被建模为一个系统,即指由人、流程和工具相互连接的生态系统,它有序协同运作以实现业务目标或结果。反馈是系统思维中的一个重要概念,其中刺激有助于控制系统的输出。

如果你对反馈循环这一术语不太熟悉,可以将其想象为实体或过程之间相互连接的关系,其中一个变化会导致第二个变化,最终再反作用于第一个变化。这就像是一个因果链,其中事件或活动的结果产生的数据被用来通过控制原始输入行为来调节结果。下面是反馈循环的简化视图:

图 14.1 – 简单反馈循环

图 14.1 – 简单反馈循环

组织中的 DevOps 流程可以通过识别与持续改进目标相一致的适当反馈循环,进行系统思维分析。反馈循环有两种类型:

  • 强化反馈循环:第一个实体输出的正向变化或增加会导致第二个实体的增加(正反馈),最终导致第一个实体的进一步增加。

例如,随着团队生产力的提高,总体速度增加。然而,存在一个拉伸极限,超过这个限度,质量可能会受到影响。

  • 稳定/平衡反馈循环:第一个实体输出的正向变化或增加作为信号,促使第二个实体减少(负反馈),最终导致第一个实体的输出进行调整,将其重新带回平衡状态。

例如,当团队专注于 CI/CD 过程中的更多质量相关检查时,这会增加整体构建时间,从而减少发布的灵活性。因此,团队随后被迫优化步骤,以便将发布的灵活性恢复到最佳状态。

在下一部分,我们将探讨设计有效反馈循环的重要性。

实施有效的反馈循环

从之前研究的示例来看,很明显并非所有的反馈循环都是重要的。因此,组织必须设计更有效的反馈循环,并放大正确的循环,以在交付灵活性、产品质量和软件发布速度之间达到正确的平衡和取舍。以下是一些初步指导建议:

  • 确保自动通知能及时得到处理,并进行定期的根本原因分析,以减少其发生。

  • 利用 VSM 技术来发现任何没有增加价值的冗余反馈循环。

  • 在反馈系统中实现稳定性与积极变化之间的正确平衡。通常,稳定性比速度更为重要。

  • 让关键利益相关者参与到反馈循环中来。人际互动可以提供更具实践性和背景性的指导,这对你的团队可能非常有益。

  • 利用操作遥测和度量指标作为关键反馈,以改进你的 DevOps 实践。

有一句流行的说法,"一刀切"的做法在任何情况下都无法奏效。因此,需要谨慎规划,以识别对你的组织和团队背景重要的反馈循环。

总结

在本章中,我们回顾了一些促进持续改进过程的重要技术。为了获得 DevOps 的真正好处,组织应在最高水平的成熟度下运营。然而,由于许多有时超出控制的因素,这种情况很少发生。因此,通过建立持续改进的文化,团队可以确保流程、方法和实践尽可能高效和有效。

遵循系统化和结构化的持续改进方法对团队来说可能是相当有益的。这需要组织对持续学习的承诺,以及团队在审查和建议渐进改进方面的开放态度。必须指定专门的角色来定义、规划和监控持续改进的举措。

此外,必须为所有人员规划定期的 DevOps 相关主题培训,作为自驱动团队的刺激来源,以便他们保持对组织 DevOps 改进的承诺。团队文化是通过展示在各种 DevOps 实践中的专业知识来实现工程卓越的基础。

在下一章中,我们将探讨组织如何通过 DevOps 加速其云转型之旅。

活动

  • 组织你的团队并进行头脑风暴会议,最好使用白板工具。

  • 创建三个垂直泳道——保持改进,和丢弃

  • 使用便签纸,建议你的团队将他们的想法发布到白板上。基本上,识别现有的流程,并将它们分组到适当的泳道中。

  • 认真分析被归为“丢弃”类别的项目,然后验证它们在实现质量和效率的总体目标时,是否为其增加了任何价值,特别是从客户的角度来看。

  • 同时,识别现有流程的改进领域,可以通过简化流程或进行变更来实现。

  • 与管理团队发布你的发现总结。

进一步阅读

第十五章:15

通过 DevOps 加速云 adoption

数字化转型的计划侧重于利用现代和新兴技术在市场中提供竞争优势。采用基于云的技术已成为其中的重要组成部分。同样,DevOps 在加速云 adoption 旅程中也扮演着至关重要的角色。

然而,组织必须首先拥抱实验文化,摆脱过时的工作方式,转向精益且迭代的软件开发方法论。DevOps 提供了恰到好处的工具、实践和文化理念,使得组织能够采纳云平台并快速交付高质量的应用程序。

本章将涵盖以下主要内容:

  • DevOps 在数字化转型中的角色

  • 现代化与云 adoption

  • 管理软件交付的现代化

  • 敏捷转型与迭代规划

  • 将 DevOps 整合到云 adoption 计划中

DevOps 在数字化转型中的角色

对于任何企业来说,数字化转型就是利用现代和新兴技术为客户创造价值,并最终为更高的收入创造机会。为了在竞争激烈的市场中生存,别无选择。

DevOps 凭借其全面的理念,致力于最大化人、流程和工具的优势,从而增加组织成功的机会。随着对自动化的高度关注,现代云平台的发展消除了许多之前存在的瓶颈。高质量产品的上市时间大大缩短,同时所需的资金也减少了。DevOps 的核心是优化,能够在相同的资源和人员条件下实现更多。它旨在通过技术改善任何现有的业务流程。此外,开发和运维团队目标的融合为团队之间更好的协作提供了合适的合作氛围。

DevOps 在数字化转型中的角色包括以下内容:

  • 推动文化心态的变革,鼓励自动化的采纳

  • 通过打破组织的壁垒,促进团队之间的协作,从而提高整体劳动力的生产力

  • 减少与任何手工或低效过程相关的浪费和成本

  • 加速产品创新周期,从而缩短上市时间

  • 创造机会以最小的干扰现代化现有的 IT 资产

在接下来的章节中,我们将回顾 DevOps 在组织现代化和云 adoption 过程中所带来的好处。

现代化与云 adoption

尽管云技术已经存在了一段时间,但它们对企业的影响在 Covid 疫情爆发时(2020 年初)变得显而易见。那些已经投资于业务现代化的组织能够在最小或没有影响的情况下生存下来,而许多传统企业则遭受了巨大损失。这里有一篇有趣的文章,解释了疫情的影响以及未来工作将如何展开:www.mckinsey.com/featured-insights/future-of-work/from-surviving-to-thriving-reimagining-the-post-covid-19-return

IT 和业务领导者意识到,采用云计算技术是提供服务的前进方向。IT 不再仅仅被视为一种支持工具;相反,它成为了在市场中生存的重要工具。现代初创公司通过利用技术创造差异化的价值主张来吸引客户。企业也已经开始效仿,数字化转型旅程中的关键战略之一就是云计算的采用。

虽然云计算和 DevOps 是相互独立的概念,但后者对于通过云技术的采用来实现商业目标至关重要。因此,DevOps 已经成为企业确保业务持续性的核心优先事项。无论是优化 IT 支出、简化开发流程、团队间协作、提供更高的服务可靠性等,DevOps 都涵盖了您 IT 战略的方方面面。

DevOps 是现代化的基础

有一句流行的说法,任何结构都注定会在没有坚实基础的情况下崩溃。同样,在现代化旅程中,围绕您的 DevOps 实践建立严格的流程至关重要。

DevOps 在现代化中的关键好处如下:

  • 通过自动化提高敏捷性

  • 通过更好的内部协作提高生产力

  • 更高的客户和员工满意度

DevOps 推动云计算的采用

云计算及相关技术,如自动化、人工智能AI)、设备等,为您的业务开辟了广阔的可能性。生产力和敏捷性的提升带来了盈利,自动化降低了 IT 成本,实时监控和反馈循环改善了客户体验。

DevOps 在云计算采用中的关键好处如下:

  • 基础设施现代化的自动化、按需供应和扩展

  • 减少 IT 管理开销,将许多任务外包给云服务提供商

  • 与多种其他平台或产品的无缝集成

  • 增强的部署敏捷性,提高市场进入时间

在下一部分,我们将回顾 DevOps 如何影响企业内部任何软件现代化计划的路线图。

管理软件交付现代化

随着组织开始现代化旅程,它们需要准备一份具有明确目标和目的的路线图,以便衡量其成功和结果。通常,DevOps 实践影响软件交付现代化的四个主要支柱,即 软件生命周期管理解决方案架构自动化实践云平台采纳

图 15.1 – DevOps 对软件交付现代化的影响

图 15.1 – DevOps 对软件交付现代化的影响

让我们在接下来的部分中回顾 DevOps 在四个支柱中的角色。

软件生命周期管理

组织必须采用迭代开发方法论,以实现发布过程的更高敏捷性。敏捷和看板方法比传统的瀑布式软件开发生命周期方法更能提供灵活性和适应性。

解决方案架构

所有现代化旅程通常涉及两组行动:

  • 将遗留应用程序迁移到基于云的平台

  • 利用创新技术开发新软件产品

两者都需要将目标解决方案架构目标从传统的单体架构转向更加微服务化的架构。然而,对于遗留工作负载,将会面临影响其转型的挑战。

团队通常部署三种关键策略来支持云采纳旅程中 IT 资产的现代化:

  • 重新托管 – 识别属于提升和迁移类别的资产,并将其迁移到新的云平台基础设施。这些通常是独立应用程序,依赖关系最少。这为企业提供了从云端启动运营的即时能力。

  • 重构 – 这通过对应用程序进行小的结构性改动并重新包装,以充分利用云平台的功能。

  • 重建 – 某些应用程序(通常占现有资产的 10-15%)必须重新设计,以便充分利用云平台的功能。这些应用程序必须完全重新开发,并采用云原生架构。

DevOps 实践,如 基础设施即代码配置管理测试自动化应用性能监控,可以在提高迁移过程的整体效率中发挥重要作用。

自动化实践

自动化是更快交付市场的关键。在 DevOps 中,你将开发和运营流程合并成一个统一的自动化管道,使所有软件组件不断地构建、集成、测试和部署。你淘汰任何手动流程,目标是实现软件开发的 100%自动化。这确保了整体质量,并提高了发布软件的可靠性。

云平台采纳

使用云原生设计方法实施所有软件产品和解决方案。这提供了更高的效率,从而使组织能够专注于其核心业务优先事项,而不是管理基础设施。基于平台即服务PaaS)模型构建的解决方案提供更好的可扩展性和可管理性,能够快速适应不断变化的客户需求。平台供应商提供的现成监控能力使得检测故障和采取纠正措施相对容易。

在接下来的部分,我们将讨论如何在云转型过程中采纳迭代规划,以交付价值。

敏捷转型和迭代规划

敏捷转型指的是在组织内采用基于敏捷的项目规划策略,以实现其愿景。所有团队都按照敏捷方法论的核心价值观和原则进行工作。因此,你可以观察到团队整体文化和行为的显著变化。

敏捷项目管理的核心是逐步交付商业价值,同时保持应对变化的灵活性。像其他实践一样,规划也必须是持续进行的。将项目组合(或产品待办事项)与业务目标和计划对齐,并分配团队执行这些任务,同时根据客户需求或业务优先级的变化不断调整。

转型路线图

创建敏捷转型路线图从来都不容易,因为每个团队及其需求都是独特的。然而,通过遵循敏捷原则,你可以推导出一个敏捷转型路线图,作为团队的某种蓝图。

一个高层次的敏捷转型路线图示例大致如下:

图 15.2 – 转型路线图

图 15.2 – 转型路线图

更详细的敏捷转型路线图应捕捉关键的里程碑,这些里程碑可以作为创建更详细计划的基础。详细计划将捕捉更多有关路线图项目和实现这些项目的行动计划的信息。这些计划有助于将团队的思维方式转向实现敏捷转型。

你可以在这里阅读更多关于迭代规划最佳实践的内容:www.scaledagileframework.com/iteration-planning/

在下一部分,我们将回顾如何通过案例研究准备云采用计划。

在您的云采用计划中整合 DevOps

DevOps 和云采用已经从根本上改变了团队构建和部署应用程序的方式。事实上,在过去的几年里,组织通过采用 DevOps 实践,真正意识到了云平台的强大潜力。

微软提供了云采用框架,其中列出了组织可以采用的某些原则和指导方针,以制定云采用计划。您可以在此处了解更多有关微软 Azure 云采用框架的内容:docs.microsoft.com/en-in/azure/cloud-adoption-framework/overview

微软还提供了有关使用微软良好架构框架在云端构建应用程序的优秀指导。您可以在此处阅读更多内容:docs.microsoft.com/en-us/azure/architecture/framework/

在下一部分,我们将考虑一个案例研究,并了解一些关键的 DevOps 策略,这些策略可以在组织的云采用过程中应用。

案例研究

作为本案例研究的一部分,我们将考虑一个情况,其中名为 Packt 保险公司的公司希望对其应用程序进行现代化,并将其部署到 Azure 云平台上。

重要提示

本案例研究旨在模拟现实世界中的场景。然而,它并非旨在全面覆盖所有业务需求或场景。

关于 Packt 保险公司

Packt 保险公司是一家在 10 多个国家(涵盖美洲和欧洲)有业务的公司,向客户提供广泛的保险产品和服务。

在过去的几年里,Packt 保险公司的年增长未能与市场机会相匹配。由于缺乏市场适应性、创新周期过长以及由于使用传统的业务线LOB)应用程序而导致的员工生产力低下和协作不畅,它逐渐在竞争中处于劣势。其云平台的使用非常有限,大多数应用程序仍然是本地部署解决方案。

在最近召开的董事会会议上,团队决定通过云采用计划加速数字化转型进程。Packt 保险公司希望通过云优先和移动优先的核心战略,甚至对其业务应用进行现代化。

在接下来的章节中,将提到关键的业务驱动因素和利益相关者的观点。对于这个案例研究,它旨在展示每个利益相关者如何看待业务问题及其对目标解决方案的要求。

关键的业务驱动因素

  • 在接下来的 2-3 年内,成为一个基于云的数字化企业

  • 提高推出新服务的速度,使其与市场趋势保持一致

  • 在新的地区和地理位置扩展业务

利益相关者的优先事项

  • 首席执行官

    • 实现超过 10-15%的增长,通过扩展产品供应、扩大分销渠道,并加强与渠道伙伴的整合。
  • 首席技术官(CTO)

    • 我不想预先投资于 IT 基础设施,而是随着业务扩展逐步增加支出。

    • 我想了解我们业务单元的盈利能力。我希望能获取能够帮助我做出决策的业务洞察。

    • 很高兴了解客户对我们喜欢和不喜欢的看法。

    • 我希望在这个领域被称为技术创新者/先锋,并吸引新一代技术精通的人才。

  • IT 运营

    • 采用现代实践,为企业的基于云的解决方案开发和部署。

    • 确保符合安全和数据隐私标准。

    • 新分支办事处的入职应快速且无缝。

  • 产品经理

    • 将新保险产品的周期时间从当前的 6-8 个月减少到最多 2-3 个月

    • 如果精算规则可以直接更新而无需任何 IT 干预,那将是非常好的。

现代化路线图

Packt 保险公司的高管们已将其核心 LOB 应用现代化的 IT 计划划分为三个阶段:

图 15.3 – 现代化路线图中的阶段

图 15.3 – 现代化路线图中的阶段

在上述图中,您可以看到以下内容:

  • 第 1 阶段 – 在此阶段,将发布面向最小可行产品MVP)的优先能力列表。此阶段的目标是快速使平台的第一个版本运作起来。

  • 第 2 阶段 – 这是第 1 阶段的延续,团队将启动下一组优先级能力。通常在这个阶段,将发布首个版本的移动应用程序和 Web 门户。

  • 第 3 阶段 – 这将是持续创新阶段,其中利用 AI 和机器学习能力推动新的产品提供和改进整体部署的数字服务。

常见挑战及 DevOps 的解救

Packt 保险公司的团队采用了 DevOps 实践和习惯,激活了云转型旅程的潜力。

团队采取的各项行动的高级摘要在此处给出:

  • 云上线和治理 – 它建立了一个云卓越中心CCoE),整合了运营和开发团队,并为云基础设施建立了治理程序。

  • 组织变革管理 – 它组织了关于敏捷方法和现代 DevOps 实践的培训,更加专注于推动文化变革。

  • 项目组合管理 – 它定义了关键的业务目标,确定了产品和服务的列表,并创建了执行的敏捷团队。

  • 持续规划 – 它创建了高层次的发布里程碑,然后根据这些里程碑优先处理待办事项。团队计划在前 6 个月发布 MVP(最小可行产品),然后根据已识别的阶段计划每 3-6 个月发布一次增量的重大版本。

  • 自动化生命周期 – 它投资于自动化实践,以提高开发、测试和产品部署的可预测性。工程团队在不妥协质量的情况下达到了良好的开发速度。

  • 传统工作负载迁移 – 它最初部署了一个混合云设置,保留了一些本地应用程序。然而,在适当的时机,这些应用程序被淘汰并为云环境重建。

  • 衡量成功 – 它识别了关键的 DevOps 指标并有效地跟踪这些指标,以衡量其成功。

  • 使用的 DevOps 工具 – 它使用了 Azure DevOps、GitHub 和 Azure Monitor 作为实施各种 DevOps 实践的主要工具。

因此,我们可以观察到 DevOps 影响着软件开发生命周期的所有领域,并且影响着组织内的变更管理。

总结

在本章中,我们回顾了云平台的采用如何减少市场发布时间,并创造推动业务转型的机会。无论是通过自动化管理云基础设施,利用监控能力优化 IT 支出,还是规划和发布优质产品,DevOps 都涉及到云基础数字化转型过程的方方面面。

云技术还使得团队能够在远程地点工作,以支持混合工作环境。云技术使团队能够使用所需的工具进行协作,而不会浪费任何时间或功能。研究(DORA 报告)表明,随着时间的推移,DevOps 和云采用将在未来几年大幅增加,直到它们成为所有组织的常态。总的来说,我们可以得出结论,当执行得当时,DevOps 和云采用策略能够显著影响组织的盈利能力。

在下一章中,我们将探讨在构建可扩展应用程序时使用容器的方式。

额外阅读

第十六章:容器

在过去几年中,容器已成为热门话题。它们允许您打包任何应用程序或工具,使用任何语言编写,并在基本主机或集群上部署。在实施 DevOps 时,容器可以提供巨大的价值。这就是为什么 DevOps 和容器经常被同时提及的原因。然而,它们并不是同一回事。虽然 DevOps 是一种文化,但容器是一种技术,是一种托管应用程序的替代方式。

在本章中,您将学习有关容器及其工作原理的更多信息。通过练习,可以创建自定义容器镜像,并在不同的托管平台上运行,例如 Azure 容器实例和 Kubernetes。

本章将涵盖以下主题:

  • 容器简介

  • 构建容器镜像

  • 在 Azure DevOps 中构建镜像并在 Azure 中运行

  • Kubernetes 简介

  • Kubernetes 实践

  • 升级容器

  • 扩展容器和 Kubernetes

  • 使用 Azure DevOps 部署到 Kubernetes

技术要求

要尝试本章描述的技术,您需要以下一项或多项:

  • Docker Desktop

  • Visual Studio 2019/Visual Studio Code

  • Azure 订阅

  • Azure 命令行界面(CLI)

所有这些都可以免费获取,或者可以免费获取一段时间用于评估。

容器简介

容器是虚拟化的进化。通过虚拟化,物理机的资源被多个虚拟机共享。共享这些资源意味着所有虚拟机都有自己的操作系统。但是使用容器时情况有所不同。容器不仅共享资源,还共享操作系统内核,因此与虚拟机镜像相比,它非常小。

由于共享操作系统内核,容器也非常易于移植。镜像可以部署在支持运行容器的任何类型的主机环境上。这是因为所有应用程序的二进制文件和配置都存储在容器内部。因此,容器外部的环境变量不会影响应用程序。

当然,有一些注意事项,例如容器共享操作系统内核。

容器提供了在单个操作系统上运行多个工作负载的能力。下图展示了常规托管、虚拟机托管和容器之间的区别:

图 16.1 – 从虚拟化到容器

图 16.1 – 从虚拟化到容器

如果您听说过容器,几乎肯定也听说过 Docker。这是因为 Docker 是最知名的容器引擎之一,可用于运行容器。

下一节将深入探讨 DevOps 和容器,而本章的其余部分将更详细地介绍容器的技术细节。

DevOps 和容器

如在介绍中所提到的,DevOps 和容器并不是同一回事。容器是让 DevOps 变得更容易的技术。这是因为容器具有使其成为 DevOps 完美工具的优势:

  • 一致性:因为您构建了容器镜像,"它在我的机器上可以工作"这一障碍被消除了。

  • 关注点分离:使用容器时,您的应用程序将被分布在多个独立的容器中,这使得维护和分离进程变得更加容易。

  • 平台:该解决方案可以在不同的平台上运行。无论是在 Microsoft Azure、Amazon Web Services、Google Cloud,还是本地环境中,包括甚至是您的开发机器上,都不重要。

此外,DevOps 更偏向文化而非技术,正如在第一章《DevOps 简介》中提到的那样,技术组件用于支持 DevOps。在本章的其余部分,我们将关注技术层面的内容。

托管选项

如前所述,容器的一个好处是它们具有极强的可移植性。这也意味着容器可以托管在多种平台和技术上。

为了运行容器,您有许多选项,这些选项将根据您的使用场景有所不同。以下是其中一些选项:

  • Azure 应用服务

  • Azure 服务网格

  • Docker Swarm

  • Docker Desktop

  • Kubernetes

根据应用程序/容器的需求,它可以在前面提到的所有选项中运行。

用于运行容器的镜像(容器镜像)也需要托管。这些镜像托管在所谓的容器注册表中。在容器注册表中,它们可以是私有发布或公开发布的。最著名的两个注册表是 Docker Registry 和 Azure 平台中的 Azure Container Registry。

现在我们已经了解了一些有关容器的背景信息,接下来我们将深入探讨容器背后的技术,了解创建自定义容器镜像所需的内容。

构建容器镜像

本节将带您了解构建容器镜像并在本地系统上执行它的过程。为此,我们将首先创建一个应用程序,然后为其添加 Docker 支持,接着创建镜像并最终进行测试。那么,开始吧!

创建应用程序

要测试和检查容器中正在运行的内容,需要一个应用程序。为此,可以创建一个新的应用程序,也可以使用现有的应用程序。

在创建一个新的应用程序时,最简单的选择是在 Visual Studio 2019 中使用默认的 ASP.NET Core 网站模板。容器支持可以通过几个点击轻松添加。只需在创建项目时勾选启用 Docker框即可:

图 16.2 – 带有 Docker 支持的 ASP.Net Core 新应用程序

图 16.2 – 带有 Docker 支持的 ASP.Net Core 新应用程序

保持新应用程序打开,或者打开现有的应用程序。在下一部分,我们将探讨如何向现有应用程序添加 Docker 支持。

向现有应用程序添加 Docker 支持

向现有应用程序添加 Docker 支持只需要几个简单步骤:

  1. 在 Visual Studio 2019 中打开项目/解决方案,并右键单击项目。

  2. 选择添加并选择Docker 支持

图 16.3 – 带有 Docker 支持的 ASP.Net Core 现有应用程序

图 16.3 – 带有 Docker 支持的 ASP.Net Core 现有应用程序

根据客户端工具和 Visual Studio 配置,也可能会有一个容器编排器支持选项。通过此选项,可以选择您选择的云编排器。在此示例中,我们使用了 Docker Compose,因为这种格式得到了主要容器编排器的支持。然而,确实存在其他云编排器选项:

  • Docker Swarm

  • Kubernetes

  • Mesos Marathon

根据使用的云编排器,项目中会添加一个特定格式的文件,适用于该编排器。

通过添加 Docker 支持,项目中会添加一个名为 Dockerfile 的新文件。Dockerfile 是容器镜像的规范。Docker 可以读取该文件,将其视为指令。该文件是一个文本文件,包含单独的命令,这些命令也可以在命令行工具中调用以组装镜像:

FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
EXPOSE 555
FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
WORKDIR /src
COPY ["WebApplication2.csproj", "."]
RUN dotnet restore "WebApplication2.csproj"
COPY . .
WORKDIR "/src/"
RUN dotnet build "WebApplication2.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "WebApplication2.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WebApplication2.dll"]

这个示例使用了一种称为多阶段构建文件的技术。这是因为该文件使用了多个FROM语句,其中有对特定镜像的引用。

在多阶段构建之前,无法使用多个 FROM 语句。在那时,构建高效的容器镜像是非常困难的。文件中的每个语句代表镜像上的一个附加层,导致镜像变得越来越大。

在此构建过程中,还需要移除一些在该过程中需要的组件。因此,在开发和生产环境中使用单独的 Dockerfile 文件是非常常见的做法。

如前所述,Dockerfile 包含了一些指令,最常用的指令如下:

  • FROM 命令用于指定镜像将基于哪个操作系统或基础镜像。在前面的示例中,使用 mcr.microsoft.com/dotnet/aspnet:3.1 AS 基础镜像来构建应用程序的生产版本。

  • RUN 命令用于在容器镜像的构建过程中安装组件或执行操作。

  • ENTRYPOINT 命令指定容器镜像的入口点。在前面的示例中,入口点指定为一个 .NET 应用程序,该应用程序引用了在编译过程中构建的库。

到目前为止,我们已经创建了应用程序并添加了 Docker 支持。接下来,我们将学习如何使用该应用程序创建镜像。

使用应用程序创建镜像

为了能够创建 Docker 镜像,必须安装 Docker Desktop,因为 Visual Studio 使用它来构建镜像。完成 Dockerfile 后,可以使用以下步骤构建镜像:

在 Visual Studio 中右击 Dockerfile 并选择构建 Docker 镜像

图 16.4 – 一个 Asp.Net Core 应用程序构建的 Docker 镜像

图 16.4 – 一个 Asp.Net Core 应用程序构建的 Docker 镜像

在编译和构建镜像时,查看输出窗口。查看它将为你提供有关容器镜像分层方法的更多见解。

Docker Desktop 还可以让你在本地运行和存储镜像。构建镜像后,打开终端并运行以下命令:

docker images

该命令显示机器上当前所有的镜像。在此列表中,也列出了在创建镜像时下载的基础镜像:

图 16.5 – Docker 镜像列表

图 16.5 – Docker 镜像列表

我们已经看过如何添加 Docker 支持并为现有和新的 ASP.NET Core 应用程序制作 Docker 镜像。接下来的部分将探讨如何运行容器镜像。

运行容器镜像

容器镜像可以通过在 Docker 中运行来本地启动。现在我们有了容器镜像,可以创建一个容器:

  1. 运行以下 docker container run 命令:

    docker container run --publish 8123:80 --detach --name [container name] [image name]
    

前述命令将在命令末尾指定的容器镜像启动。此外,还指定了不同的参数:

  • publish 参数从主机打开一个端口到容器。如前一部分中的示例所述,这将打开端口 8123 并将流量路由到容器内的端口 80

  • detach 参数将使容器在后台运行,并打印出其特定的 ID。

  • Name: 容器在 Docker 中的名称。

  1. 要列出所有正在运行的容器,请在终端中使用docker ps命令。

  2. 容器运行后,打开浏览器并访问 http://localhost:8123。如果一切正常,应该会显示一个默认的 ASP.NET Core 网页:

图 16.6 – ASP.Net Core 默认欢迎页面

图 16.6 – ASP.Net Core 默认欢迎页面

由于在本地构建东西并在你的机器上运行并不是 DevOps 的做法,接下来的章节中我们将转向不同的托管平台。

在 Azure DevOps 中构建镜像并在 Azure 中运行

为了支持持续集成和持续交付,源文件需要在一个仓库中共享。因此,让我们在 Azure Repos 中共享资源,并尝试通过 Azure Pipelines 构建我们的容器。在构建容器镜像之后,还需要一个存储镜像和运行容器的位置。在 Azure 平台上,有两个非常适合这种场景的服务:

  • Azure 容器注册表 (ACR):该服务是基于开源 Docker 注册表的托管私有 Docker 注册表。在这里,您可以维护和注册容器镜像。

  • Azure 容器实例Azure 容器实例,也称为 ACI,是一种运行隔离容器的解决方案,无需大量管理。

重要提示

为了简化本指南,文件已被添加到仓库中,Azure 资源也已创建。

在下一部分中,我们将探索如何在 ACR 中创建镜像并通过 Azure DevOps 在 ACI 中运行它们。

创建服务端点

正如本书中已讨论的,Azure DevOps 与外部服务(如 Azure 和容器注册表)的连接是通过服务端点配置的。由于 ACI 需要访问镜像,因此必须将镜像发布到容器注册表。Azure DevOps 到注册表的连接是通过服务连接进行配置的。

执行以下步骤来配置服务连接:

  1. 在 Azure DevOps 项目中,打开项目设置。

  2. 在项目设置中,点击 服务连接

  3. 在服务连接概述中,点击 创建服务连接 并选择 Docker 注册表

  4. 在弹出的菜单中,填写正确的信息并保存连接:

图 16.7 – 新的 Azure 容器注册表服务连接

图 16.7 – 新的 Azure 容器注册表服务连接

保存连接将为项目添加一个服务连接,管道可以使用该连接,或者您将来创建的管道也可以使用它。

创建新管道

为了开始构建容器镜像并将其发布到注册表,我们将创建一个新的管道。在这个例子中,我们将使用 YAML 管道体验。

执行以下步骤以开始使用管道:

  1. 打开您的 Azure DevOps 项目并点击 Pipelines

  2. 在管道概述中,点击 新建管道

  3. 选择 Azure Repos Git,选择正确的仓库,然后选择 Starter pipeline

图 16.8 – Azure DevOps – 配置新管道

图 16.8 – Azure DevOps – 配置新管道

  1. 从启动管道中,删除两个虚拟脚本任务并打开助手。

  2. 在助手中,搜索 Docker 任务并将任务添加到管道中。

  3. 选择为容器注册表创建的服务连接,并保持其他信息为默认值。

重要提示

请确保将任务的buildContext属性更改为指向正确的目录。这是 Docker 能够引用正确路径构建镜像所必需的。

添加后,YAML 应如下所示:

- task: Docker@2 
  inputs:
    containerRegistry: 'MSFT Container Registry' 
    repository: 'azuredevops'
    command: 'buildAndPush' 
    Dockerfile:'**/Dockerfile' 
    buildContext:
'$(System.DefaultWorkingDirectory)/ExistingDevOpsProject'
  1. 保存并运行管道。在第一次运行后,容器镜像被创建并发布到容器注册表。

容器注册表中的镜像可以通过使用预定义的 URL 进行检索。该 URL 由几个特定的组件组成:

[container registry]/[repository]:[tag]:
  • 容器注册表:容器注册表的基础 URL。

  • 仓库:在发布镜像过程中指定的仓库。

  • BuildId

  1. 现在我们已经获得了容器镜像的引用,ACI 应该能够检索容器并运行它。所需的唯一操作是一个 Azure CLI 命令:

    az container create --resource-group [resource group] --name [ACI name] --location westeurope --image [Image reference] --dns- name-label [dns reference] --ports 80 --registry-username [username of the registry] --registry-password [password of the registry]
    

由于每个构建的图像引用不同(对于标签值是BuildId),因此通过 Azure CLI 命令中的$(Build.BuildId)变量获取BuildId

az container create --resource-group aci-rg-devops --name aci- demo-app --location westeurope --image msftazuredevops.azurecr.io/azuredevops:$(Build.BuildId) --dns- name-label aci-msft-demo --ports 80 --registry-username
$(username) --registry-password $(password)

要执行上述脚本,需要将 Azure CLI 任务添加到管道中。在此任务中,我们通过服务端点配置正确的订阅,并设置内联脚本。

脚本将在aci-rg-devops资源组中创建一个名为aci-demo-app的容器实例,并从msftazuredevops.azurecr.io仓库中检索azuredevops容器镜像。

该任务的完整 YAML 配置如下:

- task: AzureCLI@2 
  inputs:
  azureSubscription: 'Subscription MPN' 
  scriptType: 'bash'
  scriptLocation: 'inlineScript'
  inlineScript: 'az container create --resource-group aci-rg- devops -
name aci-demo-app --location westeurope --image msftazuredevops.azurecr.io/azuredevops:$(Build.BuildId) --dns- name-label aci-msft-demo --ports 80 --registry-username
$(username) --registry-password $(password)'

运行此管道将在 Azure 中生成一个 Azure 容器实例。该容器将运行与本地运行的完全相同的应用程序:

图 16.9 – 一个运行在 ACI 中的 aci-demo-app 实例

图 16.9 – 一个运行在 ACI 中的 aci-demo-app 实例

在 Azure 门户中打开 Azure 容器实例时,您会看到它是一个正在运行的实例,并且在 Azure CLI 命令中会看到dns-name-label,例如aci-msft-demo.westeurope.azurecontainer.io。在浏览器中打开该 URL,查看我们已经推送到容器中的应用程序:

图 16.10 – aci-demo-app 欢迎页面

图 16.10 – aci-demo-app 欢迎页面

它显示的内容与本地启动的容器相同。这是因为,在两个地方都启动了相同的容器镜像。

在这一部分,我们启动了 ACI 上的容器,但当容器出现问题时,我们如何管理正在运行的容器并重新启动它们呢?这就是 Kubernetes 的作用。

Kubernetes 简介

Kubernetes 是另一个用于运行容器的服务。Kubernetes 是由 Google 最早开发的集群编排技术。它现在是一个开源平台,用于自动化部署、扩展和操作跨主机集群的应用程序容器,从而提供面向容器的基础设施。术语 Kubernetes 通常缩写为K8s。这是通过将单词ubernete中的八个字母替换为数字 8 生成的。

Kubernetes 的功能

如前所述,容器为你提供了一个打包应用程序的好方法。在运行这些应用程序时,你需要确保应用程序保持运行,这就是 Kubernetes 发挥作用的地方,因为它具有以下核心功能:

  • 服务发现和负载均衡:容器的暴露方式由 Kubernetes 内部控制,此外,它还能够在调度中平衡流量。

  • 存储编排:将不同种类的存储提供商挂载到平台的能力。

  • 发布和回滚:Kubernetes 可以自动为指定的部署创建并重启容器。

  • 自我修复:当容器发生故障时,Kubernetes 可以修复容器。

  • 秘密和配置管理:Kubernetes 内置了管理秘密(如令牌、密码和密钥)的功能。

为了提供这些功能,Kubernetes 由多个组件组成。

Kubernetes 核心组件和服务

Kubernetes 由几个核心组件组成,这些组件共同构成一个出色且稳定的容器运行与管理产品。接下来的几个子部分将逐一介绍这些组件。

主节点

Kubernetes 中的一个重要组件是主节点。该节点管理集群。它包含所有的 Kubernetes 核心组件,以便管理集群:

  • kubectl 和 Kubernetes 仪表盘。

  • etcd:用于维护 Kubernetes 集群的状态。

  • kube-scheduler:一个选择节点以便 Pods 运行的组件。

  • kube-controller-manager:控制器管理器负责监督多个小型控制器,执行如复制 Pods 和管理节点操作等任务。

通过使用这些组件,主节点可以保持集群所需的状态。需要知道的是,当你与 Kubernetes 交互时,实际上是在与主节点进行通信。然后,主节点会与集群内的其他组件进行通信。

普通节点

这些是将运行容器的节点。有时它们被称为工作节点。它们可以是虚拟机,甚至是物理机器。在这些机器上,安装了所谓的 kubeletkubelet 是用于在节点内运行 Pods/容器的代理。

正如你在前面的章节中所注意到的,Kubernetes 中还有其他核心服务,我们接下来将讨论它们。

Pod

在 Kubernetes 中,Pods 用于运行应用程序。在 Pods 内部,指定了运行应用程序所需的资源。Kubernetes 中的调度器(kube-scheduler)会根据需求和与集群连接的节点来决定应用程序的运行位置。

Pods 有有限的生命周期,在新版本部署时会被移除。此外,当节点发生故障时,Pods 可以由同一节点或另一节点上的 Pods 替代。

服务

该服务有时也被称为负载均衡器,用于提供 Pods 的逻辑分组,并为它们提供连接性(连接的方式)。

三个主要服务如下:

图 16.11 – K8s 服务关系图

图 16.11 – K8s 服务关系图

三个主要服务如下:

  • 集群 IP:为服务添加一个内部 IP。选择此选项时,服务只能从集群内部访问。这是标准的服务类型。

  • 节点端口:节点端口服务在每个集群节点上建立一个端口,因此得名,并将到达该端口的流量路由到底层服务。它将服务暴露给外部客户端。

  • 负载均衡器:此服务添加一个负载均衡资源,并在负载均衡器上配置一个外部 IP 地址。在外部,负载均衡器将根据负载均衡器中配置的规则将流量路由到特定节点,并在内部路由到正确的 Pod。

通过这些服务,Pods 的内部和外部连接得以安排。服务和 Pods 都在一个部署中被指定。

部署

Kubernetes 部署是一个资源对象,概述了应用程序的预期状态。它指定副本数量以及应用程序的更新策略。Kubernetes 会监控 Pods 的健康状况,并根据需要删除或添加 Pods,以达到部署中指定的期望状态。

这些部署在 YAML 文件中被指定。例如,在 Kubernetes 中运行容器时,必须指定副本集。副本集确保在任何给定时刻运行指定数量的 Pod 副本。

Kubernetes 操作

当你刚接触容器,尤其是 Kubernetes 时,很难立即弄清楚。为了帮助你理解这个概念,看看下面的图示:

图 16.12 – Kubernetes 操作概览

图 16.12 – Kubernetes 操作概览

将容器部署到 Kubernetes 集群中是通过所谓的部署文件 (1) 来定义的。在这些部署文件中,应用程序的期望状态被描述。这个期望状态被描述为一个 YAML 文件。

在这个例子中,期望的状态是一个负载均衡服务和三个 Pods (2)。这些 Pods 由 Kubernetes API 在运行容器的节点上划分 (3)。部署文件中定义的服务确保流量被路由到特定的 Pods。通过更新部署,可以更改部署。

调度器也可以在配置了应用程序的自动扩展时更改部署。在这种情况下,可以向集群中添加第四个 Pod。在服务中,也可以有一个外部负载均衡器,将流量路由到 Kubernetes 的内部负载均衡器 (4)。

Azure Kubernetes 服务

Azure Kubernetes 服务AKS)是微软实现的 Kubernetes。设置常规 Kubernetes 集群需要很多工作,但使用 AKS 后,操作变得更加简便。这是因为 AKS 是一个托管平台,几乎所有操作任务都由平台本身处理。

AKS 的一些关键功能如下:

  • Azure 管理关键任务,如健康监控、扩展和维护,包括根据配置和管理需求进行的 Kubernetes 版本升级和修补。

  • Kubernetes 的主节点完全由 Azure 管理。

  • 主节点是免费的,您只需为运行代理节点付费。您只需为工作节点付费;主节点是免费的,因为 Kubernetes 集群的主节点由 Azure 管理。您管理集群的代理节点,并仅为运行节点的虚拟机VMs)付费。

使用 AKS 后,Kubernetes 集群可以在几分钟内投入使用,主节点由 Azure 管理,因此重点将放在应用程序开发和部署上。现在,让我们尝试运行一个带有自定义镜像的 Kubernetes 集群。

Kubernetes 实践

在本章的前几个部分中,我们创建了一个容器并将其部署到 Azure 容器实例。现在,让我们将这个容器部署到 Kubernetes 集群中。

创建集群可以通过 Azure CLI 或 Azure 资源管理器ARM)模板进行。为了便于演示,将使用 Azure CLI。

首先,需要创建一个新的资源组来托管 Azure Kubernetes 集群:

az group create --name mpn-rg-kubernetes --location westeurope

现在,我们可以创建我们的 Kubernetes 集群。

创建 Kubernetes 集群

当资源组创建后,可以将新的 Kubernetes 集群添加到该组中:

az aks create --resource-group mpn-rg-kubernetes --name mykubernetescluster
--node-count 1 --enable-addons monitoring --generate-ssh-keys

该命令创建了一个名为 mykubernetescluster 的新 Kubernetes 集群,并且只包含一个节点。这意味着将在 Azure 门户中创建一个 VM,并将其配置为 Kubernetes 集群的节点。此外,将启用集群上的监控插件。

创建该集群将需要几分钟时间。在 Azure 中,将在指定的资源组中创建 mykubernetescluster 服务。除了该资源组,Azure 平台本身还将创建另一个资源组。

Kubernetes 基础设施

在该资源组中,创建了运行集群所需的所有虚拟化基础设施。这也意味着将来可以根据应用程序的需求将新组件添加到此资源组中:

图 16.13 – mpn-rg-kubernetes 资源组

图 16.13 – mpn-rg-kubernetes 资源组

在创建的资源组中,您将找到所有运行集群所需的上述资源:

图 16.14 – 运行集群所需的一些资源列表

图 16.14 – 运行集群所需的一些资源列表

现在 Kubernetes 基础设施已启动并运行,可以开始管理和部署资源。

管理 Kubernetes

要管理 Kubernetes,使用 kubectl 命令行工具,并且在本地安装(或者在 Azure Cloud Shell 中使用)。这是一个与 Kubernetes API 通信的命令行界面工具。让我们看看如何使用这个命令行与 Kubernetes 一起工作:

  1. 首先,下载并安装 kubectl,如果您尚未安装 Azure CLI,请运行以下命令以在您的计算机上安装 Azure CLI:

    az aks install-cli
    
  2. 要连接到集群,需要检索凭据并将其保存到本地系统。可以使用 az aks get-credentials 命令并指定资源组和集群名称来完成此操作:

    az aks get-credentials --resource-group mpn-rg-kubernetes -- name mykubernetescluster
    
  3. 配置好所有前提条件后,可以对 Kubernetes 集群执行大部分基本功能。举个例子,看看这两个命令:

    • 检索集群的节点:

      kubectl get nodes
      
    • 获取集群中的 Pods:

      kubectl get Pods
      
  4. 除了前面的命令外,您还可以尝试以下 Azure CLI 命令来打开 Kubernetes 仪表盘。该仪表盘是构建在 Kubernetes API 之上的管理界面,可以与 kubectl 命令行一起使用:

    az aks browse --resource-group mpn-rg-kubernetes --name mykubernetescluster
    

仪表盘如下所示:

图 16.15 – 查看 Kubernetes 仪表盘

图 16.15 – 查看 Kubernetes 仪表盘

需要创建一个部署文件,以便在集群内运行容器。那么,让我们看看如何做到这一点。

部署容器镜像

我们将创建一个部署文件并将其部署到 Kubernetes。为此,请执行以下步骤:

  1. 在您喜欢的文本编辑器中创建一个新文件,命名为 deploy.yaml。将以下信息添加到 deploy.yaml 文件中:

    apiVersion: apps/v1 
    kind: Deployment 
    metadata:
      name: kubernetes-deployment 
      labels:
        app: customapplication 
    spec:
    replicas: 3
    selector:
      matchLabels:
        app: customapplication 
    template:
      metadata: 
        labels:
          app: customapplication 
      spec:
        containers:
        - name: azuredevops
          image: msftazuredevops.azurecr.io/azuredevops:586 
          ports:
          - containerPort: 80
    

在这个示例中,以下内容已被指定:

  • 部署使用名称 kubernetes-deployment 创建(metadata.name)。

  • 部署将创建指定容器的三个副本(spec.replicas)。

  • 选择器与 labels 标签结合使用,用于指定该部署文件将在 Kubernetes 中管理哪些组件。

  • 部署文件将为 msftazuredevops.azurecr.io/azuredevops:586 镜像文件创建一个容器。

  1. 要将此文件部署到 Kubernetes,我们将再次使用 kubectl 命令行,并利用 apply 命令:

    kubectl apply -f deploy.yaml
    

-f 参数用于指定使用本地路径作为部署文件的引用。执行命令后,您可以打开 Kubernetes 仪表盘查看状态,甚至可能观察到错误。

重要提示

可能会遇到一个错误,提示从您的位置拉取镜像失败。这可能是一个安全问题。在底层,AKS 使用的是服务主体。在创建新的 Kubernetes 集群时,您应该也会看到这个问题。确保为该服务主体授予 Azure 容器注册表的访问权限。

  1. 执行成功后,尝试执行 get Pods 命令,查看系统中是否有三个 Pod。如果一切顺利,Kubernetes 中应该运行着三个 Pod,但应用程序仍然无法对外界可用。

为了让它可用,我们需要在部署文件中添加一个服务。

重要提示

如果你想在同一个文件中添加服务,可以在部署之间添加一行包含 --- 字符的分隔符。如果你定义了单独的部署文件,则不需要这么做。

deploy.yaml 文件中,添加以下部分:

---
apiVersion: v1 
kind: Service 
metadata:
  name: customapplication-service 
spec:
  type: LoadBalancer 
  ports:
  - port: 80 
  selector:
    app: customapplication

这个 YAML 部分会创建一个负载均衡器,并将其附加到指定的选择器(spec.selector.app),这意味着它将用于之前指定的 Pods。

在后台,Kubernetes 会创建一个 Azure 负载均衡器,并为与 Pods 连接分配一个公共 IP 地址。

  1. 若要检索服务的外部 IP 地址,请使用以下命令,直到它显示出外部 IP 地址为止:

    kubectl get service
    

这将返回所有服务及其外部 IP 地址(如果存在)。同时,快速查看 Kubernetes 的附加资源组,看看哪些 Azure 资源已被创建。

做得好!在本节中,你学习了如何创建 Kubernetes 集群,并通过 kubectl 和部署文件将容器镜像部署到其中。在下一节中,我们将继续学习如何升级这些容器。

升级容器

在 Kubernetes 中,应用程序非常容易更新。为此,Kubernetes 使用滚动更新,意味着在替换容器之前,容器的流量会先被清空。在应用程序升级过程中,Kubernetes 会部署一个额外的 Pod,并通过一些指定的探针来运行它。

探针是对 Pod 进行定期检查状态的诊断工具。在升级或创建 Pod 期间,Kubernetes 会启动额外的 Pod,并确保它通过存活探针和就绪探针。

如果新创建的 Pod 成功通过了两个探针,流量将从一个旧 Pod 中断开,转而指向新 Pod。对于这种终止,Kubernetes 使用终止宽限期。在此期间,与负载均衡器的连接被停止,活跃连接会被成功处理,新的流量会被路由到正在运行的 Pod。在默认的 30 秒宽限期内,Pod 处于终止状态,所有到该 Pod 的流量将被转发到其他 Pods。

这个过程会持续进行,直到所有 Pod 被新版本替换。这些都是 Azure Kubernetes 中的默认行为。只需调整部署文件并使用与之前相同的命令应用它,就可以触发部署:

Kubectl apply -f [file]

默认情况下,httpGet 探针会被添加到正在暴露的 Pods 中,但也可以通过在部署文件中添加就绪探针或存活探针来自定义它们:

readinessProbe:
    httpGet:
        scheme: HTTPS
        path: /index.xhtml
        port: 8483 
        initialDelaySeconds: 5
        periodSeconds: 5
        successThreshold: 1

此就绪探测在 Pod 上执行httpGet请求,并具有以下选项:

  • path: 执行httpGet请求时应调用的路径。

  • port: 用于调用的端口号。这也在我们的部署文件中配置。

  • initialDelaySeconds: 容器启动后运行探测前等待的秒数。

  • periodSeconds: 探测在超时之前等待的秒数。

  • successThreshold: 探测所需的最小成功次数为1

如前所述,部署配置了默认的滚动升级场景。可以使用以下命令检索滚动部署的配置:

kubectl describe deployment kubernetes-deployment

重要提示

如果您有兴趣这样做,请在 Kubernetes 内构建并升级容器的新版本。在运行升级之前,请确保打开了仪表板,在更新过程中刷新页面,您将看到额外的 Pod 出现并且旧的 Pod 被终止。

在本节中,我们学习了如何升级容器,这将帮助您保持最新版本。接下来,在下一节中,我们将进一步探讨容器和 Kubernetes 的扩展。

扩展容器和 Kubernetes

随着应用程序需求的增长,您需要扩展应用程序。可以通过多种方式进行应用程序的扩展,可以扩展不同的组件:

图 16.16 – AKS 中的自动缩放器

图 16.16 – AKS 中的自动缩放器

上图显示了应用程序或集群扩展的不同方式,我们将在接下来的小节中讨论。

手动扩展 Pod

可以通过更新副本数量轻松扩展 Pod。尝试使用kubectl get Pods命令获取您的 Pod,并使用以下命令增加副本数量:

kubectl scale --replicas=[number of Pods] deployment/[deploymentname]

此命令根据副本数量调整 Pod 的规模。规模按照部署配置进行调整。

Pod 自动缩放

AKS 也支持自动缩放。调度程序将根据 CPU 利用率或其他可用的指标更新 Pod 的数量。

Kubernetes 使用度量服务器进行此操作。度量服务器从集群中的节点上运行的 kubelet 代理的摘要 API 中收集度量。

自动缩放功能还需要在 Kubernetes 部署端进行一些配置。对于部署,您需要指定运行容器的请求和限制。这些值针对特定的度量标准进行指定,例如 CPU。

在以下示例中,为 CPU 度量标准指定了请求和限制。CPU 度量标准以 CPU 单位进行测量。在 Azure 中,一个单位代表一个核心。在不同的平台上,一个单位可能有不同的含义:

resources: 
  requests:
    cpu: 0.25 
limits:
    cpu: 0.5

可以将此部分添加到部署文件的容器中,这将确保在需要大量请求时可以自动扩展 Pod。

使用更新后的部署文件,部署它并在 Kubernetes 集群中设置自动扩展规则:

kubectl autoscale deployment [deployment name] --cpu-percent=60 --min=1 --max=10

该规则将更新部署并添加自动扩展功能。如果所有 Pods 的平均 CPU 利用率超过它们请求的使用量的 60%,则自动扩展器将增加 Pods 数量,最多可增加至 10 个实例。然后,为该部署定义了最小实例数为 1:

创建自动扩展器后,您可以通过运行以下命令来检查它:

kubectl get hpa

提示

HPA 代表 Horizontal Pod Autoscaler(水平 Pod 自动扩展器)。

尝试在应用程序中创建一个 CPU 密集型操作,并在执行期间检查自动创建的 Pod。Kubernetes 集群会注意到 CPU 使用量的显著增加,并会通过创建多个 Pod 自动扩展集群。

一旦密集型操作完成,Kubernetes 会将 Pods 数量缩减至最小值。

缩放节点

除了缩放 Pods 外,Kubernetes 还可以缩放 Kubernetes 集群中运行的节点数量。可以使用以下命令缩放节点数量:

  1. 首先,通过请求节点的数量,获取与当前环境相关的信息:

    az aks show --resource-group mpn-rg-kubernetes --name mykubernetescluster --query agentPoolProfiles
    
  2. 然后,使用此命令更新nodepool。从上一个命令的结果中提取nodepool的名称:

    az aks scale --resource-group mpn-rg-kubernetes --name mykubernetescluster --node-count 2 --nodepool-name nodepool1
    

增加节点数量可以大幅提升性能,但这也会使集群变得更加昂贵。通过缩减集群节点数量,可以降低成本,同时只使用应用程序实际需要的资源。为了跟踪这一点,节点也可以进行自动缩放。

自动缩放节点

除了手动缩放节点外,还可以通过更新 Kubernetes 集群来实现节点的自动缩放。这可以通过使用az aks update命令来完成。通过此命令,您可以设置节点的最小和最大数量。然后,自动扩展器将确保在需要时创建节点:

az aks update --resource-group mmpn-rg-kubernetes --name mykubernetescluster  --update-cluster-autoscaler --min-count 1 --max-count 5

AKS 还提供了使用 ACI 扩展的选项。要使用此选项,在创建 AKS 集群时需要应用特定的配置。这是因为 ACI 需要虚拟网络中的特定子网。

在这一部分,我们学习了如何缩放容器和集群,从而大幅提升性能。接下来是通过 Azure DevOps 进行部署,以实现持续部署。

使用 Azure DevOps 部署到 Kubernetes

我们已经看到了许多通过命令行部署和配置 Kubernetes 集群的选项。然而,在与 DevOps 一起工作时,变更需要以持续的方式进行应用。

为此,Azure DevOps 中有 Kubernetes 清单任务,包含许多功能来管理 Kubernetes 集群:

task: KubernetesManifest@0 
  inputs:
  action: 'deploy'
  kubernetesServiceConnection: '[service connection name]' 
  manifests: '[path to your deployment file]'
  containers: 'msftazuredevops.azurecr.io/azuredevops:$(Build.BuildID)'

在前面的示例中,配置了以下内容:

  • action:我们希望执行的操作类型。在这个示例中,使用deploy操作,因为我们要部署/应用部署文件。

  • kubernetesServiceConnection:与 Kubernetes 集群的服务连接。

  • manifests:清单文件的路径。由于我们使用的是部署操作,因此这应该是对部署文件的引用。

  • containers:一个特殊字段,可以覆盖正在部署的容器的版本。通过指定前述内容,部署清单中的每个镜像都会使用msftazuredevops.azurecr.io引用,并且azuredevops存储库将被替换为此字段中配置的新值。

在 Azure DevOps 管道中使用 Kubernetes 目标环境的另一个优点是可以看到 Azure DevOps 中运行的环境。这将显示集群中正在运行的 Pods 数量。

尝试使用以下阶段配置进行构建,该构建将发布部署文件到 Azure DevOps 的工件位置:

stages:
  - stage : Build 
    displayName : Build 
    jobs:
    - job:
      pool:
        vmImage: 'ubuntu-latest' 
      continueOnError: false 
      steps:
      - task: Docker@2 
      inputs:
        containerRegistry: '[Container Registry service connection]' 
        repository: 'azuredevops'
        command: 'buildAndPush' 
        Dockerfile: '**/Dockerfile'
        buildContext: '$(System.DefaultWorkingDirectory)/[folder path for docker]'
    - task: CopyFiles@2 
      inputs:
        SourceFolder: '$(system.defaultworkingdirectory)/[path to the deployment manifest files]'
        Contents: '*'
        TargetFolder: '$(build.artifactstagingdirectory)' flattenFolders: true
    - task: PublishBuildArtifacts@1 
      inputs:
        PathtoPublish: '$(Build.ArtifactStagingDirectory)' 
        ArtifactName: 'drop'
        publishLocation: 'Container'

在构建阶段旁边,添加以下发布阶段。在管道首次执行后,Azure DevOps 中将会有一个新的环境。在发布创建的环境中,连接 Kubernetes 集群以查看运行中的 Pods 信息:

- stage : Release 
  displayName : Release 
  jobs:
  - deployment: KubernetesDeploy 
    displayName: Deploy Kubernetes 
    pool:
      vmImage: 'ubuntu-latest' 
    environment: 'Kubernetes' 
    strategy:
      runOnce: 
        deploy:
          steps:
          - task: DownloadPipelineArtifact@2 
            displayName: 'Download pipeline artifacts' 
            inputs:
              buildType: 'current'
              targetPath: '$(Pipeline.Workspace)'
          - task: KubernetesManifest@0 
            inputs:
              action: 'deploy'
              kubernetesServiceConnection: '[Kubernetes service connection]'
              manifests: '$(Pipeline.Workspace)[deployment manifest]' 
              containers: '[container registry]:$(Build.BuildID)

在示例中,为多阶段管道指定了两个阶段。第一个阶段将通过 Docker 任务构建容器镜像,并将其发布到容器注册表。在发布镜像后,它还会发布多个构建工件——在此例中为 Kubernetes 清单。

第二个阶段将部署到名为 Kubernetes 的特定环境。如果该环境尚未添加,它也会在 Azure DevOps 中创建。在过程的其余部分,它会检索构建阶段发布的工件,并使用 Kubernetes 清单任务部署 Kubernetes 资源。

总结

在本章中,你了解了容器是什么以及它们如何与 DevOps 相关。DevOps 更多的是一种文化,而容器则是支持它的技术方式。你还学习了如何通过 Dockerfile 创建容器镜像,特别是通过使用多阶段构建文件。最后,我们深入探讨了 Kubernetes,学习了如何使用kubectl命令托管容器并管理正在运行的容器。

使用本章中获得的知识,你现在可以将应用程序部署到 Kubernetes,并确保其能够根据收到的请求数量进行扩展。

在下一章中,你将学习如何通过使用 Azure DevOps 来促进 DevOps 流程。你将了解哪些方法适合你的组织和团队,哪些方法不适合,并学习如何使用 Azure DevOps 实现这些结构和方法。

问题

在总结部分,这里列出了几个问题,帮助你测试自己对本章内容的掌握情况。你可以在附录章节的评估部分找到答案:

  1. 容器对 DevOps 有哪些好处?

  2. 一个特定的容器可以托管在不同的平台上(Azure/Amazon Web ServicesAWS)/Google Cloud PlatformGCP))——对还是错?

  3. 是否可以为现有应用程序添加容器支持?

  4. Dockerfile 中的 RUN 命令是用来做什么的?

  5. Kubernetes 可以在不同组件上进行扩展。它们是什么?

练习

  • 让我们创建并将我们的应用程序镜像发布到 Azure 容器注册表,并在 Azure Kubernetes 集群上进行部署。

  • 本实验需要配置以下 Azure 资源:

    • ACR

    • AKS

  • 设置环境 – 创建 AKS 和 ACR:

    az aks create  --resource-group az400-dev --name  packtsbookaci --generate-ssh-keys --location eastus 
    az acr create --resource-group  az400-dev  --name packtbookacr --sku Standard --location eastus 
    
  • 配置 ACR 与现有 AKS 集群的集成:

    az aks update -n 'packtsbookaci' -g 'az400-dev' --attach-acr 'packtbookacr'
    
  • 请参考仓库中的 azurecontainercluster-pipelines.yml 文件,或者创建一个包含以下内容的新 YAML 文件:

    trigger:
    - main
    pool:
      vmImage: ubuntu-latest
    variables:
      buildConfiguration: 'Release'
    steps:
    - script: echo Hello, world!
      displayName: 'Run a one-line script'
    # Authenticate nuget.exe, dotnet, and MSBuild with Azure Artifacts and optionally other repositories
    - task: NuGetAuthenticate@1
      #inputs:
        #nuGetServiceConnections: MyOtherOrganizationFeed, MyExternalPackageRepository # Optional
        #forceReinstallCredentialProvider: false # Optional
    
    - task: DotNetCoreCLI@2 
      displayName: Restore
      inputs:
        command: restore
        projects: '**/*.csproj'
        feedsToUse: config
        nugetConfigPath: $(Build.SourcesDirectory)/nugget.config
    - task: DotNetCoreCLI@2 
      displayName: Build
      inputs:
        command: build
        projects: '**/*.csproj'
        arguments: '--configuration $(buildConfiguration)' # Update this to match your need
    - task: Docker@2
      displayName: Build an image to container registry
      inputs:
        command: build
        repository: 'SampleStarter'
        dockerfile: '**/Dockerfile'
        containerRegistry: 'packtbookacr'
        tags: $(Build.BuildId)
        arguments: '--build-arg FEED_ACCESSTOKEN=$(VSS_NUGET_ACCESSTOKEN)'
    - task: Docker@2
      displayName: Build and push an image to container registry
      inputs:
        command: push
        repository: 'SampleStarter'
        dockerfile: '**/Dockerfile'
        containerRegistry: 'packtbookacr'
        tags: $(Build.BuildId)
    
  • 请注意,containerRegistry 名称是 packtbookacr,与创建 AKS 和 ACR 时使用的名称相同。containerRegistry 名称 packtbookacr 必须是全球唯一的,因此你不能使用相同的名称。

  • 使用以下内容更新 dockerfile

重要提示

在以下示例中,端点被 *** 遮蔽。请使用适当且有效的端点来处理你的 NuGet feed。

#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
RUN curl -L https://raw.githubusercontent.com/Microsoft/artifacts-credprovider/master/helpers/installcredprovider.sh  | sh
COPY ["packtbookslibrary-api.csproj", "."]
COPY ./nuget.config .
ARG FEED_ACCESSTOKEN
ENV VSS_NUGET_EXTERNAL_FEED_ENDPOINTS="{\"endpointCredentials\": [{\"endpoint\":\"https://pkgs.dev.azure.com/*****/PacktBookLibrary/_packaging/PacktBooksLibraryFeed/nuget/v3/index.json\", \"password\":\"${FEED_ACCESSTOKEN}\"}]}"
RUN dotnet restore "./packtbookslibrary-api.csproj" --interactive
COPY . .
WORKDIR "/src/."
RUN dotnet build "packtbookslibrary-api.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "packtbookslibrary-api.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "packtbookslibrary-api.dll"]
  • 我们在这里使用 Azure Artifact Credential Provider 来自动获取还原 NuGet 包所需的凭据。

  • 在通过 Azure DevOps 构建管道成功执行 azurecontainercluster-pipelines.yml 文件后,你会注意到 ACR 中创建了带有标签的镜像:

图 16.17 – 一个 ACR 列表

图 16.17 – 一个 ACR 列表

  • 你可能需要建立并授权一个 Azure DevOps 服务连接与 ACR,如前面段落所创建的,以成功执行构建管道:

图 16.18 – 建立 ACR 服务连接

图 16.18 – 建立 ACR 服务连接

进一步阅读

第十七章:规划您的 Azure DevOps 组织

在前几章中,您学习了许多与 DevOps 相关的技术和实践。本章中,我们将退后一步,看看如何构建一个强大的 DevOps 组织,并在构建时需要考虑的事项。之后,您将了解这对于安全性和可追溯性所能带来的影响。从那里开始,您将学习如何整合您的工具链组合,从而将开发生命周期的标准化主要集中在使用 Azure DevOps 上。

我们将从创建一个 Azure DevOps 组织开始,您将在其中学习如何在工具中布局您的产品和团队,并了解许可和不同方法的安全影响。之后,您将学习可追溯性及其如何用于创建可验证的软件开发过程。接下来是工具的整合。在您推进 DevOps 之旅的过程中,您可能会发现每个团队都在使用不同的工具,他们对这些工具很熟悉且喜欢使用。尽管 DevOps 的核心是赋能团队,但某种程度的标准化是可能需要的,您将学习如何进行这一操作。最后,您将学到,您可能必须接受一个事实,那就是您永远无法完全完成 DevOps 的采纳。

本章将涵盖以下主题:

  • 设置 Azure DevOps 组织

  • 确保可追溯性

  • 工具整合

  • 接受没有最终状态的事实

技术要求

要跟随本章的实践部分,您需要以下其中之一:

  • 一个 Microsoft Live 帐户,也叫个人帐户

  • 一个工作或学校帐户

设置 Azure DevOps 组织

要练习前几章中描述的一个或多个技术和方法,可能已经专门为此用途创建了一个 Azure DevOps 组织,或者可能已有一个可用于此目的的组织。然而,从零开始为公司创建一个组织需要更多的考虑。花时间合理规划组织的布局,能为以后节省大量时间。

本节描述了 Azure DevOps 的组成部分,如何使用这些组件来组织合适的安全模型,以及许可选项和成本。

Azure DevOps 是如何组织的

Azure DevOps 中的顶级构造被称为组织。对于大多数公司来说,一个组织就足够了,但也可以创建多个组织。

每个 Azure DevOps 组织都托管在特定的区域内。大部分组织的数据(源代码和工作项)都保证位于该组织所在的区域,但由于服务的全球覆盖,一些信息总是存储在其他数据中心。拥有跨不同地理位置的团队和产品的分布式组织可能是使用多个组织的原因之一。例如,如果某些团队位于澳大利亚,而另一些团队位于西欧,创建两个独立的组织并将所有团队托管在离他们最近的地理区域是有意义的。这样可以确保大多数 Azure DevOps 服务托管在一个物理上更接近团队的区域的数据中心,从而大大减少在使用 Azure DevOps 时的延迟。

一个组织可以与Azure Active DirectoryAAD)进行链接。如果启用此链接,只有位于该特定 AAD 中的用户(包括成员和访客账户)才能访问该组织。使用公司 AAD 不是强制性的;也可以使用 Microsoft 账户创建组织。管理员还可以允许拥有 GitHub 账户的用户访问该组织。然而,必须注意的是,当使用工作账户创建组织时,它将自动链接到与 AAD 账户关联的主(默认)租户。租户管理员可以防止其工作账户被链接到新的组织。

在每个组织中,可以创建一个或多个项目。一个项目是一个孤立的工作项容器,包含源代码控制库、管道定义以及所有其他 Azure DevOps 工件。项目之间的共享和链接可能性是有限的。在本文撰写时,仅有工作项、管道和代理池可以跨项目关联。通过这种方式,项目可以作为强隔离边界,用于在需要时强制实施产品或团队之间的严格安全性。通常建议尽量减少项目数量,当团队数量非常少(<10)时,最好只有一个项目。

下图展示了一个可能的 Azure DevOps 组织和项目的结构。图中显示有两个组织连接到 AAD。一个位于西欧,另一个位于澳大利亚。在西欧的组织中有两个项目,而在澳大利亚只有一个:

图 17.1 – Azure DevOps 组织与项目

图 17.1 – Azure DevOps 组织与项目

如前所述,推荐的做法是尽量减少组织和项目的数量。但在此示例中,从澳大利亚到西欧的延迟是一个合理的理由,可以将组织拆分为两个,以便将 Azure DevOps 托管在接近团队的地方。西欧的两个项目拆分可能是由于团队 4 在开发产品 3 时需要较高的隔离级别。

将团队 1 到 3 和产品 1 和 2 合并为一个项目是有意为之。这样做的原因是在单个项目中,可以定义多个产品区域和多个团队。将这些全部放在一个项目中,可以方便地将工作项链接到项目组合管理中。这样,一个团队的工作项也可以与另一个团队在其他产品上的提交或拉取请求相关联。如果功能分布在多个产品或应用(组件)中,这样做会很有帮助。

Azure DevOps 提供区域路径、团队和迭代,帮助你组织和执行产品待办事项的生命周期流程。因此,你必须谨慎选择粒度层级,以在开发生命周期中实现更高程度的并行性。以下是几个简单的规则:

  • 避免将用户链接到多个团队。相反,应将团队创建为一个逻辑上的、独立的单元。

  • 为组织中的每个主要产品(或团队)创建一个区域路径。根据需要创建子区域路径。

  • 仅在需要对 DevOps 生命周期的各个方面进行精细控制时,才创建独立的团队项目。

要在单个项目中定义所有产品和所有团队,了解 Azure DevOps 安全模型以及如何使用该模型实施访问控制非常重要。

创建 Azure DevOps 组织和项目

创建新的 Azure DevOps 组织和一个或多个项目通常由管理员完成,管理员也将负责后续的环境管理。这些组织很可能与Azure Active Directory (AAD) 关联。为了创建用于私人用途或培训和学习的组织,最好使用个人账户。

使用个人账户创建新组织,请执行以下操作:

  1. 访问 dev.azure.com

  2. 选择开始免费以开始创建新的 Azure DevOps 组织。

  3. 在弹出登录对话框时,使用个人账户登录。

  4. 登录后,选择正确的居住国家,并使用以下对话框选择是否启用提示和其他服务更新:

图 17.2 – 为你的 DevOps 组织选择国家/地区

图 17.2 – 为你的 DevOps 组织选择国家/地区

  1. 继续按钮创建新组织。

组织创建完成后,系统将自动启动新向导以创建第一个项目。要创建项目,请执行以下操作:

  1. 提供项目名称。

  2. 选择将项目设为公开还是私有。私有项目是默认设置,旨在组织内创建软件,并且不允许匿名访问。公开项目则用于开源开发。

一旦创建了新的组织和项目,就可以通过管理界面对这些选择进行更改。

重要提示

请记住,重命名组织或项目会更改 URL,因此所有现有的集成和链接可能会失效。

甚至可以在稍后更改组织的位置。这需要提出请求,并且不像更改其他设置那样简单。

一旦组织和项目可用,就可以开始设置安全性。

Azure DevOps 安全模型

在 Azure DevOps 中,授权可以分配给单个用户或安全组。安全组可以是现有 AAD 组的逻辑封装,也可以在 Azure DevOps 中自行定义。一般建议尽可能将授权分配给组,并限制单个用户的分配。

配置用户或安全组的授权时,有两种互补的方式可供选择:

  • 组织级和项目级授权

  • 对象级授权

在使用本地产品 Azure DevOps Server 时,还有可用的服务器级安全组和设置:

重要提示

在 Azure DevOps Services 中,组织称为项目集合,项目称为团队项目。有时,这些名称也用于 Azure DevOps。

  • Project Collection Build Administrators,或 [ProjectName]\Build Administrators,默认情况下具有查看、管理和编辑构建定义及构建资源的权限。

在组织和项目级别可以设置的权限会自动应用于组织或项目中的所有单个资源。

  • 对象级授权:在 Azure DevOps 中,大多数对象都可以为用户或组分配个别权限。这些权限通过对象本身上的访问控制列表ACL)进行设置。以下示例展示了一个经典的构建定义:

图 17.3 – 为项目设置权限

图 17.3 – 为项目设置权限

对于每个组、每个操作,可以配置允许拒绝未设置允许继承)权限。当操作配置为拒绝时,即使用户属于具有允许授权的组,也永远不会允许访问。换句话说,当存在两个冲突的分配(允许拒绝)时,拒绝将优先于允许未设置应解释为隐式拒绝,不具有优先权。换句话说,当存在两个冲突的分配(未设置允许)时,用户将被允许访问。

Azure DevOps 中的一些工件属于层级结构的一部分。例如,管道可以位于文件夹中。每当启用继承时,来自层级中更高(父级)级别的权限将传播到该工件。这意味着,当用户访问一个管道文件夹时,如果没有更具体的授权设置,那么他们的所有权限将传播到所有下层文件夹和管道。

虽然安全模型决定了用户拥有的授权类型,但用户的操作也受到其分配的访问级别的限制,这一点来自于他们的许可证。

Azure DevOps 许可证

创建 Azure DevOps 组织的另一个方面是管理许可证。在 Azure DevOps 中,每个用户在登录产品之前都需要分配一个访问级别。定义了三种访问级别:

  • 利益相关者:利益相关者是免费的用户,他们可以登录产品,但只能访问其功能的部分内容。利益相关者可以管理工作项、管理管道并查看仪表板。他们无法访问产品的其他区域,因此该许可证级别仅适用于非开发角色。

  • 基本:基本用户拥有付费许可证,允许他们访问产品的所有部分,除了测试管理和高级测试执行功能。

  • 基本和测试计划:使用“基本和测试计划”许可证选项的用户可以访问 Azure DevOps 的所有部分。他们与基本用户拥有相同的访问权限,但还可以访问测试管理工具,以及用户验收测试、测试执行和测试结果报告功能。

每个组织的前五个基本许可证是免费的。这意味着可以在没有产生任何费用的情况下,尝试使用产品并学习如何使用它。此外,Visual Studio 订阅者也可以获得免费的许可证。专业订阅者可以获得一个免费的基本许可证,而企业订阅者则可以获得免费的基本许可证和测试计划许可证。

许可证可以在任何时候分配和重新分配,因此对于拥有大量人员进出(如加入和离开的公司或团队),不需要购买超过当前活跃人员数量的许可证。

许可证费用并不是使用 Azure DevOps 唯一的费用;了解按需付费的费用也同样重要。

如需更多信息,请参阅此链接:docs.microsoft.com/en-us/azure/devops/organizations/security/access-levels

基于消费的费用

许可证为用户提供了对产品的访问权限,之后他们可以按固定费用使用产品中的所有服务,但以下两项除外:

  • Azure Pipelines 并行执行

默认情况下,每个 Azure DevOps 组织都会提供一个由 Microsoft 托管的并行执行作业,且每月的执行时间是有限的。这意味着虽然可以根据需要定义多个流水线,但每次只能有一个流水线在执行。当然,这个数量可以增加,但这需要额外购买更多由 Microsoft 托管的并行执行作业。

作为替代方案,还可以购买自托管作业。对于这些作业,执行代理并不是由 Microsoft 提供,而是需要由组织自行提供,并需额外付费。这为完全控制硬件提供了机会(和责任)。

  • Azure Artifacts 存储

在使用 Azure Artifacts 源时,前 2 GB 的存储是免费的。任何额外使用的存储将根据价格表另行收费。

随着团队中越来越多的用户获得 Azure DevOps 许可证并在其中完成工作,可以利用这一点来提高软件开发的可追溯性。

请参考此链接以了解更多有关 Azure DevOps 计费的信息:docs.microsoft.com/en-us/azure/devops/organizations/billing/overview

确保可追溯性

Azure DevOps 相较于本书中提到的其他工具的优势之一在于,它是一个完全集成的工具套件,每个工具都支持特定的 DevOps 流程。这种端到端的集成允许从看板上的工作到相关的二进制文件部署到环境中的整个过程都能进行详细且长时间的追溯。

当与一组只支持部分 DevOps 流程的其他工具一起使用时,通常是可以将它们集成在一起的,当然,这样会产生一定的可追溯性。例如,在使用 Jira 和 GitHub 时,可以将 GitHub 中的提交、拉取请求和其他更改与 Jira 中描述的工作关联起来。当通过 Jenkins 获取合并的更改以构建和部署产品时,Jenkins 也会有从 Jenkins 返回到 GitHub 的可追溯性。然而,无法直接查看哪个工作项与哪个 Jenkins 部署一起完成。

其缺点是,使用 Jira 工具的产品负责人无法看到一个完成的用户故事是否已经关联到某个发布版本。他们必须访问多个工具才能找到这个问题的答案;在 GitHub 中,他们需要找到与该故事相关的所有提交,然后查看这些提交是否已经通过 Jenkins 发布。

图 17.4 – 工具之间的可追溯性选项

图 17.4 – 工具之间的可追溯性选项

在使用 Azure Boards、Repos 和 Pipelines 时,这是不同的。当使用所有 Azure DevOps 服务时,追踪性可以从故事到部署,反之亦然。以下是一个示例,突出显示如何查看哪些提交首次部署到具有特定部署的环境中:

图 17.5 – 查看作为管道运行一部分的提交

图 17.5 – 查看作为管道运行一部分的提交

拥有这种端到端的追踪性可以迅速回答多个问题,包括以下几个:

  • 这项工作是否已经开始?(分支和拉取请求可以与工作项相关联。)

  • 这项工作已经包含在我们的夜间构建中吗?(提交和构建管道可以与工作项相关联。)

  • 这个 bug 是否已经修复,并且解决方案是否已经在第二环中向我们的客户提供?(发布和环境显示了哪些新的提交和工作项是最新部署的一部分。)

重要提示

在谈到追踪性时,一个特别重要的观点是,它不是关于责备的。追踪性不是用来查找谁犯了哪个错误,而是用来找出当前事物的状态以及对象之间的关系。一旦追踪性成为责备的依据,工程师们很快就会找到隐藏自己工作的方式。这将导致更多错误和更少的可见性,问题只会变得更糟。

在追踪性优势明确的情况下,让我们探讨整合工具如何帮助我们获得这些好处。

工具整合

市场中可以观察到的一个趋势是,追踪性和 DevOps 产品扩展其服务内容,不仅仅包括源代码控制、管道或部署。比如 GitHub 和 GitLab,它们正在添加新的服务。越来越多的集成应用生命周期管理ALM)或 DevOps 套件正在涌现,但 Azure DevOps 已经提供这些服务多年了。

然而,许多公司并没有实施这些集成套件。团队在不同的生态系统中运作,导致不同的工具选择。或者也许团队只是有不同的偏好,或在不同的时间点开始采用 DevOps 实践,且有其他工具可供选择。不论原因如何,许多公司在处理同一工作时有多个工具在运行。

不幸的是,使用断开连接的工具或多个工具处理相同事情会带来一些缺点:

  • 如果团队使用不同的工具,协作将受到阻碍。不论开发人员的个人偏好如何,当组织的一半使用 Jenkins 而另一半使用 Azure Pipelines 时,这会证明是生产力的障碍。更进一步说,当切换团队或帮助他人时,如果他们使用的是另一种工具,效率将受到严重影响。

  • 工具越多,成本也就越高。即使所有工具都是开源且免费的,仍然会有成本。这些成本可能包括例如支持合同或请求、培训,或者解决特定问题所需的时间。升级和维护也是如此。工具越多,整体成本就越高。

  • 多个分布式工具意味着需要进行更多的安全和监控工作,这可能会导致许多问题和漏洞。

为了克服这些挑战,许多大公司决定标准化所使用的工具,无论是完全标准化,还是至少在某些程度上进行标准化。作为替代或中间解决方案,也可以通过工具之间的集成来启动整合。

工具标准化

为了应对这些缺点,大多数公司接受了两种策略之一:

  • 集中决策,选择一个工具(对于每个 DevOps 领域)作为整个公司的标准工具

  • 集中采用有限的工具集,团队可以从中选择要采用的工具

完全集中化中,由一个中央团队或部门代表所有人决定,在组织内使用哪些 DevOps 工具。一旦做出并实施此决策,就能减少成本,并使工程师能够更轻松地协助其他团队。

当然,缺点是,单一工具不一定是最适合所有人的选择,而对整个组织而言,所选择的工具可能是最优的——这种标准化可能在一些边缘情况下造成损害。

有限集中化是一些公司为避免这种情况所采用的方法。与只选择一个工具不同,这些公司选择了一组工具作为公司的标准。现在,各团队可以根据自己的具体需求,在两三个工具中做出选择。这种方式在不牺牲拥有非常具体需求的团队生产力的情况下,限制了完全去中心化的许多缺点。

采用这两种策略之一可能意味着一些现有的工具会被弃用或完全淘汰。这可能是一个缓慢而痛苦的过程,尤其是在大型组织中,往往存在相互冲突的利益。有许多方法可以实现这一目标,但也有一些策略可以让这种迁移变得不那么痛苦。

迁移策略

减少使用的 DevOps 工具数量通常意味着必须淘汰一个或多个工具。这可能会很困难,因为这些工具通常用于实现治理和合规性,以符合法律和法规的要求。实际上,这意味着可以采取以下两种做法之一:

  • 旧工具并非完全淘汰,而是仅不再使用,以保持变更历史。

  • 在旧工具被淘汰之前,历史数据也必须迁移到新工具中。

选择迁移时,有四种方法可以实现:

  • 从 Azure DevOps Server 迁移到 Azure DevOps Services

  • 大爆炸式迁移

  • 同步

  • 重建

提示

Azure DevOps Server 以前被称为团队基础服务器TFS)。较旧版本的 TFS 需要升级到最新版本的 Azure DevOps Server,才能导入到 Azure DevOps Services 中。导入服务始终支持 Azure DevOps Server 的最新两个版本。

以下章节将详细介绍这四个步骤。

从 Azure DevOps Server 到 Azure DevOps Services 的迁移

对于希望从 Azure DevOps Server 切换到 Azure DevOps Services 的组织,提供了高保真度的迁移服务。当前存在于本地 Azure DevOps Server 环境中的每个项目集合都可以使用 Azure DevOps Server 导入服务迁移到 Azure DevOps 组织。所有当前存在于本地项目集合中的资产都将被迁移到 Azure DevOps:工作项、源代码控制库以及构建和发布定义。

项目集合的迁移包括以下高级步骤:

  1. 验证项目集合是否准备好进行迁移:此步骤不会做任何更改,只是检查迁移的所有前提条件是否已满足。

  2. 准备迁移:在此步骤中,生成一个描述如何执行迁移的 JSON 文件。如果需要,还可以提供第二个文件,将本地身份与 AAD 身份关联,以确保迁移后所有历史记录仍然正确地与进行更改的人员关联。

  3. 会进行一次迁移的演练,以验证导入过程是否能够产生预期的结果。

  4. 实际迁移:在此步骤中,项目集合将下线,从项目集合数据库中生成数据层应用程序包DACPAC),然后上传第 2 步的 DACPAC 文件和其他文件,并启动迁移。

  5. 导入完成后,需要验证所有资产,并且在某些特定场景下,必须采取一些导入后的操作。

使用迁移服务时,提供了包含清单和逐步说明的全面指南,具体链接在本章末尾。

大爆炸迁移

第二种可能的策略是“大爆炸”迁移。在某个时刻,旧工具被关闭,所有数据迁移到新工具,并启用新工具。这种方式存在较高的问题风险,通常也没有回头路。通常来说,这并不是一个好的迁移策略。

然而,在某些情况下,这种方法可能有意义,比如源代码控制的迁移。现在有一些工具可以将不同来源的代码迁移到任何类型的托管 Git 解决方案,包括 Azure DevOps。源代码控制的优势在于,变更历史深深嵌入到系统中,因此带着历史进行迁移往往比其他类型的数据更容易。

同步

另一种迁移策略是允许一段时间内同时使用两种工具。

其中一种方法是使用一种工具来同步旧工具和新工具之间的数据。这可以是单向同步,从旧工具到新工具,或者双向同步。这样,就创造了一个过渡的情况,在这个过程中,两个工具可以同时使用。每个团队可以选择在一定的时间窗口内进行迁移。这避免了强制迁移的窗口。团队也可以选择在一段时间内并行使用这两个工具。这样,他们可以学习新工具,同时如果遇到压力,仍然可以切换回他们熟悉的工具。经过一段过渡期后,旧工具可以设置为只读,或完全淘汰。这种方法通常对于工作跟踪系统效果较好。在这些系统之间,概念通常非常相似(史诗、特性、故事和冲刺),这使得同步成为一种可行的方案。

重建

一个略微不同的方法是要求团队在新工具中重新构建。这种方法也创造了一个并行的情况,但没有自动迁移或同步。团队将不得不在新工具中重新做他们的流程或工作方式。由于这可能需要一些时间,旧工具将继续存在,直到团队完成这项工作。一个常见的适用场景是构建和/或发布流水线。

无论选择哪种策略,在所有情况下,确保新工具或工具集对团队来说优于现有工具都是很重要的。这应该提升性能、可追溯性、易用性或与其他公司工具的集成。任何迁移过程中的积极氛围都能显著改善结果。

作为迁移到单一工具的替代方案,工具之间的集成可以用来将现有工具结合在一起。

集成工具

作为替代不再优选工具的方案,也可以将它们与首选工具集成。特别是当决定迁移到 Azure DevOps 时,这可以成为一种强大的策略。在前面的许多章节中,对于每个主题,都列出了与 Azure DevOps 集成的不同工具。

在追求端到端可追溯性的过程中,这些集成可以作为使工具更加紧密结合的一种手段。考虑以下示例。

一个组织正在使用 Azure DevOps 来管理工作项、托管 Git 仓库和执行构建流水线。由于历史原因,部署工作是通过 Octopus Deploy 完成的。由于完全迁移成本过高,作为替代方案,选择了集成策略。通过 Azure DevOps 自动触发 Octopus Deploy 而非手动触发,达成了多个目标:

  • 实现端到端自动化。

  • 现在,发布管理也可以在 Azure DevOps 中完成,即便在 Azure DevOps 中的每次部署仅仅是触发 Octopus Deploy。

  • 现在在 Azure DevOps 中进行发布管理,可以实现端到端的可追溯性。

在整合工具和所有与 DevOps 相关的事情时,必须准备好接受一个事实,那就是你永远不会完成。

接受没有终点状态

合理预期,在任何时候,团队都会有一个或多个改进点,想要对他们的应用程序、工具链或工作方式进行改进。为了应对这一点,建议不要一直不断地改变一切。

相反,尝试分批或通过一系列明确定义的步骤来实施变更。同时,需要注意的是,事情是有自然顺序的。如果没有先建立合适的持续集成CI)流程,是不可能实践持续部署CD)的。另外,当应用代码的 CD 流程已经就绪时,采用基础设施即代码IaC)将带来最大的价值。接下来,自动化治理和安全措施在基础设施和配置代码已成为常规实践的情况下效果最佳。一旦所有这些实践都已实施,新的事物将出现在雷达上——未来可能需要的改进。

除了这一系列的改进外,还需要意识到,并不是每个团队都在这条旅程中的同一位置,并不是每个团队都能以相同的速度前进,而且开发并非总是线性的。

但这并不意味着无法跟踪和规划未来的变化,并将一个团队的经验教训应用到其他团队中。跟踪这一过程的一种过于简化的方式可以是使用表格,如下所示。

在这里,我们可以看到五个团队在采纳不同的 DevOps 实践或理念。所有团队都在实践 CI。一些团队在实践 CD,而团队 3 仍在进行进行中的工作WIP),第五个团队还没有开始。最后,团队 2 已经在试验 IaC。最终,由于没有终点状态,下一种实践或理念的出现只是时间问题,某个团队将开始进行实验:

如果像前面所示的表格那样定期更新、评估并扩展,它有助于促进持续学习,并改进软件的创建和交付方式。学习和改进将成为标准,这将有助于提升向最终用户交付价值的效率。它还表明,采用 DevOps 永远不会“完成”。

它还提供了一个途径,可以看到哪些团队走在前列,哪些团队在跟随。通过给处于领先位置的团队更多的实验空间,并与其他团队分享知识,组织可以鼓励他们的领先团队进一步改进,同时也加速其他团队的进步。

关于持续学习、实验和自我提升的这一点,正是一个很好的时机来结束这本书。让我们在下一节中回顾这一章的内容。

总结

本章中,你学会了如何配置 Azure DevOps 组织以及如何为你的产品和团队创建布局。你了解了不同配置选项的影响以及如何应用这些配置。之后,你学会了如何利用 Azure DevOps 为你的开发过程增加可追溯性。你学会了如何在故事和任务中捕捉想法和任务,并且这些可以追溯到部署过程,反之亦然。接下来,你学习了如何在组织内整合使用的工具,并了解了何时不再尝试整合工具。最后,你了解到持续改进的重要性。

通过本章所学的内容,你现在可以为你的团队、多个团队或整个组织设置和配置 Azure DevOps。你可以创建一个适合你组织的结构,并开始使用它,支持一个或多个团队。同时,你还能够逐步标准化工作方式,并将团队统一在同一套工具上。

本书的最后一章已完成。你可以参考这本书来帮助你准备 AZ-400 考试,因为大多数话题都是类似的。不过,为了充分准备考试,我建议你从其他资源中阅读更多相关内容,并尽可能多地进行实践。在本章的结尾,有一份模拟考试,帮助你为最终考试做好准备。

祝你好运!

问题

在本章总结时,以下是一些问题,供你测试自己对本章内容的掌握情况。你可以在附录评估部分找到答案:

  1. 对或错:用户在 Azure DevOps 中存储的所有数据都保证包含在一个区域内。

  2. 按照以下顺序重新排列 Azure DevOps 的概念,使得每个元素都作为下一个概念的容器:

    • 工作项

    • 组织

    • 区域

    • 项目

  3. 对或错:通常的建议是为你的组织开发的每个应用创建一个新的项目。

  4. 哪两个元素限制了任何用户在 Azure DevOps 中可以执行的操作?

  5. 使用单一工具进行 ALM/DevOps 与使用一套工具套件相比,主要的好处是什么?

活动

假设你的组织正在计划开发下一代客户关系管理在线服务。工程团队按照部门进行组织,每个部门负责为某个特定领域构建产品或服务。以下图示总结了这一点:

图 17.6 – 组织结构

图 17.6 – 组织结构

在每个部门内部,会有多个功能团队(例如,基础设施、网页、移动、核心 API 等)负责这些产品或服务的开发。

对于正在评估的场景,请确定使用项目、区域路径和团队设置 DevOps 组织的方法。需要考虑的一点是,每个部门希望完全拥有他们的 DevOps 组件和项目生命周期。

进一步阅读

第十八章:AZ-400 模拟考试

设计 DevOps 策略

  1. 你被要求将 Azure DevOps 引入你的组织。目前,组织中有许多其他工具在用于部署。你被问到哪些工具可以与 Azure DevOps 集成。[可以有多个答案。]

    1. Octopus Deploy

    2. Jira

    3. Jenkins

    4. 应用中心

  2. 你被要求为你的团队创建一个显示其工作信息的仪表板。你应该专注于显示鼓励敏捷和 DevOps 工作方式的度量和图表。你选择哪些度量标准?[选择三个。]

    1. 一个显示工作项平均周期时间的部件

    2. 一个显示最近部署结果(成功或失败)的部件

    3. 一个显示每天添加的代码行数的部件

    4. 一个显示团队当前打开的拉取请求数量的部件

  3. 你被要求在项目中实现静态代码分析。最合适的时间是什么时候?

    1. 在构建阶段,发布工件之前

    2. 在部署阶段,实际部署到目标环境之前

  4. 你正在为每个项目集合运行 import 服务。

  • DevOps 的核心原则之一是持续为最终用户交付价值。以下哪项不是为实现这一目标而使用的?

    1. 人员

    2. 实践

    3. 过程

    4. 产品* 你在一家大型企业工作,需要为新员工自动分配许可证。为此,你创建了一个与人力资源应用程序连接的小型应用程序。你在应用程序和 Azure DevOps 之间使用哪种授权方式?

    5. 用户账户

    6. 个人访问令牌

    7. OAuth 令牌

    8. 一次性秘密* 你需要将现有的 Git 仓库迁移到 Azure DevOps。你需要做什么?[选择两个。]

    9. 创建一个初始化的 Git 仓库。

    10. 创建一个未初始化的 Git 仓库。

    11. 执行 git remote rm origin; git remote add origin <new-repository-url>; git push

    12. 执行 git remote redirect origin <new-repository-url>; git push。* 你在一个异构环境中工作,不同团队使用不同的 DevOps 工具。你的一个构建在 Azure DevOps 中运行,但另一个团队希望从其他工具中获取你的管道工件。你可以使用以下哪些工具来暂存构建工件,以便从其他工具进行连接?

    13. 管道工件

    14. Artifactory

    15. Octopus Deploy 二进制服务器

    16. 工件源/通用包* 你被要求在 Azure DevOps 中为你的团队配置源控制。以下哪些要求只能通过使用团队基金版本控制TFVC)来实现?

    17. 你需要执行四眼原则。

    18. 你需要为特定用户配置对特定文件夹的访问权限。

    19. 你需要为特定用户配置对特定文件的访问权限。

    20. 你需要连接到经典构建管道。* 你需要执行并记录探索性测试会话。除了执行测试外,你还应该能够自动在 Azure Boards 的待办事项中报告缺陷。你计划使用 Test & Feedback 扩展来实现这一目标,并为所有测试人员分配测试许可证。这完成了目标吗?

    21. 是的

    22. 不是* 你需要执行并记录探索性测试会话。除了执行测试外,你还应该能够自动在 Azure Boards 的待办事项中报告缺陷。你计划使用 Test & Feedback 扩展来实现这一目标,并为所有测试人员分配基本许可证和测试许可证。这达成了目标吗?

    23. 是的

    24. 不是* 你需要执行并记录探索性测试会话。除了执行测试外,你还应该能够自动在 Azure Boards 的待办事项中报告缺陷。你计划使用 Test & Feedback 扩展来实现这一目标,并为所有测试人员分配基本许可证。这达成了目标吗?

    25. 是的

    26. 不是* 你负责识别可用于衡量采用 DevOps 影响的指标。以下哪些你建议使用?[选择两个]

    27. 同时进行的工作量

    28. 敏捷度

    29. 周期时间

    30. Sprint 持续时间* 以下哪项不是 DevOps 习惯?

    31. 团队自治与企业对齐

    32. 最大化未完成工作的量

    33. 假设驱动开发

    34. 实时站点文化* 你负责为你和你的团队正在创建的新应用程序制定测试策略。以下哪些建议你应该给出?

    35. 为了验证最关键的用户场景是否仍然正常工作,应该编写一个或多个系统测试。

    36. 为了验证最关键的用户场景是否仍然正常工作,在每次部署之前应该执行压力测试。

    37. 每十个单元测试中,应该至少有一个集成测试。

    38. 在生产环境启用新功能之前,应该在生产环境中执行最终烟雾测试。* 源代码是你公司最有价值的资产之一。当用户未通过公司网络连接时,你希望为 Azure DevOps 访问实施多因素身份验证。你可以使用以下哪项来做到这一点?

    39. Azure Active Directory 条件访问

    40. Azure Active Directory 网络许可

    41. Azure DevOps 网络控制

    42. Azure Active Directory 帐户组* 你正在一个为其他团队提供 Azure 订阅和资源组的团队中工作。作为你工作的一个部分,你希望监控所有团队是否实施了推荐的 Azure 安全最佳实践。你会使用以下哪项?

    43. Azure 策略

    44. Azure 安全中心(现为 Microsoft Defender for Cloud)

    45. Azure Key Vault

    46. Azure Security Monitor* 你负责提供从 Azure DevOps 内启动 SonarCloud 扫描的方式。以下哪些步骤能创建一个完整的解决方案?[选择两个]

    47. 更新 Azure DevOps 项目中的 SonarCloud 配置。

    48. 创建新的 SonarCloud 服务连接。

    49. 激活 SonarCloud 集成包。

    50. 安装 SonarCloud 扩展。* 你需要对 Kubernetes 中的部署进行更新—你应该使用哪个命令?

    51. kubectl apply

    52. kubectl deployments

    53. kubectl get services

    54. kubectl deploy 你在管理 Azure Kubernetes 集群时需要使用的基本工具是什么?[选择所有适用项]

    55. Azure CLI/PowerShell

    56. kubectl

    57. Azure DevOps

实施 DevOps 开发流程

  1. 你正在开发一个 Microsoft .NET Core 应用程序,并希望分析该应用程序,检查是否使用了任何具有已知安全漏洞的开源库。你可以使用以下哪些产品进行此类分析?[选择两个]

    1. Jenkins

    2. WhiteSource Bolt

    3. Snyk

    4. App Center

  2. 你当前使用 (i) JIRA, (ii) GitLab 和 (iii) Octopus Deploy 来处理部分 DevOps 流程。你想整合你的 DevOps 工具,并选择使用 Azure DevOps。你应该使用哪些 Azure DevOps 服务来替代这些工具?[匹配三个对]

    1. Azure Pipelines

    2. Azure Repos

    3. Azure Boards

    4. Azure Artifacts

  3. 你正在评估不同的构建代理选项。选择私有代理而非 Microsoft 托管代理的有效理由是什么?[选择两个]

    1. 你需要在任何作业执行之前,确保代理上已经有了自定义软件。

    2. 你需要确保在销毁之前,只有一个管道作业使用相同的环境进行执行。

    3. 你需要从构建代理直接连接到本地网络。

    4. 你需要确保始终使用最新的镜像。

  4. 你负责管理团队部署到 Azure App Service 的应用程序设置。以下哪些服务无法用于实现这一目标?

    1. Azure App Configuration

    2. ARM 模板

    3. Azure Policy

    4. Azure Key Vault

  5. 你负责为团队创建大量的构建管道。几乎所有管道都需要相同的结构。哪个 Azure DevOps Pipelines 构件可以帮助你?

    1. 分支策略

    2. 任务组或模板

    3. Azure Artifacts

    4. 部署组

  6. 你正在使用 Entity Framework 作为应用程序的数据库访问层。你负责管理数据库升级,并希望使用 Entity Framework 来管理数据库模式。你应该使用哪种类型的模式迁移?

    1. 基于迁移

    2. 基于最终状态

  7. 你需要将本地更改保存到 Git 仓库。你需要使用哪些命令?[选择两个]

    1. git clone 和 git push

    2. git commit 和 git push

    3. git add, git commit, 和 git push

    4. git add 和 git commit

  8. 你需要防止任何人在变更无法编译或单元测试失败时合并到主分支。以下哪些可以实现这一目标?

    1. 分支保护中心

    2. Azure Repos 分支策略

    3. Azure Repos 分支安全性

    4. 这在 Azure DevOps 中不可行;你需要使用其他产品,例如 GitHub。

  9. 你的公司使用 GitHub Enterprise 本地部署作为源代码托管服务。为了实现持续集成和持续部署,你打算使用 Azure DevOps。以下哪些组件组成了一个完整的解决方案,使这成为可能?[选择两项]

    1. 外部 Git 服务连接

    2. 打开防火墙,以允许 Azure Pipelines 从 GitHub Enterprise 进行 HTTPS 连接

    3. 一个用于 HTTP 的 Git 源代理

    4. 本地代理

  10. 一个新团队加入公司,他们必须开始开发一个新应用。他们请求你推荐一个分支策略,允许他们并行开发多个功能,并随时部署新版本,同时减少后期合并更改的需要。你推荐哪种方法?

    1. 为每个团队成员创建一个永久分支,并挑选提交进行合并。

    2. 为每个功能创建一个分支,并在完成后合并该分支。

    3. 为每个任务创建一个分支,并在完成后合并该分支。

    4. 在完成可以交付的工作时,尽可能频繁地创建并合并分支。

  11. 你被任务分配为在 Azure DevOps 中为你的团队配置源代码管理。你会选择哪种源代码控制系统,最好是?

    1. TFVC

    2. Git

  12. 以下哪项是正确的?

    1. 在 Azure DevOps 项目中,你可以有任意数量的 Git 仓库和 TFVC 仓库。

    2. 在 Azure DevOps 项目中,你最多可以有一个 Git 仓库和一个 TFVC 仓库。

    3. 在 Azure DevOps 项目中,你最多可以有一个 TFVC 仓库和任意数量的 Git 仓库。

    4. 在 Azure DevOps 项目中,你只能选择使用 Git 仓库或 TFVC 仓库,不能同时使用两者。

  13. 你的团队正在开发一个移动应用,并希望使用 App Center 将应用分发到应用商店和团队内部的测试人员。以下哪项是你应该使用的?[选择两项]

    1. 仅限邀请的预发布组

    2. 推送到商店的集成

    3. 存储连接

    4. 分发组

  14. 你正在为一个新项目创建一系列微服务。你希望找到一种方式从集中位置管理配置。许多配置设置在微服务之间共享。以下哪个解决方案最适合这个用例?

    1. Azure 密钥保管库

    2. Azure 应用配置

    3. Azure 配置中心

    4. ARM 模板

  15. 你必须确保在代码至少被两个人查看过之前,无法将其提交到仓库的主分支。以下哪项是一个完整的解决方案?[选择三项]

    1. 强制要求通过拉取请求来合并更改到主分支。

    2. 在向分支推送新提交时,重置拉取请求的批准投票。

    3. 最少有两个审核者,但允许每个人合并自己的更改。

    4. 至少需要一个审阅者,并且该审阅者不能是提交拉取请求的人。

  16. 你必须为团队生产的二进制文件(DLL)签名,以便其他团队可以验证这些二进制文件没有被修改,并且确实是来自你的团队。你必须安全地存储用于签名的证书。你可以在哪里做到这一点,并且仍然能够在管道中使用该文件?如果有多个答案适用,请选择最简单的解决方案。

    1. Azure Pipelines 库

    2. Azure Key Vault

    3. 在源代码控制中加密

    4. Azure DevOps 证书存储

  17. 你必须确保每个构建管道都包含一个由你的团队预共享的任务组。你可以使用以下哪个 Azure DevOps 构件来做到这一点?

    1. 管道装饰器。

    2. 管道验证器。

    3. 管道执行前任务。

    4. 这是不可能的——你必须实现手动审核过程。

  18. 你的源代码存储在 Subversion 源代码控制系统中。你希望迁移到 Azure DevOps Pipelines 进行持续集成,但又不希望迁移源代码并连接 Pipelines 到 Subversion。这样做可能吗?

    1. 是的

    2. 不是

  19. 开发团队正在创建一个容器化应用程序。该解决方案需要部署到 Azure 中的 Kubernetes 集群。你需要创建该集群并确保应用程序按预期运行。请选择你应执行的命令,并将其按执行顺序排列。

    1. kubectl apply

    2. az group create

    3. az aks create

    4. az appservice plan create

    5. kubectl get deployments

    6. az aks get-credentials

    7. kubectl get hpa

    8. kubectl get services

  20. 运行容器而非虚拟机的一个巨大优势是容器共享操作系统内核。这也使得容器镜像比虚拟机镜像更小。这是否正确?

    1. 是的

    2. 不是

实现持续集成

  1. 根据测试范围的大小对以下类型的测试进行排序,从最小范围的测试类型开始。

    1. 集成测试

    2. 单元测试

    3. 系统测试

  2. 以下哪些是正确的?[选择多个答案。]

    1. 压力测试通过对系统施加不断增长的负载,来识别系统的断点。

    2. 集成测试始终包括数据库。

    3. 性能测试用于测量系统执行给定任务的速度。

    4. 可用性测试用于识别系统响应过慢的使用场景。

  3. 你正在创建一个 Azure DevOps 仪表板,以便为团队提供关于编写代码质量的洞察。以下哪个小部件不属于该仪表板?

    1. 一个显示最近部署及其是否成功的小部件

    2. 一个显示每天提交次数的小部件

    3. 一个显示单元测试代码覆盖率随时间变化的小部件

    4. 一个显示最新构建是否失败的小部件

  4. 以下哪个不是有效的合并策略?

    1. 压缩提交

    2. 变基

    3. 交错

    4. 合并提交

  5. 以下哪项不是 OWASP Top 10 的一部分?

    1. 注入漏洞

    2. 敏感数据暴露

    3. 最小权限原则违规

    4. 使用已知漏洞的依赖项

  6. 你的团队正在创建容器镜像,这些镜像以后将部署到 Azure。以下哪些选项可以用来存储你的镜像?[选择两个]

    1. Azure 容器实例

    2. Azure 容器注册表

    3. Azure Kubernetes 服务

    4. Docker Hub

  7. 你希望在任何集成构建失败时收到通知,并在 Azure DevOps 项目中配置了电子邮件订阅。这样能达到目标吗?

  8. 你希望在 Azure Artifacts feed 中托管的工件有新版本时触发 YAML 管道。这可能吗?

  9. 你希望在多阶段 YAML 管道的同一阶段中使用托管代理、云中的私有代理和本地代理的组合。这可能吗?

  10. 你希望创建一个每天固定时间触发的发布管道,并排除周日。这可能吗?

  11. 开发团队正在创建一个容器托管应用,并希望在互联网上共享该镜像。团队通过 Docker 构建镜像,并尝试通过 Kubernetes 托管它。这是正确的做法吗?

  12. 以下哪些是可以存储容器镜像的位置?[选择所有适用的选项]

    1. Azure 容器实例

    2. Docker Hub

    3. Azure 容器注册表

    4. Azure 容器存储

实施持续交付

  1. 你所在的公司使用 ServiceNow 作为变更管理系统。规定应使用 ServiceNow 跟踪每次部署到生产环境的情况。你负责确保,如果变更管理系统中没有注册有效的变更,你的应用不会部署到生产环境。以下哪些操作可以实现这一目标?[选择两个]

    1. 你实施一个部署回调,以检查 ServiceNow 中的有效变更。

    2. 你将部署门作为生产阶段的前置条件进行部署。

    3. 你在 QA 阶段完成部署时,添加了一个部署门作为后置条件。

    4. 你创建一个环境,命名为 Production-ServiceNow check

  2. 你需要将一个应用部署到十二台本地虚拟机上,并将它们分为三个子网。为了完成这个任务,应该执行以下哪些操作?[选择三个]

    1. 创建一个新的部署组,并将正确的代理添加到该组。

    2. 在需要部署的所有虚拟机上下载并安装私有代理。

    3. 在发布管道中添加选择作业,以选择要使用的部署组。

    4. 在每个子网中精确地在一台虚拟机上下载并安装私有代理。

    5. 在发布管道中添加一个部署组作业,执行部署应用所需的任务。

    6. 在部署管道上配置用户名和密码,配置如何连接到代理。

  3. 你需要配置一个发布管道,以满足在可以开始部署到生产环境之前的几个条件。这些条件是:(i)审批委员会中至少四个成员中的两个应批准部署;(ii)发布后的第一小时内,应该检查 Azure Monitor 是否有任何警报。这可以使用 Azure DevOps Pipelines 完成吗?

    1. 是的

  4. 你正在使用 SQL Server 数据工具 (SSDT) 来描述数据库架构作为代码。你还希望使用 SSDT 进行架构升级。你应该使用哪种类型的架构迁移?

    1. 基于迁移

    2. 基于最终状态

  5. 你正在使用无模式数据库。这是否完全消除了架构管理的问题?

    1. 是的

  6. 你的团队必须遵守规定,要求每个新版本必须由测试经理手动批准,才能部署到生产环境。以下哪个更改最能有效满足这一要求?

    1. 你在生产阶段添加了一个部署前门控,验证在自建系统中的签署情况,该系统记录所有已签署的应用版本。

    2. 你在 QA 阶段添加了一个部署后门控,当所有自动化测试成功时,测试经理在指定系统中进行审批。

    3. 你在 QA 阶段添加了一个部署后审批,必须由测试经理批准。

    4. 你禁用了生产阶段的自动批准,并指示所有人只有在咨询包含所有测试经理签署的系统后才开始部署。

  7. 你正在为你的团队创建多个发布管道。许多管道将在某些任务中使用相同的配置值。以下哪项可以帮助你以完整的解决方案重复这些值?

    1. 变量组

    2. 任务组

    3. 变量容器

    4. Azure 密钥保管库

  8. 你希望从部署中自动生成发布说明。你希望做到这一点而不使用任何扩展或插件—只使用 Azure DevOps 的内建功能。这可能吗?

    1. 是的

  9. 你想从 Azure DevOps 部署应用到 Azure Service Fabric。你需要为此安装任务扩展吗?

    1. 是的

  10. 你需要在 Kubernetes 中应用更新。你应该使用哪个命令?

    1. kubectl apply

    2. kubectl deployments

    3. kubectl get services

    4. kubectl deploy

  11. 在 Azure DevOps 中用于将容器部署到 Azure Kubernetes 的任务是什么?

    1. Kubernetes 清单

    2. Kubernetes

    3. Kubernetes 常规任务

    4. Kubectl

  12. 什么类型的文件最适合部署资源到 Kubernetes 集群?

    1. ARM 模板

    2. Terraform 文档

    3. PowerShell 脚本

    4. YAML 部署文件

实现依赖管理

  1. 你正在使用 Azure Artifacts 托管团队创建的 NuGet 包。你有一个新需求,需要将你创建的一个(且仅一个)包提供给你组织中的所有其他团队。以下哪些是有效的解决方案?[选择多个。]

    1. 你创建了一个新的 feed,并允许 Azure Active Directory 中的任何用户使用该 feed 中的包。你将包移动到这个 feed 中进行共享。

    2. 你允许组织内的所有用户使用你现有的 feed。

    3. 你创建了一个新的 feed,并允许你组织中的任何用户使用该 feed 中的包。你将包移动到这个视图中进行共享。

    4. 你在现有的 feed 中创建了一个新的视图,并将要共享的包发布到该视图。接下来,你配置所有组织成员可以从该视图读取包。

    5. 你将现有的 feed 添加为上游源到其他团队使用的 feed,以便他们也能拉取你的包。

  2. 以下哪些理由支持将你的解决方案拆分为多个较小的解决方案,并使用共享包或库来从这些较小的解决方案组装完整的应用程序?[选择两个。]

    1. 你的一个解决方案中有超过 25 个 C# 项目。

    2. 你的代码库变得越来越大,以至于编译和/或运行单元测试开始变得非常耗时。

    3. 你的团队变得太大,已经分为两部分。将解决方案拆分也可以明确划分所有权:每个团队一个解决方案。

    4. 你的 Git 仓库中的文件数已接近 10,000 个限制。

  3. 对于以下哪些情况,Azure Artifacts 支持上游源?[选择两个。]

    1. Composer

    2. Python

    3. Gems

    4. Maven

  4. 你正在使用 Azure Artifacts 提供的依赖管理服务。你正在依赖一个通过 NuGet 公共可用的库。以下哪些方式可以用来通过现有的 feed 消耗这个包?

    1. 上游源

    2. 外部视图

    3. 上游视图

    4. 依赖视图

  5. 你有一个在两个应用程序中使用的库,但仅在你自己团队内使用。以下哪种策略是共享这个库的最佳方法?

    1. 将共享库链接为两个使用方解决方案中的共享项目。

    2. 将库放在一个单独的仓库中,使用构建管道构建库并将其作为 NuGet 包上传到 Azure Artifacts。在你的两个应用程序中从那里消费它。

  6. 你希望使用通用包从 Azure DevOps 向不同的部署协调器分发应用程序组件。这可能吗?

实现应用程序基础架构

  1. 你正在开发一个将部署在两个不同的 Azure 区域的应用程序,以支持故障转移场景。以下哪些组合构成一个有效的解决方案?[选择两个。]

    1. 你创建了一个包含两个参数文件的 ARM 模板。第一个参数文件对应第一个 Azure 区域,第二个参数文件对应第二个 Azure 区域。你使用 ARM 模板来更新基础设施。

    2. 你创建了一个 ARM 模板和参数文件,只更新一个区域的基础设施。另一个区域的基础设施手动更新,以防止配置漂移。

    3. 你先在两个区域更新基础设施。只有当基础设施成功更新后,才会将应用程序部署到两个区域。

    4. 你先在一个区域更新基础设施,然后部署应用程序。只有当这一步成功后,才会在另一个区域更新基础设施并将应用程序部署到该区域。

  2. 你需要通过 Azure DevOps 管道将一个 Azure 资源管理器模板部署到 Azure 资源组。你需要使用的一些参数存储在 Azure 密钥保管库中。以下哪个选项组合不是完整解决方案的必要部分?

    1. 创建一个新的变量组,并通过服务连接将其链接到正确的密钥保管库。

    2. 给 Azure Active Directory 服务主体在正确的 Azure 密钥保管库上分配 Reader RBAC 角色。

    3. 在你的 Azure DevOps 项目中配置一个新的 Azure 资源管理器服务连接,并以此方式创建一个新的 Azure Active Directory 服务主体。

    4. 给 Azure Active Directory 服务主体在正确的密钥保管库上分配以下访问策略:listget

  3. 你需要在 Azure 中创建和配置多个虚拟机。你应该使用哪些工具的组合?[选择两个。]

    1. Azure 自动化 DSC

    2. Azure 运行时运行簿

    3. ARM 模板

  4. 你正在配置将来应用程序将被部署到的 Azure 资源组。你已获得一个用于从 Azure DevOps Pipelines 进行部署的预创建服务主体。你应当为该服务主体分配什么 RBAC 角色,以便部署资源?

    1. 读取者

    2. 贡献者

    3. 部署者

    4. 拥有者

  5. 你需要为你的团队设置 RBAC 角色分配。你希望遵循最小权限原则,同时确保需要访问资源的团队成员能够访问资源。以下哪个解决方案最为合适?

    1. 你将用于部署的服务主体和所有团队成员添加到一个 Azure Active Directory 组中。你将此 Azure Active Directory 组分配为 Azure 资源组的贡献者角色。

    2. 你给用于部署的服务主体在资源组上分配贡献者权限,并给所有团队成员分配读取者角色。

    3. 你创建了两个新的 Azure Active Directory 组:reader 和 writer。你将用于部署的服务主体添加到 writer 组中,并将所有团队成员添加到 reader 组中。你将读取者角色分配给 reader 组,将贡献者角色分配给 writer 组。

    4. 您创建了一个新的 Azure Active Directory 组。您将用于部署的服务主体添加到该组中,并为该组分配贡献者角色。您为团队成员创建了一个升级流程,以便他们可以暂时被添加到此 Azure Active Directory 组中。

  6. 以下哪些工具不能用于管理 Azure 资源?

    1. Terraform

    2. Azure DevOps CLI

    3. Azure PowerShell

    4. CloudFormation

  7. 您正在实践基础设施即代码,并希望将 ARM 模板从 Azure DevOps 部署作为部署过程的一部分。以下哪种解决方案以最简单的方式完成此操作?

    1. 您从部署管道中的 Cmd 任务执行 Azure CLI 脚本。

    2. 您从部署管道执行 PowerShell 脚本。

    3. 您使用内置任务来部署 ARM 模板。

    4. 您将模板上传到 Azure 存储帐户,并使用 HTTP REST 调用启动 ARM 模板的部署。

  8. 您正在通过实践一切即代码来改变团队交付应用程序的方式。以下哪些不能使用 Azure 蓝图或 ARM 模板创建?

    1. Azure 订阅

    2. Azure Active Directory 安全组

    3. Azure RBAC 自定义角色

    4. Azure RBAC 角色分配

  9. 您正在为一个团队提供 Azure 订阅和资源组,作为工作的一部分,您希望限制团队可以创建的 Azure 资源类型。您使用以下哪种工具?

    1. Azure RBAC 角色和角色分配

    2. Azure 策略

    3. OWASP Zed 攻击代理

    4. Azure 安全中心(现在是 Microsoft Defender for Cloud)

  10. 使用基础设施和配置即代码工作有哪些不是好处?

    1. 最小化配置漂移

    2. 同行评审支持

    3. 降低配置更改的前置时间

    4. 配置更改的源代码控制历史

实现持续反馈

  1. 您需要收集您的团队创建的应用程序的崩溃报告。您可以使用哪些工具来完成此任务?[选择两个。]

    1. Snyk

    2. Raygun

    3. App Center

    4. Azure Automation

  2. 您正在配置许多警报。某些警报需要通过电子邮件发送警告,其他则是严重错误,需要通过短信发送。无论警报是警告还是错误,您还需要更新家庭构建系统,以便警报被触发。

您创建了以下解决方案:一个用于警告的操作组既发送电子邮件,也调用家庭构建系统的 WebHook。一个用于错误的操作组既发送短信,也调用家庭构建系统的 WebHook。对于警告类型的警报,您配置操作组一;对于错误类型的警报,您配置操作组二。

这是一个完整且正确的解决方案吗?

  1. 是的

  2. 您正在配置许多警报。某些警报需要通过电子邮件发送警告,而其他则是严重错误,需要通过短信发送。无论警报是警告还是错误,您还需要更新家庭构建系统,以便警报被触发。

您创建以下解决方案:一个动作组发送电子邮件、发送短信文本消息,并在主构建系统上调用 WebHook。您在所有警报上配置此警报组,并添加警报条件配置,以仅在警告时发送电子邮件,并仅在错误时发送短信文本消息。

这是一个完整且正确的解决方案吗?

  1. 您正在配置许多警报。某些警报需要通过电子邮件发出警告,而其他警报则是严重错误,需要发送短信文本消息。无论是警告还是警报,您还需要更新主构建系统以处理已触发的警报。

您创建以下解决方案:一个警告动作组发送电子邮件。第二个错误动作组发送短信文本消息。第三个动作组在主构建系统上调用 WebHook。对于警告警报,您配置动作组一和二。对于错误,您配置动作组二和三。

这是一个完整且正确的解决方案吗?

  1. 您希望邀请用户在您的产品上提供想法和建议。这应该在公共场所进行,以便其他用户可以评论和投票这些建议。以下哪些工具可以以最简单的方式实现这一点?[选择两个。]

    1. Azure Blob 存储静态站点

    2. GitHub 问题

    3. UserVoice

    4. Azure Boards 公共视图扩展

答案

  1. A, B, C, D

  2. A, B, D

  3. A

  4. D – A – C – B – E

  5. B

  6. B

  7. B, C

  8. D

  9. C

  10. D

  11. A

  12. A

  13. A, C

  14. B

  15. A, D

  16. A

  17. B

  18. B, D

  19. A

  20. A, B

  21. B, C

  22. A – (iii), B – (ii), C – (i)

  23. A, C

  24. C

  25. B

  26. A

  27. B, C

  28. B

  29. A, B

  30. D

  31. B

  32. C

  33. C, D

  34. B

  35. A, B, C

  36. A

  37. A

  38. A

  39. B, C, F, A

  40. A

  41. B, A, C

  42. A, C

  43. B

  44. C

  45. C

  46. B, D

  47. A

  48. A

  49. A

  50. A

  51. B

  52. C

  53. B, C

  54. A, B, E

  55. A

  56. B

  57. B

  58. C

  59. A

  60. B

  61. B

  62. A

  63. A

  64. D

  65. C, D

  66. B, C

  67. B, D

  68. A

  69. B

  70. A

  71. A, D

  72. B

  73. A, C

  74. B

  75. D

  76. D

  77. C

  78. B

  79. B

  80. C

  81. B, C

  82. A

  83. B

  84. A

  85. B, C

评估

第一章,DevOps 简介

  1. 正确。在传统组织中,开发通常负责对软件进行更改,而运维负责维护这些更改的目标系统的稳定性。由于更改本质上带有风险并可能扰乱稳定性,因此运维通常对更改持抵制态度。

  2. 错误。虽然理论上可以孤立地应用不同的 DevOps 实践,但真正的价值来自于将它们结合起来。例如,没有连续集成和测试自动化的持续部署不仅毫无意义,而且实际上在没有连续集成和测试自动化提供的质量保证的情况下进行持续部署是危险的。

  3. 错误答案是 D。DevOps 不是一个职称,而是一种文化运动。实际上,在开发和运维之间创建一个新的 DevOps 团队往往与 DevOps 的理念相悖。与其说有两个各自目标的团队或部门,不如说现在有三个。

  4. 快速通道是一种加快非计划、高优先级工作进度超过计划工作的方法,同时保持整个团队使用单一迭代看板。

  5. DevOps 有很多定义。常见的主要元素包括业务价值、最终用户、持续部署、自动化和协作。

第二章,站点可靠性工程基础

  1. 正确。SRE 团队的主要责任是维护应用程序的可靠性。企业或产品的声誉和成功取决于生产环境中云解决方案的可靠性。

  2. 错误。通常,可用性以“九”表示。例如,三个九(99.95%的可用性)在一年中的平均停机时间为 4 小时 22 分钟 58 秒。

  3. 正确。SRE 采用自动化来解决常见和重复出现的问题。由于自动化,常见问题的响应时间得以缩短。

  4. 95% 的可用性意味着允许的停机时间为:

    • 每周:8 小时 24 分钟 0 秒

    • 每月:1 天 12 小时 31 分钟 27 秒

    • 每年:18 天 6 小时 17 分钟 27 秒

  5. 正确答案是 A、B 和 C。DevOps 管理和缺陷跟踪不会影响或给维护可靠性带来挑战。

  6. 正确。服务可靠性的级别需要由关键利益相关者定义,SRE 团队将通过使用 SLOs 和 SLIs 来帮助维持这些服务可靠性水平。

  7. 劳动是与运行生产服务相关的一种工作,通常是手动的、重复性的、可自动化的、战术性的、缺乏持久价值的,并且随着服务的增长而线性扩展。

第三章,充分发挥 DevOps 工具的优势

  1. 错误。你可以使用 Microsoft 或 GitHub 账户免费注册 Azure DevOps。

  2. 正确。Azure Boards 允许多个团队在单一项目上协作工作。

  3. 正确。Application Insights 是 Azure Monitor 的一项功能,提供可扩展的应用性能管理APM)和实时 Web 应用的监控。

  4. 正确。你可以使用 Azure Pipelines 自动构建、测试、打包、发布和部署 GitHub 仓库代码。

  5. 错误。GitHub 是微软提供的企业级服务,支持公共和私有仓库。

第四章,一切从源代码管理开始

  1. 集中式和去中心化源代码管理的主要区别在于,去中心化源代码管理系统中,每个用户都有完整的源代码历史。而在集中式系统中,只有服务器拥有完整的历史。去中心化系统在断开服务器连接时表现最佳,而集中式系统通常允许更详细的访问控制。

  2. 正确。Git 是最知名的去中心化源代码管理系统。

  3. 正确答案是 B。Rebasing 不是一种分支策略,而是一种合并策略。

  4. 在使用 Git 时,拉取请求用于将一个分支的更改合并到另一个分支。拉取请求可以被审查、批准或拒绝。为了强制使用拉取请求,可以使用 Git 策略。

  5. 正确答案是 B。基于主干的开发不是合并策略,而是分支策略。

第五章,迁移到持续集成

  1. 错误。持续集成是指每天至少将每个开发人员的工作与同事的工作集成,并构建和测试集成后的源代码。仅仅运行每日构建并不构成持续集成。

  2. 正确。经典构建管道始终连接到源代码仓库。尽管构建管道中可能不会使用源代码,但连接始终存在。

  3. 错误。可以创建一个直接以阶段开始的 YAML 管道,源代码仓库的链接不再是必需的。

  4. 正确答案是 C:服务连接。服务连接是在包含需要调用外部工具的管道的组织或项目中配置的。配置好服务连接后,可以在一个或多个管道中使用它。

  5. 正确答案是 A 和 D:访问封闭网络的权限和安装额外软件的能力。自托管代理部署在你拥有的基础设施上。这意味着你可以将它们部署在你控制的网络上,从而使它们能够访问该网络。由于代理部署在你的基础设施上,你还可以决定安装哪些软件(以及哪些不安装)。任务和扩展任务会在代理执行作业之前自动下载到代理上。你可以拥有任意数量的并行管道,而无需使用自托管代理。然而,为此你需要从 Microsoft 购买额外的并行执行。

第六章,实现持续部署和发布管理

  1. 错误。也可以通过定时或手动触发新发布。

  2. 所有答案都是正确的。

  3. A 和 C 都是正确的。环形部署和金丝雀部署只将新版本的应用程序暴露给有限的用户群体。功能开关也用于渐进式曝光,但不是用于限制部署的风险,而是用于限制新特性发布的风险。

  4. 正确。部署组用于在发布管道中执行任务时,不仅仅在组中的一个代理上执行,而是在所有代理上执行。部署组旨在用于在运行代理的机器上部署软件。

  5. 一个可能的优势是,所有步骤的端到端可追溯性都保留在 Azure DevOps 中。如果你也在 Azure DevOps 中管理工作项和源代码,你将保持从工作项到发布的端到端可追溯性,而这一切都可以在实际部署中使用 App Center。

第七章,依赖管理

  1. 错误。一个包的版本可以在多个视图中可见。

  2. 错误。管道工件只能在 Azure DevOps 中的其他管道内消费。

  3. 正确。Azure Artifact feeds 可以用于将通用包共享给其他产品。这使你能够在 Azure DevOps 中编译应用程序,将二进制文件作为通用包上传,然后在另一个产品中再次下载它。这在使用其他部署工具(例如 Octopus Deploy)时非常有用。

  4. 正确。依赖关系管理的三个主要方面是标准化、包格式与来源,以及版本控制。

  5. 正确答案是 B 和 D。A 选项不正确,因为包引用(无论是在 .csproj 文件中还是在 nuget.config 文件中)应仅通过名称和版本引用包。C 选项不正确,因为 consumer 不是 Azure Artifact feeds 中有效的访问级别。正确的访问级别是 reader(或更高级别),因此 B 选项正确。D 选项也是正确的,你需要将包位置添加到 NuGet 配置中。

  6. 一个动因可能是解决方案的规模。如果编译和测试解决方案的时间太长,以至于开发人员不得不等待与其工作相关的反馈,分割解决方案为更小的部分可能更好。这将缩短开发人员的反馈周期,从而提高速度。另一个动因可能是多个团队正在开发一个应用程序,并且他们希望增加团队之间的隔离程度。

第八章,实施基础设施和配置作为代码

  1. 正确。ARM 模板允许你为 Azure 资源组中的所有资源指定最终状态。应用 ARM 模板将始终导致创建缺失的资源并更新现有资源。如果指定了部署模式 complete,即使是模板中没有的资源也会被删除。

  2. 正确答案是 B。模块、Run As 帐户和变量都是在 第八章 中讨论的构造,实施基础设施和配置作为代码

  3. 错误。ARM 模板参数允许引用 Azure Key Vault 中的值,以防止用户在源代码控制中输入机密或其他敏感信息。在部署时,这些机密将被检索并在 Azure 中使用,前提是启动操作的身份具有访问该密钥库的权限。

  4. 正确。你可以在 Azure Automation 帐户中定义一个或多个计划,并将它们与 Runbook 关联。

  5. 在实践基础设施即代码时可以预期许多好处。两个常见的例子是防止配置漂移和按需创建新环境。配置漂移通过按计划重新应用相同的基础设施规范到环境中来防止。按需环境可以用于快速创建新的测试环境,进行测试后再移除该环境。这允许更可重复的测试结果,并可能节省测试基础设施方面的费用。

第九章,在 DevOps 场景中处理数据库

  1. 正确。Entity Framework 和 Entity Framework Core 都内建支持在架构定义变更后生成迁移。

  2. 错误。大多数基于迁移的方法使用一个额外的表格来跟踪哪些迁移已经应用到数据库中。

  3. 正确。基于最终状态的方法通过比较当前的架构和目标架构来工作。这样会生成一个一次性的 SQL 脚本,运行该脚本以更新数据库中的架构。运行之间没有存储任何状态。

  4. 正确答案是 A 和 B。并行运行,如果操作正确,能大幅减少变更风险。如果出现问题,你可以随时移除所有新增代码以及数据库副本,然后从一个正常的工作状态重新开始。让两个情境都正常运行还可以非常精确地测量生产工作负载的性能。然而,其中一个缺点是,周期时间可能会增加。你必须一个一个地将多个小的变更移到生产环境中,这会增加总的时间消耗。

  5. 错误。你的架构(无论是隐式的还是在数据对象中捕获的)仍然会发生变化。然而,只有在从数据库中读取之前使用旧版本对象持久化的对象时,这一变化才会显现。从本质上来说,你只是推迟了处理架构变更的问题。

  6. 你可以选择不使用数据库级的编码技术,如存储过程和触发器。你将逻辑尽可能地放在数据库之外,可以减少需要做的数据库变更次数。

第十章,集成持续测试

  1. 正确。在单元测试中,测试的是单个组件,且是孤立测试。在面向对象语言中,这通常是单一的类。

  2. 错误。在集成测试中,验证的是一组组件的正确工作,而不是整个组装好的系统。如果是测试整个组装和部署的系统,这被称为系统测试。

  3. 答案 B 是正确的。测试金字塔建议进行大量的单元测试,以验证尽可能多的需求。集成测试只添加用于覆盖无法通过单元测试覆盖的风险,因此集成测试的数量较少。系统测试的数量更少,仅用于覆盖单元测试和集成测试都未覆盖的风险。

  4. 答案 C 是正确的。所有其他类型的测试都在本章中讨论。

  5. 这里可以提到的两种技术是代码审查和管道门控。代码审查允许开发人员审查同事的工作,以帮助彼此保持高质量。管道门控可以在不满足某些条件时,阻止应用程序的构建或版本进一步传播。示例条件包括某些质量度量或测试覆盖率或测试结果的最低标准。

第十一章,管理安全性和合规性

  1. 错误。为了安全地创建和交付软件,整个过程,特别是管道,需要被保护。仅仅在最后加入安全措施是行不通的,因为安全性必须贯穿交付过程的各个步骤。

  2. OWASP Zed 攻击代理ZAP)可以用于这种类型的测试。

  3. 正确。在现代应用程序中,最多 80%的代码可能来自开源库或框架。

  4. 正确的答案是 A、B、D 和 F。没有所谓的 Azure DevOps 安全变量或 Azure DevOps 密钥库。

  5. Azure 策略可以用于禁止或列出不希望的 Azure 配置,通常与基础设施配置或配置相关。Azure 安全中心(现为 Microsoft Defender for Cloud)可用于识别和修复运行时的安全风险。

第十二章,应用程序监控

  1. 错误。Azure 发出的平台度量是由每个独立服务定义的,无法更改。

  2. 93 天。这个数字保证了至少有三个月的历史记录。

  3. 正确。可以在您自己的应用程序代码中计算自定义度量并通过 SDK 或 REST API 发送到 Application Insights。

  4. 警报疲劳。

  5. 正确。Azure 允许创建包含 Web 钩子的操作组,以便在触发警报时响应。

第十三章,收集用户反馈

  1. 错误。一个可能的缺点是失去市场竞争优势。如果竞争对手知道你将要开发什么,他们可能会抢先一步。

  2. 可能的担忧是某些用户或用户群体比其他用户更为激进,这可能导致大众意见与听到的意见之间的差异。而且,关于公共路线图的反馈最可能来自现有用户。虽然保留这些用户很重要,但潜在客户可能不会就他们缺失的功能在你的路线图上发表评论。

  3. 本章讨论的两个例子是社交媒体渠道上的情感分析和支持请求的数量及严重性。

  4. 答案 C 是正确的。假设陈述了某个功能是必要的信念。在假设中,第二部分是一个可测量的用户反馈,在确认信念之前需要观察到。这被称为确认阈值。假设还没有得出结论。

  5. 用户访谈或焦点小组的潜在好处是,它们通常在较小的规模上进行,不仅可以衡量反馈,还可以理解反馈背后的原因。另一个好处是,参与者可以被精心挑选,以代表所有用户或某一特定细分群体。

第十六章,容器

  1. 容器对 DevOps 的好处包括一致性、关注点分离和平台可移植性。

  2. 正确。根据宿主操作系统的不同,容器托管在哪里并不重要。

  3. 是的,这是可能的。通过添加 Docker 支持和项目级别可以实现。

  4. RUN 命令用于在构建容器镜像的过程中安装组件或执行操作。

  5. 节点和容器可以在 Azure Kubernetes 服务中进行调度。这些组件都可以手动或自动扩展。

第十七章,规划您的 Azure DevOps 组织

  1. 错误。有些信息可以传输到其他区域或全球可用。例如,当选定区域的容量不足时,代理有时会在其他区域运行。

  2. 工作项 | 项目 | 组织 | 区域。Azure DevOps 组织是用户可以创建的最高级别构建。每个组织都严格位于一个区域,该区域由 Microsoft 维护。在一个组织内,可以创建一个或多个项目。反过来,一个项目可以包含许多工作项,如用户故事、功能或史诗。

  3. 错误。一般的推荐是只创建足够的项目:越少越好。隔离性和非常严格的授权边界可能是选择使用多个项目的原因。

  4. 授权和许可。可以为每个用户或用户组设置可访问的权限限制。分配给用户的许可证还可能禁止使用某些功能。例如,具有利益相关者许可证的用户不能使用源代码控制。

  5. 端到端可追溯性。当从单一工具执行工作管理、源代码控制、构建、工件和部署时,可以追踪部署过程。

posted @ 2025-06-26 15:33  绝不原创的飞龙  阅读(172)  评论(0)    收藏  举报