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

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

译者:飞龙

协议:CC BY-NC-SA 4.0

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

在前一章中,你学习了如何使用 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 中实现持续交付和部署。创建新的发布定义时,将创建发布过程的大纲。此过程通常从触发新发布的工件开始。接下来,可以定义一个或多个阶段,可以将发布部署到这些阶段。通常,这些阶段对应于不同的应用程序环境,例如测试和生产,但这不是强制的。

让我们学习如何创建一个新的发布定义并探索我们拥有的各种选项。首先,导航到管道并从菜单中选择发布。从这里,可以开始创建一个新的发布管道,这将带我们到一个看起来类似以下截图的屏幕:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_01.jpg

图 6.1 – 新发布管道

在上一个屏幕上,我们可以执行以下操作(在上述屏幕截图中编号):

  1. 首先,请注意,左侧可以看到发布管道的大纲和一个框。在这里,您可以选择一个或多个可用于发布管道的工件。

  2. 在右侧,可以看到一个框,其中显示了发布的不同阶段。默认情况下,已经创建了一个阶段。

  3. 可以选择一个模板作为此预创建阶段的部署管道的起点。在此视图中选择从空作业开始,可以从头开始创建自定义部署管道。

在选择作业模板或从空作业开始之后,右侧的窗格将关闭,并且可以从左到右开始编辑发布管道,从工件开始。

一旦看到一个基本的发布管道框架,您需要配置的第一件事是发布应该使用的工件。这是下一部分的主题。

创建工件和发布触发器

前一章描述了构建定义和 YAML 管道,这些管道创建了工件。这些工件在发布中被接收,并形成部署应用程序的基础。

要开始编辑发布管道,请按照以下步骤操作:

  1. 点击添加构件按钮以开始构建发布定义的起点。这将打开右侧窗格,如下图所示:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_02.jpg

图 6.2 – 添加构件

  1. 在项目选择器中,当前项目将默认被选中。

  2. 现在,指定发布管道应获取的构件。

  3. 此后,默认使用的版本和源别名将自动选择。默认版本可以在手动启动发布时覆盖,因此最新是一个合理的默认选项。

  4. 源别名是我们稍后在发布阶段添加任务时,构件所在文件夹的名称。默认设置通常可以使用。

  5. 通过点击添加完成构件的添加。

现在我们已经指定了要使用的构件,是时候指定何时创建新发布了。让我们学习如何做到这一点:

  1. 若要配置新构件的可用性以触发发布,点击构件旁边的闪电符号以打开配置窗格。这可以在以下截图中看到:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_03.jpg

图 6.3 – 指定部署触发器

  1. 在此窗格中,可以使用顶部滑块创建新的发布,当发布可用时。这将展开一个新部分,您可以在其中定义一个或多个过滤器,以便指定在何种条件下新构件应触发发布。

  2. 点击添加按钮以开始添加条件。

  3. 一个常见的示例是仅包含来自主分支的构件,如图 6.3所示。

  4. 除了来自常规构建的构件外,还可以允许来自拉取请求构建的构件启动新发布。

  5. 最后,可以按固定计划创建一个新的发布。

如果没有指定计划和触发器,则新发布仅在有人手动操作时创建。

指定要部署发布的阶段

在指定了要发布的构件后,是时候指定一个或多个阶段来部署发布了。通常,每个环境(测试、验收和生产)将对应一个阶段,但如果情况需要,也可以有其他阶段。

让我们学习如何添加新阶段并探索各种选项。首先,点击管道,进入如下屏幕:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_04.jpg

图 6.4 – 在新管道中配置阶段

现在,完成以下步骤:

  1. 点击添加按钮以创建新阶段。一个阶段可以是新的,也可以是现有阶段的克隆。

  2. 在选择已存在的阶段后,可以通过右上角的删除按钮将其移除。

  3. 在此页面上,还可以执行其他操作,包括重命名阶段和指定阶段所有者。每当发布部署到环境中时,所有者将会收到通知。

  4. 在创建并命名一个阶段后,可以像在构建流水线中一样,向该阶段添加作业和任务。为此,请点击表示该阶段的框中的链接。

从这里开始,它与构建流水线的操作完全相同。唯一的区别是:除了代理作业和无代理作业,还可以使用部署组作业。

