しばやん雑記

ASP.NET Core とメイドさんが大好きなフリーランスのプログラマーのブログ

Azure Pipelines での Multi-stage pipelines の利用と既存パイプラインの移行

Azure Pipelines のドキュメントを読んでいると Release Pipeline に Classic と付けられていたので、先行きが少し怪しいです。Microsoft 的には YAML + Multi-stage pipelines を推奨して行くということのようです。

Build 2019 で発表されていたらしいですが、余り真面目に読んでませんでした。

まだ一部の機能は対応していないようですが、継続的に対応を進めていくらしいです。

これまでの GUI で設定してきた Build / Release Pipeline の構成は、Build stage と Release stage として扱えば、そのまま Multi-stage pipelines として移行出来ます。

簡単かと思っていましたが、情報が少ないのもあって地味に Multi-stage pipelines への移行で苦労してしまったので、引っかかった部分と実際に移行した例を紹介します。

Multi-stage pipelines を使い始める

これまでは step だけ書くことが多かったかも知れませんが、Multi-stage pipelines では stage > job > step の順で書いていきます。複数の job をまとめる単位として stage が導入されたという形です。

まだドキュメントがかなりしょぼいので、どう書くのがシンプルか試しながらという感じです。

試している限りでは依存関係周りが少し不可解な挙動をしていました。ちなみに dependsOn を使って各 stage の依存関係を定義できますが、何も定義しなければ書いた順に処理されます。

GitHub にサンプルの YAML がいくつか置いてありますが、まあサンプルだなという感じです。

ドキュメントにある YAML schema が個人的には一番参考になりました。

Multi-stage pipelines はプレビューなので、使う場合は Preview features から有効化する必要があります。

f:id:shiba-yan:20190718151019p:plain

有効化するとサイドバーから Build が消えて Pipeline に置き換わります。以下のように UI もガラッと新しくなりますが、新しい UI の方がビルドの状態が見やすくなったと思います。

f:id:shiba-yan:20190719010015p:plain

これで準備は出来たので、YAML で各 stage を書いていくという流れになります。

Multi-stage pipelines を使う基本的な YAML

YAML Editor は Multi-stage pipelines 向けの便利機能はパッと見なさそうなので、頑張って書いていくことになります。何もなしで書くのはしんどいので、テンプレ的な YAML を用意しました。

trigger:
- master

stages:
- stage: Build
  jobs:
  - job: Build
    steps:
    - script: echo 'Build'

- stage: Release
  dependsOn:
  - Build
  jobs:
  - job: Release
    steps:
    - script: echo 'Release'

dependsOn を使って Release stage は Build stage に依存していることを定義します。

これまで Release Pipeline は master や特定のブランチ、タグの時だけ動くようにするケースも多かったと思います。その場合は Release stage の condition に条件を書いて対応します。

trigger:
- master

stages:
- stage: Build
  jobs:
  - job: Build
    steps:
    - script: echo 'Build'

- stage: Release
  dependsOn:
  - Build
  condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
  jobs:
  - job: Release
    steps:
    - script: echo 'Release'

これで Build stage が成功かつ master ブランチの時のみ Release stage が実行されるようになります。忘れずに succeeded() を条件に加えないと、Build stage が失敗しても動くようになってしまいます。

ビルドに使う VM Image を設定

デフォルトでは Ubuntu 16.04 が使われますが、それ以外を使う場合は job 単位で vmImage を設定する必要があります。これが地味に面倒なので、変数にするのが良いでしょう。

trigger:
- master

variables:
  imageName: windows-2019

stages:
- stage: Build
  jobs:
  - job: Build
    pool:
      vmImage: $(imageName)
    steps:
    - script: echo 'Build'

- stage: Release
  dependsOn:
  - Build
  jobs:
  - job: Release
    pool:
      vmImage: $(imageName)
    steps:
    - script: echo 'Release'

これで Visual Studio 2019 + Windows Server 2019 な VM 上で実行されるようになります。デフォルトの VM Image を指定することが YAML からは出来ないのが少し残念です。*1

Pipeline Artifacts への移行

地味に分かりにくかったのが Build Artifacts から Pipeline Artifacts への移行です。あまり違いが無さそうに感じますが、ドキュメント曰く出力先のストレージが速くなっているらしいので移行が推奨されています。

We recommend upgrading from build artifacts to pipeline artifacts (preview) for faster output storage speeds.

Publish and consume build artifacts in builds - Azure Pipelines and TFS | Microsoft Docs

Pipeline Artifacts はドキュメントが比較的整備されているので参考になります。

これまではビルドパイプラインで PublishBuildArtifacts タスクを使って Artifacts を保存していましたが、これからは publishdownload を使って書いていくことになります。

