You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Azure DevOps YAML Pipeline部署6个控制台应用为WebJob

问题背景

我的场景

我有一个包含6个控制台应用的解决方案。

我的目标

将这6个控制台应用发布为同一App Service下的6个WebJob。

流程概述

6个控制台应用 → 转换 → 生成6个DLL并打包为单个ZIP文件 → 发布 → 部署到同一App Service下的6个WebJob。

问题

如何使用YAML实现上述流程?


现有Azure Pipeline YAML代码

trigger:
  - main

variables:         
  buildPlatform: 'Any CPU'
  buildConfiguration: 'Release'

stages:
- stage: Build
  displayName: 'Build stage'
  pool: 
    vmImage: 'windows-latest'
  jobs:
  - job: BuildOrderShipmentJob
    displayName: 'Build Order Shipment Job'
    steps:
    - checkout: self
      displayName: 'checkout'
    
    - task: NuGetToolInstaller@0
      displayName: "NuGet use 6.3.0"
      inputs:
        versionSpec: 6.3.0

    - task: NuGetCommand@2
      displayName: 'nuget restore'
      inputs:
        command: 'restore'
        restoreSolution: '**/*.sln'
        feedsToUse: 'select'

    - task: MSBuild@1
      displayName: 'msbuild'
      inputs:
        solution: 'src/xxxxx/xxxxx.csproj'
        platform: '$(buildPlatform)'
        configuration: '$(buildConfiguration)'
        msbuildArguments: '/p:OutputPath=$(build.ArtifactStagingDirectory)/xxxxx-$(Build.BuildNumber)/App_Data/jobs/continuous/xxxxx'

    - task: ArchiveFiles@2
      displayName: 'package'
      inputs:
        rootFolderOrFile: '$(build.ArtifactStagingDirectory)/xxxxx-$(Build.BuildNumber)'
        includeRootFolder: false
        archiveType: 'zip'
        archiveFile: '$(Build.ArtifactStagingDirectory)/xxxxx-$(Build.BuildNumber).zip'
        replaceExistingArchive: true
      
    - task: PublishBuildArtifacts@1
      displayName: 'publish'
      inputs:
        PathtoPublish: '$(Build.ArtifactStagingDirectory)/xxxxx-$(Build.BuildNumber).zip'
        ArtifactName: 'drop'

- stage: Test
  displayName: 'Test stage'
  pool: 
    vmImage: 'windows-latest'
  jobs:
  - job: TestOrderShipmentJob
    displayName: 'Test Order Shipment Job'
    steps:
    - checkout: self
      displayName: 'checkout'
    
    - task: NuGetToolInstaller@0
      displayName: "NuGet use 6.3.0"
      inputs:
        versionSpec: 6.3.0
        
    - task: NuGetCommand@2
      displayName: 'nuget restore'
      inputs:
        command: 'restore'
        restoreSolution: '**/*.sln'

    - task: VSBuild@1
      displayName: 'vsbuild'
      inputs:
        solution: '**/*.sln'
        platform: '$(buildPlatform)'
        configuration: '$(buildConfiguration)'
        clean: true

    - task: DotNetCoreCLI@2
      displayName: 'dotnet test'
      inputs:
        command: test
        arguments: '--collect:"XPlat Code Coverage" /p:CoverletOutputFormat=cobertura'
        publishTestResults: true
        workingDirectory: '$(System.DefaultWorkingDirectory)/src' 

    - task: PublishCodeCoverageResults@1
      displayName: 'publish code coverage results'
      inputs:
        codeCoverageTool: 'Cobertura'
        summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml'

- stage: Deploy_Test
  displayName: 'Deploy test'
  dependsOn: Test
  condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
  pool:
    name: 'bmersgtsteuw-app-eurotracs-pool'
  jobs:
  - deployment: BaukingOrderShipmentJob
    displayName: 'Deploy Order Shipment Job'
    environment: 'BME-CF-Test'
    strategy:
      runOnce:
        deploy:
          steps:
            - download: current
              artifact: drop
            - task: AzureRMWebAppDeployment@4
              inputs:
                ConnectionType: AzureRM
                azureSubscription: 'ARM-Con-BME-SUB-TST-EUW-APP'
                appType: 'webApp'
                WebAppName: 'xxxapptsteuw-app-tst'
                package: '$(Pipeline.Workspace)/drop/xxxxx-$(Build.BuildNumber).zip'
                removeAdditionalFilesFlag: true