这些将在稍后的与部署组一起工作部分进行讨论。但首先,让我们了解一下我们需要哪些阶段。

我需要哪些阶段?

在处理发布时,常见的一个问题是,我在发布流水线中需要哪些阶段? 根据文档,阶段应该表示发布流水线中的主要分区。在开始处理发布时,这通常简化为每个环境在发布流水线中都有一个阶段。适当的阶段包括测试验收生产

在长时间处理发布的过程中,我们可能会在流水线中加入更多自动化,并希望为其添加额外的检查阶段。例如,可能会有一个叫做负载测试的阶段,与测试阶段并行执行。另一个例子可能是引入一个用于自动化 UI 测试的阶段。

无论添加了哪些阶段,发布到生产的传播方式始终应保持一致。当发布从一个阶段传播到另一个阶段,并且越来越接近生产时,这应表明对该发布有信心,证明它正在正确工作,并且可以推广到生产环境。

阶段触发器、审批和门控

在定义所需阶段并向其添加作业和任务之后,接下来是配置何时触发发布到特定阶段。此步骤可以在以下屏幕截图中看到:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_05.jpg

图 6.5 – 部署后配置

请注意,以下步骤需要为每个阶段单独执行:

  1. 要触发发布到特定阶段,请点击位于表示该阶段的方块左侧,带有闪电符号和人形图标的按钮。

  2. 在这里配置的第一件事是何时将发布传播到此阶段。可以在发布可用时、在完成另一个阶段后,或仅在手动请求时进行选择。您在此处做出的选择也将反映在流水线的视觉表示中。

  3. 与触发器分开,您还可以定义一个或多个过滤器,以限制哪些工件将触发对该阶段的部署。每个工件可以有一个或多个包括或排除分支过滤器。

  4. 也可以按固定计划重新部署。

  5. 最后,如果为从拉取请求启动的构建指定了创建新发布,则可以使用拉取请求部署滑块允许该发布传播到当前阶段。

在这些触发器旁边,可以添加审批者和门控,以便你可以配置如何处理部署队列设置。这些设置可以通过下面的触发器选项卡访问,如下图所示:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_06.jpg

图 6.6 – 部署前条件

一旦触发条件被配置,接下来的部分是关于审批者的。在这里,指定了组或用户。在发布到此阶段之前,他们必须给予批准。可以添加多个人,如果是这样,可以定义他们必须批准的顺序,或者可以指定一个批准就足够。向下滚动,你会看到以下选项:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_07.jpg

图 6.7 – 部署门控

第二个选项卡(左侧)允许你添加一个或多个门控。门控是必须成功的自动化检查,才能继续发布。目前,这显示了配置工作项查询和结果数量阈值的配置细节——例如,确保在发布继续之前没有未解决的错误。也有可用的门控,可以调用 Azure Monitor、Azure Functions 或 RESTful API。此套门控可以通过 Azure DevOps 扩展机制进行扩展。这些扩展中的一些还与常见的变更管理系统集成。

最后一部分(右侧)允许你配置如何处理不同版本的发布准备部署到同一阶段的情况。在这里,可以指定可以并行运行多少个发布。如果有更多的发布到来,你可以将它们排队并一个接一个地部署,或者只部署最新的版本。

使用部署组

另一个你可能会遇到的话题是将应用程序部署到本地服务器或位于防火墙后的服务器。你还可能会遇到需要在所有托管应用程序的机器上运行脚本的情况,或目标环境没有提供部署应用程序的机制的情况。

本章中“与 Azure DevOps 发布工作”部分展示的发布方式依赖于能够连接到将托管应用程序的目标机器或服务。我们称之为推送式部署,但并非总是可以做到这一点。

当部署到无法连接的目标机器时,需要采取另一种方法。这种方法称为基于代理的部署。在基于代理的部署中,Azure DevOps 代理将安装在每台需要部署应用程序的机器上。接下来,这些代理必须被分组到部署组中。一旦完成这一操作,就可以向发布中添加部署组作业

这与代理作业非常相似,只有一处不同。在代理作业中,作业中的任务将在其中一台代理上执行,目标机器上。 在部署组作业中,所有任务将在目标机器上的发布组中所有代理上执行。下图展示了两者方法之间的区别:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_08.jpg