publish を使って保存された Artifacts はビルドログから簡単に参照できます。

f:id:shiba-yan:20190719015506p:plain

見た感じ保存時にいい感じに圧縮してくれるようなので、単純にディレクトリを指定すれば良さそうです。

VuePress でビルドしたサイトを Pipeline Artifacts に保存する時には以下のように定義を書きました。publish で指定するパスは $(System.DefaultWorkingDirectory) からの相対パスになります。

steps
- publish: dist
  artifact: site

Artifact 名は適当に分かりやすい名前を付けておけば良いです。後続のダウンロード時のディレクトリ名になるので、変な名前にすると少し面倒になります。

保存した Artifacts を Release stage から読みだす場合は download を使います。結構オプションが多いですが、基本は以下のような形で使えば問題ないでしょう。

steps
- download: current
  artifact: site

上のように current を指定すると、現在実行している Pipeline Artifacts からファイルをダウンロードしてきます。別のパイプラインから取ってくることも出来るみたいですが、使う機会が浮かびませんでした。

ダウンロードされるパスがデフォルトだと $(Pipeline.Workspace) 以下になるので注意が必要です。

Deployment job と Environments を使ってデプロイ

ここまで Release Pipeline に相当する stage を単純な job として書いてきましたが、Multi-stage pipelines では新しく Deployment jobs というものが導入されたので、これを使うとデプロイに特化した Job とデプロイ履歴といった機能が提供されます。

具体的には Deployment jobs ではソースのチェックアウトが行われず、自動で Pipeline Artifacts のダウンロードが行われるようになります。ドキュメントも一応用意されています。

同時に追加された Environments と組み合わせることで、デプロイ先リソースを事前に定義しつつ柔軟なデプロイが実現可能になるはずですが、現在は Kubernetes のみ対応なので積極的に使いたい理由が残念ながらあまり見当たりません。今後の対応リソース拡充に期待しています。

Deployment jobs を使うための YAML は以下のようになります。ちょっと深いです。

trigger:
- master

stages:
- stage: Build
  jobs:
  - job: Build
    steps:
    - script: echo 'Build'

- stage: Release
  dependsOn:
  - Build
  jobs:
  - deployment: Release
    environment: production
    strategy:
      runOnce:
        deploy:
          steps:
          - script: echo 'Release'

今後 strategy で指定できる値は runOnce の他に rolling が増える予定らしいです。

App Service へのデプロイを行う場合は、空の Environment を作成して対応します。空の Environment でもデプロイの履歴は残るので Pipeline の履歴から辿るよりは便利になります。

f:id:shiba-yan:20190719022719p:plain

それぞれの Deployment を選択すると、そのデプロイに含まれているコミットを見ることも出来るので、障害発生時にどのコミットが原因になったのかという調査が楽になりそうです。

f:id:shiba-yan:20190719022731p:plain

特に難しくないので Deployment job を使っておいても良いと思いますが、今後 App Service などに対応してきた場合に移行で苦労しそうな気もするので、今やる必要があるのかといわれると悩みます。

App Service に Environments が対応したタイミングで移行しようかと考えています。

実際に Multi-stage pipelines へ移行した例

手持ちのプロジェクトをいくつか Multi-stage pipelines へ移行したので紹介します。まだ試行錯誤しているのでこれが正解という感じはしないですが、YAML の参考にはなると思います。

App Service へのデプロイ

ASP.NET Core アプリケーションと VuePress で作成したサイトを App Service へ Run From Package を使ってデプロイする YAML 定義です。

master にコミットされた時だけデプロイするように条件を組んでいます。

f:id:shiba-yan:20190718171910p:plain

地味に条件が複雑に見えてしまうので、シンプルにビルド&テスト用とデプロイ用で Pipeline を分けた方がいい気がしてきました。YAML も別々で管理するので、分かりやすくなりそうです。

静的サイトの Azure Storage へのデプロイ

VuePress で作成したサイトを Azure Storage の静的サイトホスティングを使って公開する定義です。

基本は App Service へのデプロイと同じですが、このサンプルでは Deployment jobs を使っています。

f:id:shiba-yan:20190718151320p:plain

ビルドの依存関係も単純に Build => Deploy という形になっています。案外 Job 名を決めるのが面倒です。

NuGet パッケージのビルドと発行

v から始めるタグが打たれた時に nupkg を作成し、そのまま NuGet に公開する定義です。

これもまた App Service へのデプロイの時と同じような流れです。タグが打たれた時という条件を付けるために、少しややこしくはなっています。

f:id:shiba-yan:20190718161108p:plain

やっぱり Pipeline を分割した方が分かりやすいし、管理も楽になりそうな気がしてきました。

*1:Trigger 設定から辿ればデフォルトの VM Image の変更はできる