ROMANCE DAWN for the new world

Microsoft Azure を中心とした技術情報を書いています。

Azure DevOps の Multi-Stage Pipelines で Variable groups を使ってパイプラインを構築する

先日の記事で、Azure DevOps の Multi-Stage Pipelines を使って Azure Web Apps にデプロイする内容を記載しました。
gooner.hateblo.jp
YAML をソースコードのリポジトリで管理する想定なので、環境毎に異なる情報やパスワードなどのシークレットな情報をリポジトリに含めたくありません。そこで、Azure DevOps の Variable groups を使って、パイプラインを書き換えてみました。

パイプラインの全体設計

先日の記事と同様に、パイプラインは、自動デプロイされる開発環境向け(Commit Stage)と承認デプロイされる本番環境向け(Production Stage)を作りました。
YAML を Build と Release に分けて、可変部をパラメータで切り替えできるテンプレートを作ることで再利用性を向上させます。

f:id:TonyTonyKun:20191220105519p:plain

Variable groups とは

Variable groups は、Azure DevOps 内に定義した Key-Value のデータをグループ化し、YAML パイプラインに渡すことができる機能です。
docs.microsoft.com
Azure DevOps の Library メニューから Variable groups を登録します。このユースケースでは、ブランチ毎にデプロイ先の Azure Web Apps が異なるので、Commit Stage と Production Stage でグループを分けて、webAppsName を登録します。
variable-group-commitvariable-group-production の2つの Variable groups を登録しました。

f:id:TonyTonyKun:20200127140616p:plain

今回は利用しませんが、パスワードなどのシークレットな情報は、Value の右横の鍵マークをクリックすると、値をマスクすることができ、登録した人以外には非公開にすることも可能です。また、Azure Key Vault で値を管理することもできます。

パイプライン

変更する YAML は、パイプライン起動用の azure-pipelines.yml です。

trigger:
- master

variables:
- group: variable-group-commit
- name: imageName
  value: 'windows-2019'
- name: buildConfiguration
  value: 'Release'
- name: projects
  value: '**/WebApplication.csproj'
- name: testProjects
  value: '**/WebApplication.Tests.csproj'
- name: azureSubscription
  value: 'AzureSponsorships'
- name: webAppsType
  value: 'webApp'
- name: environment
  value: '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)

variables の webAppsName を削除する代わりに、group: variable-group-commit を追加することで、Variable groups から値を受け取ることができます。
Variable groups と YAML に定義する variables の両方を使いたいので、既存の variables を name-value 形式で書き換えています。
同様の手順で、本番環境向けパイプラインの azure-pipelines-production.yml も変更すれば、完了です。

まとめ

YAML をソースコードのリポジトリで管理する場合、環境毎に異なる情報やパスワードなどのシークレットな情報をリポジトリに含めたくありません。
Azure DevOps の Variable groups を使うことで、YAML パイプラインに値を渡すことができるので、実プロジェクトに適用する際にはうまく活用したい機能です。
こちらから、パイプライン YAML の全体を確認できます。
github.com

Azure DevOps の Multi-Stage Pipelines で Azure Web Apps のパイプラインを構築する

今年の5月に、Azure DevOps の Build と Release のパイプラインに代わって、Multi-Stage Pipelines で構築できるようになる発表がありました。
devblogs.microsoft.com
現時点では、Multi-Stage Pipelines はプレビュー機能ですが、従来の Build と Release のパイプラインは Classic 扱いとなっているので、そろそろ触っておかねばと思い、試してみました。

前準備

Azure DevOps の右上のユーザーアイコンから、Preview features で Multi-Stage Pipelines を有効にします。

f:id:TonyTonyKun:20191219143946p:plain

あとは、デプロイ先の Azure Web Apps のサブスクリプション情報を service connection として登録しておきます。

パイプラインの全体設計

今回は、リポジトリのブランチとデプロイ先の関係が分かりやすい GitLab flow を想定しています。
パイプラインは、自動デプロイされる開発環境向け(Commit Stage)と承認デプロイされる本番環境向け(Production Stage)を作ります。

f:id:TonyTonyKun:20191219144724p:plain

YAML を Build と Release を分けて、可変部をパラメータで切り替えできるテンプレートを作ることで再利用性を向上させます。

f:id:TonyTonyKun:20191220105519p:plain

開発環境向けパイプライン

まずは、パイプライン起動用の 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 で登録しておきます。

f:id:TonyTonyKun:20191219144756p:plain

次に、ビルド用の 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 taskDotNetCoreCLI@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 taskAzureWebApp@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 で登録しておきます。