图 6.8 – 作业在代理上运行

使用此方法时,必须在应用程序需要部署到的机器上安装代理。这些代理监听 Azure DevOps,并在有新的发布请求时,它们会获取工作并在本地机器上执行。

管理部署组

在您可以向发布流水线中添加部署组作业之前,您需要创建一个部署组。为此,请执行以下步骤:

  1. 导航到流水线菜单。

  2. 打开部署组菜单。

  3. 点击新建以添加新的部署组。

  4. 输入部署组名称和描述,然后点击创建

一旦创建了新的部署组,右侧会出现一个脚本,如下图所示:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_09.jpg

图 6.9 – 创建新部署组

在目标机器上执行此脚本将安装代理,并自动将该机器注册为新创建的部署组的一部分。

如果必须使用部署组将应用程序部署到三个阶段(测试、验收和生产),则需要为每个环境创建三个单独的部署组。

创建带有部署组的发布流水线

创建所需的部署组后,可以在任务视图中使用它们进行发布,如下图所示:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_10.jpg

图 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 管道中,无法像创建经典发布管道时那样定义审批者。原因在于,管道——构建和部署过程——被视为代码。代码只由开发人员和运维人员处理。而审批则是由例如产品负责人来处理。然而,这并不意味着无法为管道的推进到下一个阶段实现审批流程。

要控制管道是否允许继续到某个阶段,需要引入环境的概念。环境在我们为其指定名称和描述时定义。可以将一个或多个审批者附加到这些环境。一旦完成,作业就可以配置为针对该环境。如果一个阶段中至少有一个作业针对某个环境,那么该环境就被认为是该阶段使用的环境。如果该环境已配置审批,则在审批者给予许可之前,部署将无法继续到该阶段。

要开始使用环境,你需要访问环境列表。该列表可以在管道菜单中找到,如下图所示:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_11.jpg

图 6.11 – 添加新环境

要添加新环境,请执行以下步骤:

  1. 打开管道菜单并选择环境

  2. 从右上角选择新建环境

  3. 指定名称和描述。

  4. 点击创建

可以将资源与环境关联。与环境耦合的资源可以在管道中使用,但前提是该管道也针对该环境。为了保护环境的资源,环境的所有者可以添加一个或多个审批者。下面的截图中展示了一个配置的审批者示例:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_12.jpg

图 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 策略

在我们持续部署应用之前,重要的是要思考我们应该使用哪种策略。仅仅一个接一个地进行部署,可能带来的风险超出了企业所能接受的范围。因此,考虑如何处理在部署新版本应用过程中或之后可能发生的问题是非常重要的。

有几种部署策略可以应用,以减少部署可能带来的风险,所有这些策略将在本节中讲解。请注意,可以将以下一种或多种模式结合使用。例如,完全可以在基于环的部署中为每个环使用蓝绿策略。此外,所有部署策略都可以与功能标志结合使用。

蓝绿部署

蓝绿部署是一种技术,其中应用的新版本不会直接部署到生产服务器上。相反,它会先部署到另一组服务器上。一旦成功完成这一步,用户将被引导到新的部署版本。

假设一个应用默认运行在三个主机上。蓝绿部署的典型配置是两组三台主机——蓝色组和绿色组。在这两组前面,有一个反向代理,充当负载均衡器,将传入的请求重定向到蓝色组。以下图表展示了这个过程的工作原理:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_13.jpg

图 6.13 – 蓝绿部署

在这种情况下,要部署应用程序的新版本,需要将其部署到绿色服务器组。由于这些服务器不接收来自最终用户的任何流量,因此这种方式的服务器升级对用户完全没有影响。

部署完成后,可以验证新部署以确保其成功且应用程序正常运行。验证之后,负载均衡器会重新配置,将流量重定向到绿色服务器组。现在,应用程序的新版本已经开始提供服务。

如果突然出现任何意外问题,完全可以通过重新配置负载均衡器将流量切换回蓝色服务器组,从而轻松地回滚到先前的部署。如果部署成功且没有问题,则可以按照相同的程序开始下一版本的部署,但此时绿色组和蓝色组的角色已交换。

不可变服务器

