如何使用 Azure 和 NX 部署多个应用程序 (monorepo)
How to deploy multiple apps (monorepo) with Azure and NX
我正在使用 NX 工具来管理具有多个应用程序的 monorepo,我正在努力了解如何使用 Azure 进行部署和发布管道。
免责声明:我对 Azure 和 devops 非常陌生。
我的理解是这样的:
我创建了一个管道(不是发布管道,如果有意义的话只是一个 "regular one")并向其插入一个 yml。此外,管道链接到 Azure Repos 上的一个存储库,这意味着每次我推送到这个存储库时,它都会触发管道和 运行 yaml 命令。
在这个命令中,我 运行 lint、测试和构建。
这是我能做的,能理解的,下面就比较晦涩了:
如果我 pusing/merging 在我可以调节的 master 上,那么构建作业应该会创建一个工件。
现在我可以创建一个发布管道,当它链接到 repo 时将触发该管道将创建一个工件。然后,此发布管道可以将此工件发送到应用程序服务,该应用程序服务是应用程序所在的插槽。
好的,但我使用的是 monorepo,这意味着构建将生成多个应用程序,并且这些应用程序中的每一个都应该部署到正确的应用程序服务。
经过一些研究,I found that 总体思路是为每个应用程序创建一个发布管道。这些发布管道都链接到同一个 monorepo,但它们有一个过滤器,它是一个构建标签。使用 yml 文件构建应用程序时添加构建标签。
所以这些基本上就是我对这一切的理解。下面是问题:
- 构建标签到底是什么?它位于何处?它是否以某种方式与神器相关联?
- 我们的想法是为每个工件创建一个构建标签,对吗?
- 我未能创建构建标签,我该怎么做?
压缩和发布工件的正确方法是什么?
这是我正在使用的 yaml:
jobs:
- job: Lint
steps:
- task: NodeTool@0
inputs:
versionSpec: '12.x'
displayName: 'Install Node.js'
- task: Npm@1
displayName: 'Npm install'
- pwsh: 'npm run nx affected -- --target=lint --parallel --base=origin/master --maxParallel=4'
displayName: 'Running lint'
- job: Test
steps:
- task: NodeTool@0
inputs:
versionSpec: '12.x'
displayName: 'Install Node.js'
- task: Npm@1
displayName: 'npm install'
- pwsh: 'npm run nx affected -- --target=test --parallel --code-coverage --base=origin/master --maxParallel=4'
displayName: 'Running tests'
- job: Build
steps:
- task: NodeTool@0
inputs:
versionSpec: '12.x'
displayName: 'Install Node.js'
- task: Npm@1
displayName: 'npm install'
- pwsh: 'npm run nx affected -- --target=build --parallel --base=origin/master --prod'
displayName: 'Running build'
- pwsh: |
npm run nx affected:apps -- --base=HEAD~1 --head=HEAD | grep -E '( - )(\w|-|\d|_)+' | sed -E 's/ - /##vso[build.addbuildtag]/g'
displayName: 'Adding build tags'
当 运行 执行此操作时,测试、lint 和构建工作正常,但我不认为它添加了构建标签,这是日志:
好像什么都没发生...如何正确添加标签并触发发布管道?
我还找到了这个片段来压缩和发布工件,但我不知道我是否可以使用它,因为在 monorepo 中我们应该 - 我认为 - 创建多个工件。
5) 所以最后一个问题是:我怎样才能创建多个工件,这甚至是一件好事吗?
非常感谢您的帮助,我知道这很无聊 post 帮助菜鸟可能很无聊,但我坚持了很长时间...
1,您可以从 UI 页面(构建摘要页面,见下文)或使用 Rest api 为构建添加标签。它可用于在构建之间进行过滤。如果您的构建管道生成了多个工件,构建标签将无法过滤在一个构建中生成的工件。
2,因此您可以考虑如何为管道中的每个应用程序分离构建工件。
您可以使用 Archive files task to zip your build artifacts and publish using publish build artifacts task.
参见下面的 yaml 示例:我使用两个存档文件任务分别打包 app1 和 app2 的构建工件。并将压缩的工件保存在文件夹 $(Build.ArtifactStagingDirectory)
中。
然后发布构建工件任务将工件发布到 azure devops 云。 (发布管道将下载工件以部署到您的应用服务器)
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: $(system.defaultworkingdirectory)/app1/dist
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/app1/dist1.zip'
includeRootFolder: false
enabled: true
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: $(system.defaultworkingdirectory)/app2/dist
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/app2/dist2.zip'
includeRootFolder: false
enabled: true
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: $(Build.ArtifactStagingDirectory)/
artifactName: build
3、然后你可以在你的发布管道中使用多个阶段并使用Azure App Service Deploy task。对于以下示例:
在第一阶段添加 Azure App Service Deploy task
并将程序包设置为 $(System.DefaultWorkingDirectory)/**/app1/dist1.zip
以部署 app1。在第二阶段将其设置为 $(System.DefaultWorkingDirectory)/**/app2/dist2.zip 以部署 app2。
在 monorepo 中部署多个应用程序的另一种解决方法是创建多个 build/release 管道,每个应用程序一个。并在构建管道中使用 path filter 让构建管道仅在更新对应的应用程序时触发。
trigger:
paths:
include:
- root/app1/*
希望以上内容对您有所帮助!
@levi Lu-MSFT 的回答对我帮助很大,基本上我们就是这样做的,但他们并没有真正给出完整的 yaml 代码。
我们设置发布管道的方式是每个应用程序一个发布管道,查看发布触发器的构建标签。我们目前将所有应用程序捆绑在一个工件中,因此缺点是当应用程序的主分支被触发时,发布过程将下载所有应用程序。
我动态添加构建标签的方式是从这个 nx 命令开始的:
npx nx affected:apps --base=origin/main --plain
打印出已更改的应用程序列表。从那里,我遍历应用程序并动态创建构建标签。
这里我有两个工作,一个在 PR 上运行,一个在主分支上运行。
trigger:
branches:
include:
- main
pool:
name: My-Pool
demands: Agent.OS -equals Windows_NT
jobs:
- job: NX_AFFECTED_PR
pool:
name: WBOmega-Pool
demands: Agent.OS -equals Windows_NT
condition: eq(variables['Build.Reason'], 'PullRequest')
steps:
- task: Npm@1
displayName: 'npm install'
inputs:
command: 'install'
verbose: true
- powershell: |
npx nx affected:apps --base=origin/main --plain | Tee-Object -Variable output
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12
foreach ($appName in ($output -replace '\s+', ' ').Split()) {
Write-Host "this is the app-name: $appName"
if($appName.Trim() -eq ""){
Write-Host "App name is blank"
} else {
$url="https://dev.azure.com/YOUR_ORG/$(System.TeamProject)/_apis/build/builds/$(build.buildid)/tags/" + $appName + "?api-version=6.0"
Write-Host $url
$result = Invoke-RestMethod -Uri $url -Headers @{Authorization = "Bearer $(System.AccessToken)"} -Method Put
}
}
name: set_build_tags
- powershell: |
npx nx affected --target=build --base=origin/main --parallel --max-parallel=3
name: build
- powershell: |
npx nx affected --target=lint --base=origin/main --parallel --max-parallel=3
name: lint
- powershell: |
npx nx affected --target=test --base=origin/main --parallel --max-parallel=3
name: test
- job: NX_AFFECTED_MAIN
pool:
name: My-Pool
demands: Agent.OS -equals Windows_NT
condition: ne(variables['Build.Reason'], 'PullRequest')
steps:
- checkout: self
persistCredentials: true
- powershell: |
npx nx affected:apps --base=HEAD~1 --plain | Tee-Object -Variable output
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12
foreach ($appName in ($output -replace '\s+', ' ').Split()) {
Write-Host "this is the app-name: $appName"
if($appName.Trim() -eq ""){
Write-Host "App name is blank"
} else {
$url="https://dev.azure.com/YOUR_ORG/$(System.TeamProject)/_apis/build/builds/$(build.buildid)/tags/" + $appName + "?api-version=6.0"
Write-Host $url
$result = Invoke-RestMethod -Uri $url -Headers @{Authorization = "Bearer $(System.AccessToken)"} -Method Put
}
}
name: set_build_tags
- powershell: |
npx nx affected --target=build --base=HEAD~1 --parallel --max-parallel=3
name: build
- powershell: |
npx nx affected --target=lint --base=HEAD~1 --parallel --max-parallel=3
name: lint
- powershell: |
npx nx affected --target=test --base=HEAD~1 --parallel --max-parallel=3
name: test
- task: CopyFiles@2
displayName: 'Copy Files to: $(build.artifactstagingdirectory)\apps\'
inputs:
SourceFolder: '$(Build.SourcesDirectory)\dist\apps\'
TargetFolder: '$(build.artifactstagingdirectory)\apps\'
CleanTargetFolder: true
OverWrite: true
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact'
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)\apps'
ArtifactName: 'Apps'
我正在使用 NX 工具来管理具有多个应用程序的 monorepo,我正在努力了解如何使用 Azure 进行部署和发布管道。
免责声明:我对 Azure 和 devops 非常陌生。
我的理解是这样的: 我创建了一个管道(不是发布管道,如果有意义的话只是一个 "regular one")并向其插入一个 yml。此外,管道链接到 Azure Repos 上的一个存储库,这意味着每次我推送到这个存储库时,它都会触发管道和 运行 yaml 命令。 在这个命令中,我 运行 lint、测试和构建。
这是我能做的,能理解的,下面就比较晦涩了:
如果我 pusing/merging 在我可以调节的 master 上,那么构建作业应该会创建一个工件。 现在我可以创建一个发布管道,当它链接到 repo 时将触发该管道将创建一个工件。然后,此发布管道可以将此工件发送到应用程序服务,该应用程序服务是应用程序所在的插槽。
好的,但我使用的是 monorepo,这意味着构建将生成多个应用程序,并且这些应用程序中的每一个都应该部署到正确的应用程序服务。
经过一些研究,I found that 总体思路是为每个应用程序创建一个发布管道。这些发布管道都链接到同一个 monorepo,但它们有一个过滤器,它是一个构建标签。使用 yml 文件构建应用程序时添加构建标签。
所以这些基本上就是我对这一切的理解。下面是问题:
- 构建标签到底是什么?它位于何处?它是否以某种方式与神器相关联?
- 我们的想法是为每个工件创建一个构建标签,对吗?
- 我未能创建构建标签,我该怎么做?
压缩和发布工件的正确方法是什么?
这是我正在使用的 yaml:
jobs:
- job: Lint
steps:
- task: NodeTool@0
inputs:
versionSpec: '12.x'
displayName: 'Install Node.js'
- task: Npm@1
displayName: 'Npm install'
- pwsh: 'npm run nx affected -- --target=lint --parallel --base=origin/master --maxParallel=4'
displayName: 'Running lint'
- job: Test
steps:
- task: NodeTool@0
inputs:
versionSpec: '12.x'
displayName: 'Install Node.js'
- task: Npm@1
displayName: 'npm install'
- pwsh: 'npm run nx affected -- --target=test --parallel --code-coverage --base=origin/master --maxParallel=4'
displayName: 'Running tests'
- job: Build
steps:
- task: NodeTool@0
inputs:
versionSpec: '12.x'
displayName: 'Install Node.js'
- task: Npm@1
displayName: 'npm install'
- pwsh: 'npm run nx affected -- --target=build --parallel --base=origin/master --prod'
displayName: 'Running build'
- pwsh: |
npm run nx affected:apps -- --base=HEAD~1 --head=HEAD | grep -E '( - )(\w|-|\d|_)+' | sed -E 's/ - /##vso[build.addbuildtag]/g'
displayName: 'Adding build tags'
当 运行 执行此操作时,测试、lint 和构建工作正常,但我不认为它添加了构建标签,这是日志:
好像什么都没发生...如何正确添加标签并触发发布管道?
我还找到了这个片段来压缩和发布工件,但我不知道我是否可以使用它,因为在 monorepo 中我们应该 - 我认为 - 创建多个工件。
5) 所以最后一个问题是:我怎样才能创建多个工件,这甚至是一件好事吗?
非常感谢您的帮助,我知道这很无聊 post 帮助菜鸟可能很无聊,但我坚持了很长时间...
1,您可以从 UI 页面(构建摘要页面,见下文)或使用 Rest api 为构建添加标签。它可用于在构建之间进行过滤。如果您的构建管道生成了多个工件,构建标签将无法过滤在一个构建中生成的工件。
2,因此您可以考虑如何为管道中的每个应用程序分离构建工件。
您可以使用 Archive files task to zip your build artifacts and publish using publish build artifacts task.
参见下面的 yaml 示例:我使用两个存档文件任务分别打包 app1 和 app2 的构建工件。并将压缩的工件保存在文件夹 $(Build.ArtifactStagingDirectory)
中。
然后发布构建工件任务将工件发布到 azure devops 云。 (发布管道将下载工件以部署到您的应用服务器)
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: $(system.defaultworkingdirectory)/app1/dist
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/app1/dist1.zip'
includeRootFolder: false
enabled: true
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: $(system.defaultworkingdirectory)/app2/dist
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/app2/dist2.zip'
includeRootFolder: false
enabled: true
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: $(Build.ArtifactStagingDirectory)/
artifactName: build
3、然后你可以在你的发布管道中使用多个阶段并使用Azure App Service Deploy task。对于以下示例:
在第一阶段添加 Azure App Service Deploy task
并将程序包设置为 $(System.DefaultWorkingDirectory)/**/app1/dist1.zip
以部署 app1。在第二阶段将其设置为 $(System.DefaultWorkingDirectory)/**/app2/dist2.zip 以部署 app2。
在 monorepo 中部署多个应用程序的另一种解决方法是创建多个 build/release 管道,每个应用程序一个。并在构建管道中使用 path filter 让构建管道仅在更新对应的应用程序时触发。
trigger:
paths:
include:
- root/app1/*
希望以上内容对您有所帮助!
@levi Lu-MSFT 的回答对我帮助很大,基本上我们就是这样做的,但他们并没有真正给出完整的 yaml 代码。
我们设置发布管道的方式是每个应用程序一个发布管道,查看发布触发器的构建标签。我们目前将所有应用程序捆绑在一个工件中,因此缺点是当应用程序的主分支被触发时,发布过程将下载所有应用程序。
我动态添加构建标签的方式是从这个 nx 命令开始的:
npx nx affected:apps --base=origin/main --plain
打印出已更改的应用程序列表。从那里,我遍历应用程序并动态创建构建标签。
这里我有两个工作,一个在 PR 上运行,一个在主分支上运行。
trigger:
branches:
include:
- main
pool:
name: My-Pool
demands: Agent.OS -equals Windows_NT
jobs:
- job: NX_AFFECTED_PR
pool:
name: WBOmega-Pool
demands: Agent.OS -equals Windows_NT
condition: eq(variables['Build.Reason'], 'PullRequest')
steps:
- task: Npm@1
displayName: 'npm install'
inputs:
command: 'install'
verbose: true
- powershell: |
npx nx affected:apps --base=origin/main --plain | Tee-Object -Variable output
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12
foreach ($appName in ($output -replace '\s+', ' ').Split()) {
Write-Host "this is the app-name: $appName"
if($appName.Trim() -eq ""){
Write-Host "App name is blank"
} else {
$url="https://dev.azure.com/YOUR_ORG/$(System.TeamProject)/_apis/build/builds/$(build.buildid)/tags/" + $appName + "?api-version=6.0"
Write-Host $url
$result = Invoke-RestMethod -Uri $url -Headers @{Authorization = "Bearer $(System.AccessToken)"} -Method Put
}
}
name: set_build_tags
- powershell: |
npx nx affected --target=build --base=origin/main --parallel --max-parallel=3
name: build
- powershell: |
npx nx affected --target=lint --base=origin/main --parallel --max-parallel=3
name: lint
- powershell: |
npx nx affected --target=test --base=origin/main --parallel --max-parallel=3
name: test
- job: NX_AFFECTED_MAIN
pool:
name: My-Pool
demands: Agent.OS -equals Windows_NT
condition: ne(variables['Build.Reason'], 'PullRequest')
steps:
- checkout: self
persistCredentials: true
- powershell: |
npx nx affected:apps --base=HEAD~1 --plain | Tee-Object -Variable output
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12
foreach ($appName in ($output -replace '\s+', ' ').Split()) {
Write-Host "this is the app-name: $appName"
if($appName.Trim() -eq ""){
Write-Host "App name is blank"
} else {
$url="https://dev.azure.com/YOUR_ORG/$(System.TeamProject)/_apis/build/builds/$(build.buildid)/tags/" + $appName + "?api-version=6.0"
Write-Host $url
$result = Invoke-RestMethod -Uri $url -Headers @{Authorization = "Bearer $(System.AccessToken)"} -Method Put
}
}
name: set_build_tags
- powershell: |
npx nx affected --target=build --base=HEAD~1 --parallel --max-parallel=3
name: build
- powershell: |
npx nx affected --target=lint --base=HEAD~1 --parallel --max-parallel=3
name: lint
- powershell: |
npx nx affected --target=test --base=HEAD~1 --parallel --max-parallel=3
name: test
- task: CopyFiles@2
displayName: 'Copy Files to: $(build.artifactstagingdirectory)\apps\'
inputs:
SourceFolder: '$(Build.SourcesDirectory)\dist\apps\'
TargetFolder: '$(build.artifactstagingdirectory)\apps\'
CleanTargetFolder: true
OverWrite: true
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact'
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)\apps'
ArtifactName: 'Apps'