今年の5月に、Azure DevOps の Build と Release のパイプラインに代わって、Multi-Stage Pipelines で構築できるようになる発表がありました。
devblogs.microsoft.com
現時点では、Multi-Stage Pipelines はプレビュー機能ですが、従来の Build と Release のパイプラインは Classic 扱いとなっているので、そろそろ触っておかねばと思い、試してみました。
前準備
Azure DevOps の右上のユーザーアイコンから、Preview features で Multi-Stage Pipelines を有効にします。
あとは、デプロイ先の Azure Web Apps のサブスクリプション情報を service connection として登録しておきます。
パイプラインの全体設計
今回は、リポジトリのブランチとデプロイ先の関係が分かりやすい GitLab flow を想定しています。
パイプラインは、自動デプロイされる開発環境向け(Commit Stage)と承認デプロイされる本番環境向け(Production Stage)を作ります。
YAML を Build と Release を分けて、可変部をパラメータで切り替えできるテンプレートを作ることで再利用性を向上させます。
開発環境向けパイプライン
まずは、パイプライン起動用の azure-pipelines.yml です。
trigger:
- master
variables:
imageName: 'windows-2019'
buildConfiguration: 'Release'
projects: '**/WebApplication.csproj'
testProjects: '**/WebApplication.Tests.csproj'
azureSubscription: 'AzureSponsorships'
webAppsName: '<Web Apps Name>'
webAppsType: 'webApp'
environment: 'Commit-Stage'
stages:
- stage: Build
jobs:
- template: pipelines/build-pipelines.yml
parameters:
imageName: $(imageName)
buildConfiguration: $(buildConfiguration)
projects: $(projects)
testProjects: $(testProjects)
- stage: Release
dependsOn:
- Build
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
jobs:
- template: pipelines/release-pipelines.yml
parameters:
imageName: $(imageName)
azureSubscription: $(azureSubscription)
webAppsName: $(webAppsName)
webAppsType: $(webAppsType)
environment: $(environment)
ポイントは、template
で Build と Release のYAMLを指定している箇所です。condition
で、Build ステージが成功した場合のみ、Release ステージを実行する条件指定も忘れずに設定します。
リリースの JOB は、Environments を使います。
開発環境向けパイプラインでは、自動デプロイしたいので、空の Environments を Commit-Stage
で登録しておきます。
次に、ビルド用の build-pipelines.yml です。
parameters:
imageName: ''
buildConfiguration: ''
projects: ''
testProjects: ''
jobs:
- job: Build
pool:
vmImage: ${{parameters.imageName}}
steps:
- task: DotNetCoreCLI@2
displayName: 'Build'
inputs:
command: 'build'
projects: ${{parameters.projects}}
arguments: '--configuration ${{parameters.buildConfiguration}}'
- task: DotNetCoreCLI@2
displayName: 'Test'
inputs:
command: 'test'
projects: ${{parameters.testProjects}}
arguments: '--configuration ${{parameters.buildConfiguration}}'
- task: DotNetCoreCLI@2
displayName: 'Publish'
inputs:
command: 'publish'
publishWebProjects: true
arguments: '--configuration ${{parameters.buildConfiguration}} --output $(System.DefaultWorkingDirectory)/publish'
zipAfterPublish: true
- publish: publish
displayName: 'Publish artifact'
artifact: webapp
.NET Core CLI task の DotNetCoreCLI@2
を使って、ビルド→テスト→発行を行っています。
最後に、リリース用の release-pipelines.yml です。
parameters:
imageName: ''
azureSubscription: ''
webAppsName: ''
webAppsType: ''
webAppsSlotName: 'production'
environment: ''
logLevel: 'Information'
jobs:
- deployment: Deploy_WebApps
displayName: 'Release'
pool:
vmImage: ${{parameters.imageName}}
environment: ${{parameters.environment}}
strategy:
runOnce:
deploy:
steps:
- task: AzureWebApp@1
displayName: 'Deploy to Azure Web Apps'
inputs:
azureSubscription: ${{parameters.azureSubscription}}
appType: ${{parameters.webAppsType}}
appName: ${{parameters.webAppsName}}
slotName: ${{parameters.webAppsSlotName}}
package: '$(Pipeline.Workspace)/**/*.zip'
deploymentMethod: runFromPackage
appSettings: -Logging:LogLevel:Default ${{parameters.logLevel}}
Azure Web App task の AzureWebApp@1
を使って、デプロイを行っています。
Azure Web Apps の Deployment Slot 機能にも対応したいので、slotName
を指定しますが、必須にはしたくないので、初期値で production
を設定しています。
appSettings
では、アプリケーション設定のログレベルを設定しています。Azure CLI の az webapp config コマンドでも設定できますが、リソースグループ名が必要になってしまうので、ここで指定しました。
本番環境向けパイプライン
パイプライン起動用の azure-pipelines-production.yml です。
trigger:
- production
variables:
imageName: 'windows-2019'
buildConfiguration: 'Release'
projects: '**/WebApplication.csproj'
testProjects: '**/WebApplication.Tests.csproj'
azureSubscription: 'AzureSponsorships'
webAppsName: '<Web Apps Name>'
webAppsType: 'webApp'
environment: 'Production-Stage'
webAppsSlotName: 'staging'
stages:
- stage: Build
jobs:
- template: pipelines/build-pipelines.yml
parameters:
imageName: $(imageName)
buildConfiguration: $(buildConfiguration)
projects: $(projects)
testProjects: $(testProjects)
- stage: Release
dependsOn:
- Build
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/production'))
jobs:
- template: pipelines/release-pipelines.yml
parameters:
imageName: $(imageName)
azureSubscription: $(azureSubscription)
webAppsName: $(webAppsName)
webAppsType: $(webAppsType)
environment: $(environment)
webAppsSlotName: $(webAppsSlotName)
トリガーとなるブランチが production
となっている箇所が開発環境向けパイプラインとの違いです。
本番環境では、Azure Web Apps の Deployment Slot 機能を利用したいので、staging
を設定しています。また、承認デプロイもしたいので、承認設定した Environments を Production-Stage
で登録しておきます。
結果確認
リポジトリの master ブランチへの更新がトリガーとなり、ビルドとリリースのパイプラインが実行され、Azure Web Apps へのデプロイまでが自動で実行されます。
master ブランチから production ブランチへプルリクエストのマージがトリガーとなり、ビルドとリリースのパイプラインが実行されます。Azure Web Apps へのデプロイは承認が必須となっているので、Waiting になっていることが分かります。
この後、リリースを承認すると、Deployment Slot の staging へデプロイされます。最終動作確認後、Azure Portal などからの Swap することを想定しています。万が一、リリースによる障害が発生した場合には、旧バージョンに切り戻すこともできます。
まとめ
Azure DevOps の Multi-Stage Pipelines を使って、Azure Web Apps 向けのパイプラインを構築してみました。
可変部をパラメータで切り替えできるようにテンプレート化しているので、これからの CI/CD が捗りそうです。
こちらから、パイプライン YAML の全体を確認できます。
github.com
追記
今回のユースケースですと、YAML をソースコードのリポジトリで管理する想定なので、環境毎に異なる情報やパスワードなどのシークレットな情報をリポジトリに含めたくありません。
Azure DevOps の Variable groups を使って、パイプラインを書き換えてみましたので、こちらを参照してください。
gooner.hateblo.jp