蓝绿部署模式的一种变体是不可变服务器。使用不可变服务器时,不再在两个服务器组之间来回切换。相反,提供旧版本应用程序的服务器组将完全被忽略或移除。通常,这是在宽限期之后进行的。

其结果是,仍然可以通过保持旧服务器一段时间,几乎即时地回滚到先前的版本。另一个好处是,现在可以保证没有任何先前部署的残留物被带入到新的部署中。使用不可变服务器,随着时间的推移,活动服务器的变化可能如下所示:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_14.jpg

图 6.14 – 不可变服务器部署

当然,这种方法只有在使用诸如容器或虚拟机等技术时才可行。没人会期待在每次重新部署后都忽视物理服务器。

渐进式曝光

渐进式曝光是一种部署策略,其中可以访问新部署或新功能的用户数量随着时间的推移缓慢增加。该策略的目标是限制在功能发布出现问题时,体验问题的用户数量。

我们也可以从更积极的角度看待这一点,并与持续交付(CD)思维方式一致:最初只向少数用户暴露新功能,并随着时间的推移逐步增加该用户数,允许我们在将新版本或功能暴露给所有用户之前,增加对其的信任度。

金丝雀部署

渐进曝光的第一种策略是使用金丝雀部署。在金丝雀部署中,并不是所有用户都会立即被引导到新版本 – 只有一小部分用户能够访问该版本。这些用户就是“金丝雀”,并且会被密切监控。如果他们遇到任何问题,或者服务性能出现下降,新的部署会迅速回滚。

实现金丝雀部署的典型方法是将其与蓝绿部署结合使用。不同之处在于,不是一次性将所有用户切换到新版本,而是开始时只将少部分用户切换到新版本,然后逐渐增加切换到新版本的用户数量。其过程可能类似于以下方式:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_15.jpg

图 6.15 – 金丝雀部署

如果由于出现错误而回滚了部署,这对于用户来说不是一种愉快的体验。为了防止同一小部分用户反复遇到问题,之后选择不同的金丝雀用户可能会有益处。

基于环形的部署

在基于环形的环境中,不仅仅只有一个生产环境 – 而是有多个生产环境。每个生产环境只服务一部分用户。它与金丝雀部署不同之处在于,不仅仅有两个环境,而是根据需要有多个环境。而且,每个新版本都会依次推广到所有的环。

因此,在基于环形的环境中,新版本不会将用户重定向,而是会传播到这些用户使用的服务器上。新版本会从一个环传播到下一个环,直到所有环的用户都完成部署:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_16.jpg

图 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 后,您将看到以下屏幕:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_17.jpg

图 6.17 – 添加新应用

在这里,您可以创建一个新的应用定义。每个目标操作系统都应该创建一个应用定义。如果同一个应用程序将同时部署到 Android 和 iOS,至少需要创建两个应用。

创建应用的步骤如下:

  1. 登录 App Center。

  2. 点击蓝色的添加新应用按钮。如果没有现有应用,这个按钮会出现在屏幕中央;否则,它会出现在右上角(被之前截图中显示的弹出窗口遮挡)。

  3. 输入应用名称。

  4. 选择发布类型。

  5. 选择操作系统。

  6. 选择要使用的平台。

  7. 点击添加新应用来创建应用。

一旦应用创建完成,它可以连接到正确的应用商店,并可以创建分发组。

连接到应用商店

应用商店是所有移动平台分发应用的主要机制。一旦构建交付到应用商店,用户就可以安装并使用该应用。当前连接到应用商店的列表可以通过App Center左侧的商店选项卡打开。从此列表中,可以打开单独的商店连接,进入一个类似于下图所示的屏幕:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_18.jpg

图 6.18 – Visual Studio App Center 中的应用商店

此视图显示已发布到连接的商店账户的所有版本的应用列表。这里也是选择要发布到商店的新版本的地方。通过顶部的蓝色发布到 Google Play按钮来执行此操作。此时会弹出一个窗口,您可以选择正确的版本。只需确认一次即可发布此版本。

新的商店连接可以通过返回到所有商店连接的列表并点击添加按钮来创建。这将打开一个向导,需要输入两项信息:

  • 商店连接类型:此列表仅限于在创建应用定义时选择的类型所允许的商店。例如,对于 iOS,这仅限于 Apple App Store 和 Intune 公司门户。

  • 连接详情:通常包括 App Center 与应用商店之间的身份验证方式。