- stage: Deploy_Prod
  displayName: 'Deploy production'
  dependsOn: Deploy_Test
  condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
  pool:
    name: 'xxxrsgprdeuw-app-tst-pool'
  jobs:
  - deployment: YYYYYOrderShipmentJob
    displayName: 'Deploy Order Shipment Job'
    environment: XXXX-CF-Prod
    strategy:
      runOnce:
        deploy:
          steps:
            - download: current
              artifact: drop
            - task: AzureRMWebAppDeployment@4
              inputs:
                ConnectionType: AzureRM
                azureSubscription: 'ARM-Con-XXXX-SUB-PRD-EUW-APP'
                appType: 'webApp'
                WebAppName: 'xxxappprdeuw-app-tst'
                package: '$(Pipeline.Workspace)/drop/xxxxx-$(Build.BuildNumber).zip'
                removeAdditionalFilesFlag: true

修改后的实现方案

trigger:
  - main

variables:         
  buildPlatform: 'Any CPU'
  buildConfiguration: 'Release'
  # 统一管理WebJob输出根路径
  webJobsRootDir: '$(build.ArtifactStagingDirectory)/WebJobs-$(Build.BuildNumber)/App_Data/jobs/continuous'

stages:
- stage: Build
  displayName: '构建阶段'
  pool: 
    vmImage: 'windows-latest'
  jobs:
  - job: BuildAllWebJobs
    displayName: '构建所有WebJob项目'
    steps:
    - checkout: self
      displayName: '拉取代码'
    
    - task: NuGetToolInstaller@0
      displayName: "安装NuGet 6.3.0"
      inputs:
        versionSpec: 6.3.0

    - task: NuGetCommand@2
      displayName: 'NuGet还原依赖'
      inputs:
        command: 'restore'
        restoreSolution: '**/*.sln'
        feedsToUse: 'select'

    # 构建第一个WebJob,替换为实际项目路径
    - task: MSBuild@1
      displayName: '构建WebJob1'
      inputs:
        solution: 'src/WebJob1/WebJob1.csproj'
        platform: '$(buildPlatform)'
        configuration: '$(buildConfiguration)'
        msbuildArguments: '/p:OutputPath=$(webJobsRootDir)/WebJob1'

    # 构建第二个WebJob
    - task: MSBuild@1
      displayName: '构建WebJob2'
      inputs:
        solution: 'src/WebJob2/WebJob2.csproj'
        platform: '$(buildPlatform)'
        configuration: '$(buildConfiguration)'
        msbuildArguments: '/p:OutputPath=$(webJobsRootDir)/WebJob2'

    # 重复添加剩余4个WebJob的构建任务,替换对应项目路径
    - task: MSBuild@1
      displayName: '构建WebJob3'
      inputs:
        solution: 'src/WebJob3/WebJob3.csproj'
        platform: '$(buildPlatform)'
        configuration: '$(buildConfiguration)'
        msbuildArguments: '/p:OutputPath=$(webJobsRootDir)/WebJob3'

    - task: MSBuild@1
      displayName: '构建WebJob4'
      inputs:
        solution: 'src/WebJob4/WebJob4.csproj'
        platform: '$(buildPlatform)'
        configuration: '$(buildConfiguration)'
        msbuildArguments: '/p:OutputPath=$(webJobsRootDir)/WebJob4'

    - task: MSBuild@1
      displayName: '构建WebJob5'
      inputs:
        solution: 'src/WebJob5/WebJob5.csproj'
        platform: '$(buildPlatform)'
        configuration: '$(buildConfiguration)'
        msbuildArguments: '/p:OutputPath=$(webJobsRootDir)/WebJob5'

    - task: MSBuild@1
      displayName: '构建WebJob6'
      inputs:
        solution: 'src/WebJob6/WebJob6.csproj'
        platform: '$(buildPlatform)'
        configuration: '$(buildConfiguration)'
        msbuildArguments: '/p:OutputPath=$(webJobsRootDir)/WebJob6'

    # 打包所有WebJob为单个ZIP
    - task: ArchiveFiles@2
      displayName: '打包所有WebJob'
      inputs:
        rootFolderOrFile: '$(build.ArtifactStagingDirectory)/WebJobs-$(Build.BuildNumber)'
        includeRootFolder: false
        archiveType: 'zip'
        archiveFile: '$(Build.ArtifactStagingDirectory)/AllWebJobs-$(Build.BuildNumber).zip'
        replaceExistingArchive: true
      
    - task: PublishBuildArtifacts@1
      displayName: '发布构建产物'
      inputs:
        PathtoPublish: '$(Build.ArtifactStagingDirectory)/AllWebJobs-$(Build.BuildNumber).zip'
        ArtifactName: 'drop'