f:id:TonyTonyKun:20191219144048p:plain

結果確認

リポジトリの master ブランチへの更新がトリガーとなり、ビルドとリリースのパイプラインが実行され、Azure Web Apps へのデプロイまでが自動で実行されます。

f:id:TonyTonyKun:20191219145206p:plain

master ブランチから production ブランチへプルリクエストのマージがトリガーとなり、ビルドとリリースのパイプラインが実行されます。Azure Web Apps へのデプロイは承認が必須となっているので、Waiting になっていることが分かります。

f:id:TonyTonyKun:20191219144408p:plain

この後、リリースを承認すると、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

書籍「プログラミング ASP.NET Core」の感想

Dino Esposite が書いた「Programming ASP.NET Core」の日本語翻訳版が出版されると知って、早速「プログラミング ASP.NET Core」を読んでみました。

プログラミングASP.NET Core (マイクロソフト関連書)

プログラミングASP.NET Core (マイクロソフト関連書)

  • 作者: Dino Esposito,日本マイクロソフト井上章監訳,株式会社クイープ
  • 出版社/メーカー: 日経BP社
  • 発売日: 2019/05/23
  • メディア: 単行本
  • この商品を含むブログを見る

目次

第1部 新しい ASP.NET の概要
 第1章 新しい ASP.NET の存在意義
 第2章 初めての ASP.NET Core プロジェクト
第2部 ASP.NET MVC のアプリケーションモデル
 第3章 ASP.NET MVC の起動
 第4章 ASP.NET MVC のコントローラー
 第5章 ASP.NET MVC のビュー
 第6章 Razor の構文
第3部 横断的関心事
 第7章 設計について考える
 第8章 アプリケーションのセキュリティ
 第9章 アプリケーションデータへのアクセス
第4部 フロントエンド
 第10章 Web API の設計
 第11章 クライアント側からのデータ送信
 第12章 クライアント側のデータバインディング
 第13章 デバイスフレンドリなビューの構築
第5部 ASP.NET Core のエコシステム
 第14章 ASP.NET Core のランタイム環境
 第15章 ASP.NET Core アプリケーションのデプロイメント
 第16章 移行戦略と導入戦略

書籍で解説されているサンプルコードは、GitHub でも公開されています。
github.com

どんな書籍なのか

この書籍は、Web 開発の最低限の知識を持っていれば、ASP.NET の初心者にも理解できる内容ですが、やはり従来の ASP.NET 開発者に読んでほしい書籍です。

第1部では、これまでの .NET の歴史を振り返りながら、ASP.NET Core が登場した背景を明らかにし、Hello World な ASP.NET Core アプリケーションを動かすまでのイントロダクションとなっています。

第2部では、ASP.NET Core の MVC アプリケーションモデルについて、順を追ってアーキテクチャを解説しており、従来の ASP.NET との違いを押さえつつ、構造や仕組みを理解することができます。

第3部では、ASP.NET Core では欠かすことができない DI の役割を紹介し、従来の3層アーキテクチャではなく、プレゼンテーション、アプリケーション、ドメイン、インフラストラクチャの4層の Layered Architecture パターンをベースに横断的関心事を解説しています。セキュリティとして ASP.NET Identity による認証認可、データアクセスとして Entity Framework Core を解説していますが、モノリシックなアプリケーションを例に挙げたコマンドとクエリの分割にも触れ、Layered Architecture パターンと CQRS パターンの組み合わせまで解説している部分がよかったです。

第4部では、従来の ASP.NET では MVC とは別実装となっていた Web API について、ASP.NET Core での Web API をしっかりと解説しています。合わせて、クライアント側からのデータ送信やデータバインディングも解説しています。

第5部では、ASP.NET Core のランタイム環境やデプロイメント、最後に移行戦略を述べています。従来の ASP.NET アプリケーションの移行は、それなりのコストがかかるので、単純に最新版へのマイグレーションという目的だけでは不十分であり、クロスプラットフォーム、マイクロサービス、コンテナーといったクラウドネイティブなアプリケーションへの移行にビジネス上の価値があると見極めることが重要とする意見に同意です。

まとめ

先日の Microsoft Build 2019 では、.NET Core 3.0 の次バージョンが .NET 5 となるロードマップが発表されました。.NET 5 のネーミングはアレですが、ひとつの .NET として統一されることになります。
従来の ASP.NET 開発者に読んでもらい、クラウドネイティブなアプリケーションという未来に向かってチャレンジしてほしいと感じました。
devblogs.microsoft.com