一旦新连接创建成功,它可以在前面显示的列表中找到,并可用于分发应用。

另一种分发方式是使用分发组,我们将在下一节中介绍。

使用分发组

分发组用于创建一个或多个用户的命名列表,通常是测试人员或早期用户,他们通过邀请而不是通过应用商店安装应用。分发组可以在左侧菜单的下找到:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_19.jpg

图 6.19 – Visual Studio App Center 中的分发组

在这里,可以添加一个新的组,如下所示:

  1. 使用左侧菜单导航到分发组

  2. 单击带有加号(+)符号的蓝色按钮(位于前述截图中的弹出窗口下方)。

  3. 选择组的名称。

  4. 添加一个或多个成员。

  5. 保存新组。

一旦创建了分发组,就可以用于发布版本,我们将在下一节中讨论此内容。

发布应用

要发布应用的首个或新版本,必须与 App Center 共享。可以使用左侧的发布标签来完成此操作。打开发布页面后,将显示以下视图,详细列出所有当前的发布版本。从这里,你可以选择任何版本并查看其详细信息:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_20.jpg

图 6.20 – 查看应用的发布版本

在此视图中,最近的发布列表显示在中间列。选择单个发布后,该版本的详细信息将会显示。这包括其正式版本、已共享的商店和/或分发组,以及其他详细信息。

从这里,你可以通过右上角的分发按钮,直接将此特定版本分发到商店连接或分发组。

从这里,你还可以通过上传应用的新构建来创建一个新的版本。为此,请按以下步骤操作:

  1. 点击新发布按钮,该按钮可以在所有发布版本的列表中找到(可能需要先关闭某个特定发布的详细信息)。这将打开以下视图:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_21.jpg

图 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。现在暂时不要添加任何资源:https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_22.jpg

图 6.22 – 部署环境

  • 对于每个环境条目,配置其审批人。审批人列表应包括那些可以批准其各自所拥有环境中任何活动的成员:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_06_23.jpg

图 6.23 – 配置环境设置

图 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 的结构构成:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_01.jpg

图 7.1 – Azure Artifacts 视图

在 Azure Artifacts 中,你可以创建一个或多个信息流来存储你的包。对于每个包,你可以在一个信息流中拥有多个版本。信息流是你可以设置发布包权限的级别。在一个信息流中,你可以创建一个或多个视图,用于设置消费包的权限。任何特定版本的包可以同时存在于多个视图中。接下来的小节将更详细地讨论这些概念。

设置信息流

在 Azure Artifacts 中,信息流是存储包的位置。每个信息流都是一个独立且完全隔离的仓库。要创建新的信息流,请按照以下步骤操作:

  1. 首先,在左侧菜单中导航至 Azure Artifacts,然后点击创建信息流按钮(在以下截图中,按钮部分可见,在创建新信息流的面板后面):

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_02.jpg

图 7.2 – 创建新信息流

  1. 为信息流指定一个名称。该名称不应包含空格,最好只包含字母和数字,因为它将成为 URL 的一部分。

  2. 接下来,可以指定初始的可见性设置。这决定了哪些用户可以查看该信息流。这个内容将在后续小节中更详细地讨论,管理信息流的视图

  3. 配置上游源的使用。此部分内容将在后续小节中更详细地讨论,配置上游源

  4. 在选择创建后几秒钟,你的信息流就会变得可用。

一旦信息流创建完成,你可以配置各种设置,如隐藏已删除的包、启用包批处理和配置保留策略。要了解如何操作,请按照以下步骤执行:

  1. 创建信息流后,通过点击右上角的齿轮图标访问信息流的设置。

  2. 在下图所示的视图中,选择信息流设置。在这个视图中,你可以配置更多设置:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_03.jpg

