Azure DevOps Services |Azure DevOps Server |Azure DevOps Server 2022 |Azure DevOps Server 2020
重要说明
- 作业和阶段名称不得与保留关键字冲突(如
deployment作业类型) - 阶段中的每个作业必须具有唯一的名称。
在 YAML 管道中,我们建议你将部署步骤放在一种特殊类型的作业(称为“部署作业”)中。 部署作业是针对环境按顺序运行的步骤集合。 部署作业和传统作业可以存在于同一阶段。 Azure DevOps 支持 runOnce、滚动和 canary 策略。
部署作业提供以下优势:
- 部署历史记录:可以跨管道获取部署历史记录,具体到特定资源和部署状态以供审核。
- 应用部署策略:可以定义应用程序的推出方式。
部署作业不会自动克隆源存储库。 您可以在作业中使用checkout: self检查源存储库。
注意
本文重点介绍使用部署作业进行部署。 若要了解如何使用管道部署到 Azure,请参阅部署到 Azure 概述。
架构
转到 YAML 架构以查看 jobs.deployment 和 jobs.deployment.environment 定义。
对于虚拟机,无需定义池。 使用虚拟机资源在部署作业中定义的任何步骤都针对该虚拟机运行,而不是针对池中的代理运行。 对于其他资源类型(如 Kubernetes),需要定义一个池,以便任务可以在该计算机上运行。
部署策略
部署应用程序更新时,用于提供更新的技术必须:
- 启用初始化。
- 部署更新。
- 将流量路由到更新的版本。
- 在路由流量后测试更新的版本。
- 如果失败,请运行相应步骤以还原到前一个能良好运行的版本。
我们通过使用可以在部署期间运行步骤的生命周期钩子来实现这一点。 每个生命周期挂钩都解析为代理作业或服务器作业(或将来的容器或验证作业),具体取决于 pool 属性。 默认情况下,生命周期挂钩继承 pool 作业指定的 deployment。
部署作业使用 $(Pipeline.Workspace) 系统变量。
生命周期挂钩的说明
preDeploy:用于在应用程序部署开始之前运行初始化资源的步骤。
deploy:用于运行部署应用程序的步骤。 下载工件任务将仅在部署作业的 deploy 挂钩中自动注入。 要停止下载工件,请使用 - download: none,或通过指定下载管道工件任务来选择要下载的特定工件。
routeTraffic:用于运行向更新版本发送流量的步骤。
postRouteTraffic:用于在路由流量后运行步骤。 通常,这些任务按定义的时间间隔监视更新版本的运行状况。
on: failure 或 on: success:用于运行回滚操作或清理的步骤。
RunOnce 部署策略
runOnce 是最简单的部署策略,其中所有生命周期挂钩(即preDeploydeployrouteTraffic,和postRouteTraffic)执行一次。 然后,执行 on:success 或 on:failure。
strategy:
runOnce:
preDeploy:
pool: [ server | pool ] # See pool schema.
steps:
- script: [ script | bash | pwsh | powershell | checkout | task | templateReference ]
deploy:
pool: [ server | pool ] # See pool schema.
steps:
...
routeTraffic:
pool: [ server | pool ]
steps:
...
postRouteTraffic:
pool: [ server | pool ]
steps:
...
on:
failure:
pool: [ server | pool ]
steps:
...
success:
pool: [ server | pool ]
steps:
...
如果使用自托管代理,则可以使用工作区清理选项来清理部署工作区。
jobs:
- deployment: MyDeploy
pool:
vmImage: 'ubuntu-latest'
workspace:
clean: all
environment: staging
滚动部署策略
滚动部署将应用程序的早期版本的实例替换为每个迭代中固定虚拟机(滚动集)上应用程序的新版本实例。
目前,我们仅支持将策略滚动到 VM 资源。
例如,滚动部署通常等待每组虚拟机上的部署完成,然后继续下一组部署。 可以在每次迭代后执行运行状况检查,如果出现重大问题,可以停止滚动部署。
可以通过在 rolling: 节点下指定关键字 strategy: 来配置滚动部署。
strategy.name 变量在此策略块中可用,该策略块采用策略的名称。 在本例中为 rolling。
strategy:
rolling:
maxParallel: [ number or percentage as x% ]
preDeploy:
steps:
- script: [ script | bash | pwsh | powershell | checkout | task | templateReference ]
deploy:
steps:
...
routeTraffic:
steps:
...
postRouteTraffic:
steps:
...
on:
failure:
steps:
...
success:
steps:
...
支持所有生命周期挂钩,并创建生命周期挂钩作业以在每个 VM 上运行。
preDeploy、deploy、routeTraffic 和 postRouteTraffic 根据 maxParallel 定义的批大小执行一次。
然后,执行 on: success 或 on: failure。
使用 maxParallel: <# or % of VMs>,可以控制要并行部署到的虚拟机目标的数量/百分比。 这可确保应用在这些计算机上运行,并且能够在其余计算机上进行部署时处理请求,从而减少总体停机时间。
注意
此功能存在一些已知的差距。 例如,重试某个阶段时,它会在所有 VM 上重新运行部署,而不仅仅是失败的目标。
金丝雀部署策略
Canary 部署策略是一种高级部署策略,可帮助降低推出新版本应用程序时所涉及的风险。 使用此策略,可以首先将更改推送到一小部分服务器。 当你对新版本更有信心时,可以将其发布到基础结构中的更多服务器,并向其路由更多流量。
strategy:
canary:
increments: [ number ]
preDeploy:
pool: [ server | pool ] # See pool schema.
steps:
- script: [ script | bash | pwsh | powershell | checkout | task | templateReference ]
deploy:
pool: [ server | pool ] # See pool schema.
steps:
...
routeTraffic:
pool: [ server | pool ]
steps:
...
postRouteTraffic:
pool: [ server | pool ]
steps:
...
on:
failure:
pool: [ server | pool ]
steps:
...
success:
pool: [ server | pool ]
steps:
...
Canary 部署策略支持 preDeploy 生命周期挂钩(执行一次),并使用 deploy、routeTraffic 和 postRouteTraffic 生命周期挂钩进行迭代。 然后以 success 或 failure 挂钩退出。
此策略中提供了以下变量:
strategy.name:策略的名称。 例如,canary。
strategy.action:要对 Kubernetes 群集执行的操作。 例如,部署、提升或拒绝。
strategy.increment:当前交互中使用的增量值。 此变量仅在 deploy、routeTraffic 和 postRouteTraffic 生命周期挂钩中可用。
示例
RunOnce 部署策略
以下示例 YAML 代码片段通过 runOnce 部署策略演示了部署作业的简单用法。 此示例包含签出步骤。
jobs:
# Track deployments on the environment.
- deployment: DeployWeb
displayName: deploy Web App
pool:
vmImage: 'ubuntu-latest'
# Creates an environment if it doesn't exist.
environment: 'smarthotel-dev'
strategy:
# Default deployment strategy, more coming...
runOnce:
deploy:
steps:
- checkout: self
- script: echo my first deployment
每次运行此作业时,系统会根据 smarthotel-dev 环境记录部署历史记录。
注意
- 还可以创建具有空资源的环境,并将其用作抽象 shell 来记录部署历史记录,如前面的示例所示。
下一个示例演示管道如何引用要用作部署作业目标的环境和资源。
jobs:
- deployment: DeployWeb
displayName: deploy Web App
pool:
vmImage: 'ubuntu-latest'
# Records deployment against bookings resource - Kubernetes namespace.
environment: 'smarthotel-dev.bookings'
strategy:
runOnce:
deploy:
steps:
# No need to explicitly pass the connection details.
- task: KubernetesManifest@1
displayName: Deploy to Kubernetes cluster
inputs:
action: deploy
namespace: $(k8sNamespace)
manifests: |
$(System.ArtifactsDirectory)/manifests/*
imagePullSecrets: |
$(imagePullSecret)
containers: |
$(containerRegistry)/$(imageRepository):$(tag)
此方法提供以下优势:
- 记录环境中特定资源的部署历史记录,而不是记录环境中所有资源的历史记录。
- 部署作业中的步骤自动继承资源的连接详细信息(在本例中为 Kubernetes 命名空间
smarthotel-dev.bookings),因为部署作业已链接到环境。 在为作业的多个步骤设置相同的连接详细信息的情况下,这很有用。
注意
如果使用专用 AKS 群集,请确保连接到群集的虚拟网络,因为 API 服务器终结点不会通过公共 IP 地址公开。
Azure Pipelines 建议在有权访问群集虚拟网络的 VNET 中设置自托管代理。 有关详细信息,请参阅用于连接到专用群集的选项。
滚动部署策略
VM 的滚动策略在每次迭代中最多更新五个目标。
maxParallel 将确定可以并行部署的目标数。 选择包括在任何时候必须保持可用的目标的绝对数量或百分比,不包括正在部署的目标。 它还用于确定部署期间的成功和失败情况。
jobs:
- deployment: VMDeploy
displayName: web
environment:
name: smarthotel-dev
resourceType: VirtualMachine
strategy:
rolling:
maxParallel: 5 # for percentages, mention as x%
preDeploy:
steps:
- download: current
artifact: drop
- script: echo initialize, cleanup, backup, install certs
deploy:
steps:
- task: IISWebAppDeploymentOnMachineGroup@0
displayName: 'Deploy application to Website'
inputs:
WebSiteName: 'Default Web Site'
Package: '$(Pipeline.Workspace)/drop/**/*.zip'
routeTraffic:
steps:
- script: echo routing traffic
postRouteTraffic:
steps:
- script: echo health check post-route traffic
on:
failure:
steps:
- script: echo Restore from backup! This is on failure
success:
steps:
- script: echo Notify! This is on success
金丝雀部署策略
在下一个示例中,AKS 的 Canary 策略将首先部署有 10% Pod 的更改,然后部署有 20% Pod 的更改,同时在 postRouteTraffic 期间监视运行状况。 如果一切顺利,这个比例会提升到 100%。
jobs:
- deployment:
environment: smarthotel-dev.bookings
pool:
name: smarthotel-devPool
strategy:
canary:
increments: [10, 20]
preDeploy:
steps:
- script: initialize, cleanup....
deploy:
steps:
- script: echo deploy updates...
- task: KubernetesManifest@1
inputs:
action: $(strategy.action)
namespace: 'default'
strategy: $(strategy.name)
percentage: $(strategy.increment)
manifests: 'manifest.yml'
postRouteTraffic:
pool: server
steps:
- script: echo monitor application health...
on:
failure:
steps:
- script: echo clean-up, rollback...
success:
steps:
- script: echo checks passed, notify...
使用管道修饰器自动注入步骤
管道修饰器可用于部署作业,以自动将任何自定义步骤(如漏洞扫描程序)注入到每个部署作业的每个生命周期挂钩执行中。 由于管道修饰器可以应用于组织中的所有管道,因此这可以作为强制实施安全部署做法的一部分进行应用。
此外,部署作业可以作为容器作业与服务 side-car(如果已定义)一起运行。
支持输出变量
在部署作业的生命周期挂钩中定义输出变量,并在同一阶段的其他下游步骤和作业中使用它们。
若要在阶段之间共享变量,请在一个阶段中输出工件,然后在后续阶段使用,或使用stageDependencies中所述的 语法。
在执行部署策略时,可以使用以下语法访问跨作业的输出变量。
- 对于 runOnce 策略:
$[dependencies.<job-name>.outputs['<job-name>.<step-name>.<variable-name>']](例如,$[dependencies.JobA.outputs['JobA.StepA.VariableA']]) - 对于 runOnce 策略和 resourceType:
$[dependencies.<job-name>.outputs['Deploy_<resource-name>.<step-name>.<variable-name>']]。 (例如:$[dependencies.JobA.outputs['Deploy_VM1.StepA.VariableA']]) - 对于 canary 策略:
$[dependencies.<job-name>.outputs['<lifecycle-hookname>_<increment-value>.<step-name>.<variable-name>']] - 对于滚动策略:
$[dependencies.<job-name>.outputs['<lifecycle-hookname>_<resource-name>.<step-name>.<variable-name>']]
# Set an output variable in a lifecycle hook of a deployment job executing canary strategy.
- deployment: A
pool:
vmImage: 'ubuntu-latest'
environment: staging
strategy:
canary:
increments: [10,20] # Creates multiple jobs, one for each increment. Output variable can be referenced with this.
deploy:
steps:
- bash: echo "##vso[task.setvariable variable=myOutputVar;isOutput=true]this is the deployment variable value"
name: setvarStep
- bash: echo $(setvarStep.myOutputVar)
name: echovar
# Map the variable from the job.
- job: B
dependsOn: A
pool:
vmImage: 'ubuntu-latest'
variables:
myVarFromDeploymentJob: $[ dependencies.A.outputs['deploy_10.setvarStep.myOutputVar'] ]
steps:
- script: "echo $(myVarFromDeploymentJob)"
name: echovar
对于 runOnce 作业,请指定作业的名称,而不是生命周期挂钩:
# Set an output variable in a lifecycle hook of a deployment job executing runOnce strategy.
- deployment: A
pool:
vmImage: 'ubuntu-latest'
environment: staging
strategy:
runOnce:
deploy:
steps:
- bash: echo "##vso[task.setvariable variable=myOutputVar;isOutput=true]this is the deployment variable value"
name: setvarStep
- bash: echo $(setvarStep.myOutputVar)
name: echovar
# Map the variable from the job.
- job: B
dependsOn: A
pool:
vmImage: 'ubuntu-latest'
variables:
myVarFromDeploymentJob: $[ dependencies.A.outputs['A.setvarStep.myOutputVar'] ]
steps:
- script: "echo $(myVarFromDeploymentJob)"
name: echovar
在部署作业中定义环境时,输出变量的语法因环境定义方式而异。 在此示例中,env1 使用速记表示法,env2 包含具有已定义资源类型的完整语法。
stages:
- stage: StageA
jobs:
- deployment: A1
pool:
vmImage: 'ubuntu-latest'
environment: env1
strategy:
runOnce:
deploy:
steps:
- bash: echo "##vso[task.setvariable variable=myOutputVar;isOutput=true]this is the deployment variable value"
name: setvarStep
- bash: echo $(System.JobName)
- deployment: A2
pool:
vmImage: 'ubuntu-latest'
environment:
name: env2
resourceName: vmsfortesting
resourceType: virtualmachine
strategy:
runOnce:
deploy:
steps:
- script: echo "##vso[task.setvariable variable=myOutputVarTwo;isOutput=true]this is the second deployment variable value"
name: setvarStepTwo
- job: B1
dependsOn: A1
pool:
vmImage: 'ubuntu-latest'
variables:
myVarFromDeploymentJob: $[ dependencies.A1.outputs['A1.setvarStep.myOutputVar'] ]
steps:
- script: "echo $(myVarFromDeploymentJob)"
name: echovar
- job: B2
dependsOn: A2
pool:
vmImage: 'ubuntu-latest'
variables:
myVarFromDeploymentJob: $[ dependencies.A2.outputs['A2.setvarStepTwo.myOutputVarTwo'] ]
myOutputVarTwo: $[ dependencies.A2.outputs['Deploy_vmsfortesting.setvarStepTwo.myOutputVarTwo'] ]
steps:
- script: "echo $(myOutputVarTwo)"
name: echovartwo
当你从第一阶段的作业中输出一个变量后,在下一阶段的部署作业中引用它时,其语法会因你的具体用途而异:是想设置一个新变量,还是想将其用作阶段的执行条件。
stages:
- stage: StageA
jobs:
- job: A1
steps:
- pwsh: echo "##vso[task.setvariable variable=RunStageB;isOutput=true]true"
name: setvarStep
- bash: echo $(System.JobName)
- stage: StageB
dependsOn:
- StageA
# when referring to another stage, stage name is included in variable path
condition: eq(dependencies.StageA.outputs['A1.setvarStep.RunStageB'], 'true')
# Variables reference syntax differs slightly from inter-stage condition syntax
variables:
myOutputVar: $[stageDependencies.StageA.A1.outputs['setvarStep.RunStageB']]
jobs:
- deployment: B1
pool:
vmImage: 'ubuntu-latest'
environment: envB
strategy:
runOnce:
deploy:
steps:
- bash: echo $(myOutputVar)
从部署作业输出变量时,请使用 stageDependencies 语法从下一阶段引用它(例如,$[stageDependencies.<stage-name>.<job-name>.outputs[Deploy_<resource-name>.<step-name>.<variable-name>]])。
stages:
- stage: StageA
jobs:
- deployment: A1
environment:
name: env1
resourceName: DevEnvironmentV
resourceType: virtualMachine
strategy:
runOnce:
deploy:
steps:
- script: echo "##vso[task.setvariable variable=myVar;isOutput=true]true"
name: setvarStep
- script: |
echo "Value of myVar in the same Job : $(setVarStep.myVar)"
displayName: 'Verify variable in StageA'
- stage: StageB
dependsOn: StageA
# Full Variables syntax for inter-stage jobs
variables:
myOutputVar: $[stageDependencies.StageA.A1.outputs['Deploy_DevEnvironmentV.setvarStep.myVar']]
jobs:
- deployment: B1
pool:
vmImage: 'ubuntu-latest'
environment: envB
strategy:
runOnce:
deploy:
steps:
- bash: echo $(myOutputVar)
详细了解如何设置多作业输出变量
FAQ
我的管道卡住,并且显示消息“作业正在挂起...”。 如何解决此问题?
当两个作业之间存在名称冲突时,可能会发生这种情况。 验证同一阶段中的任何部署作业是否具有唯一的名称,以及该作业和阶段名称不包含关键字。 如果重命名无法解决问题,请查看管道运行疑难解答。
部署组中是否支持修饰器?
否。 不能在部署组中使用修饰器。