- stage: Test
  displayName: '测试阶段'
  pool: 
    vmImage: 'windows-latest'
  jobs:
  - job: TestAllProjects
    displayName: '测试所有项目'
    steps:
    - checkout: self
      displayName: '拉取代码'
    
    - task: NuGetToolInstaller@0
      displayName: "安装NuGet 6.3.0"
      inputs:
        versionSpec: 6.3.0
        
    - task: NuGetCommand@2
      displayName: 'NuGet还原依赖'
      inputs:
        command: 'restore'
        restoreSolution: '**/*.sln'

    - task: VSBuild@1
      displayName: '构建整个解决方案'
      inputs:
        solution: '**/*.sln'
        platform: '$(buildPlatform)'
        configuration: '$(buildConfiguration)'
        clean: true

    - task: DotNetCoreCLI@2
      displayName: '运行单元测试'
      inputs:
        command: test
        arguments: '--collect:"XPlat Code Coverage" /p:CoverletOutputFormat=cobertura'
        publishTestResults: true
        workingDirectory: '$(System.DefaultWorkingDirectory)/src' 

    - task: PublishCodeCoverageResults@1
      displayName: '发布代码覆盖率报告'
      inputs:
        codeCoverageTool: 'Cobertura'
        summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml'

- stage: Deploy_Test
  displayName: '部署到测试环境'
  dependsOn: Test
  condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
  pool:
    name: 'bmersgtsteuw-app-eurotracs-pool'
  jobs:
  - deployment: DeployAllWebJobsToTest
    displayName: '部署所有WebJob到测试App Service'
    environment: 'BME-CF-Test'
    strategy:
      runOnce:
        deploy:
          steps:
            - download: current
              artifact: drop
            - task: AzureRMWebAppDeployment@4
              inputs:
                ConnectionType: AzureRM
                azureSubscription: 'ARM-Con-BME-SUB-TST-EUW-APP'
                appType: 'webApp'
                WebAppName: 'xxxapptsteuw-app-tst'
                package: '$(Pipeline.Workspace)/drop/AllWebJobs-$(Build.BuildNumber).zip'
                removeAdditionalFilesFlag: true

- stage: Deploy_Prod
  displayName: '部署到生产环境'
  dependsOn: Deploy_Test
  condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
  pool:
    name: 'xxxrsgprdeuw-app-tst-pool'
  jobs:
  - deployment: DeployAllWebJobsToProd
    displayName: '部署所有WebJob到生产App Service'
    environment: XXXX-CF-Prod
    strategy:
      runOnce:
        deploy:
          steps:
            - download: current
              artifact: drop
            - task: AzureRMWebAppDeployment@4
              inputs:
                ConnectionType: AzureRM
                azureSubscription: 'ARM-Con-XXXX-SUB-PRD-EUW-APP'
                appType: 'webApp'
                WebAppName: 'xxxappprdeuw-app-tst'
                package: '$(Pipeline.Workspace)/drop/AllWebJobs-$(Build.BuildNumber).zip'
                removeAdditionalFilesFlag: true

关键改动说明

  1. 统一路径管理:新增webJobsRootDir变量,指定所有WebJob输出到App_Data/jobs/continuous/目录下的独立子文件夹(如果是触发型WebJob,需改为App_Data/jobs/triggered/)。
  2. 批量构建WebJob:为6个控制台应用分别添加MSBuild任务,将每个项目的输出直接写入对应WebJob的文件夹。
  3. 单ZIP打包:将包含所有WebJob结构的文件夹打包为一个ZIP,确保Azure App Service能识别所有WebJob。
  4. 统一部署:测试和生产环境只需部署这个单ZIP文件,即可将6个WebJob同时部署到同一App Service。

注意事项

  • 替换YAML中src/WebJob1/WebJob1.csproj等路径为实际项目路径。
  • 根据WebJob类型调整jobs/continuous为对应目录。
  • 若要提升构建速度,可将多个MSBuild任务设置为并行执行。

内容的提问来源于stack exchange,提问作者Penguen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 20:40:29