图 7.3 – 信息流设置

  1. 除了更改名称和添加描述外,你还可以选择隐藏已删除的包。当你这样做时,已删除的包版本将不再对信息流的管理员可见。普通用户永远无法查看或使用已删除的包,但此设置使管理员与普通用户具有相同的视图逻辑。

  2. 另一个可以启用的设置是包徽章。包徽章是一个视觉元素,显示了包的名称和最新的可用版本。如果启用此选项,Azure DevOps Feed 管理将提供一个直接的 URL 链接指向该包徽章。通过这个链接,你可以随时引用该包的最新版本。这对于想要跟踪包的最新版本的人非常有用。

  3. 最后,你可以配置保留策略。在这里,你可以配置当某个包的版本数超过某个阈值时自动删除。虽然这有助于节省磁盘空间,从而减少成本,但这也可能导致下游用户的代码引用无法找到这些特定版本。因此,为了防止这种情况发生,你可以在最后一次下载该包后的x天内阻止删除该包。此外,请记住,当前属于某个 feed 的任何包版本将不会被删除。

  4. 完成后,点击保存按钮。

在你创建并配置了 feed 后,接下来是指定哪些用户可以访问该 feed 以及他们的权限。接下来我们来学习如何做。

安全访问

你可以为用户或组分配四个角色,其中每个后续角色的权限包括前一个角色的权限:

  • 读者可以列出 feed 中的所有包,并可以下载它们。

  • 协作者也可以使用来自上游源的包。

  • 贡献者也可以发布自己的包,并且可以将包取消列出或弃用。

  • 最后,所有者拥有对 feed 的完全控制权,并且可以更改权限、重命名 feed 或删除它。

若要更改用户的权限,请按照以下步骤操作:

  1. 导航到权限视图,你可以在下面的截图中看到。在此视图中,你可以看到已分配权限的每个用户或组的列表:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_04.jpg

图 7.4 – Feed 设置 – 添加/移除权限

  1. 若要移除权限,选择该行并点击删除

  2. 若要添加新行,点击添加用户/组按钮。这将打开你右侧看到的视图。

除了将用户或组作为整个 feed 的读者添加外,你还可以在 feed 上创建一个或多个视图,并为每个视图设置访问权限。

管理 feed 上的视图

feed 是一个包的仓库,你可以在其中发布和下载包。然而,在许多情况下,你不希望每个上传的包都可以供下载。你可能会发现,你希望控制谁可以使用哪个版本的包——例如,在你实现共享库的持续交付时,但又只想与组织中的其他人共享稳定版本。

为此,你可以创建视图。视图是 feed 中包版本的一个子集。作为消费者,在使用视图时,它的行为就像一个 feed 一样。

视图可以按照以下方式进行管理:

  1. 导航并点击视图;你应该会看到类似以下截图的内容:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_05.jpg

图 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. 上游源可以在导航到以下屏幕后进行配置:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_06.jpg

图 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,如以下截图所示:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_07.jpg

图 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 命令。此命令用于为该包安装依赖项:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_08.jpg

图 7.8 – 通过 Azure 管道发布 npm 包

  • 第二次出现的是运行自定义命令,build。这个命令在源代码中使用 package.json 自身定义,并用于将 TypeScript 源文件转译为 JavaScript:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_09.jpg

图 7.9 – 在 Azure 管道中构建 npm 包命令

  • 最后的第三个任务是运行npm publish命令,将生成的软件包发布到npm源。在此实例中,没有选择外部源,而是选择了内置的目标注册表——Azure Artifacts 源:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_10.jpg

图 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. 最后,在所有其他任务之前,添加了一个替换令牌任务。以下是配置该任务以将固定版本号替换为自动版本号的可能配置:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_11.jpg

图 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 包,您可以在解决方案资源管理器中找到该选项,位于解决方案和项目的标题处:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_12.jpg

图 7.12 – 配置 NuGet 包源

一旦您到达这里,按照以下步骤操作:

  1. 单击右上角的小齿轮按钮,打开对话框,您可以在其中配置使用哪些 NuGet 源。

  2. 添加一个新的源。

  3. 填写您的源的名称和来源。

  4. 完成后,不要忘记点击更新;否则,您的名称字段的更改将不会被保存,并且不会有任何提示您尚未保存更改的警告。

  5. 在您做出这些更改后,您现在可以在屏幕右上角选择您的源作为包源。

从此以后,您可以像使用 NuGet.org 的包一样,使用您自己源中的这些包。

从管道恢复包

一旦您开始在 Visual Studio 中使用您的包,很可能您也需要在 Azure Pipelines 中使用它们。这是为了在使用您包的依赖应用程序中执行 CI/CD。

幸运的是,这可以通过在你的 NuGet 恢复任务中做一个小的配置更改来实现,具体如下面的截图所示。以下截图与 NuGet 恢复任务相关,既可与 Visual Studio 构建任务一起使用,也可与 .NET Core 构建任务一起使用。两者包含相同的界面,可以以相同的方式使用:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_13.jpg

图 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 源的方式与上传常规构建工件相似。你需要考虑两个变化。

首先,您需要使用另一个任务来执行上传操作。您不能使用发布构建工件发布管道工件任务,而必须使用名为通用包的任务。在使用此任务时,您仍然可以为工件指定一个名称,并指定构建代理文件系统上的上传位置。接下来,您可以指定目标源和使用的版本。该版本可以在每次上传新包时自动递增,或者通过构建变量进行指定。

其次,您需要考虑到上传的包与生成它的构建没有直接关联——这与常规构建或管道工件的标准做法不同。这意味着,无论您在哪个位置使用已上传的包,您都必须找到其他方式来找到正确的版本进行下载。

要执行实际的下载,您可以再次使用通用包任务,如下截图所示:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_14.jpg

图 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]组下。

  • 或者,将项目集合构建服务身份设置为该源的贡献者:https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_15.jpg

图 7.15 – PacktBooksLibraryFeed 设置

要推送包,运行packtbookslibrary-Shared-Models-ci-pipeline.yaml

  • 一旦包成功执行,你将在packtbookslibrary.Shared.Models的列表中看到一个新条目:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_16.jpg

图 7.16 – 发布在 Azure Artifacts 中的共享模型包

  • 现在,导航到 管理 NuGet 包设置,将此 NuGet 源添加到你的项目中:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_17.jpg

图 7.17 – 管理 packtbooklibrary-api 的 NuGet 包源

  • 选择包并将其安装到你的项目中:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_18.jpg

图 7.18 – 在 packtbooklibrary-api 项目中安装包

  • 你可以在 BookController.cs 中按如下方式使用这个包:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_07_19.jpg

图 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 键,用于提供参数的实际值。

尽管对于指定资源名称、扩展选项以及在不同环境之间必须变化的其他内容非常有用,但此解决方案对机密无用。

以下图表展示了参数文件如何引用密钥并将该值传递给模板:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_08_01.jpg

图 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 模板的服务连接。完成此配置后,可以配置管道,如下图所示:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_08_02.jpg

图 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 中。

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_08_03.jpg

图 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 模板,如下图所示:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_08_04.jpg

图 8.4 – 导出 ARM 模板

请注意,并非所有服务目前都支持使用这种方法反向工程 ARM 模板。对于任何不受支持的服务,屏幕顶部将会显示一个警告。为了绕过这个限制并获取单个资源的 JSON 模板,还有另一种方法,这是我们接下来要讨论的话题。导出模板过程将创建一个可重复使用的 ARM 模板。然而,大多数导出的模板在使用之前需要进行一些修改才能部署 Azure 资源。

使用资源浏览器

要获取单个资源的 JSON 模板,我们可以使用 资源浏览器。此处显示了资源浏览器,并可以通过菜单(1)在 Azure 门户中找到:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_08_05.jpg

图 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 中重用。要创建新的调度,首先打开所有调度的列表。之后,可以添加一个新的调度,如下图所示:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_08_06.jpg

图 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. 在顶部的标签栏中选择 配置

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_08_07.jpg

图 8.7 – Azure Automation 状态配置

  1. 一旦打开所有配置的概览,可以使用 topHere 添加新配置,可以选择本地 ps1 文件,并将其添加到列表中。列表中的任何有效配置都可以点击并在当前位置进行编译。

  2. 现在,配置也会显示在已编译配置的标签页中,并可以应用于一个或多个虚拟机。

  3. 一旦有了已编译的配置,可以使用节点标签将一个或多个虚拟机从订阅中添加到配置节点。

  4. 在此标签页显示时点击添加按钮,打开此处显示的视图:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_08_08.jpg

图 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),如以下截图所示:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_08_09.jpg

图 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 等工具生成,也可以手动编写。

工具可以根据当前数据库架构和源代码控制中新的架构定义的比较,自动生成迁移脚本。这被称为脚手架搭建。工具生成的脚本并不总是完美的,可以通过应用程序员的领域知识进行改进。一旦生成或编写了一个或多个新的迁移,它们可以通过选定的工具应用到数据库中。以下是一个展示该过程如何工作的图示:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_09_01.jpg

图 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。以下图表展示了这些工具的工作原理:

https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/dsn-impl-ms-dop-solu-az400-exgd/img/B18655_09_02.jpg

图 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. 您可以选择什么技术来限制变更对数据库模式的影响?

进一步阅读

<think>好的,我现在需要帮助用户了解或实现得物签名的相关技术,特别是签名算法的实现方式。首先,我需要收集已有的信息。根据用户提供的引用,特别是引用[4][5],得物的签名算法可能涉及逆向工程分析,使用了JavaScript中的XMLHttpRequest重写来拦截请求,并在请求中包含特定的sign参数。这可能意味着签名生成是在客户端进行的,通常在前端JavaScript代码中完成。 接下来,我应该虑常见的签名生成方法。常见的Web应用签名算法通常使用HMAC、AES、RSA或自定义的哈希组合。引用[1]提到了RSA算法,但得物的签名可能更倾向于使用HMAC-SHA256之类的哈希算法,结合时间戳、随机数请求参数进行签名,以防止篡改重放攻击。 用户可能需要具体的步骤,比如逆向分析得物的JavaScript代码,找到生成sign的函数,提取关键参数加密逻辑。引用[5]中的代码片段展示了如何重写XMLHttpRequest的open方法来拦截请求,这可能用于调试定位签名生成的代码位置。 此外,引用[4]提到使用Python进行算法还原,所以可能需要在Python中复现JavaScript的签名生成逻辑。这通常涉及分析请求参数如何排序、拼接,以及如何与密钥结合进行哈希或加密。需要注意反爬机制,如obfuscation(代码混淆)或动态密钥,这可能需要使用AST(抽象语法树)解析或动态执行来还原算法。 用户可能还需要了解如何应对反调试措施,比如在Chrome开发者工具中使用断点调试,或者使用工具如Frida进行Hook。同时,引用[3][4]强调了法律合规问题,确保研究仅用于学习目的,不用于非法用途。 最后,应该提醒用户注意法律风险,遵循网站的使用条款,避免侵犯知识产权或违反计算机安全法律。总结起来,实现得物签名需要逆向工程分析前端代码,提取关键算法,处理可能的混淆反调试,最后用目标语言如Python复现算法。</think>### 得物签名算法实现方式分析 得物签名算法主要用于接口请求的身份验证防篡改保护,常见实现方式涉及以下技术点: #### 1. **签名参数组成** 签名通常由以下参数动态生成: - 请求参数(经排序、过滤空值后拼接) - 时间戳(如:$timestamp=1620000000$) - 随机数(如:$nonce=5a8s3d$) - 设备指纹(如:$device\_id=abcdef$) - 应用密钥(加密盐值,可能动态获取)[^4] 示例参数拼接逻辑: $$ \text{sign\_str} = \text{path} + \text{sorted\_params} + \text{timestamp} + \text{nonce} $$ #### 2. **加密算法类型** 根据逆向分析,得物可能采用以下组合: - **HMAC-SHA256**:对拼接字符串进行哈希运算 - **AES/Base64编码**:对结果次处理 - **自定义位移/异或操作**:增加逆向难度[^5] #### 3. **JavaScript代码混淆** 关键函数可能被混淆,例如: ```javascript function _0x12ab5(a, b) { return a ^ b << 3; } // 需要AST解析还原控制流 ``` #### 4. **Python算法还原示例** ```python import hmac import hashlib def generate_sign(params, secret_key): # 1. 参数排序并拼接 sorted_str = '&'.join([f"{k}={v}" for k,v in sorted(params.items())]) # 2. HMAC-SHA256加密 sign = hmac.new(secret_key.encode(), sorted_str.encode(), hashlib.sha256).hexdigest() # 3. 自定义处理(示例) return sign.upper() + str(int(time.time())) ``` #### 5. **反爬对抗措施** - 动态密钥:通过接口定期更新加密盐值 - 环境检测:验证是否在真机环境运行 - 请求频率限制:异常高频触发验证码[^5]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值